Trouble rendering specular light with Phong Shading

Started by
5 comments, last by TheStudent111 7 years, 10 months ago

I've been trying to use the Phong shading technique, also known as ADS shading, but have had trouble getting the right result. What I am looking for is the specular effect that is seen in the combined(phong) image below, the bright spot. The problem is my implementation does not give that effect. What can I be doing wrong?

Vertex Shader


#version 410



layout (location = 0) in vec3 VertexPosition;

layout (location = 2) in vec3 VertexNormal;



out vec3 LightIntensity;



struct LightInfo

{

    vec4 Position;    // Light position in eye coords

    vec3 La;          // Ambient light intensity

    vec3 Ld;          // Diffuse light intensity    

    vec3 Ls;          // Specular light intensity



};



uniform LightInfo Light;



struct MaterialInfo

{

    vec3 Ka;            // Ambient reflectivity

    vec3 Kd;            // Diffuse reflectivity

    vec3 Ks;            // Specular reflectivity

    float Shininess;     // Specular shininess factor

};



uniform MaterialInfo Material;



uniform mat4 ModelViewMatrix;

uniform mat3 NormalMatrix;

uniform mat4 rotation_matrix;

uniform mat4 ProjectionMatrix;

uniform mat4 PVM;



void main()

{

    vec3 tnorm     = normalize(NormalMatrix * VertexNormal);

    vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition, 1.0f);



    vec3 s = normalize(vec3(Light.Position - eyeCoords));



    vec3 v = normalize(-eyeCoords.xyz);



    vec3 r = reflect(-s, tnorm);



    vec3 ambient = Light.La * Material.Ka;



    float sDotN = max(dot(s, tnorm), 0.0);



    vec3 diffuse = Light.Ld * Material.Kd * sDotN;



    vec3 spec = vec3(0.0);



    if(sDotN > 0.0)

        spec = Light.Ls * Material.Ks * pow(max(dot(r,v), 0.0), Material.Shininess);



    LightIntensity = ambient + diffuse + spec;



    gl_Position = PVM * rotation_matrix * vec4(VertexPosition, 1.0);



}

 

Fragment Shader


#version 400



in vec3 LightIntensity;



layout (location = 0) out vec4 FragColor;



void main()

{

    FragColor = vec4(LightIntensity, 1.0);

}

