PDA

View Full Version : Help with basic lighting in shader



davoring
12-27-2014, 04:03 AM
Hello,

I'm trying to make a shader that works as close as possible to the fixed functionality in terms of lighting, and I'm having some difficulties with that. My problem is that the end result is darker than when I use fixed unctionality, and I don't understand why.

I used 3DLabs' ShaderGen to generate a shader for one directional light, since this is what I use in my scene. Since the generated shader does not take the color assigned to the vertex into account, I added the line "color = color * gl_Color;". This is the vertex shader I end up with:




/************************************************** *****
* Fixed.vert Fixed Function Equivalent Vertex Shader *
* Automatically Generated by 3Dlabs GLSL ShaderGen *
* http://developer.3dlabs.com *
************************************************** *****/
vec4 Ambient;
vec4 Diffuse;
vec4 Specular;


void directionalLight(in int i, in vec3 normal)
{
float nDotVP; // normal . light direction
float nDotHV; // normal . light half vector
float pf; // power factor

nDotVP = max(0.0, dot(normal, normalize(vec3 (gl_LightSource[i].position))));
nDotHV = max(0.0, dot(normal, vec3 (gl_LightSource[i].halfVector)));

if (nDotVP == 0.0)
{
pf = 0.0;
}
else
{
pf = pow(nDotHV, gl_FrontMaterial.shininess);

}
Ambient += gl_LightSource[i].ambient;
Diffuse += gl_LightSource[i].diffuse * nDotVP;
Specular += gl_LightSource[i].specular * pf;
}

vec3 fnormal(void)
{
//Compute the normal
vec3 normal = gl_NormalMatrix * gl_Normal;
normal = normalize(normal);
return normal;
}

void flight(in vec3 normal, in vec4 ecPosition, float alphaFade)
{
vec4 color;
vec3 ecPosition3;
vec3 eye;

ecPosition3 = (vec3 (ecPosition)) / ecPosition.w;
eye = vec3 (0.0, 0.0, 1.0);

// Clear the light intensity accumulators
Ambient = vec4 (0.0);
Diffuse = vec4 (0.0);
Specular = vec4 (0.0);

directionalLight(0, normal);

color = gl_FrontLightModelProduct.sceneColor +
Ambient * gl_FrontMaterial.ambient +
Diffuse * gl_FrontMaterial.diffuse;
color += Specular * gl_FrontMaterial.specular;
color = color * gl_Color; // This is the line I added
color = clamp( color, 0.0, 1.0 );
gl_FrontColor = color;

gl_FrontColor.a *= alphaFade;
}


void main (void)
{
vec3 transformedNormal;
float alphaFade = 1.0;

// Eye-coordinate position of vertex, needed in various calculations
vec4 ecPosition = gl_ModelViewMatrix * gl_Vertex;

// Do fixed functionality vertex transform
gl_Position = ftransform();
transformedNormal = fnormal();
flight(transformedNormal, ecPosition, alphaFade);
}



This is what I get if I use the shader:

1573

and this is when I get if I use NO shader:

1574


What could I be doing wrong?

GClements
12-27-2014, 07:29 AM
Since the generated shader does not take the color assigned to the vertex into account, I added the line "color = color * gl_Color;".
That's wrong. In the fixed-function pipeline, if lighting is enabled, then the colour is only used if GL_COLOR_MATERIAL is enabled. In that case, the current colour replaces the material colour(s) as specified by glColorMaterial; it doesn't modulate them. If lighting is enabled and GL_COLOR_MATERIAL is disabled, the current colour is ignored; the colour generated by the lighting calculations replaces any vertex colour set by glColor (or glColorPointer).

Note that any parameters obtained from the compatibility uniforms (gl_FrontMaterial, gl_FrontLightProduct, etc) aren't affected by GL_COLOR_MATERIAL (uniforms cannot vary per-vertex); if you want to emulate the behaviour of GL_COLOR_MATERIAL, the shader must use gl_Color in place of the relevant material colour(s).

Also, the alpha component of the colour produced by lighting is taken directly from the alpha component of the diffuse material colour.


What could I be doing wrong?
I suggest that you start by simplifying the lighting model, both in the shader and the equivalent fixed-function code. Start with just gl_LightModelParameters.ambient, then add one extra lighting term (emission, ambient, diffuse, specular) at a time, ensuring that you resolve any issues with each term before moving onto the next.

davoring
12-29-2014, 01:30 PM
Thanks or the very informative answer. I think I understand most of it. :)

After some more research and fiddling, I think I got it.

I changed the bit of code from



color = gl_FrontLightModelProduct.sceneColor +
Ambient * gl_FrontMaterial.ambient +
Diffuse * gl_FrontMaterial.diffuse;
color += Specular * gl_FrontMaterial.specular;
color = color * gl_Color; // This is the line I added

to


color = (gl_Color * gl_LightModel.ambient) +
Ambient * gl_Color +
Diffuse * gl_Color;
color += Specular * gl_Color;


and that seems to produce the same result as with fixed-function.

Does this seem correct?

GClements
12-30-2014, 07:35 AM
Does this seem correct?
Not entirely.

If you enable GL_COLOR_MATERIAL, you can make the current colour replace either any one of the material colours, or both ambient and diffuse; you can't make it affect all of them. Accepted parameters for glColorMaterial (https://www.opengl.org/sdk/docs/man2/xhtml/glColorMaterial.xml) are GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, and GL_AMBIENT_AND_DIFFUSE. The initial value is GL_AMBIENT_AND_DIFFUSE.

If a material colour tracks the current colour, the accumulated lighting component should be multiplied by gl_Color, otherwise it should be multiplied by the corresponding material component (from gl_FrontMaterial or gl_BackMaterial).

Note that for two-sided lighting, the vertex shader needs to calculate separate front and back colours, because you don't know which one you'll need until you get to the fragment shader (a single vertex may be part of multiple triangles, some of which are front-facing and some back-facing).