OpenGL ES 2.0 Lighting 2 Per-fragment Lighting CS421 Advanced Computer Graphics Jay Urbain, Ph.D. 1
Objectives Per-fragment lighting Foundation for Phong Model, textures, etc. Shader implementation in OpenGL ES 2.0 References: Fallout Software: OpenGL Lighting or How Light Sources Work (Long, In-depth Tutorial) Clockworkcoders Tutorials: Per Fragment Lighting Lighthouse3D.com: The Normal Matrix arcsynthesis.org: OpenGL Tutorials: Normal Transformation OpenGL Programming Guide: Chapter 5 Lighting OpenGL ES 2.0 Programming Guide http://www.learnopengles.com 2
Per-vertex lighting Front face of cube appears flatshaded. No evidence of a light nearby. Why? The four points of the front face are (approximately) equidistant from the light. Low light intensity at each of the four points is interpolated (smoothed) across the two triangles that make up the front face. 3
Per-pixel vs. Per-fragment Lighting Per vertex lighting Per fragment lighting The per-fragment version shows a nice highlight 4 in comparison.
Lighting Techniques Per-vertex: smooth shading (Gouraud shading). Lightmaps: Can give very nice results. Expensive light calculations are typically pre-computed. Downside: they take up a lot of memory, and doing dynamic lighting is limited to simple calculations. Shaders: Calculations can be offloaded to the GPU. Allows for many more effects to be done in real-time. Per-fragment lighting: relatively new phenomenon with the advent of shaders. 5
Per-pixel vs. Per-fragment Lighting Vertex (Gouraud) shaded cube Vertex shading: As light source moves near the corner of the front face of the cube, a triangle-like effect can be seen. Front face is composed of two triangles. As the values are interpolated in different directions across each triangle we can see the underlying geometry. Fragment shading: Shows no signs of interpolation errors, nice circular highlight near the edge. Fragment shaded cube 6
Vertex Shader Lighting uniform mat4 u_mvpmatrix; // model/view/projection matrix. uniform mat4 u_mvmatrix; // model/view matrix. attribute vec4 a_position; // Per-vertex position information we will pass in. attribute vec4 a_color; // Per-vertex color information we will pass in. attribute vec3 a_normal; // Per-vertex normal information we will pass in. 7
Vertex Shader Lighting (cont.) void main() { } // Transform the vertex & normal into eye space. vec3 modelviewvertex = vec3(u_mvmatrix * a_position); vec3 modelviewnormal = vec3(u_mvmatrix * vec4(a_normal, 0.0)); // Used for attenuation. float distance = length(u_lightpos - modelviewvertex); // Lighting direction vector from the light to the vertex. vec3 lightvector = normalize(u_lightpos - modelviewvertex); // Dot product of the light vector and vertex normal. float diffuse = max(dot(modelviewnormal, lightvector), 0.1); // Attenuate the light based on distance. diffuse = diffuse *(1.0/(1.0 + (0.1* distance * distance))); // Multiply color by the illumination level. Interpolated across the triangle. v_color = a_color * diffuse; // gl_position is a special variable used to store the final position. gl_position = u_mvpmatrix * a_position; 8
Vertex Shader after Moving Lighting Code to Fragment Shader uniform mat4 u_mvpmatrix; // Combined model/view/projection matrix. uniform mat4 u_mvmatrix; // Combined model/view matrix. attribute vec4 a_position; // Per-vertex position information we will pass in. attribute vec4 a_color; // Per-vertex color information we will pass in. attribute vec3 a_normal; // Per-vertex normal information we will pass in. varying vec3 v_position; varying vec4 v_color; varying vec3 v_normal; // Passed into the fragment shader. // Passed into the fragment shader. // Passed into the fragment shader. 9
Lighting -Vertex Shader (cont.) void main() { // Transform the vertex into eye space. v_position = vec3(u_mvmatrix* a_position); // Pass through the color. v_color = a_color; // Transform the normal's orientation into eye space. v_normal = vec3(u_mvmatrix* vec4(a_normal, 0.0)); // gl_position is used to store the final position. // Multiply vertex by the matrix to get final point in normalized screen coordinates. gl_position = u_mvpmatrix* a_position; } 10
Fragment Shader precision mediump float; // Set default precision uniform vec3 u_lightpos; // Position of the light in eye space. varying vec3 v_position; varying vec4 v_color; varying vec3 v_normal; // Interpolated position for this fragment. // Color from the vertex shader interpolated across the // triangle per fragment. // Interpolated normal for this fragment. 11
Fragment Shader (cont.) void main() { // Used for attenuation. float distance = length(u_lightpos- v_position); // Get a lighting direction vector from the light to the vertex. vec3 lightvector = normalize(u_lightpos- v_position); // Dot product of the light vector and vertex normal. float diffuse = max(dot(v_normal, lightvector), 0.1); // Add attenuation. diffuse = diffuse * (1.0 / (1.0 + (0.1 * distance * distance))); } // Multiply the color by the diffuse illumination level to get final output color. gl_fragcolor = v_color* diffuse; 12