Application




        glm::mat4 model_matrix(1.0f);

        glm::mat4 view_matrix = glm::lookAt(glm::vec3(4.0f, 3.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

        

        glm::mat4 ModelViewMatrix =  view_matrix * model_matrix;



        glm::mat3 NormalMatrix      =  glm::mat3(1.0f);



        glm::mat4x4 rot_matrix(1.0f);

        glm::mat4x4 proj_matrix = glm::perspective(45.0f, 4.0f/ 3.0f, 0.1f, 100.0f);

        glm::mat4x4 PVM = proj_matrix * view_matrix * model_matrix;



    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
 

Loop


  

        glUniformMatrix4fv(rot_location,  1, GL_FALSE, glm::value_ptr(rot_matrix));

        glUniformMatrix4fv(PVM_Location, 1, GL_FALSE, glm::value_ptr(PVM));



        glUniformMatrix4fv(ModelViewMatrix_location, 1, GL_FALSE, glm::value_ptr(ModelViewMatrix));



        glUniformMatrix3fv(NormalMatrix_location, 1, GL_FALSE, glm::value_ptr(glm::mat3(glm::vec3(ModelViewMatrix[0]), glm::vec3(ModelViewMatrix[1]), glm::vec3(ModelViewMatrix[2]))));



        glm::vec4 worldLight = view_matrix * lightpos;



        glUniform4f(LightPos_location, worldLight.x, worldLight.y, worldLight.z, worldLight.w);

        glUniform3f(KD_location, kd.x, kd.y, kd.z);

        glUniform3f(LD_location, ld.x, ld.y, ld.z);

        glUniform3f(MatKd_location, 0.9f, 0.5f, 0.3f);

        glUniform3f(MatKa_location, 0.9f, 0.5f, 0.3f);

        glUniform3f(MatKs_location, 0.8f, 0.8f, 0.8f);

        glUniform1f(MatShin_location, 100.0f);

        glUniform3f(LightLd_location, 1.0f, 1.0f, 1.0f);

        glUniform3f(LightLa_location, 0.4f, 0.4f, 0.4f);

        glUniform3f(LightLs_location, 1.0f, 1.0f, 1.0f);
Advertisement

You probably have to adjust your shininess. Take a look at this for comparison

fig5_12.jpg

Problem is my shininess is at 100,


glUniform1f(MatShin_location, 100.0f);


Could it be because it is per vertex and not per fragment? Edited the code, the result of the image is below.

Shininess was dropped to 1.0f.

If it's per vertex, you're not going to get a nice spot like that unless your cube has far more vertices than it really needs. So, yes, doing it in the vertex shader will keep you from getting that nice round spot on a cube.

You might check out my video series on the subject. It's in HLSL, but most of it is math anyway.

I'm doing OGL now, as soon as I can learn OGL and get my stuff converted over anyway. In the very near future (within a month probably and maybe as soon as next weekend), I'll be rewriting this shader in GLSL. At that point I could just hand you the code, but you caught me a couple weeks too early. In the meantime, those HLSL videos might be helpful. I'm literally about half a day away from rewriting this shader in OGL, but right now finding a half a day of free time in my life can be a challenge.

It looks like you're applying the shader per vertex, which amounts this to a goround shading. This can be visibly seen when you turned up the specular intensity. See how the light appears the strongest on one side of the box? And then how the specular seems to radiate outwards from the vertex? That is a result of vertex shading. When you shade per vertex, the shading gets interpolated with other vertexes.

Phong is a per pixel shader, which means that the magic needs to happen in the fragment shader. Move your shading code into the fragment shader instead, and it should fix it's self.

Here is my spot light fragment shader - perhaps looking at the source will help you out. I left irrelevant portions out.


struct SpotLight
{
	float ambientIntensity;
	float diffuseIntensity;
	vec3 color;
	vec3 position;
	float attConstant;
	float attLinear;
	float attExp;
	float shadowDarkness;
	vec3 direction;
	float cutoff;
};

struct Material
{
	float spec_power;
	float spec_intensity;
	vec3 spec_color;
};

uniform SpotLight light;
uniform bool castShadows;

uniform Material materials[MAX_MATERIALS];

vec4 light_influence(vec3 worldPos, vec3 norm, Material mat)
{
	vec3 lightDirection = normalize(light.direction); // used just for spot light attenuation

	vec3 lightToPixel = worldPos - light.position;
	float distance = length(lightToPixel);
	lightToPixel = normalize(lightToPixel);

	float spotFactor = dot(lightToPixel, lightDirection);
	float shadowFactor = 1.0;
	if (castShadows)
		shadowFactor = shadow_factor(worldPos);

	if (spotFactor > light.cutoff)
	{
		vec4 ambientColor = vec4(light.color, 1.0f) * light.ambientIntensity;
		float diffuseFactor = dot(norm, -lightToPixel);

		vec4 diffuseColor  = vec4(0, 0, 0, 0);
		vec4 specularColor = vec4(0, 0, 0, 0);

		if (diffuseFactor > 0) 
		{
			diffuseColor = vec4(light.color, 1.0f) * light.diffuseIntensity * diffuseFactor;
			vec3 vertexToEye = normalize(camWorldPos - worldPos);
			vec3 lightReflect = normalize(reflect(lightToPixel, norm));
			float specularFactor = dot(vertexToEye, lightReflect);
			specularFactor = pow(specularFactor, mat.spec_power);
			if (specularFactor > 0)
			{
				specularColor = vec4(mat.spec_color, 1.0f) * mat.spec_intensity * specularFactor;
			}
		}

		vec4 color = ambientColor + shadowFactor*(diffuseColor + specularColor);

		// light attenuation
		float attenuation = 1.0f / (light.attConstant + light.attLinear * distance + light.attExp * distance * distance);
		color *= attenuation;

		// Spot light attenuation formula
		return color * ( 1.0 - ( (1.0 - spotFactor) / (1.0 - light.cutoff) ) );
	}
	else
	{
		return vec4(0,0,0,0);
	}
}

The main thing to note is that worldPos and norm (passed in to light_influence function) should be the fragment's worldPos and norm - that is the interpolated value between the vertices.

You can get this in a number of ways - the easiest for you would be to output them from the vertex shader and input them in the fragment.. ie..


// vertex shader

out vec3 normal;
out vec4 worldPos;
// assign a value to normal and worldPos - likely multiplying by your world and camera tform matrices

// Fragment shader
in vec3 normal;
in vec4 worldPos;

Like others have mentioned - trying to calculate the lighting per vertex is not going to work well for that cube.

Thank you, for answering everybody. Still having trouble with the specular highlight. The one on the left has a shininess value of 1.0f while the one on the right has the value of 100.

Vertex Shader:


#version 400



layout (location = 0) in vec3 VertexPosition;

layout (location = 2) in vec3 VertexNormal;



out vec3 fragPosition;

out vec3 fragNormal;



uniform mat4 rotation_matrix;

uniform mat4 PVM;



void main()

{



    fragPosition = VertexPosition;

    fragNormal   = VertexNormal;

    gl_Position = PVM * rotation_matrix * vec4(VertexPosition, 1.0);

}

 

Fragment Shader:


#version 400



layout (location = 0) out vec4 FragColor;



in vec3 fragPosition;

in vec3 fragNormal;

out vec3 LightIntensity;



struct LightInfo

{

    vec4 Position;    // Light position in eye coords

    vec3 La;          // Ambient light intensity

    vec3 Ld;          // Diffuse light intensity    

    vec3 Ls;          // Specular light intensity



};



uniform LightInfo Light;



struct MaterialInfo

{

    vec3 Ka;             // Ambient reflectivity

    vec3 Kd;             // Diffuse reflectivity

    vec3 Ks;             // Specular reflectivity

    float Shininess;     // Specular shininess factor

};



uniform MaterialInfo Material;



uniform mat4 ModelViewMatrix;

uniform mat3 NormalMatrix;





void main()

{

    vec3 tnorm     = normalize(NormalMatrix * fragNormal);

    vec4 eyeCoords = ModelViewMatrix * vec4(fragPosition, 1.0f);



    vec3 s = normalize(vec3(Light.Position - eyeCoords));



    vec3 v = normalize(-eyeCoords.xyz);



    vec3 r = reflect(-s, tnorm);



    vec3 ambient = Light.La * Material.Ka;



    float sDotN = max(dot(s, tnorm), 0.0);



    vec3 diffuse = Light.Ld * Material.Kd * sDotN;



    vec3 spec = vec3(0.0);



    if(sDotN > 0.0)

        spec = Light.Ls * Material.Ks * pow(max(dot(r,v), 0.0), Material.Shininess);



    LightIntensity = ambient + diffuse + spec;



    FragColor = vec4(LightIntensity, 1.0);

}
 

@EarthBanana, might try your spotlight implementation.

This topic is closed to new replies.

Advertisement