How to transform directional light to camera space in GLSL
I have the following GLSL code for lighting:
uniform vec3 lightDir; // Parallel light
uniform float ambient;
uniform vec3 lightColour;
void main()
{
gl_Position = ftransform();
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
fl开发者_如何学Coat intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
gl_FrontColor = gl_Color * vec4(lightColour, 1.0) * intensity;
}
The light vector is specified in world space. I set the camera orientation and position using gluLookAt
. Since OpenGL assumes the light vector is in camera space, the light moves with the camera instead of staying in a fixed orientation.
I tried activating the shader and setting the light vector both before and after I call gluLookAt
, but I get the same effect. What exactly do I have to do to properly transform the light vector?
You must decide in which space you will do the lighting. If you want to follow "standard" OpenGL then it is view space. Your light vector must then be rotated by rotation part of the view matrix before passing to a shader (imho simplest solution).
You can also apply lights in other spaces (tangent, object, world or even screen space - for deffered shading) but that's different topic. Also note that for advanced use you will need to forget OpenGL and do everything with your own matrices and shaders (no default lighting/transformation stuff).
You have a common problem, often seen in GLSL tutorials online. You are passing your lighting values via uniform. Why don't you use the glLight
calls, like with fixed function rendering, and pull the values from gl_LightSource[n]
. The difference is that openGL will automatically translate everything in view space.
void main()
{
// remember: directional lights are GL_POSITION with w = 0
vec3 lightDir = vec3(gl_LightSource[0].position);
float ambient = gl_LightSource[0].ambient;
vec3 lightColour = vec3(gl_LightSource[0].diffuse);
gl_Position = ftransform();
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
float intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
gl_FrontColor = gl_Color * vec4(lightColour, 1.0) * intensity;
}
A always great resource on the subject: http://www.lighthouse3d.com/opengl/glsl/index.php?lights
And always useful is the GLSL Cheat Cheat: http://www.cs.brown.edu/courses/cs123/resources/glslQuickRef.pdf
The problem in you shader, is that you have eye space normals, and worldspace lights.
I'm not sure, but transforming your light vector by your normal matrix should fix the problem.
Or, to be more efficient, you can transform the light vec in object-space (in program code), then no transformation for each normal is requiered.
I suspect part of the problem lies in:
float intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
.
The ambient term is, in Lambertian based shaders, scaled by the dot product. Here the ambient term will assert itself often because it is likely to be greater than the dot product. The use of gl_NormalMatrix is also a problem because, as commented above, it means the lightDir is in object space while the normal vector is transformed out of object space and into eye coordinates.
Try:
vec3 n=normalize(normal);
float ndotl=max(0.0, dot(n, normalize(lightDir)));
vec3 diffuse=ambient * ndotl;
the code in shader calculates directional light which does not change with the position of light, so you should use spotLight or pointLight functions.
Try to find shaders for these light sources. if you can not find, tell me so i will send you one.
精彩评论