SpotLight is not seen - OpenGL - c++

I am doing a project on spotlight in OpenGL. I guess I wrote the code correctly but I couldn't able to see a round spot in my output. Your help would be much appreciated. Here I am writing my fragment shader file and light definition.
fragmentShader.fs
#version 330
in vec3 N; // interpolated normal for the pixel
in vec3 v; // interpolated position for the pixel
// Uniform block for the light source properties
layout (std140) uniform LightSourceProp {
// Light source position in eye space (i.e. eye is at (0, 0, 0))
uniform vec4 lightSourcePosition;
uniform vec4 diffuseLightIntensity;
uniform vec4 specularLightIntensity;
uniform vec4 ambientLightIntensity;
// for calculating the light attenuation
uniform float constantAttenuation;
uniform float linearAttenuation;
uniform float quadraticAttenuation;
// Spotlight direction
uniform vec3 spotDirection;
uniform float cutOffExponent;
// Spotlight cutoff angle
uniform float spotCutoff;
};
// Uniform block for surface material properties
layout (std140) uniform materialProp {
uniform vec4 Kambient;
uniform vec4 Kdiffuse;
uniform vec4 Kspecular;
uniform float shininess;
};
out vec4 color;
// This fragment shader is an example of per-pixel lighting.
void main() {
// Now calculate the parameters for the lighting equation:
// color = Ka * Lag + (Ka * La) + attenuation * ((Kd * (N dot L) * Ld) + (Ks * ((N dot HV) ^ shininess) * Ls))
// Ka, Kd, Ks: surface material properties
// Lag: global ambient light (not used in this example)
// La, Ld, Ls: ambient, diffuse, and specular components of the light source
// N: normal
// L: light vector
// HV: half vector
// shininess
// attenuation: light intensity attenuation over distance and spotlight angle
vec3 lightVector;
float attenuation = 1.0;
float se;
// point light source
lightVector = normalize(lightSourcePosition.xyz - v);
//Calculate Spoteffect
// calculate attenuation
float angle = dot( normalize(spotDirection),
normalize(lightVector));
angle = max(angle,0);
// Test whether vertex is located in the cone
if(acos (angle) > radians(5))
{
float distance = length(lightSourcePosition.xyz - v);
angle = pow(angle,2.0);
attenuation = angle / (constantAttenuation + (linearAttenuation * distance)
+(quadraticAttenuation * distance * distance));
//calculate Diffuse Color
float NdotL = max(dot(N,lightVector), 0.0);
vec4 diffuseColor = Kdiffuse * diffuseLightIntensity * NdotL;
// calculate Specular color. Here we use the original Phong illumination model.
vec3 E = normalize(-v); // Eye vector. We are in Eye Coordinates, so EyePos is (0,0,0)
vec3 R = normalize(-reflect(lightVector,N)); // light reflection vector
float RdotE = max(dot(R,E),0.0);
vec4 specularColor = Kspecular * specularLightIntensity * pow(RdotE,shininess);
// ambient color
vec4 ambientColor = Kambient * ambientLightIntensity;
color = ambientColor + attenuation * (diffuseColor + specularColor);
}
else
color = vec4(1,1,0,1); // lit (yellow)
}
The light definition in main.cpp
struct SurfaceMaterialProp {
float Kambient[4]; //ambient component
float Kdiffuse[4]; //diffuse component
float Kspecular[4]; // Surface material property: specular component
float shininess;
};
SurfaceMaterialProp surfaceMaterial1 = {
{1.0f, 1.0f, 1.0f, 1.0f}, // Kambient: ambient coefficient
{1.0f, 0.8f, 0.72f, 1.0f}, // Kdiffuse: diffuse coefficient
{1.0f, 1.0f, 1.0f, 1.0f}, // Kspecular: specular coefficient
5.0f // Shininess
};
struct LightSourceProp {
float lightSourcePosition[4];
float diffuseLightIntensity[4];
float specularLightIntensity[4];
float ambientLightIntensity[4];
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
float spotlightDirection[4];
float spotlightCutoffAngle;
float cutOffExponent;
};
LightSourceProp lightSource1 = {
{ 0.0,400.0,0.0, 1.0 }, // light source position
{1.0f, 0.0f, 0.0f, 1.0f}, // diffuse light intensity
{1.0f, 0.0f, 0.0f, 1.0f}, // specular light intensity
{1.0f, 0.2f, 0.0f, 1.0f}, // ambient light intensity
1.0f, 0.5, 0.1f, // constant, linear, and quadratic attenuation factors
{0.0,50.0,0.0}, // spotlight direction
{5.0f}, // spotlight cutoff angle (in radian)
{2.0f} // spotexponent
};

