12 votes

Le lancer de rayons - comment combiner la couleur diffuse et la couleur spéculaire?

J'ai lu de nombreux articles sur le ray tracing et le shading, mais mon image ray traced ne semble pas très bonne. Je parle de la zone verte très lumineuse près du reflet spéculaire. La couleur verte résultante est maximisée ici, on dirait. Comment ajuster les couleurs et/ou les calculs de shading pour que cela paraisse correct?

(Peu importe le code ridicule, j'essaie juste de comprendre les principes correctement d'abord).

Voici à quoi ça ressemble:

saisir la description de l'image ici

Voici la composante diffuse seulement:

saisir la description de l'image ici

Voici la composante spéculaire seulement:

saisir la description de l'image ici

ÉDIT: Changer la diffuse à Color diffuseColor = ColorMake(0.0f, 0.6f, 0.0f); Ensuite, l'image ressemble à ça:

saisir la description de l'image ici

Point lightPosition = PointMake(-100.0f, 100.0f, -100.0f);
Color diffuseColor  = ColorMake(0.0f, 1.0f, 0.0f);
Color specularColor = ColorMake(1.0f, 1.0f, 1.0f);
Color pixelColor    = ColorMake(0.0f, 0.0f, 0.0f);

// Tracer...

            // Diffuse
            Point intersectionPosition = PointMake(x, y, z);
            Vector intersectionNormal = VectorMake((x - xs) / rs, (y - ys) / rs, (z - zs) / rs);
            Vector intersectionNormalN = VectorNormalize(intersectionNormal);
            Vector lightVector          = VectorSubtract(lightPosition, intersectionPosition);
            Vector lightVectorN         = VectorNormalize(lightVector);
            float cosTheta              = VectorDotProduct(intersectionNormalN, lightVectorN);
            if (cosTheta < 0.0f)
            {
                cosTheta = 0.0f;
            }

            pixelColor = ColorMultScalar(diffuseColor, cosTheta);

            // Spéculaire
            Vector incomVector    = VectorSubtract(intersectionPosition, lightPosition);
            Vector incomVectorN   = VectorNormalize(incomVector);

            float myDot = - VectorDotProduct(incomVectorN, intersectionNormalN);
            float myLen = 2.0f * myDot;

            Vector tempNormal     = VectorMultScalar(intersectionNormalN, myLen);
            Vector reflectVector  = VectorAdd(tempNormal, incomVectorN);
            Vector reflectVectorN = VectorNormalize(reflectVector);

            float mySpec = MAX(-VectorDotProduct(reflectVectorN, incomVectorN), 0);
            mySpec       = powf(mySpec, 5);

            specularColor = ColorMultScalar(specularColor, mySpec);
            pixelColor    = ColorAdd(pixelColor, specularColor);
            pixelColor    = ColorClamp(pixelColor);

            [self putPixelatX:i andY:j andR:pixelColor.r andG:pixelColor.g andB:pixelColor.b];

8voto

Marcus Fritzsch Points 3676

Le problème est que lorsque vous calculez la couleur diffuse de la sphère, vous avez déjà une petite zone de pixels qui sont à 1 ou très proches de 1 (dans le canal vert). En ajoutant le composant "phong" (qui a une valeur proche de un dans tous les canaux) à cela, cela donne une zone de pixels qui sont >= 1. Lorsque vous limitez ensuite la valeur de couleur à 1, cette zone >=1 ressort.

Vous pouvez tester cela en utilisant un programme d'édition d'images et en superposant les deux couches en mode "addition" (la couche de phong au-dessus de la couche diffuse). Cela donne le résultat que vous voyez - et qui est à prévoir.

Vous pouvez éviter le problème en prenant un certain nombre de mesures :

  1. Vous pouvez assombrir un peu la source de lumière, c'est-à-dire multiplier la force diffuse que vous avez calculée à partir du cosinus par la luminosité - disons 0,8 ou 0,7.
  2. Vous pourriez limiter la saturation des couleurs (vertes) de la sphère et la rendre moins verte ;)
  3. Utilisez un opérateur de mappage des tons pour normaliser les valeurs de couleur des pixels dans une plage [0..1] - toutefois, ce sujet est vaste - Wikipedia pourrait donner une bonne introduction. Vous n'êtes même pas obligé d'aller jusqu'au bout avec ceux-ci, car pour le rendu non basé sur des principes physiques, des opérateurs de mappage des tons plus simples peuvent suffire et produire des résultats agréables à l'œil.

Mes expériences de ray tracing remontent à quelques années, mais vous pouvez essayer ces choses.


Mise à jour 1 :

Une chose que j'ai remarquée, lorsque vous corrigez le gamma de votre image de sortie - l'effet est moins prononcé ;) - Bon, c'est un peu bricolé.

La solution ultime est d'adopter une approche physiquement correcte ou simplement d'utiliser un autre modèle d'ombrage : Wikipedia sur le point spéculaire.


Mise à jour 2 :

Une solution réelle consisterait à calculer la contribution de phong à la couleur finale du pixel (c'est votre variable mySpec). L'idée est d'utiliser seulement une partie du composant diffus là où le spéculaire n'est en fait pas à 0, c'est-à-dire, si vous avez un certain composant spéculaire, vous ne voyez pas vraiment le composant diffus de manière significative (ou pas du tout) donc il peut être ajusté de cette manière :

float diffuseContrib = 1.f - mySpec;

Cela devrait avoir un bel aspect, mais je ne suis pas vraiment sûr de son exactitude :).

Notez cependant ; cela suppose que vos composants spéculaires et diffus sont dans la plage [0..1].

Mon résultat ressemble à ceci :

contribution diffuse calculée en utilisant la contribution spéculaire

6voto

Rahul Banerjee Points 2183

Cela a longtemps été un problème avec le modèle d'éclairage "spéculaire + diffus + ambiant". En effet, c'est une astuce et donc, elle n'a aucune garantie de correction.

Si vous êtes si désireux de consolider vos bases en premier lieu, jetez un coup d'œil à l'excellent livre "Physically Based Ray Tracing" de Matt Pharr et Greg Humphreys.

2voto

mike Points 1239

Vous devriez vous renseigner sur le modèle Blinn/Phong. Voici un exemple de code fragment shader. Essentiellement, vous mettez à l'échelle les composantes individuelles (ambiante, diffuse, terme spéculaire) avec leurs angles respectifs et les additionnez.

varying vec3 N;
varying vec3 v;    
void main (void)  
{  
   vec3 L = normalize(gl_LightSource[0].position.xyz - v);   
   vec3 E = normalize(-v); // nous sommes en coordonnées Eye, donc EyePos est (0,0,0)  
   vec3 R = normalize(-reflect(L,N));  

   //calcul du Terme Ambiante:  
   vec4 Iamb = gl_FrontLightProduct[0].ambient;    

   //calcul du Terme Diffus:  
   vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);
   Idiff = clamp(Idiff, 0.0, 1.0);     

   // calcul du Terme Spéculaire:
   vec4 Ispec = gl_FrontLightProduct[0].specular 
                * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess);
   Ispec = clamp(Ispec, 0.0, 1.0); 
   // écriture de la Couleur Totale:  
   gl_FragColor = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;     
}

issu de : http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/lighting.php

1voto

Martin Points 197

Pourquoi ne pas faire (lumière + ambiance) * diffuse + spéculaire? c'est-à-dire ajouter le composant spéculaire après avoir multiplié l'éclairage et les ombres avec la diffusion? Cela donnera un point culminant clair.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X