The following example computes per-pixel lighting (only the diffuse contribution is computed) on a sub-scene graph. Per-pixel lighting gives better lighting quality than per-vertex lighting (with classical OpenGL pipeline) because the light contributions are computed at each pixel instead of at each vertex.
The original example (located in $OIVHOME/src/Inventor/examples/Features/Shaders/PixelLighting), enables you to select when the application starts the shading language used for the per-pixel lighting.
The example described below, includes only the essential part of the complete source.
Example 21.1. Writing a per-pixel shader
Contents of PixelLighting.cxx
void configureShaders() { // Initialize and set the vertex shader VertexShader = new SoVertexShader; // Initialize and set the fragment shader FragmentShader = new SoFragmentShader; // Specify the vertex and fragment shaders VertexShader-> sourceProgram.setValue("../../../data/Shaders/PixelLightingVtx.glsl"); FragmentShader-> sourceProgram.setValue("../../../data/Shaders/PixelLightingFrag.glsl"); // Initialize and set the shader program SoShaderProgram *shaderProgram = new SoShaderProgram; shaderProgram->shaderObject.set1Value(0, VertexShader); shaderProgram->shaderObject.set1Value(1, FragmentShader); // Add the shader program to the separator ShaderSep->insertChild(shaderProgram, 0); } int main(int argc, char **argv) { ... SoSeparator *root = new SoSeparator; root->ref(); // Point light PointLight = new SoPointLightManip; ShaderSep = new SoSeparator; SoSeparator *sphereSep = new SoSeparator; sphereSep->addChild(new SoSphere); // Configures the shader configureShaders(); // Add the sphere to the shaders' separator to apply the shaders to it. ShaderSep->addChild(sphereSep); // Material applied to the model SoMaterial *modelMat = new SoMaterial; modelMat->ambientColor.setValue(0.2f, 0.2f, 0.2f); modelMat->diffuseColor.setValue(0.3f, 0.3f, 0.9f); modelMat->specularColor.setValue(0.6f, 0.6f, 0.8f); modelMat->emissiveColor.setValue(0.0f, 0.0f, 0.0f); modelMat->shininess.setValue(0.2f); // Build the scene graph root->addChild(modelMat); root->addChild(PointLight); root->addChild(ShaderSep); ... }
void ConfigureShaders() { // Initialize and set the vertex shader SoVertexShader VertexShader = new SoVertexShader(); // Initialize and set the fragment shader SoFragmentShader FragmentShader = new SoFragmentShader(); // Specify the vertex and fragment shaders VertexShader.sourceProgram.Value = "$OIVNETHOME/src/Inventor/examples/data/Shaders/PixelLightingVtx.glsl"; FragmentShader.sourceProgram.Value = "$OIVNETHOME/src/Inventor/examples/data/Shaders/PixelLightingFrag.glsl"; // Initialize and set the shader program SoShaderProgram shaderProgram = new SoShaderProgram(); shaderProgram.shaderObject[0] = VertexShader; shaderProgram.shaderObject[1] = FragmentShader; // Add the shader program to the separator ShaderSep.InsertChild(shaderProgram, 0); } void CreateSample() { ... SoSeparator root = new SoSeparator(); // Point light SoPointLightManip PointLight = new SoPointLightManip(); ShaderSep = new SoSeparator(); SoSeparator sphereSep = new SoSeparator(); SoTranslation sphereTranslation = new SoTranslation(); sphereTranslation.translation.SetValue(3f, 0f, -2f); sphereSep.AddChild(sphereTranslation); sphereSep.AddChild(new SoSphere()); // Configures the shader ConfigureShaders(); // Add the sphere to the shaders' separator to apply the shaders to it. ShaderSep.AddChild(sphereSep); // Material applied to the model SoMaterial modelMat = new SoMaterial(); modelMat.ambientColor.SetValue(0.2f, 0.2f, 0.2f); modelMat.diffuseColor.SetValue(0.3f, 0.3f, 0.9f); modelMat.specularColor.SetValue(0.6f, 0.6f, 0.8f); modelMat.emissiveColor.SetValue(0.0f, 0.0f, 0.0f); modelMat.shininess.SetValue(0.2f); // Build the scene graph root.AddChild(modelMat); root.AddChild(PointLight); root.AddChild(ShaderSep); ... }
Contents of PixelLightingVtx.glsl Vertex Shader:
/* !!GLSL */ varying vec4 eposition; varying vec3 normal; varying vec3 diffuseColor; varying vec3 specularColor; varying vec3 emissiveColor; varying vec3 ambientColor; varying float shininess; void main() { // Position in clip space gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; // Position in eye space eposition = gl_ModelViewMatrix * gl_Vertex; // Normal in eye space normal = mat3(gl_ModelViewMatrix) * gl_Normal; // Retrieves diffuse, specular emissive, and ambient color from the OpenGL state. diffuseColor = vec3(gl_FrontMaterial.diffuse); specularColor = vec3(gl_FrontMaterial.specular); emissiveColor = vec3(gl_FrontMaterial.emission); ambientColor = vec3(gl_FrontMaterial.ambient); shininess = gl_FrontMaterial.shininess; }
Contents of PixelLightingFrag.glsl fragment shader:
/* !!GLSL */ varying vec4 eposition; varying vec3 normal; varying vec3 diffuseColor; varying vec3 specularColor; varying vec3 emissiveColor; varying vec3 ambientColor; varying float shininess; void main() { const vec3 lightColor = vec3(1, 1, 1); const vec3 globalAmbient = vec3(0.2, 0.2, 0.2); // Position in eye space vec3 P = vec3(eposition); // Normalize normal in eye space vec3 N = normalize(normal); // Compute the emissive term vec3 emissive = emissiveColor; // Compute the ambient term vec3 ambient = ambientColor * globalAmbient; // Compute the diffuse term // Normalized vector toward the light source vec3 L = normalize(vec3(gl_LightSource[0].position) - P); float diffuseLight = max(dot(N, L), 0); vec3 diffuse = diffuseColor * lightColor * diffuseLight; // Compute the specular term vec3 V = normalize(-P); // Normalized vector toward the viewpoint vec3 H = normalize(L + V); // Normalized vector that is halfway between V and L float specularLight = pow(max(dot(N, H),0), shininess); if(diffuseLight <= 0) specularLight = 0; vec3 specular = specularColor * lightColor * specularLight; // Define the final vertex color gl_FragColor.xyz = emissive + ambient + diffuse + specular; gl_FragColor.w = 1.0; }