The order of a couple of members of the LightSourceProp struct in the C++ code is different from the one in the uniform block.
Last two members of the uniform block:
uniform float cutOffExponent;
uniform float spotCutoff;
};
Last two members of C++ struct:
float spotlightCutoffAngle;
float cutOffExponent;
};
These two values are swapped.
Also, the cutoff angle looks suspiciously large:
{5.0f}, // spotlight cutoff angle (in radian)
That's an angle of 286 degrees, which isn't much of a spotlight. For an actual spotlight, you'll probably want something much smaller, like 0.1f or 0.2f.
Another aspect that might be giving you unexpected results is that you have a lot of ambient intensity:
{1.0f, 1.0f, 1.0f, 1.0f}, // Kambient: ambient coefficient
...
{1.0f, 0.2f, 0.0f, 1.0f}, // ambient light intensity
Depending on how you use these values in the shader code, it's likely that your colors will be saturated from the ambient intensity alone, and you won't get any visible contribution from the other terms of the light source and material. Since the ambient intensity is constant, this would result in a completely flat color for the entire geometry.

Related

Lighting abnormal when calculating in tangent space. Probably something wrong with coordinate transformation matrix

I'm trying to calculate lighting in tangent space. But I just keep getting abnormal results. I was modifying the book's demo code and I wander if there maybe something wrong with the transformation matrix I created.
I'm having trouble solving a problem in Introduction to 3D Game Programming with DirectX 11. I tried to use matrix TBN
Tx, Ty, Tz,
Bx, By, Bz,
Nx, Ny, Nz
as the book provided but I found the light vector to be wrongly transformed to tangent space and now I have no clue how to debug this shader.
Here is my Pixel Shader:
float4 PS1(VertexOut pin,
uniform int gLightCount,
uniform bool gUseTexure,
uniform bool gAlphaClip,
uniform bool gFogEnabled,
uniform bool gReflectionEnabled) : SV_Target{
// Interpolating normal can unnormalize it, so normalize it.
pin.NormalW = normalize(pin.NormalW);
pin.TangentW = normalize(pin.TangentW);
// The toEye vector is used in lighting.
float3 toEye = gEyePosW - pin.PosW;
// Cache the distance to the eye from this surface point.
float distToEye = length(toEye);
// Calculate normalMapSample
float3 normalMapSample =
normalize(SampledNormal2Normal(gNormalMap.Sample(samLinear, pin.Tex).rgb));
// normalize toEye
toEye = normalize(toEye);
// Default to multiplicative identity.
float4 texColor = float4(1, 1, 1, 1);
if (gUseTexure)
{
// Sample texture.
texColor = gDiffuseMap.Sample(samLinear, pin.Tex);
if (gAlphaClip)
{
// Discard pixel if texture alpha < 0.1. Note that we do this
// test as soon as possible so that we can potentially exit the shader
// early, thereby skipping the rest of the shader code.
clip(texColor.a - 0.1f);
}
}
//
// Lighting.
//
float4 litColor = texColor;
if (gLightCount > 0)
{
// Start with a sum of zero.
float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
// Sum the light contribution from each light source.
[unroll]
for (int i = 0; i < gLightCount; ++i)
{
float4 A, D, S;
ComputeDirectionalLightInTangent(gMaterial, gDirLights[i],
normalMapSample, World2TangentSpace(pin.NormalW, pin.TangentW, gTexTransform), toEye,
A, D, S);
ambient += A;
diffuse += D;
spec += S;
}
litColor = texColor*(ambient + diffuse) + spec;
if (gReflectionEnabled)
{
float3 incident = -toEye;
float3 reflectionVector = reflect(incident, normalMapSample);
float4 reflectionColor = gCubeMap.Sample(samLinear, reflectionVector);
litColor += gMaterial.Reflect*reflectionColor;
}
}
//
// Fogging
//
if (gFogEnabled)
{
float fogLerp = saturate((distToEye - gFogStart) / gFogRange);
// Blend the fog color and the lit color.
litColor = lerp(litColor, gFogColor, fogLerp);
}
// Common to take alpha from diffuse material and texture.
litColor.a = gMaterial.Diffuse.a * texColor.a;
return litColor;
}
And Here are function SampledNormal2Normal, World2TangentSpace and ComputeDirectionalLightInTangent:
float3 SampledNormal2Normal(float3 sampledNormal)
{
float3 normalT = 2.0f*sampledNormal - 1.0f;
return normalT;
}
float3x3 World2TangentSpace(float3 unitNormalW, float3 tangentW, float4x4 texTransform)
{
// Build orthonormal basis.
float3 N = unitNormalW;
float3 T = normalize(tangentW - dot(tangentW, N)*N);
float3 B = cross(N, T);
float3x3 TBN = float3x3(T, B, N);
/*float3x3 invTBN = float3x3(T.x, T.y, T.z, B.x, B.y, B.z, N.x, N.y, N.z);
return invTBN;*/
float3 T_ = T - dot(N, T)*N;
float3 B_ = B - dot(N, B)*N - (dot(T_, B)*T_) / dot(T_, T_);
float3x3 invT_B_N = float3x3(T_.x, T_.y, T_.z, B_.x, B_.y, B_.z, N.x, N.y, N.z);
return invT_B_N;
}
void ComputeDirectionalLightInTangent(Material mat, DirectionalLight L,
float3 normalT, float3x3 toTS, float3 toEye,
out float4 ambient,
out float4 diffuse,
out float4 spec)
{
// Initialize outputs.
ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
// The light vector aims opposite the direction the light rays travel.
float3 lightVec = -L.Direction;
lightVec = mul(lightVec, toTS);
lightVec = normalize(lightVec);
// toEye to Tangent Space
toEye = mul(toEye, toTS);
toEye = normalize(toEye);
// Add ambient term.
ambient = mat.Ambient * L.Ambient;
// Add diffuse and specular term, provided the surface is in
// the line of site of the light.
float diffuseFactor = dot(lightVec, normalT);
// Flatten to avoid dynamic branching.
[flatten]
if (diffuseFactor > 0.0f)
{
float3 v = reflect(-lightVec, normalT);
float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);
diffuse = diffuseFactor * mat.Diffuse * L.Diffuse;
spec = specFactor * mat.Specular * L.Specular;
}
}
The result I got seem to be much darker in most places and too bright in several highlight area. I wonder if anyone can help me with my code or give me advice on how to debug a hlsl shader. My thousand thanks!

