Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

GLSL Programming/GLUT/Diffuse Reflection

From Wikibooks, open books for an open world
<GLSL Programming |GLUT
The light reflection from the surface of the moon is (in a good approximation) only diffuse.

This tutorial coversper-vertex diffuse reflection.

It's the first in a series of tutorials about basic lighting in OpenGL 2.x. In this tutorial, we start with diffuse reflection from a single directional light source and then include point lights and spotlights. Further tutorials cover extensions of this, in particular specular reflection, per-pixel lighting, two-sided lighting, and multiple light sources.

When appropriate, we'll use the same conventions as in OpenGL 1.x, so that people upgrading their OpenGL knowledge will feel at ease.

Diffuse reflection can be computed using the surface normal vector N and the light vector L, i.e. the vector to the light source.

Diffuse Reflection

[edit |edit source]

The moon exhibits almost exclusively diffuse reflection (also called Lambertian reflection), i.e. light is reflected into all directions without specular highlights. Other examples of such materials are chalk and matte paper; in fact, any surface that appears dull and matte.

In the case of perfect diffuse reflection, the intensity of the observed reflected light depends on the cosine of the angle between the surface normal vector and the ray of the incoming light. As illustrated in the figure to the left, it is common to consider normalized vectors starting in the point of a surface, where the lighting should be computed: the normalized surface normal vectorN is orthogonal to the surface and the normalized light directionL points to the light source.

For the observed diffuse reflected lightIdiffuse{\displaystyle I_{\text{diffuse}}}, we need the cosine of the angle between the normalized surface normal vectorN and the normalized direction to the light sourceL, which is the dot productN·L because the dot producta·b of any two vectorsa andb is:

ab=|a||b|cos(a,b){\displaystyle \mathbf {a} \cdot \mathbf {b} =\left\vert \mathbf {a} \right\vert \left\vert \mathbf {b} \right\vert \cos \measuredangle (\mathbf {a} ,\mathbf {b} )}.

In the case of normalized vectors, the lengths |a| and |b| are both 1.

If the dot productN·L is negative, the light source is on the “wrong” side of the surface and we should set the reflection to 0. This can be achieved by using max(0,N·L), which makes sure that the value of the dot product is clamped to 0 for negative dot products. Furthermore, the reflected light depends on the intensity of the incoming lightIincoming{\displaystyle I_{\text{incoming}}} and a material constantkdiffuse{\displaystyle k_{\text{diffuse}}} for the diffuse reflection: for a black surface, the material constantkdiffuse{\displaystyle k_{\text{diffuse}}} is 0, for a white surface it is 1. The equation for the diffuse reflected intensity is then:

Idiffuse=Iincomingkdiffusemax(0,NL){\displaystyle I_{\text{diffuse}}=I_{\text{incoming}}\,k_{\text{diffuse}}\max(0,\mathbf {N} \cdot \mathbf {L} )}

For colored light, this equation applies to each color component (e.g. red, green, and blue). Thus, if the variablesIdiffuse{\displaystyle I_{\text{diffuse}}},Iincoming{\displaystyle I_{\text{incoming}}}, andkdiffuse{\displaystyle k_{\text{diffuse}}} denote color vectors and the multiplications are performed component-wise (which they are for vectors in GLSL), this equation also applies to colored light. This is what we actually use in the shader code.

Shader Code for One Directional Light Source

[edit |edit source]

If we have only one directional light source, the shader code for implementing the equation forIdiffuse{\displaystyle I_{\text{diffuse}}} is relatively small. In order to implement the equation, we follow the questions about implementing equations, which were discussed in thetutorial on silhouette enhancement:

  • Should the equation be implemented in the vertex shader or the fragment shader? We try the vertex shader here. In thetutorial on smooth specular highlights, we will look at an implementation in the fragment shader.
  • In which coordinate system should the equation be implemented? We chose world space, so that lights are described in the same coordinate system as positioned objects.
  • Where do we get the parameters from? The answer to this is a bit longer:

As described in thetutorial on shading in view space, we make the diffuse material colorkdiffuse{\displaystyle k_{\text{diffuse}}} available asmaterial.diffuse. The direction to the light sourceL in view space is available inlight0.position (with the 4th vector element set to0.0) and the light colorIincoming{\displaystyle I_{\text{incoming}}} is available aslight0.diffuse. We get the surface normal vector in object coordinates from the attributev_normal. Since we implement the equation in world space, we have to convert the surface normal vector from object space to world space as discussed in thetutorial on silhouette enhancement.

