I'm using OpenGL 3.3 and having some odd lighting issue, I'll first show two screenshots at different angles and then give the shader code.
First angle:
Second angle:
What you see here is:
A cube, with its middle on the origin;
A directional light source, coming from the yellow point through the origin;
In cyan you see the normals of the vertices.
I know the normals of the vertices are "wrong", but I was exactly trying to debug those.
What I expected was: A (from top-to-bottom) varying color of every face, depending on the position of the "sun" and the camera.
But what I get is that two parts of the cube (upper and lower) that both have varying colors, but not in the way I expected.
There is code for shadows in the shader, but I deliberately disabled them here to avoid confusion.
Vertex shader:
#version 430 core
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 normal;
layout(location = 0) uniform mat4 model_matrix;
layout(location = 1) uniform mat4 view_matrix;
layout(location = 2) uniform mat4 proj_matrix;
layout(location = 3) uniform mat4 shadow_matrix;
out VS_OUT {
vec3 N;
vec3 L;
vec3 V;
vec4 shadow_coord;
} vs_out;
uniform vec4 light_pos = vec4(-20.0, 7.5, -20.0, 1.0);
void main(void) {
vec4 local_light_pos = view_matrix * light_pos;
vec4 p = view_matrix * model_matrix * position;
//normal
vs_out.N = normalize(normal);
//light vector
vs_out.L = local_light_pos.xyz - p.xyz;
//view vector
vs_out.V = -p.xyz;
//light space coordinates
vs_out.shadow_coord = shadow_matrix * position;
gl_Position = proj_matrix * p;
}
Fragment shader:
#version 430 core
out vec4 color;
in VS_OUT {
vec3 N;
vec3 L;
vec3 V;
vec4 shadow_coord;
} fs_in;
layout(binding = 0) uniform sampler2DShadow shadow_tex;
uniform vec3 light_ambient_albedo = vec3(1.0);
uniform vec3 light_diffuse_albedo = vec3(1.0);
uniform vec3 light_specular_albedo = vec3(1.0);
uniform vec3 ambient_albedo = vec3(0.0, 0.2, 0.0);
uniform vec3 diffuse_albedo = vec3(0.2, 0.7, 0.2);
uniform vec3 specular_albedo = vec3(0.0, 0.0, 0.0);
uniform float specular_power = 128.0;
vec3 rgb_to_grayscale_luminosity(vec3 color) {
float value = color.r * 0.21 + color.g * 0.71 + color.b * 0.07;
return vec3(value);
}
void main(void) {
//normalize
vec3 N = normalize(fs_in.N);
vec3 L = normalize(fs_in.L);
vec3 V = normalize(fs_in.V);
//calculate R
vec3 R = reflect(-L, N);
//calcualte ambient
vec3 ambient = ambient_albedo * light_ambient_albedo;
//calculate diffuse
vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo * light_diffuse_albedo;
//calcualte spcular
vec3 specular = pow(max(dot(R, V), 0.0), specular_power) * specular_albedo * light_specular_albedo;
//apply shadow and write color
float shadow_value = textureProj(shadow_tex, fs_in.shadow_coord);
if (shadow_value > 0.0001 || true) {
//no shadow
color = vec4(ambient + diffuse + specular, 1.0);
}
else {
//in shadow
//color = vec4(rgb_to_grayscale_luminosity((ambient + diffuse) * (1 - shadow_value)), 0.5);
//color = vec4(vec3(shadow_value), 0.5);
color = vec4((ambient + diffuse) * (1 - shadow_value) * 0.5, 1.0);
}
}
What could be going wrong here?
Assuming your normals only point upwards/downwards (x=0 and z=0 in the OpenGL coordinate system) what you see should be the expected behavior (no bug concerning the shaders/graphics pipeline).
During the rasterization stage in the graphics pipeline the attributes are interpolated among the vertices (barycentric coordinates).
Assuming that all normals above the plane "y=0" are
"vec3(0, 1, 0)"
and all normals below this plane are
"vec3(0, -1, 0)"
then for every pixel the interpolated normal will be
"vec3(0, *, 0)" where * is >0 above the "y=0"-plane and <0 below that plane.
In your fragment shader you normalize all normals hence they will all again be
"vec3(0, 1, 0)" if the corresponding vertex lies above the "y=0"-plane and
"vec3(0, -1, 0)" if the corresponding vertex lies below that plane.
This will result in the same color for all vertices below and above the "y=0"-plane.
You could check this if you would remove the normal-"normalization" within the fragment shader or if you add a minimal offset to the x- or z-coordinate of some normals e.g.
vec3(0.0000001, +/-1, 0)
Related
I'm trying to implement this version of ssao with this tutorial:
http://www.learnopengl.com/#!Advanced-Lighting/SSAO
Here is what I end up with for my render textures.
When I move the camera the shadows seem to follow
Seems like I am missing some kind of matrix multiplication with the camera.
CODE
gBuffer Vertex
#version 330 core
layout (location = 0) in vec3 vertexPosition;
layout (location = 1) in vec3 vertexNormal;
out vec3 position;
out vec3 normal;
uniform mat4 m;
uniform mat4 v;
uniform mat4 p;
uniform mat4 n;
void main()
{
vec4 viewPos = v * m * vec4(vertexPosition, 1.0f);
position = viewPos.xyz;
gl_Position = p * viewPos;
normal = vec3(n * vec4(vertexNormal, 0.0f));
}
gBuffer Fragment
#version 330 core
layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gColor;
in vec3 position;
in vec3 normal;
const float NEAR = 0.1f;
const float FAR = 50.0f;
float LinearizeDepth(float depth)
{
float z = depth * 2.0f - 1.0f;
return (2.0 * NEAR * FAR) / (FAR + NEAR - z * (FAR - NEAR));
}
void main()
{
gPosition.xyz = position;
gPosition.a = LinearizeDepth(gl_FragCoord.z);
gNormal = normalize(normal);
gColor.rgb = vec3(1.0f);
}
SSAO Vertex
#version 330 core
layout (location = 0) in vec3 vertexPosition;
layout (location = 1) in vec2 texCoords;
out vec2 UV;
void main(){
gl_Position = vec4(vertexPosition, 1.0f);
UV = texCoords;
}
SSAO Fragment
#version 330 core
out float FragColor;
in vec2 UV;
uniform sampler2D gPositionDepth;
uniform sampler2D gNormal;
uniform sampler2D texNoise;
uniform vec3 samples[32];
uniform mat4 projection;
// parameters (you'd probably want to use them as uniforms to more easily tweak the effect)
int kernelSize = 32;
float radius = 1.0;
// tile noise texture over screen based on screen dimensions divided by noise size
const vec2 noiseScale = vec2(1024.0f/4.0f, 1024.0f/4.0f);
void main()
{
// Get input for SSAO algorithm
vec3 fragPos = texture(gPositionDepth, UV).xyz;
vec3 normal = texture(gNormal, UV).rgb;
vec3 randomVec = texture(texNoise, UV * noiseScale).xyz;
// Create TBN change-of-basis matrix: from tangent-space to view-space
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);
// Iterate over the sample kernel and calculate occlusion factor
float occlusion = 0.0;
for(int i = 0; i < kernelSize; ++i)
{
// get sample position
vec3 sample = TBN * samples[i]; // From tangent to view-space
sample = fragPos + sample * radius;
// project sample position (to sample texture) (to get position on screen/texture)
vec4 offset = vec4(sample, 1.0);
offset = projection * offset; // from view to clip-space
offset.xyz /= offset.w; // perspective divide
offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0
// get sample depth
float sampleDepth = -texture(gPositionDepth, offset.xy).w; // Get depth value of kernel sample
// range check & accumulate
float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth ));
occlusion += (sampleDepth >= sample.z ? 1.0 : 0.0) * rangeCheck;
}
occlusion = 1.0 - (occlusion / kernelSize);
FragColor = occlusion;
}
I've read around and saw someone had a similar issue and passed the view matrix into the ssao shader and multiplied the sampleDepth:
float sampleDepth = (viewMatrix * -texture(gPositionDepth, offset.xy)).w;
But seems like it just makes things worse.
Heres another view from up top where you can see the shadows move with the camera
If I position my camera in certain ways things line up
Although I can only assume the value of your normal matrix n in the gBuffer vertex shader, it seems like you don't store your normals in view space but in world space. Since the SSAO calculations are done in screen space, this could (at least partially) explain the unexpected behavior. In that case, you either need to multiply your view matrix v to your normals before storing them to the gBuffer (potentially more efficient, but may interfere with your other shading calculations) or after retrieving them.
So I am currently working on trying to create a spotlight in my vertex shader, currently I can produce directional and/or point light by using the Phong lighting model.
Im finding it hard to calculate the correct angles for the spotlight, basically just want a spotlight that comes from 0,0,0 in eye space and looks down the Z co-ord.
I am trying to just make everything (for now) in the cone to be bright white and everything outside it dark
#version 130
uniform mat4 model_view_matrix;
uniform mat4 projection_matrix;
uniform mat3 normal_matrix;
uniform int light_mode;
uniform vec4 light_pos;
uniform vec3 light_ambient;
uniform vec3 light_diffuse;
uniform vec3 light_specular;
uniform vec3 mtl_ambient;
uniform vec3 mtl_diffuse;
uniform vec3 mtl_specular;
uniform float mtl_shininess;
// Spotlight test
const float spotCutOff = 100.00f;
in vec3 position;
in vec3 normal;
in vec2 texCoord;
out vec2 st;
out vec4 litColour;
vec3 phongLight(in vec4 position, in vec3 norm)
{
// s is the direction from the light to the vertex
vec3 s;
if (light_pos.w == 0.0) {
s = normalize(light_pos.xyz);
}
else {
s = normalize(vec3(light_pos - position));
}
// v is the direction from the eye to the vertex
vec3 v = normalize(-position.xyz);
// r is the direction of light reflected from the vertex
vec3 r = reflect(-s, norm);
vec3 ambient = light_ambient * mtl_ambient;
// The diffuse component
float sDotN = max(dot(s,norm), 0.0);
vec3 diffuse = light_diffuse * mtl_diffuse * sDotN;
// Specular component
vec3 spec = vec3(0.0);
if (sDotN > 0.0)
spec = light_specular * mtl_specular * pow(max(dot(r,v), 0.0), mtl_shininess);
return ambient + diffuse + spec;
}
vec3 spotLight(in vec4 position, in vec3 norm)
{
vec3 ambient = vec3(0.2, 0.2, 0.2);
vec3 lightDir = normalize(vec3(light_pos - position));
vec3 spotDir = vec3(0.0, 0.0, -1.0);
float angle = degrees(acos(dot(spotDir, lightDir)));
//angle = max (angle, 0);
if ((angle) < spotCutOff) {
return vec3(1.0, 1.0, 1.0);
}
float dist = sqrt(positon.x * position.x + position.y + position.y + position.z * position.z);
if (dist < 1) {
return vec3(1.0,1.0,0.0);
}
return vec3(0.2, 0.2, 0.2);
}
void main(void)
{
// Convert normal and position to eye coords
vec3 eyeNorm = normalize(normal_matrix * normal);
vec4 eyePos = model_view_matrix * vec4(position, 1.0);
// No lighting effect
if (light_mode == 0)
{
litColour = vec4(1.0, 1.0, 1.0, 1.0);
}
// Directional overhead light
else if (light_mode == 1)
{
litColour = vec4(phongLight(eyePos, eyeNorm), 1.0);
}
// Point light
else if (light_mode == 2)
{
litColour = vec4(phongLight(eyePos, eyeNorm), 1.0);
}
else if (light_mode == 3)
{
litColour = vec4(spotLight(eyePos, eyeNorm), 1.0);
}
//litColour = vec4(normal*1000, 1.0);
gl_Position = projection_matrix * eyePos;
st = texCoord;
}
Your spotlight is defined by a position (ps) and a direction (ds). So for every vertex at position vp you can compute d=vp-ps, normalize that to dn=normalize(d), and then dot(dn,ds) will give you the angle in the spotlight. Just scale it or compare it to a cut off to get a scalar!
Alternatively, and in the long term better, is to think of a spotlight as a camera. Do the same as you do for your camera: A model and view matrix! Transform every vertex into that space, and project it from x,y,z,w to x,y,z. z is the distance which is always useful for lighting and x,y you can use to look up in a texture that has a round shape (or any other).
One thing to mind with both techniques is back projection: Make sure you check that the light only points forward! Check the sign of z or the dot product!
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.
I have a very strange behaviour of specular(phong light model) light. It seems to be appering on both sides of all objects. Does anyone know what could be the issue ?
The actual calculation seems to be alright, as I can see that the light changes its position as object rotates.
#version 330
in vec4 CameraPos0;
in vec3 Pos0;
in vec4 Colour0;
in vec3 Normal0;
out vec4 FragColor;
// Ambient light parameters
uniform vec3 gAmbientLightIntensity;
// Directional light parameters
uniform vec3 gDirectionalLightIntensity;
uniform vec3 gDirectionalLightDirection;
// Specular light parameter
uniform vec3 gSpecularLightIntensity;
uniform vec3 gLightSourcePosition;
uniform vec3 gCameraPosition;
// Material constants
uniform float gKa;
uniform float gKd;
uniform float gKs;
void main()
{
// 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(Normal0, lightDirection);
diffuseFactor = clamp(diffuseFactor, 0.0, 1.0);
// N.L * light source colour * intensity
vec4 diffuseLightIntensity = gKd * vec4(gDirectionalLightIntensity, 1.0f) * diffuseFactor;
// Phong light
vec3 L = normalize(gLightSourcePosition - Pos0);
vec3 V = normalize(-Pos0);
vec3 R = normalize(2 * Normal0 * dot(Normal0, L) - L);
float specularFactor = pow(dot(R, V), 0.1f);
vec4 specularLightIntensity = gKs * vec4(gSpecularLightIntensity, 1.0f) * specularFactor;
specularLightIntensity = clamp(specularLightIntensity, 0.0, 1.0);
// Final vertex colour is the product of the vertex colour
// and the total light intensity at the vertex
vec4 lightedFragColor = Colour0 * (ambientLightIntensity + diffuseLightIntensity + specularLightIntensity);
FragColor = lightedFragColor;
}
Vertex Shader
#version 330
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec4 Colour;
out vec3 Pos0;
out vec4 Colour0;
out vec3 Normal0;
out vec4 CameraPos0;
uniform mat4 gModelToWorldTransform;
uniform mat4 gWorldToViewTransform;
uniform mat4 gProjectionTransform;
void main()
{
vec4 vertexPositionInModelSpace = vec4(Position, 1);
vec4 vertexInWorldSpace = gModelToWorldTransform * vertexPositionInModelSpace;
vec4 vertexInViewSpace = gWorldToViewTransform * vertexInWorldSpace;
vec4 vertexInHomogeneousClipSpace = gProjectionTransform * vertexInViewSpace;
gl_Position = vertexInHomogeneousClipSpace;
vec3 normalInWorldSpace = (gModelToWorldTransform * vec4(Normal, 0.0)).xyz;
normalInWorldSpace = normalize(normalInWorldSpace);
Normal0 = normalInWorldSpace;
CameraPos0 = vertexInViewSpace;
Pos0 = vertexInWorldSpace.xyz;
Colour0 = Colour;
}
you need to clamp the dot result from the saturation calculus because on the back side the result is negative and the pow can return a positive number instead of clamping it to zero.
float specularFactor = pow(clamp(dot(R, V),0.0,1.0), 0.1f);
Edit:
Also the V should be a vector pointing to the camera position, not to the vertex position in world space:
vec3 V = normalize(CameraPos0 - Pos0);
I am looking for some help debugging my GLSL phong shading code. Here is my vertex shader:
layout(std140) uniform Matrices {
mat4 model[1024];
};
layout(location = 0) in vec4 vertexCoord;
layout(location = 2) in vec3 vertexNormal;
uniform mat4 view; // from lookAt()
uniform mat4 projection; // perspective projection
out vec3 Position;
out vec3 Normal;
out vec4 lightPosEye;
void main() {
mat4 modelView = view * model[gl_InstanceID];
mat3 normalMatrix = mat3(transpose(inverse(modelView)));
//mat3 normalMatrix = mat3(modelView);
vec4 lightPos = vec4(350, 350, 350, 1);
lightPosEye = modelView * lightPos;
Position = vec3(modelView * vertexCoord);
Normal = normalize(normalMatrix * vertexNormal);
gl_Position = projection * vec4(Position, 1.0);
}
and here is my fragment shader:
in vec3 Position;
in vec3 Normal;
in vec4 lightPosEye;
layout(location = 0) out vec4 FragColor;
vec3 ads() {
vec3 Ka = vec3(0, 0.5, 0);
vec3 Kd = vec3(0, 0.5, 0);
vec3 Ks = vec3(0, 0.1, 0);
vec3 intensity = vec3(0.3, 0.5, 0.0);
float shininess = 0.1;
vec3 n = normalize(Normal);
vec3 s = normalize(vec3(lightPosEye) - Position);
vec3 v = normalize(vec3(-Position));
vec3 r = reflect(-s, n);
return intensity * (Ka + Kd * max(dot(s, n), 0.0) + Ks * pow(max(dot(r,v), 0.0), shininess));
}
void main() {
FragColor = vec4(ads(), 1);
}
Here is a screenshot of the result (I am also rendering the normals with another geometry shader for debugging purposes):
The artifacts on the cube are wrong, and the "shadows" on the circle also moves a bit when I move the camera around (change the view matrix).
Any obvious errors in my GLSL code?