Lighting in C++ using a glsl

I am currently using this glsl file to handle lighting for a 3d object that I am trying to display. I am not sure what values I need to put in for light_position_world, Ls, Ld, La, Ks, Kd, Ka, Ia and fragment_color. The scene I am trying to illuminate is centered at (427, 385, 89) roughly. I dont need it to be perfect but I need some values that will let me see my design on screen so that I can manipulate them and learn how this all works. Any additional tips or explanation would be much appreciated. Thanks!
#version 410
in vec3 position_eye, normal_eye;
uniform mat4 view_mat;
// fixed point light properties
vec3 light_position_world = vec3 (427.029, 385.888, 0);
vec3 Ls = vec3 (1.0f, 0.0f, 0.0f);
vec3 Ld = vec3 (1.0f, 0.0f, 0.0f);
vec3 La = vec3 (1.0f, 0.2f, 0.0f);
// surface reflectance
vec3 Ks = vec3 (1.0f, 1.0f, 1.0f);
vec3 Kd = vec3 (1.0f, 0.8f, 0.72f);
vec3 Ka = vec3 (1.0f, 1.0f, 1.0f);
float specular_exponent = 10.0; // specular 'power'
out vec4 fragment_colour; // final colour of surface
void main () {
// ambient intensity
vec3 Ia = vec3 (0, 0, 0);
// diffuse intensity
// raise light position to eye space
vec3 light_position_eye = light_position_world; //vec3 (view_mat * vec4 (light_position_world, 1.0));
vec3 distance_to_light_eye = light_position_eye - position_eye;
vec3 direction_to_light_eye = normalize (distance_to_light_eye);
float dot_prod = dot (direction_to_light_eye, normal_eye);
dot_prod = max (dot_prod, 0.0);
vec3 Id = Ld * Kd * dot_prod; // final diffuse intensity
// specular intensity
vec3 surface_to_viewer_eye = normalize (-position_eye);
// blinn
vec3 half_way_eye = normalize (surface_to_viewer_eye + direction_to_light_eye);
float dot_prod_specular = max (dot (half_way_eye, normal_eye), 0.0);
float specular_factor = pow (dot_prod_specular, specular_exponent);
vec3 Is = Ls * Ks * specular_factor; // final specular intensity
// final colour
fragment_colour = vec4 (255, 25, 25, 0);
}
There are a few problems with your code.
1) Assuming, light_position_world is the position of the light in world space, the light is below your scene. So the scene won't be illuminated from above.
2) Assuming, *_eye means a coordinate in eye space and *_world is a coordinate in world space, you may not interchange between those spaces by simply assigning vectors. You have to use a view matrix and it's inverse view matrix to go from world to eye space and from eye space to world space respectivly.
3) The output color of the shader, fragment_colour, is always set to a dark red-ish color. So the compiler will just leave out all the lighting calculations. You have to use something like this: fragment_colour = Ia + Id * material + Is * material, where material is the color of your material - e.g. gray for metal.
It seems like you don't understand the underlying basics. I suggest you read a few articles or tutorials about lighting and transformation/maths in OpenGL.
If you have consumed a fair bit of literature, experiment with your code. Try out, what different calculations do and how they influence the end product. You won't get 100% physically accurate lighting anyways, so there's nothing to go wrong.

