• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

3D Not understanding specular light

F

Fishman1175

Guest
Hey all,

I've been trying to learn shaders for a little bit, and now I've had a lot of trouble with a simple specular light shader.

I've been following this article series:
http://learnwebgl.brown37.net/09_lights/lights_specular.html

My code is pretty much copied from that article (with a bunch of stuff commented out... I was experimenting)
Vertex shader
Code:
//
attribute vec3 in_Position;                  // (x,y,z)
attribute vec3 in_Normal;                  // (x,y,z)     
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec3 v_vNormal;
varying vec3 v_vVertex;
//varying vec3 v_vCameraVertex;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
    
    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
    
    v_vVertex = vec3(gm_Matrices[MATRIX_WORLD] * vec4(in_Position, 1.0));
    //v_vCameraVertex = vec3(gm_Matrices[MATRIX_WORLD_VIEW] * vec4(in_Position, 1.0));
    v_vNormal = vec3(gm_Matrices[MATRIX_WORLD] * vec4(in_Normal, 0.0));
}
Fragment
Code:
//
// Specular lighting
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec3 v_vNormal;
varying vec3 v_vVertex;
varying vec3 v_vCameraVertex;

uniform vec3 u_vLight;
uniform vec3 u_vAmbient;
uniform float u_fShin;

void main()
{
    vec3 to_light;
    vec3 vertex_normal;
    vec4 col;
    float cos_angle;
    vec3 diffuse_light;
    vec3 spec_light;
    vec3 to_camera;
    vec3 reflection;
    float shine_alpha;
    
    // Calculate a vector from the fragment location to the light source
    to_light = u_vLight - v_vVertex;
    to_light = normalize(to_light);
    
    // The vertex's normal vector is being interpolated across the primitive
    // which can make it un-normalized. So normalize the vertex's normal vector.
    vertex_normal = normalize(v_vNormal);
    
    // Calculate the cosine of the angle between the vertex's normal vector
    // and the vector going to the light.
    cos_angle = dot(vertex_normal, to_light);
    cos_angle = clamp(cos_angle, 0.0, 1.0);
    
    diffuse_light = vec3(v_vColour)*cos_angle;
    
    reflection = (2.0 * dot(vertex_normal,to_light) * vertex_normal) - to_light;

    // Calculate a vector from the fragment location to the camera.
    // The camera is at the origin, so negating the vertex location gives the vector
    to_camera = -1.0 * v_vVertex; //this seems to be the problem...
    
    // Calculate the cosine of the angle between the reflection vector
    // and the vector going to the camera.
    reflection = normalize( reflection );
    to_camera = normalize( to_camera );
    cos_angle = dot(reflection, to_camera);
    cos_angle = clamp(cos_angle, 0.0, 1.0);
    shine_alpha = cos_angle;
    cos_angle = pow(cos_angle, u_fShin);
    
    col = vec4(/*u_vAmbient + diffuse_light + */ vec3(1.0,1.0,1.0) * cos_angle,1.0);
    col = col * texture2D( gm_BaseTexture, v_vTexcoord );
    //col.a = max(col.a,shine_alpha);
    gl_FragColor = col;
}
Draw code:
the shininess is 0.8
Code:
var am = obj_ads_settings.ambient;
shader_set(shd_ads_light);
var shader_param = shader_get_uniform(shd_ads_light,"u_vLight");
var ambient_param = shader_get_uniform(shd_ads_light,"u_vAmbient");
var shin_param = shader_get_uniform(shd_ads_light,"u_fShin");
shader_set_uniform_f(shader_param,obj_light.x,obj_light.y,obj_light.z);
shader_set_uniform_f(ambient_param,am,am,am);
shader_set_uniform_f(shin_param,shin);
d3d_model_draw(model,0,0,z,background_get_texture(b_dblue));
shader_reset();

The little orange ball is the light source. As you can see, the well with water is totally dark (I'm only drawing specular light though). The sphere has nonsensical lighting.


I have a couple questions.
1. In the article, they use the "model view" matrix, but game maker has no such named thing. I've been using the "world" matrix in game maker. It worked for the diffuse shader. Is this the right matrix? I tried all of them but none of them worked.

2. I want to say that my problem comes from calculating the vector to the camera. I hardcoded a value in there, moved the camera in my game to that position, and the shader appeared to work. I'm not sure how to calculate the to_camera vector using the "world" matrix.
 

Kentae

Member
Is all that fragment code really just for a specular light? o_O

Seems a tad overkill.
Here is my specular shader. You can copy the code if you want but it's probably a good idea to study the code and learn from it too ^^

VERTEX:
Code:
attribute vec3 in_Position;                  // (x,y,z)
attribute vec3 in_Normal;                    // (x,y,z)
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec3 v_vPosition;
varying vec3 v_vNormal;
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

//uniform mat4 viewMatrix;

varying vec3 toCameraVec;

vec3 cameraPos = ( -gm_Matrices[MATRIX_VIEW][3] * gm_Matrices[MATRIX_VIEW] ).xyz;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
 
    v_vPosition = in_Position;
    v_vNormal = (gm_Matrices[MATRIX_WORLD] * vec4( in_Normal, 0.0 )).xyz;
    v_vColour = in_Colour;
    v_vTexcoord = in_TextureCoord;
 
    toCameraVec = cameraPos - ( gm_Matrices[MATRIX_WORLD] * object_space_pos ).xyz;
}
FRAGMENT:
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec3 v_vNormal;
varying vec3 v_vPosition;

uniform float reflection;
uniform float damping;

varying vec3 toCameraVec;

void main()
{

    vec4 Col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
 
    // diffuse light
    vec3 lightDir = vec3( 0.5, 0.5, 1.0);
    vec3 lightCol = vec3( 0.8, 0.8, 1.0);
    float lighting = (dot( normalize(v_vNormal), normalize(lightDir)) * 0.5 + 0.5);
 
    // specular
    vec3 reflectedLight = reflect( normalize(-lightDir), normalize(v_vNormal) );
    float specFactor = dot( normalize( reflectedLight ), normalize( toCameraVec ) );
    specFactor = max( specFactor, 0.0 );
    float dampFactor = pow( specFactor, damping );
 
    vec3 finalSpec = dampFactor * reflection * Col.xyz;

    gl_FragColor = Col * vec4( lighting * lightCol, 1.0) + vec4( finalSpec, 0.0 );
}
Hope it helps ^^

EDIT: Mind you that this shader also includes a per-pixel directional light ^^
 
Last edited:
F

Fishman1175

Guest
Thanks for the response Kentae!

Could you explain how you came up with these lines in the vertex shader? I understand why they are used in the fragment shader, but not why these specific calculations work.
Code:
vec3 cameraPos = ( -gm_Matrices[MATRIX_VIEW][3] * gm_Matrices[MATRIX_VIEW] ).xyz;
Code:
toCameraVec = cameraPos - ( gm_Matrices[MATRIX_WORLD] * object_space_pos ).xyz;
 
Top