I am trying to use specular highlights in GLSL shaders, but I can't quite get it to work correctly. I am using Haskell, but it should not matter.
I am using OpenGL's matrices instead of uniforms.
Here is how I am "transforming" the player.
glLoadIdentity
glPushAttrib gl_TRANSFORM_BIT
-- Rotate Player
let (xr, yr, zr) = playerRotation player
glRotatef xr (-1) 0 0
glRotatef yr 0 (-1) 0
glRotatef zr 0 0 (-1)
-- Translate Player
let (x, y, z) = playerPosition player
glTranslatef (-x) (-y) (-z)
-- Reset attributes to former state?
glPopAttrib
After that section of code, I render all the actual renderable objects. So from GLSL's perspective, the camera is at (0, 0, 0), and everything else is transformed inversely to the player's position. Pretty standard.
So the following shaders don't work, they basically make it look like the light follows the player everywhere:
Vertex:
#version 400 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
layout(location = 3) in vec3 color;
layout(location = 4) in float textureId;
out vec3 fragColor;
out vec3 vertex;
out vec2 textureCoord;
out vec3 norm;
out int texId;
void main()
{
vertex = position;
textureCoord = texCoord;
norm = normal;
fragColor = color;
// Excuse this
texId = int(textureId);
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0);
}
Fragment:
#version 400
in vec3 fragColor;
in vec3 vertex;
in vec3 norm;
in vec2 textureCoord;
in int texId;
out vec4 outColor;
// Not used
layout(location = 6) uniform vec3 cameraPosition;
layout(location = 7) uniform sampler2D[7] textures;
vec3 lightPos = vec3(3, 1, 0);
void main()
{
//Position of vertex in modelview space
vec3 vertexPosition = (gl_ModelViewMatrix * vec4(vertex, 1.0)).xyz;
//Surface normal of current vertex
vec3 surfaceNormal = normalize((gl_NormalMatrix * norm).xyz);
//Direction light has traveled to get to vertexPosition
vec3 lightDirection = normalize(lightPos - vertexPosition);
//Basically how much light is hitting the vertex
float diffuseLightIntensity = max(0.0, dot(surfaceNormal, lightDirection));
//"Main color"(diffuse) of vertex
vec3 diffColor = diffuseLightIntensity * fragColor;
//Adjust color depending upon distance from light
diffColor /= max(distance(lightPos, vertexPosition)/10, 1);
//Lowest light level possible
vec3 ambColor = vec3(0.01, 0.01, 0.01);
//"View vector"
vec3 viewVec = normalize(-vertexPosition);
//Direction light is reflected off of surface normal
vec3 reflectionDirection = normalize(reflect(-lightDirection, surfaceNormal));
//The intensity of reflection (specular)
float specular = max(0.0, dot(reflectionDirection, viewVec));
float shininess = 2.0;
float totalSpec = pow(specular, shininess);
totalSpec /= max(distance(gl_LightSource[0].position.xyz, vertexPosition)/4, 1);
vec3 specColor = vec3(totalSpec, totalSpec, totalSpec);
// Excuse this
if(texId != -1)
{
vec4 textureColor = texture(textures[texId], textureCoord);
outColor = vec4(ambColor, 1.0) + textureColor + vec4(specColor, 1.0);
}
else
{
outColor = vec4(ambColor, 1.0) + vec4(diffColor + specColor, 1.0);
}
}
I am simply passing the position of the player to cameraPosition using glUniform
.
How can I make the specular highlights work with my given variables?
Looks to me as is the light direction us always the same, regardless of the camera position. Try to define the light position in the vertex shader, transform it using the model view matrix and pass it to the fragment shader.