Stuck on rendering lights after binding FBO using deffered rendering using OpenGL

First of all I am sorry for this long post after trying to make this work the whole day.
I have many questions about this especially because I use inheritance in C++ to build lights.
I use directional light as my core model for light since i can give the light direction and it will calculate the light, then on top of it I build point light where i just calculate the vector from light to fragment position, and finally for spot light I use point light with the addition of cut off angle to create spot lights (just ignore whatever is outside the cone). I have tested lights and they work fine with forward rendering but now I would like to change my light model to PBR (basically just change how I calculate light in directional light) and move to differed rendering.
Today i started working on deferred rendering and I can get the position, texture, normal and depth buffers, however i have a problem when trying to render lights.
That was the first problem, the second, since each type of light has it own shader and i build them using polymorphism. My second question is I can loop through each light in C++ and call each light to be renderer or there is another way that i can solve this in shaders.
Prototypes of lights are
EDIT: I fixed a small issue where iw as transforming the render quat with VP projection but still i can not draw anything and i have no idea if FB are working correctlly now. Nvidia opengl debugger is just crashing.
Light(glm::vec3& color, float intensity, float ambient, ShaderProgram& lightShader);
DirectionalLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f), float intensity = 1.0f, float ambient = 0.0f, ShaderProgram& lightShader = ShaderProgram("Directional Light"));
PointLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f), float intensity = 1.0f, float ambient = 0.0f, LightAttenuation& lightAttenuation = LightAttenuation(), ShaderProgram& lightShader = ShaderProgram("Point Light"));
SpotLight(glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f),
My render path looks like this.
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
defferedShader_.startProgram();
defferedShader_.setUniformMat4("VP", camera.getVP());
glBindFramebuffer(GL_FRAMEBUFFER, deferredFbo);
//the scene is small and does not need culling.
for (auto* mesh : world.getMeshes()) {
//mesh->draw(light->getLightShader());
//mesh->draw(activeLight_->getLightShader());
mesh->draw(defferedShader_);
drawCallCounter += mesh->getMeshObjectSize();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
defferedShader_.stopProgram();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, positionFbo);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, normalFbo);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, albedoFbo);
//This is where i got stuck, I would like to make directional light work then test other lights then test the whole program with more than one light
//for (auto* light : world.getLights()) {
// //glEnable(GL_CULL_FACE);
// //glCullFace(GL_FRONT);
glDisable(GL_DEPTH_TEST);
activeLight_->getLightShader().startProgram();
activeLight_->getLightShader().setUniformMat4("VP", camera.getVP());
activeLight_->getLightShader().setUniformVec3("eyePosition", camera.getCameraPosition());
//activeLight_->getLightShader();
RenderQuad();
activeLight_->getLightShader().stopProgram();
//}
The shader code that i started building is (PS i removed the shadows for now)
Vertex Shader
#version 410 core
#include "../Global/GlobalShader.inc"
#include "../Global/GlobalMesh.inc"
out vec3 Position;
out vec2 TexCoord;
//out vec4 ShadowCoord;
//uniform mat4 ShadowMatrix;
void main() {
Position = position;
TexCoord = texCoord;
//ShadowCoord = ShadowMatrix * vec4(position, 1.0);
gl_Position = VP * vec4(position, 1.0);
}
Fragment shader
One thing that is bothering me is i can not set the uniform values for gPosition, gPosition and gAlbedoSpec even if I use them, and no matter what i change in the shader the output will be the same.
#version 410 core
#include "../Global/GlobalShader.inc"
#include "../Global/GlobalMesh.inc"
#include "../Global/GlobalLight.inc"
//#include "../Global/ShadowSampling.inc"
in vec3 Position;
in vec2 TexCoord;
//in vec4 ShadowCoord;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
float specularStrength = 32.0f; // to be impelemented
out vec4 gl_FragColor;
void main() {
//vec4 lightning = vec4(0.0f);
////vec4 shadowMapping = vec4(0.0f);
//
vec3 FragPos = texture(gPosition, TexCoord).rgb;
vec3 Normal = texture(gNormal, TexCoord).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoord).rgb;
float Specular = texture(gAlbedoSpec, TexCoord).a;
//vec3 Diffuse = texture(gAlbedoSpec, TexCoord).rgb;
//lightning = calculateDirectionalLight(directionalLight.light, directionalLight.position, Normal, Position, specularStrength, eyePosition, material, TexCoord);
//gl_fragColor = vec3(Position, 1.0);
//shadowMapping = calculateShadow(shadowMap, ShadowCoord, directionalLight.light.ambient);
//gl_FragColor = vec4(Diffuse, 1.0);
gl_FragColor = vec4(1.0); //vec4(Diffuse, 1.0);// lightning;//g * shadowMapping;
//gl_FragColor = lightning;// * shadowMapping;
}
in case you want to see global light
struct Light
{
vec3 color;
float intensity;
float ambient;
};
struct DirectionalLight
{
Light light;
vec3 position;
};
struct Attenuation
{
float constant;
float linear;
float quadratic;
};
struct PointLight
{
Light light;
Attenuation atten;
vec3 position;
float range;
};
struct SpotLight
{
PointLight pointLight;
//vec3 lookAt;
vec3 direction;
float cutOff;
};
vec3 GAMMA = vec3(1.0/2.2);
vec4 calculateDirectionalLight(Light light, vec3 direction, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
vec3 diffuseFactor = ( light.color * material.diffuse * vec3(texture(material.texture.diffuse, texCoord.st)) )
* (light.intensity * clamp(dot(normal, direction), 0.0, 1.0) ) ;
vec3 viewDir = normalize(eyePosition - worldPosition);
vec3 reflectDir = normalize(reflect(-direction, normal));
float specularFactor = pow(clamp(dot(viewDir, reflectDir), 0.0, 1.0), specularIntensity);
vec3 specularColor = ( light.color * material.specular * vec3(texture(material.texture.specular, texCoord.st)) ) * (specularFactor * material.shininess);
return vec4(pow((diffuseFactor + specularColor + light.ambient + material.ambient), GAMMA), 1.0);
}
vec4 calculatePointLight(PointLight pointLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
// DO NOT NORMALIZE lightDirection, WE NEED IT TO CALCULATE THE DISTANCE TO COMPARE RANGE OF LIGHT
vec3 lightDirection = pointLight.position - worldPosition;
float distanceToPoint = length(lightDirection);
// I dont like conditionals in shader, but since this is fragment based lighting i believe
// this will speed-up things insetead of calculating the light
if(distanceToPoint > pointLight.range)
return vec4(0.0,0.0,0.0,0.0);
vec4 light = calculateDirectionalLight(pointLight.light, lightDirection, normal, worldPosition, specularIntensity, eyePosition, material, texCoord);
// light attenuateion explained https://developer.valvesoftware.com/wiki/Constant-Linear-Quadratic_Falloff
// http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Light+Attenuation+Shortcut
float attenuation = max(pointLight.atten.constant
+ pointLight.atten.linear * distanceToPoint
+ pointLight.atten.quadratic * distanceToPoint * distanceToPoint,
1.0);
return light / attenuation;
}
vec4 calculateSpotLight(SpotLight spotLight, vec3 normal, vec3 worldPosition, float specularIntensity, vec3 eyePosition, Material material, vec2 texCoord)
{
vec3 lightDirection = normalize(spotLight.pointLight.position - worldPosition);
float spotFactor = dot(lightDirection, spotLight.direction);
vec4 light = vec4(0.0f);
if(spotFactor > spotLight.cutOff)
{
light = calculatePointLight(spotLight.pointLight, normal, worldPosition, specularIntensity, eyePosition, material, texCoord) * (1.0 - (1.0 - spotFactor)/(1.0 - spotLight.cutOff));
}
return light;
}
Global mesh
struct Texture {
sampler2D diffuse;
sampler2D specular;
sampler2D normal;
sampler2D ambient;
sampler2D height;
//vec2 texCoord;
};
struct Material {
vec3 ambient; // Ka
vec3 diffuse; // Kd
vec3 specular; // Ks
vec3 transmittance; // Tr
vec3 emission; // Ke
float shininess; // Ns
float ior; // Ni
float dissolve; // Dissolve
int illum; // Illum
Texture texture;
};
uniform Material material;
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;
Global Shader
uniform mat4 VP;
uniform mat4 P;
What i am getting now after binding the buffers and running the directional shader is
and just as example to see the scene this is the position buffer
Fixed it. I had the clean buffer and color in the wrong place. It should be after I bind the buffer not at the beginning of each frame.
glEnable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, deferredFbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
defferedShader_.startProgram();
defferedShader_.setUniformMat4("VP", camera.getVP());
.
.
. rest of the code

