Advertisement

Problems with directional lighting shader

Started by January 29, 2018 10:12 AM
12 comments, last by DividedByZero 7 years ago

Hi Guys,

I have been struggling for a number of hours trying to make a directional (per fragment) lighting shader. 

I have been following this tutorial in the 'per fragment' section of the page - http://www.learnopengles.com/tag/per-vertex-lighting/ along with tutorials from other sites.

This is what I have at this point.


// Vertex shader

varying vec3 v_Normal;
varying vec4 v_Colour;
varying vec3 v_LightPos;

uniform vec3 u_LightPos;
uniform mat4 worldMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

void main()
{
    vec4 object_space_pos = vec4(in_Position, 1.0);
    gl_Position = worldMatrix * vec4(in_Position, 1.0);
    gl_Position = viewMatrix * gl_Position;     // WV
    gl_Position = projectionMatrix * gl_Position;

    mat4 WV = worldMatrix * viewMatrix;
    
    v_Position = vec3(WV * object_space_pos);
    v_Normal = vec3(WV * vec4(in_Normal, 0.0));
    v_Colour = in_Colour;
    v_LightPos = u_LightPos;
}

And 


// Fragment

varying vec3 v_Position;
varying vec3 v_Normal;
varying vec4 v_Colour;
varying vec3 v_LightPos;

void main()
{
    float dist = length(v_LightPos - v_Position);
    vec3 lightVector = normalize(v_LightPos - v_Position);
    float diffuse_light = max(dot(v_Normal, lightVector), 0.1);
    diffuse_light = diffuse_light * (1.0 / (1.0 + (0.25 * dist * dist)));
    
    gl_FragColor = v_Colour * diffuse_light;
}

If I change the last line of the fragment shader to 'gl_FragColor = v_Colour;' the model (a white sphere) will render to the screen in solid white, as expected.

But if I leave the shader as is above, the object is invisible.

I am suspecting that it is something to do with this line in the vertex shader, but am at a loss as to what is wrong.


v_Position = vec3(WV * object_space_pos);

If I comment the above line out, I get some sort of shading going on which looks like it is trying to light the subject (with the normals calculating etc.)

Any help would be hugely appreciated.

Thanks in advance :)

Hi, lonewolff!

My initial guess would be that your v_Position and v_LightPos are in different coordinate spaces, making the distance and light direction computation meaningless. v_Position is in view space:


v_Position = vec3(WV * object_space_pos);

Can you make sure that you also transform v_LightPos into view space somewhere in your app?

Does it help if you comment out attenuation?


diffuse_light = diffuse_light * (1.0 / (1.0 + (0.25 * dist * dist)));

Also, probably just a formality, but directional lighting doesn't use light position or attenuation, it's an approximation for the case when the light source is very far relative to the scale of the scene, e.g. the sun illuminating a building. In such a case we assume that the light rays all travel in the same direction and the light's intensity falloff with distance is negligible. Point lighting would be a better name in this case.

Advertisement

So basically tutorial shows how you manage per pixel lighting which is say really easy to implement.

You pass verts to shader and test the distance between light and pixel fragment, whenever fragment is in radius you color it as diffuse light color

 

Let me show the code first

 

Vs


attribute vec3 Vpos;

uniform vec4 MVP1;
uniform vec4 MVP2;
uniform vec4 MVP3;
uniform vec4 MVP4;

uniform vec4 WM1;
uniform vec4 WM2;
uniform vec4 WM3;
uniform vec4 WM4;

vec4 vertexClip;

varying highp vec3 vertex_pos;

float dp43(vec4 matrow, vec3 p)
{
return ( (matrow.x*p.x) + (matrow.y*p.y) + (matrow.z*p.z) + matrow.w );
}


 
void main()
{
vertexClip.x = dp43(MVP1, Vpos);
vertexClip.y = dp43(MVP2, Vpos);
vertexClip.z = dp43(MVP3, Vpos);
vertexClip.w = dp43(MVP4, Vpos);


vertex_pos.x = dp43(WM1, Vpos);
vertex_pos.y = dp43(WM2, Vpos);
vertex_pos.z = dp43(WM3, Vpos);


gl_Position = vertexClip;

}


Fs

 


varying highp vec3 vertex_pos;
uniform highp vec3 LPOS;
uniform highp vec3 LDIFF;

uniform highp float LRadius;

highp float n3ddistance(highp vec3 first_point, highp vec3 second_point)
{
highp float x = first_point.x-second_point.x;
highp float y = first_point.y-second_point.y;
highp float z = first_point.z-second_point.z;
highp float val = x*x + y*y + z*z;
return sqrt(val);
}

