Sure I can post some code and screenshots. It went through lots of changes over time though since I added cubemapping for reflection and other goodies. But I can't for the life of me figure out why specular lighting is backwards. The normals I know are correct because the objects I created on Autodesk 3D Studio Max show the normals are pointing out of the object.
Vertex Shader
#version 300 es
uniform mat4 u_model_matrix;
uniform mat4 u_mvp_matrix;
uniform vec3 u_camera_position;
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec4 a_color;
layout (location = 2) in vec2 a_texturecoordinates;
layout (location = 3) in vec3 a_normal;
out vec2 v_texturecoordinates;
out vec4 v_color;
out vec3 v_normal;
out vec4 v_position;
out vec3 v_model_normal;
out vec3 v_model_position;
out vec3 v_model_camera_position;
void main() {
v_color = a_color;
v_texturecoordinates = a_texturecoordinates;
gl_Position = u_mvp_matrix * vec4(vec3(a_position), 1.0);
v_normal = a_normal;
v_position = a_position;
v_model_position = vec3(u_model_matrix * vec4(vec3(a_position), 1.0)).xyz;
v_model_normal = vec3(u_model_matrix * vec4(a_normal, 0.0));
v_model_camera_position = vec3(u_model_matrix * vec4(u_camera_position, 1.0)).xyz;
}
Fragment Shader
#version 300 es
precision highp float;
// Texture units
uniform sampler2D u_textureunit;
uniform samplerCube u_cubemap_unit;
// Texture enabled
uniform int u_texture_enabled;
uniform int u_cubemap_enabled;
// Matrices
uniform mat4 u_view_matrix;
uniform mat4 u_model_matrix; // model: local and world matrices combined
uniform mat4 u_model_view_matrix;
// Light enabled
uniform int u_light_enabled;
// Light Color
uniform vec4 u_ambient_color;
uniform vec4 u_diffuse_color;
uniform vec4 u_specular_color;
// Enabled light types
uniform int u_ambient_enabled;
uniform int u_diffuse_enabled;
uniform int u_specular_enabled;
// Light intensities
//TODO diffuse intensity
uniform float u_specular_intensity;
// Positions
uniform vec3 u_object_position;
uniform vec3 u_camera_position;
uniform vec3 u_light_position;
// Angles
uniform vec3 u_camera_angle;
uniform vec3 u_light_direction;
// Selectable color;
uniform vec4 u_RGBA;
// Enabled two sided lit side
uniform int u_two_sided_enabled;
// Reverse reflection
uniform int u_reverse_reflection;
uniform int u_invert_normals;
uniform int u_invert_x_normal;
uniform int u_invert_y_normal;
uniform int u_invert_z_normal;
// Light type
uniform int u_light_type;
in vec2 v_texturecoordinates;
in vec4 v_color;
in vec3 v_normal;
in vec4 v_position;
in vec3 v_model_normal;
in vec3 v_model_position;
in vec3 v_model_camera_position;
out vec4 color;
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
mat4 view_matrix;
mat4 model_matrix;
mat4 model_view_matrix;
vec3 model_position;
vec3 model_camera_position;
vec3 model_light_position;
vec3 model_normal;
// Not used
//////////////////////////////////
vec3 modelview_position;
vec3 modelview_camera_position;
vec3 modelview_light_position;
vec3 modelview_normal;
//////////////////////////////////
float diffuseFactor;
float specularityFactor;
vec3 finalLitColor;
vec3 linearColor;
vec3 gamma = vec3(1.0/2.2);
vec3 hdrColor;
vec3 toneMap;
vec3 normal;
vec3 camera_world_position;
vec3 object_world_position;
vec3 light_world_position;
vec3 light_direction;
void ambientLight(){
if(u_ambient_enabled == 1){
ambient_color = u_ambient_color;
}
else{
ambient_color = vec4(1.0, 1.0, 1.0, 1.0);
}
}
void diffuseLight(){
if(u_diffuse_enabled == 1){
// Use only modelNormal, not modelViewNormal
// Reason is because it will change colors as you move the camera around,
// which is not a real world scenario
// Observations:
// normal = normalize(v_model_normal) means the values of the norm will never change.
// For example when the object rotates, the light colors are stuck on
// that side of the object!
diffuseFactor = max(0.0, dot(normal, light_direction));
diffuse_color = clamp(vec4(diffuseFactor * u_diffuse_color.rgb, 1.0), 0.0, 1.0);
}
}
void specularLight(){
if (u_specular_enabled == 1){
// Use model_position, not vec3(v_model_position). When the object rotates, once the normals
// point the other way, specular light disappears! With model_position, the light
// stays on them at least the whole way round.
// the reflect() method does this formula: reflect(I, N) = I - 2.0 * dot(N, I) * N
vec3 reflect_direction = reflect(-light_direction, normal);
vec3 camera_direction = normalize(camera_world_position - object_world_position);
float cos_angle = max(0.0, dot(camera_direction, reflect_direction));
specularityFactor = 0.0;
if (diffuseFactor >= 0.0) {
specularityFactor = pow(cos_angle, u_specular_intensity);
}
specular_color = clamp(vec4(vec3(u_specular_color) * specularityFactor, 1.0), 0.0, 1.0);
}
}
void main() {
model_matrix = u_model_matrix;
mat4 TI_model_matrix = transpose(inverse(u_model_matrix));
model_position = vec3(model_matrix * vec4(vec3(v_position), 1.0));
model_camera_position = vec3(model_matrix * vec4(u_camera_position, 1.0));
model_light_position = vec3(model_matrix * vec4(u_light_position, 1.0));
model_normal = normalize(vec3(TI_model_matrix * vec4(v_normal, 0.0)));
if (u_invert_normals == 1) {
model_normal = model_normal * vec3(-1.0, -1.0, -1.0);
}
if (u_invert_x_normal == 1) {
model_normal = model_normal * vec3(-1.0, 1.0, 1.0);
}
if (u_invert_y_normal == 1) {
model_normal = model_normal * vec3(1.0, -1.0, 1.0);
}
if (u_invert_z_normal == 1) {
model_normal = model_normal * vec3(1.0, 1.0, -1.0);
}
object_world_position = model_position;
camera_world_position = model_camera_position;
light_world_position = u_light_position;
normal = model_normal;
vec4 texture_color0 = texture(u_textureunit, v_texturecoordinates);
/* reflect ray around normal from eye to surface */
vec3 incident_eye = normalize(camera_world_position - object_world_position);
vec3 reflect_normal = model_normal;
vec3 reflected = reflect(-incident_eye, reflect_normal);
reflected = vec3(inverse(u_view_matrix) * vec4(reflected, 0.0));
vec4 texture_color1 = texture(u_cubemap_unit, reflected);
if (u_light_enabled == 1){
if (u_light_type == 0) {
// Point Light
light_direction = normalize(light_world_position - object_world_position);
}
else if (u_light_type == 1) {
// Directional Light
light_direction = normalize(u_light_direction);
}
else if (u_light_type == 2) {
// TODO Spot Light
light_direction = vec3(0.0);
}
ambientLight();
diffuseLight();
specularLight();
finalLitColor = vec3(1.0, 1.0, 1.0);
if (u_light_type == 0) {
// Point Light
float dist = distance(light_world_position, object_world_position);
float attenuation_constant = 1.0; // Infinite light emmission for now.
float attenuation_linear = 0.0;
float attenuation_exp = 0.0;
float attenuation = 1.0 / (attenuation_constant + attenuation_linear * dist + attenuation_exp * dist * dist);
linearColor = vec3(u_ambient_color.r + attenuation * (diffuse_color.r + specular_color.r),
u_ambient_color.g + attenuation * (diffuse_color.g + specular_color.g),
u_ambient_color.b + attenuation * (diffuse_color.b + specular_color.b));
vec3 gamma = vec3(1.0/2.2);
finalLitColor = pow(linearColor, gamma);
}
else {
// Directional Light
linearColor = vec3(u_ambient_color.r + diffuse_color.r + specular_color.r,
u_ambient_color.g + diffuse_color.g + specular_color.g,
u_ambient_color.b + diffuse_color.b + specular_color.b);
vec3 gamma = vec3(1.0/2.2);
finalLitColor = pow(linearColor, gamma);
}
if (u_texture_enabled == 1 && u_cubemap_enabled == 1){
// Incase you forget how to add a toneMap overtime...
// gl_FragColor = vec4(toneMap.rgb, texture(u_textureunit, v_texturecoordinates).a) *
// Problem is, is that it is not that colorful. its a dull resident evil greyish world then
color = vec4(texture_color0.r + texture_color1.r,
texture_color0.g + texture_color1.g,
texture_color0.b + texture_color1.b,
texture_color0.a + texture_color1.a) *
vec4(v_color.r * finalLitColor.r * u_RGBA.r,
v_color.g * finalLitColor.g * u_RGBA.g,
v_color.b * finalLitColor.b * u_RGBA.b,
v_color.a * u_RGBA.a);
}
else if (u_texture_enabled == 1 && u_cubemap_enabled == 0){
color = vec4(texture_color0.r,
texture_color0.g,
texture_color0.b,
texture_color0.a) *
vec4(v_color.r * finalLitColor.r * u_RGBA.r,
v_color.g * finalLitColor.g * u_RGBA.g,
v_color.b * finalLitColor.b * u_RGBA.b,
v_color.a * u_RGBA.a);
}
else if (u_texture_enabled == 0 && u_cubemap_enabled == 1){
color = vec4(texture_color1.r,
texture_color1.g,
texture_color1.b,
texture_color1.a) *
vec4(v_color.r * finalLitColor.r * u_RGBA.r,
v_color.g * finalLitColor.g * u_RGBA.g,
v_color.b * finalLitColor.b * u_RGBA.b,
v_color.a * u_RGBA.a);
}
else{
color = vec4(v_color.r * finalLitColor.r * u_RGBA.r,
v_color.g * finalLitColor.g * u_RGBA.g,
v_color.b * finalLitColor.b * u_RGBA.b,
v_color.a * u_RGBA.a);
}
}
else{
// No light
if (u_texture_enabled == 1){
color = texture(u_textureunit, v_texturecoordinates) * vec4(v_color.r * u_RGBA.r, v_color.g * u_RGBA.g, v_color.b * u_RGBA.b, v_color.a * u_RGBA.a);
}
else{
color = vec4(v_color.r * u_RGBA.r, v_color.g * u_RGBA.g, v_color.b * u_RGBA.b, v_color.a * u_RGBA.a);
}
}
}
Now, with this current code, I get this on the front side...
![920693317_LightScreenshot1.png.51318d24603ceacfe01a746c066899e5.png](https://uploads.gamedev.net/monthly_2019_05/920693317_LightScreenshot1.png.51318d24603ceacfe01a746c066899e5.png)
...and this on the back side when i move the camera over. And here you can clearly see the light source which is slightly behind the camera towards the left assuming I was facing front
![563101817_LightScreenshot2.png.c4e6944b90e4c931af7621003466f689.png](https://uploads.gamedev.net/monthly_2019_05/563101817_LightScreenshot2.png.c4e6944b90e4c931af7621003466f689.png)
As you can see, the specular light is backwards. Hitting the opposite of side of where it suppose to be. But its only backwards for the Z's and Y's, not the X. Besides it being backwards for the Z side, when the objects above the light source, the light hits the top of the object (suppose to be the bottom). When its below the light source, the light hits the bottom of the object (suppose to be the top). However the X's are correct which is strange. So in my specularLight() method where I'm reflecting:
vec3 reflect_direction = reflect(-light_direction, normal);
and I take away the negative light direction and make it positve, I get this:
![2084433257_LightScreenshot3.png.11b8a1dce18112e4166908907dd729db.png](https://uploads.gamedev.net/monthly_2019_05/2084433257_LightScreenshot3.png.11b8a1dce18112e4166908907dd729db.png)
Which doesn't make sense because now the math is backwards. But I get what seems to be correct results.....sort of. Keyword there. Now this is ok for the Mario guy and sphere in the background, seems legit on all sides and positions and angles, but when I use a tubular object such as this ship in the image or a flat polygonal quad, the specular light is now going the wrong way in the X direction. Take a look at what happens when I move the ship to the left of the light:
![471005436_LightScreenshot4.png.94fd24a0e509a3cc0efda00e1ea86929.png](https://uploads.gamedev.net/monthly_2019_05/471005436_LightScreenshot4.png.94fd24a0e509a3cc0efda00e1ea86929.png)
And its true for vise versa. I don't know if the code written in those articles are for left handed coordinate systems or what. But I'm using right handed. And OpenGL by default is right handed. So I'm completely confused as to why its backwards whether just in the X direction or the Y-Z direction. Hopefully I was clear enough there ![:P :P](https://uploads.gamedev.net/emoticons/medium.tongue.webp)