The vertex shader then looks like this:

attributevec4v_coord;attributevec3v_normal;uniformmat4m,v,p;uniformmat3m_3x3_inv_transp;varyingvec4color;structlightSource{vec4position;vec4diffuse;};lightSourcelight0=lightSource(vec4(-1.0,1.0,-1.0,0.0),vec4(1.0,1.0,1.0,1.0));structmaterial{vec4diffuse;};materialmymaterial=material(vec4(1.0,0.8,0.8,1.0));voidmain(void){mat4mvp=p*v*m;vec3normalDirection=normalize(m_3x3_inv_transp*v_normal);vec3lightDirection=normalize(vec3(light0.position));vec3diffuseReflection=vec3(light0.diffuse)*vec3(mymaterial.diffuse)*max(0.0,dot(normalDirection,lightDirection));color=vec4(diffuseReflection,1.0);gl_Position=mvp*v_coord;}

And the fragment shader is:

varyingvec4color;voidmain(void){gl_FragColor=color;}

For now we only create one directional light source in the scene.

Changes for a Point Light Source

[edit |edit source]

In the case of a directional light sourcelight0.position specifies the direction from where light is coming. In the case of a point light source (or a spot light source), however,light0.position will specify the position of the light source in view space and we have to compute the direction to the light source as the difference vector from the position of the vertex in view space to the position of the light source. Since the 4th coordinate of a point is 1 and the 4th coordinate of a direction is 0, we can easily distinguish between the two cases:

vec3lightDirection;if(light0.position.w==0.0)// directional light{lightDirection=normalize(vec3(light0.position));}else// point or spot light{lightDirection=normalize(vec3(light0.position-m*v_coord)); }

While there is no attenuation of light for directional light sources, we should add some attenuation with distance to point and spot light source. As light spreads out from a point in three dimensions, it's covering ever larger virtual spheres at larger distances. Since the surface of these spheres increases quadratically with increasing radius and the total amount of light per sphere is the same, the amount of light per area decreases quadratically with increasing distance from the point light source. Thus, we should divide the intensity of the light source by the squared distance to the vertex.

We can specify a combination of quadratic attenuation with distance (rather rapid), linear attenuation with distance, and constant attenuation. The code would be:

structlightSource{vec4position;vec4diffuse;floatconstantAttenuation,linearAttenuation,quadraticAttenuation;};lightSourcelight0=lightSource(vec4(-1.0,1.0,-1.0,1.0),vec4(1.0,1.0,1.0,1.0),1.0,0.0,0.0);
vec3lightDirection;floatattenuation;if(light0.position.w==0.0)// directional light{attenuation=1.0;// no attenuationlightDirection=normalize(vec3(light0.position));}else// point or spot light{vec3vertexToLightSource=vec3(light0.position-m*v_coord);floatdistance=length(vertexToLightSource);attenuation=1.0/(light0.constantAttenuation+light0.linearAttenuation*distance+light0.quadraticAttenuation*distance*distance);lightDirection=normalize(vertexToLightSource);}

The factorattenuation should then be multiplied withlight0.diffuse to compute the incoming light; see the complete shader code below. Note that spot light sources have additional features, which are discussed in the next section.

Also note that this code is unlikely to give you the best performance because anyif is usually quite costly. Sincelight0.position.w is either 0 or 1, it is actually not too hard to rewrite the code to avoid the use ofif and optimize a bit further:

vec3vertexToLightSource=vec3(light0.position-m*v_coord*light0.position.w);floatdistance=length(vertexToLightSource);attenuation=mix(1.0,1.0/(light0.constantAttenuation+light0.linearAttenuation*distance+light0.quadraticAttenuation*distance*distance),gl_LightSource[0].position.w);lightDirection=vertexToLightSource/distance;

However, we will use the version withif for clarity. (“Keep it simple, stupid!”)

Changes for a Spotlight

[edit |edit source]

The 0th light source is a spotlight only iflight0.spotCutoff is less than or equal to 90.0 (otherwise we set it to 180.0). Thus, the test could be:

if(light0.spotCutoff<=90.0)// spotlight