Trouble with Specular Lighting in OpenGL

I'm having some issues with my specular lighting, I have ambient and diffuse but I am now looking at specular to try make a nice looking model.
I have the following in my vertexShader:
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
out vec4 Colour0;
// Transforms
uniform mat4 gModelToWorldTransform;
uniform mat4 gWorldToViewToProjectionTransform;
// Ambient light parameters
uniform vec3 gAmbientLightIntensity;
// Directional light parameters
uniform vec3 gDirectionalLightIntensity;
uniform vec3 gDirectionalLightDirection;
// Material constants
uniform float gKa;
uniform float gKd;
uniform float gKs;
uniform float gKsStrength;
void main()
{
// Transform the vertex from local space to homogeneous clip space
vec4 vertexPositionInModelSpace = vec4(Position, 1);
vec4 vertexInWorldSpace = gModelToWorldTransform * vertexPositionInModelSpace;
vec4 vertexInHomogeneousClipSpace = gWorldToViewToProjectionTransform * vertexInWorldSpace;
gl_Position = vertexInHomogeneousClipSpace;
// Calculate the directional light intensity at the vertex
// Find the normal in world space and normalise it
vec3 normalInWorldSpace = (gModelToWorldTransform * vec4(Normal, 0.0)).xyz;
normalInWorldSpace = normalize(normalInWorldSpace);
// Calculate the ambient light intensity at the vertex
// Ia = Ka * ambientLightIntensity
vec4 ambientLightIntensity = gKa * vec4(gAmbientLightIntensity, 1.0);
// Setup the light direction and normalise it
vec3 lightDirection = normalize(-gDirectionalLightDirection);
//lightDirection = normalize(gDirectionalLightDirection);
// Id = kd * lightItensity * N.L
// Calculate N.L
float diffuseFactor = dot(normalInWorldSpace, lightDirection);
diffuseFactor = clamp(diffuseFactor, 0.0, 1.0);
// N.L * light source colour * intensity
vec4 diffuseLightIntensity = gKd * vec4(gDirectionalLightIntensity, 1.0f) * diffuseFactor;
vec3 lightReflect = normalize(reflect(gDirectionalLightDirection, Normal));
//Calculate the specular light intensity at the vertex
float specularFactor = dot(normalInWorldSpace, lightReflect);
specularFactor = pow(specularFactor, gKsStrength);
vec4 specularLightIntensity = gKs * vec4(gDirectionalLightIntensity, 1.0f) * specularFactor;
// Final vertex colour is the product of the vertex colour
// and the total light intensity at the vertex
vec4 colour = vec4(0.0, 1.0, 0.0, 1.0);
Colour0 = colour * (ambientLightIntensity + diffuseLightIntensity + specularLightIntensity);
}
Then in my main.cpp I have the some code to try and get this working together, the specular light is definitely doing something, only, rather than making the model look shiny, it seems to intensify the light to the point where I can't see any details.
I create the following variables:
// Lighting uniforms location
GLuint gAmbientLightIntensityLoc;
GLuint gDirectionalLightIntensityLoc;
GLuint gDirectionalLightDirectionLoc;
GLuint gSpecularLightIntensityLoc;
// Materials uniform location
GLuint gKaLoc;
GLuint gKdLoc;
GLuint gKsLoc;
GLuint gKsStrengthLoc;
I then set my variables like so in the renderSceneCallBack() function which is called in the main:
// Set the material properties
glUniform1f(gKaLoc, 0.2f);
glUniform1f(gKdLoc, 0.9f);
glUniform1f(gKsLoc, 0.5f);
glUniform1f(gKsStrengthLoc, 0.5f);
I then create a initLights() function which I would like to handle all lighting, this is also called in the main:
static void initLights()
{
// Setup the ambient light
vec3 ambientLightIntensity = vec3(0.2f, 0.2f, 0.2f);
glUniform3fv(gAmbientLightIntensityLoc, 1, &ambientLightIntensity[0]);
// Setup the direactional light
vec3 directionalLightDirection = vec3(0.0f, 0.0f, -1.0f);
normalize(directionalLightDirection);
glUniform3fv(gDirectionalLightDirectionLoc, 1, &directionalLightDirection[0]);
vec3 directionalLightIntensity = vec3(0.8f, 0.8f, 0.8f);
glUniform3fv(gDirectionalLightIntensityLoc, 1, &directionalLightIntensity[0]);
//Setup the specular Light
vec3 specularLightIntensity = vec3(0.5f, 0.5f, 0.5f);
glUniform3fv(gSpecularLightIntensityLoc, 1, &specularLightIntensity[0]);
}
Can anyone see what I might be doing wrong, I could have some of the calculatiuons wrong and I just don't see it. Both the ambient/diffuse lighting are working correctly. This photo illustrates whats currently happening, ambient on the left, diffuse in the middle and specular with strength set to 30 on the right.
Answer
I forgot to pass this value into the main:
gKsStrengthLoc = glGetUniformLocation(shaderProgram, "gKsStrength");
//assert(gDirectionalLightDirectionLoc != 0xFFFFFFFF);
Everything works now using the answer selected
Your value for gKsStrength looks way too small:
glUniform1f(gKsStrengthLoc, 0.5f);
This value controls how shiny the object is, with higher values making it more shiny. This makes sense if you look at the calculation in the shader:
specularFactor = pow(specularFactor, gKsStrength);
The larger the exponent, the faster the value drops off, which means that the specular highlight becomes more narrow.
Typical values might be something like 10.0f for moderately shiny, 30.0f for quite shiny, and even higher for very shiny materials.
With your value of 0.5f, you get a very wide specular "highlight". Your value for the specular intensity is also fairly high (0.5), so the highlight is going to cover most of the object, and saturate the colors for large parts.

