web-dev-qa-db-fra.com

Pourquoi mon shader OpenGL Phong se comporte-t-il comme un shader plat?

J'apprends OpenGL depuis quelques semaines et j'ai eu des problèmes pour implémenter un shader Phong. Il ne semble pas faire d'interpolation entre les sommets malgré mon utilisation du qualificatif smooth. Est-ce que j'ai râté quelque chose? Pour donner du crédit là où le crédit est dû, le code pour les vertex et fragment shaders crèche lourdement de la OpenGL SuperBible Fifth Edition. Je recommande fortement ce livre!

Vertex Shader:

#version 330
in vec4 vVertex;
in vec3 vNormal;
uniform mat4 mvpMatrix;  // mvp = ModelViewProjection
uniform mat4 mvMatrix; // mv = ModelView
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;

void main(void) {
 vVaryingNormal = normalMatrix * vNormal;
 vec4 vPosition4 = mvMatrix * vVertex;
 vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
 vVaryingLightDir = normalize(vLightPosition - vPosition3);
 gl_Position = mvpMatrix * vVertex;
}

Fragment Shader:

#version 330
out vec4 vFragColor;
uniform vec4 ambientColor;
uniform vec4 diffuseColor;
uniform vec4 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;

void main(void) {
 float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
 vFragColor = diff * diffuseColor;
 vFragColor += ambientColor;
 vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir),normalize(vVaryingNormal)));
 float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));

 if(diff != 0) {
   float fSpec = pow(spec, 32.0);
   vFragColor.rgb += vec3(fSpec, fSpec, fSpec);
 }

}

Cette image (domaine public) de Wikipédia montre exactement quel type d'image je reçois et ce que je vise - je reçois l'image "plate" mais je veux l'image "Phong". 

Toute aide serait grandement appréciée. Je vous remercie!

edit: Si cela fait une différence, j'utilise PyOpenGL 3.0.1 et Python 2.6.

edit2:

Solution

Il s'avère que le problème était avec ma géométrie; Kos avait raison. Pour toute autre personne ayant ce problème avec les modèles Blender, Kos a souligné que faire Edit->Faces->Set Smooth fait l'affaire. J'ai trouvé que Wings 3D fonctionnait "prêt à l'emploi".

48
Jonba

Hmm ... Vous interpolez la normale en tant que variable varying, donc le fragment shader devrait recevoir la normale par pixel correcte.

La seule explication (à laquelle je peux penser) du fait que vous obtenez le résultat comme sur votre image de gauche est que chaque fragment sur un visage donné reçoit finalement la même normale. Vous pouvez le confirmer avec un fragment shader comme:

void main() {
   vFragColor = normalize(vVaryingNormal);
}

Si c'est le cas, la question demeure: pourquoi? Le vertex shader semble OK.

Alors peut-être qu'il y a quelque chose qui ne va pas dans votre géométrie? Quelles sont les données que vous envoyez au shader? Êtes-vous sûr d'avoir correctement calculé les normales par sommet, pas seulement les normales par face?

normals

Les lignes oranges sont des normales de la face diagonale, les lignes rouges sont des normales de la face horizontale.

Si vos données ressemblent à l'image ci-dessus, même avec un shader correct, vous obtiendrez un ombrage plat. Assurez-vous que les normales par sommet sont correctes, comme sur l'image inférieure. (Ils sont vraiment simples à calculer pour une sphère.)

49
Kos

En complément de cette réponse, voici un shader de géométrie simple qui vous permettra de visualiser vos normales. Modifiez le vertex shader d'accompagnement selon vos besoins en fonction de vos emplacements d'attributs et de la façon dont vous envoyez vos matrices.

Mais d'abord, une photo d'une tête de lapin géante de notre ami le lapin de Stanford comme exemple du résultat!

normals for the Stanford bunny

Avertissement majeur: notez que je m'en tire avec la transformation des normales avec la matrice modelview au lieu d'une matrice normale appropriée. Cela ne fonctionnera pas correctement si votre vue de modèle contient une mise à l'échelle non uniforme. De plus, la longueur de vos normales ne sera pas correcte, mais peu importe si vous voulez simplement vérifier leur direction.

Vertex shader:

#version 330

layout(location = 0) in vec4 position;
layout(location = 1) in vec4 normal;
layout(location = 2) in mat4 mv;

out Data
{
    vec4 position;
    vec4 normal;
    vec4 color;
    mat4 mvp;
} vdata;

uniform mat4 projection;

void main()
{
    vdata.mvp = projection * mv;
    vdata.position = position;
    vdata.normal = normal;
}

Shader de géométrie:

#version 330
layout(triangles) in;
layout(line_strip, max_vertices = 6) out;

in Data
{
    vec4 position;
    vec4 normal;
    vec4 color;
    mat4 mvp;
} vdata[3];

out Data
{
    vec4 color;
} gdata;

void main()
{
    const vec4 green = vec4(0.0f, 1.0f, 0.0f, 1.0f);
    const vec4 blue = vec4(0.0f, 0.0f, 1.0f, 1.0f);

    for (int i = 0; i < 3; i++)
    {
        gl_Position = vdata[i].mvp * vdata[i].position;
        gdata.color = green;
        EmitVertex();

        gl_Position = vdata[i].mvp * (vdata[i].position + vdata[i].normal);
        gdata.color = blue;
        EmitVertex();

        EndPrimitive();
    }
}

Shader de fragment:

#version 330

in Data
{
    vec4 color;
} gdata;

out vec4 outputColor;

void main()
{
    outputColor = gdata.color;
}
65
Nicolas Lefebvre