The shape of a spotlight is described by the variablelight0.spotDirection,light0.spotExponent andlight0.spotCutoff. Specifically, if the cosine of the angle between-lightDirection andlight0.spotDirection is smaller than the cosine oflight0.spotCutoff, i.e. if the shaded point is outside the cone of light around the spotlight direction, then the attenuation factor is set to0.0{\displaystyle 0.0}. We can compute the cosine of the angle between the two vectors by a dot product of the two normalized vectors. (In OpenGL 1.x, the cosine oflight0.spotCutoff was provided asgl_LightSource[0].spotCosCutoff.) If we include clamping of the cosine at 0 in order to ignore points behind the spotlight, the test is:

floatclampedCosine=max(0.0,dot(-lightDirection,normalize(light0.spotDirection)));if(clampedCosine<cos(light0.spotCutoff*3.14159/180.0))// outside of spotlight cone{attenuation=0.0;}

Otherwise, if a point is inside the cone of light, the attenuation in OpenGL is supposed to be computed this way:

attenuation=attenuation*pow(clampedCosine,light0.spotExponent);

This allows for wider (smallerspotExponent) and narrower (largerspotExponent) spotlights.

Complete Shader Code

[edit |edit source]

All in all, our new vertex shader for a single directional light, point light, or spotlight with linear attenuation becomes:

attributevec4v_coord;attributevec3v_normal;uniformmat4m,v,p;uniformmat3m_3x3_inv_transp;varyingvec4color;structlightSource{vec4position;vec4diffuse;floatconstantAttenuation,linearAttenuation,quadraticAttenuation;floatspotCutoff,spotExponent;vec3spotDirection;};lightSourcelight0=lightSource(vec4(0.0,1.0,2.0,1.0),vec4(1.0,1.0,1.0,1.0),0.0,1.0,0.0,80.0,20.0,vec3(-1.0,-0.5,-1.0));structmaterial{vec4diffuse;};materialmymaterial=material(vec4(1.0,0.8,0.8,1.0));voidmain(void){mat4mvp=p*v*m;vec3normalDirection=normalize(m_3x3_inv_transp*v_normal);vec3lightDirection;floatattenuation;if(light0.position.w==0.0)// directional light{attenuation=1.0;// no attenuationlightDirection=normalize(vec3(light0.position));}else// point or spot light (or other kind of light){vec3vertexToLightSource=vec3(light0.position-m*v_coord);floatdistance=length(vertexToLightSource);lightDirection=normalize(vertexToLightSource);attenuation=1.0/(light0.constantAttenuation+light0.linearAttenuation*distance+light0.quadraticAttenuation*distance*distance);if(light0.spotCutoff<=90.0)// spotlight{floatclampedCosine=max(0.0,dot(-lightDirection,normalize(light0.spotDirection)));if(clampedCosine<cos(light0.spotCutoff*3.14159/180.0))// outside of spotlight cone{attenuation=0.0;}else{attenuation=attenuation*pow(clampedCosine,light0.spotExponent);}}}vec3diffuseReflection=attenuation*vec3(light0.diffuse)*vec3(mymaterial.diffuse)*max(0.0,dot(normalDirection,lightDirection));color=vec4(diffuseReflection,1.0);gl_Position=mvp*v_coord;}

And the fragment shader is still:

varyingvec4color;voidmain(void){gl_FragColor=color;}

In your C++ source code, make sure you updatem_3x3_inv_transp:

/* Transform normal vectors with transpose of inverse of upper left     3x3 model matrix (ex-gl_NormalMatrix): */glm::mat3m_3x3_inv_transp=glm::transpose(glm::inverse(glm::mat3(mesh.object2world)));glUniformMatrix3fv(uniform_m_3x3_inv_transp,1,GL_FALSE,glm::value_ptr(m_3x3_inv_transp));

Summary

[edit |edit source]

Congratulations! You already learned a lot about OpenGL lights. This is essential for the following tutorials about more advanced lighting. Specifically, we have seen:

  • What diffuse reflection is and how to describe it mathematically.
  • How to implement diffuse reflection for a single directional light source in a shader.
  • How to extend the shader for point light sources with a linear attenuation.
  • How to further extend the shader to handle spotlights.

Further Reading

[edit |edit source]

If you still want to know more


<GLSL Programming/GLUT

Unless stated otherwise, all example source code on this page is granted to the public domain.
Back toOpenGL Programming - Lighting sectionBack toGLSL Programming - GLUT section
Retrieved from "https://en.wikibooks.org/w/index.php?title=GLSL_Programming/GLUT/Diffuse_Reflection&oldid=4070285"
Categories:

[8]ページ先頭

©2009-2025 Movatter.jp