I've been learning OpenGL for the past couple of weeks and I've run into some trouble implementing a Phong shader. It appears to do no interpolation between vertexes despite my use of the smooth
qualifier. Am I missing something here? To give credit where credit is due, the code for the vertex and fragment shaders cribs heavily from the OpenGL SuperBible Fifth Edition. I would highly recommend this book!
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); } }
This (public domain) image from Wikipedia shows exactly what sort of image I'm getting and what I'm aiming for -- I'm getting the "flat" image but I want the "Phong" image.
Any help would be greatly appreciated. Thank you!
edit: If it makes a difference, I'm using PyOpenGL 3.0.1 and Python 2.6.
edit2:
It turns out the problem was with my geometry; Kos was correct. For anyone else that's having this problem with Blender models, Kos pointed out that doing Edit->Faces->Set Smooth
does the trick. I found that Wings 3D worked "out of the box."
The OpenGL Shading Language (GLSL) is the principal shading language for OpenGL. While, thanks to OpenGL Extensions, there are several shading languages available for use in OpenGL, GLSL (and SPIR-V) are supported directly by OpenGL without extensions. GLSL is a C-style language.
The Vertex Shader is the programmable Shader stage in the rendering pipeline that handles the processing of individual vertices. Vertex shaders are fed Vertex Attribute data, as specified from a vertex array object by a drawing command.
A Shader is a user-defined program designed to run on some stage of a graphics processor. Shaders provide the code for certain programmable stages of the rendering pipeline. They can also be used in a slightly more limited form for general, on-GPU computation.
As an addition to this answer, here is a simple geometry shader which will let you visualize your normals. Modify the accompanying vertex shader as needed based on your attribute locations and how you send your matrices.
But first, a picture of a giant bunny head from our friend the Stanford bunny as an example of the result !
Major warning: do note that I get away with transforming the normals with the modelview matrix instead of a proper normal matrix. This won't work correctly if your modelview contains non uniform scaling. Also, the length of your normals won't be correct but that matters little if you just want to check their 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; }
Geometry shader:
#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(); } }
Fragment shader:
#version 330 in Data { vec4 color; } gdata; out vec4 outputColor; void main() { outputColor = gdata.color; }
Hmm... You're interpolating the normal as a varying
variable, so the fragment shader should receive the correct per-pixel normal.
The only explanation (I can think of) of the fact that you're having the result as on your left image is that every fragment on a given face ultimately receives the same normal. You can confirm it with a fragment shader like:
void main() { vFragColor = normalize(vVaryingNormal); }
If it's the case, the question remains: Why? The vertex shader looks OK.
So maybe there's something wrong in your geometry? What is the data which you send to the shader? Are you sure you have correctly calculated per-vertex normals, not just per-face normals?
The orange lines are normals of the diagonal face, the red lines are normals of the horizontal face.
If your data looks like the above image, then even with a correct shader you'll get flat shading. Make sure that you have correct per-vertex normals like on the lower image. (They are really simple to calculate for a sphere.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With