void main()
{
	highp float dst = n3ddistance(LPOS, vertex_pos);
	highp float intensity = clamp(1.0 - dst / LRadius, 0.0, 1.0);
	highp vec4 color = vec4(LDIFF.x, LDIFF.y, LDIFF.z, 1.0)*intensity;
	gl_FragColor = color;
}

 

 

Now short explenation you pass vertex world coordinate to fragment shader then you can test that against light position that means: your object could be centered at 0,0,0 pos then you could apply that


Matrix44<float> wrld;
	wrld.TranslateP(ship[i]->pos);// make translation matrix 
wrld = ship[i]->ROTATION_MAT * wrld;
		 MVP = (wrld * ACTUAL_VIEW) * ACTUAL_PROJECTION;

Then we are ready to make a directional light always when you pass normal is either affected by object rotation matrix or you have it already fixed in buffer

Then dot(normal, lightdir) whenever is0 or positive gives you ambient lightning color else we can get diffuse lighting

The idea is to find which fragment is in the cone, to do that you could simply define light radius and radius of a base of this cone bR

 

Since we have base radius and light radius and light direction we could simply check whenever a fragment is in the spotlight, to do that you need closestpointonline function

First define two line ends first is your lightpos second one is lightpos+lightdir*radius

Now you test fragment position against this line and get the distance between fragment and closest point on line,

Now having this we need to test whenever lets say fragment is in triangle

Given closest point on line we can find distance from light pos to cone line center let it be cvX now divide it by light radius we will get 'percentage of the distance' now multiply that percentage by base radius IF FRAGMENT DISTANCE TO CONE LINE CENTER IS LESS THAN THIS YOU CAN COLOR FRAGMENT WITH DIFFUSE COLOR

 

end of story

Thanks for the replies guys. :)

@dietrich- Right you are! I have moved the light into view space and taken away attenuation for the time being to turn it in to a directional light only. 

Things are looking better, but still some anomalies.

NqeIbs1.png

The only thing I can see is that the light position is off on the z-axis.

Sphere is at 0,0,0
light is at -5, 2, 5
camera is at 0, 0, -5

Going by this, the light should be behind the object, not in front.

Does this give a hint at any calculation I may have missed?

Here is the shader in its current form.


attribute vec3 in_Position;
attribute vec3 in_Normal;
attribute vec4 in_Colour;

varying vec3 v_Position;
varying vec3 v_Normal;
varying vec4 v_Colour;
varying vec3 v_LightPos;

uniform vec3 u_LightPos;
uniform mat4 worldMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

void main()
{
    vec4 object_space_pos = vec4(in_Position, 1.0);
    gl_Position = worldMatrix * vec4(in_Position, 1.0);
    gl_Position = viewMatrix * gl_Position;
    gl_Position = projectionMatrix * gl_Position;

    mat4 WV = worldMatrix * viewMatrix;
    
    v_Position = vec3(WV * object_space_pos);
    v_Normal = vec3(WV * vec4(in_Normal, 0.0));
    v_Colour = in_Colour;
    
    vec4 object_space_light = vec4(u_LightPos, 1.0);
    v_LightPos = vec3(WV * object_space_light);
}

and


varying vec3 v_Position;
varying vec3 v_Normal;
varying vec4 v_Colour;
varying vec3 v_LightPos;

void main()
{
    float dist = length(v_LightPos - v_Position);
    vec3 lightVector = normalize(v_LightPos - v_Position);
    float diffuse_light = max(dot(v_Normal, lightVector), 0.1);
//  diffuse_light = diffuse_light * (1.0 / (1.0 + (0.0000001 * dist * dist)));
    
//    gl_FragColor = v_Colour * diffuse_light;
    gl_FragColor = vec4(v_Colour.rgb * diffuse_light, v_Colour.a);
}

Thanks again, this is hugely appreciated :)

No problem, @lonewolff

That looks interesting:) worldMatrix is at the moment an identity matrix, correct?

Yes, I believe so.

I am setting it as in identity matrix and am not applying any translations.

Advertisement

Then I'd suggest to simplify things a bit and move the lighting computations into world space, and see if it changes anything.

v_Position will then become


v_Position = vec3(worldMatrix * object_space_pos);

and light position will remain unchanged,


v_LightPos = u_LightPos;

 

Oddly enough, the result is the same.

@dietrich

Arggh! Turns out I was working with a screwy model. I have enabled backface culling and all is now great.

A huge thanks to you though, I have learned a lot today! :D

Ah, well, that happens too, glad to hear it's fixed:)

One more thing: you're currently using v_Normal as is, but you really want to normalize it again in the fragment shader before doing any computations with it (the tutorial seems to be missing this step). Each fragment receives an interpolated normal, and a linear interpolation of unit vectors is not necessarily a unit vector itself, here's a nice illustration (image via google):

Figure1.png

This topic is closed to new replies.

Advertisement