OpenGL: debugging "Single-pass Wireframe Rendering"

I'm trying to implement the paper "Single-Pass Wireframe Rendering", which seems pretty simple, but it's giving me what I'd expect as far as thick, dark values.
The paper didn't give the exact code to figure out the altitudes, so I did it as I thought fit. The code should project the three vertices into viewport space, get their "altitudes" and send them to the fragment shader.
The fragment shader determines the distance of the closest edge and generates an edgeIntensity. I'm not sure what I'm supposed to do with this value, but since it's supposed to scale between [0,1], I multiply the inverse against my outgoing color, but it's just very weak.
I had a few questions that I'm not sure are addressed in the papers. First, should the altitudes be calculated in 2D instead of 3D? Second, they site DirectX features, where DirectX has a different viewport-space z-range, correct? Does that matter? I'm premultiplying the outgoing altitude distances by the w-value of the viewport-space coordinates as they recommend to correct for perspective projection.
image trying to correct for perspective projection
no correction (not premultiplying by w-value)
The non-corrected image seems to have clear problems not correcting for the perspective on the more away-facing sides, but the perspective-corrected one has very weak values.
Can anyone see what's wrong with my code or how to go about debugging it from here?
my vertex code in GLSL...
float altitude(in vec3 a, in vec3 b, in vec3 c) { // for an ABC triangle
vec3 ba = a - b;
vec3 bc = c - b;
vec3 ba_onto_bc = dot(ba,bc) * bc;
return(length(ba - ba_onto_bc));
}
in vec3 vertex; // incoming vertex
in vec3 v2; // first neighbor (CCW)
in vec3 v3; // second neighbor (CCW)
in vec4 color;
in vec3 normal;
varying vec3 worldPos;
varying vec3 worldNormal;
varying vec3 altitudes;
uniform mat4 objToWorld;
uniform mat4 cameraPV;
uniform mat4 normalToWorld;
void main() {
worldPos = (objToWorld * vec4(vertex,1.0)).xyz;
worldNormal = (normalToWorld * vec4(normal,1.0)).xyz;
//worldNormal = normal;
gl_Position = cameraPV * objToWorld * vec4(vertex,1.0);
// also put the neighboring polygons in viewport space
vec4 vv1 = gl_Position;
vec4 vv2 = cameraPV * objToWorld * vec4(v2,1.0);
vec4 vv3 = cameraPV * objToWorld * vec4(v3,1.0);
altitudes = vec3(vv1.w * altitude(vv1.xyz,vv2.xyz,vv3.xyz),
vv2.w * altitude(vv2.xyz,vv3.xyz,vv1.xyz),
vv3.w * altitude(vv3.xyz,vv1.xyz,vv2.xyz));
gl_FrontColor = color;
}
and my fragment code...
varying vec3 worldPos;
varying vec3 worldNormal;
varying vec3 altitudes;
uniform vec3 cameraPos;
uniform vec3 lightDir;
uniform vec4 singleColor;
uniform float isSingleColor;
void main() {
// determine frag distance to closest edge
float d = min(min(altitudes.x, altitudes.y), altitudes.z);
float edgeIntensity = exp2(-2.0*d*d);
vec3 L = lightDir;
vec3 V = normalize(cameraPos - worldPos);
vec3 N = normalize(worldNormal);
vec3 H = normalize(L+V);
//vec4 color = singleColor;
vec4 color = isSingleColor*singleColor + (1.0-isSingleColor)*gl_Color;
//vec4 color = gl_Color;
float amb = 0.6;
vec4 ambient = color * amb;
vec4 diffuse = color * (1.0 - amb) * max(dot(L, N), 0.0);
vec4 specular = vec4(0.0);
gl_FragColor = (edgeIntensity * vec4(0.0)) + ((1.0-edgeIntensity) * vec4(ambient + diffuse + specular));
}
I have implemented swine's idea, and the result is perfect, here is my screenshot:
struct MYBUFFEREDVERTEX {
float x, y, z;
float nx, ny, nz;
float u, v;
float bx, by, bz;
};
const MYBUFFEREDVERTEX g_vertex_buffer_data[] = {
-1.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f, 0.0f,
};
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
vertex shader:
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
uniform mat4 u_mvp_matrix;
uniform vec3 u_light_direction;
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;
attribute vec3 a_barycentric;
varying vec2 v_texcoord;
varying float v_light_intensity;
varying vec3 v_barycentric;
void main()
{
// Calculate vertex position in screen space
gl_Position = u_mvp_matrix * vec4(a_position, 1.0);
// calculate light intensity, range of 0.3 ~ 1.0
v_light_intensity = max(dot(u_light_direction, a_normal), 0.3);
// Pass texture coordinate to fragment shader
v_texcoord = a_texcoord;
// Pass bary centric to fragment shader
v_barycentric = a_barycentric;
}
fragment shader:
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
uniform sampler2D u_texture;
varying vec2 v_texcoord;
varying float v_light_intensity;
varying vec3 v_barycentric;
void main()
{
float min_dist = min(min(v_barycentric.x, v_barycentric.y), v_barycentric.z);
float edgeIntensity = 1.0 - step(0.005, min_dist);
// Set diffuse color from texture
vec4 diffuse = texture2D(u_texture, v_texcoord) * vec4(vec3(v_light_intensity), 1.0);
gl_FragColor = edgeIntensity * vec4(0.0, 1.0, 1.0, 1.0) + (1.0 - edgeIntensity) * diffuse;
}
first, your function altitude() is flawed, ba_onto_bc is calculated wrong because bc is not unit length (either normalize bc, or divide ba_onto_bc by dot(bc, bc) which is length squared - you save calculating the square root).
The altitudes should be calculated in 2D if you want constant-thickness edges, or in 3D if you want perspective-correct edges.
It would be much easier just to use barycentric coordinates as a separate vertex attribute (ie. vertex 0 of the triangle would get (1 0 0), the second vertex (0 1 0) and the last vertex (0 0 1)). In fragment shader you would calculate minimum and use step() or smoothstep() to calculate edge-ness.
That would only require 1 attribute instead of current two, and it would also eliminate the need for calculating height in vertex shader (although it may be useful if you would like to prescale the barycentric coordinates so you have uniformly thick lines - but calculate it offline). It should also be working pretty much instantly so it would be a good starting point to get to the desired behavior.