GLSL (user defined) function parameters and uniform blocks strange behavior - opengl

I am writing simple deferred rendering 'engine' for fun and encountered a strange behaviour with GLSL. Calling a function caused a slight malfunction while simply pasting the content of the function solved the problem (details below).
Am I doing something terribly wrong or is it possible that I hit a limitation or a glsl compiler bug (14.12 AMD Catalyst Omega drivers) ?
Original call (in a for loop over shadow):
ColorOut.rgb += phong(position.xyz, normal, color, Shadows[shadow].position.xyz, Shadows[shadow].color.rgb);
Using this, the color of my shadow casting lights are all the same, Shadows[shadow].color.rgb seem to always be equal the to first one of the array (in the phong function at least).
My 'solution' was to replace the call by the content of the function:
vec3 L = normalize(Shadows[shadow].position.xyz - position.xyz);
float dNL = dot(normal, L);
float diffuseFactor = max(dNL, minDiffuse);
vec3 V = normalize(cameraPosition - position.xyz);
vec3 R = normalize(reflect(-L, normal));
float specularFactor = pow(max(dot(R, V), 0.f), 8.0);
ColorOut.rgb += diffuseFactor * color * Shadows[shadow].color.rgb + specularFactor * Shadows[shadow].color.rgb;
With this, everything works fine.
Other related code snippets, shadow casting lights data structure:
layout(std140, binding = 2) uniform ShadowBlock
{
vec4 position;
vec4 color;
mat4 depthMVP;
} Shadows[8];
The phong function:
vec3 phong(vec3 p, vec3 N, vec3 diffuse, vec3 lp, vec3 lc)
{
vec3 L = normalize(lp - p);
float dNL = dot(N, L);
float diffuseFactor = max(dNL, minDiffuse);
vec3 V = normalize(cameraPosition - p);
vec3 R = normalize(reflect(-L, N));
float specularFactor = pow(max(dot(R, V), 0.f), 8.0);
return diffuseFactor * diffuse * lc + specularFactor * lc;
}

Related

How to fix improperly distributed lighting projected onto a model in OpenGL?

I've been trying to implement the Blinn-Phong lighting model to project lighting onto an imported Wavefront OBJ model through Assimp(github link).
The model seems to be loaded correctly, however, there seems to be a point where the lighting appears to be "cut off" near the middle of the model.
Image of the imported model with and without lighting enabled.
As you can see on the left of the image above, there is a region in the middle of the model where the light effectively gets "split up" which is not what is intended. It can be seen that there is a sort of discrepancy where the side facing towards the light source appears brighter than normal and the side away from the light source appears darker than normal without any sort of easing in between the two sides.
I believe there might be something wrong with how I've implemented the lighting model in the fragment shader but I cannot say for sure as to why this is happening.
Vertex shader:
#version 330 core
layout (location = 0) in vec3 vertPos;
layout (location = 1) in vec3 vertNormal;
layout (location = 2) in vec2 vertTexCoords;
out vec3 fragPos;
out vec3 fragNormal;
out vec2 fragTexCoords;
uniform mat4 proj, view, model;
uniform mat3 normalMat;
void main() {
fragPos = vec3(model * vec4(vertPos, 1));
gl_Position = proj * view * vec4(fragPos, 1);
fragTexCoords = vertTexCoords;
fragNormal = normalMat * vertNormal;
}
Fragment shader:
#version 330 core
in vec3 fragPos;
in vec3 fragNormal;
in vec2 fragTexCoords;
out vec4 FragColor;
const int noOfDiffuseMaps = 1;
const int noOfSpecularMaps = 1;
struct Material {
sampler2D diffuseMaps[noOfDiffuseMaps], specularMaps[noOfSpecularMaps];
float shininess;
};
struct Light {
vec3 direction;
vec3 ambient, diffuse, specular;
};
uniform Material material;
uniform Light light;
uniform vec3 viewPos;
const float pi = 3.14159265;
uniform float gamma = 2.2;
float near = 0.1;
float far = 100;
float LinearizeDepth(float depth)
{
float z = depth * 2 - 1;
return (2 * near * far) / (far + near - z * (far - near));
}
void main() {
vec3 normal = normalize(fragNormal);
vec3 calculatedColor = vec3(0);
for (int i = 0; i < noOfDiffuseMaps; i++) {
vec3 diffuseTexel = texture(material.diffuseMaps[i], fragTexCoords).rgb;
// Ambient lighting
vec3 ambient = diffuseTexel * light.ambient;
// Diffuse lighting
float diff = max(dot(light.direction, normal), 0);
vec3 diffuse = diffuseTexel * light.diffuse * diff;
calculatedColor += ambient + diffuse;
}
for (int i = 0; i < noOfSpecularMaps; i++) {
vec3 specularTexel = texture(material.specularMaps[0], fragTexCoords).rgb;
vec3 viewDir = normalize(viewPos - fragPos);
vec3 halfWayDir = normalize(viewDir + light.direction);
float energyConservation = (8 + material.shininess) / (8 * pi);
// Specular lighting
float spec = pow(max(dot(halfWayDir, normal), 0), material.shininess);
vec3 specular = specularTexel * light.specular * spec * energyConservation;
calculatedColor += specular;
}
float depthColor = 1 - LinearizeDepth(gl_FragCoord.z) / far;
FragColor = vec4(pow(calculatedColor, vec3(1 / gamma)) * depthColor, 1);
}
Make sure your texture and colors are also linear(it is a simple pow 2.2) because you are doing gamma encoding at the end.
Also note, it is expected to have a harsh terminator.
http://filmicworlds.com/blog/linear-space-lighting-i-e-gamma/
Beyond that, if you expect soft falloffs, it must be coming from an area light. For that you can implement wrap lighting or area lights.

How to fix floating point errors in OpenGL PBR

After finishing my PBR implementation I noticed that when I get close to a reflective surface, artifacts appear which I think are floating point errors.
I would really wish not to using doubles as my GPU tends to handle doubles in a performance heavy way
Here's my PBR IBL code (All the environment maps are correct as well as the deferred buffers):
// Deferred rendering
vec2 texcoord = gl_FragCoord.xy / textureSize(R_GBP, 0);
vec3 P = texture2D(R_GBP, texcoord).xyz;
vec4 A = texture2D(R_GBA, texcoord).rgba;
vec3 N = texture2D(R_GBN, texcoord).xyz;
float M = texture2D(R_GBM, texcoord).r;
float R = texture2D(R_GBR, texcoord).r;
vec3 V = normalize(R_CameraPos - P);
vec3 T = reflect(-V, N);
vec3 F0 = mix(vec3(0.04), A.rgb, M);
float NDV = max(dot(N, V), 0);
vec3 kS = FresnelSchlickRoughness(NDV, F0, R);
vec3 kD = (1 - kS) * (1 - M);
vec3 irr = textureCube(R_EnvironmentIrradiance, N).rgb;
vec3 dfs = irr * A.rgb;
const float MaxReflectionLod = 4;
vec3 preF_col = textureLod(R_EnvironmentPreFilter, T, R * MaxReflectionLod).rgb;
vec2 brdf = texture2D(R_EnvironmentBRDF, vec2(NDV, R)).rg;
vec3 specular = preF_col * (kS * brdf.x + brdf.y);
vec3 ambient = kD * dfs + specular;

glDisableVertexAttribArray works in one Visual Studio solution, but not another

I am having a very strange occurrence where glDisableVertexAttribArray works in my one solution but when I get the solution from my Perforce repository, it doesn't run and throws an assert.
I checked out this forum question but it, unfortunately, didn't solve my problem. This is for shadow mapping that I have been working on and when I try to render things to the depth buffer and then disable the vertex attributes, it throws an error.
Here's how my code is laid out:
glUseProgram(shaderProgram);
glUniform1i(u_diffuseTextureLocation, 0);
glUniform1i(u_shadowMapLocation, 1);
[...]
glUseProgram(shaderProgram);
[Render some stuff to depth buffer]
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDisableVertexAttibArray(a_normalAttribLocation); // This gives the GL_INVALID_OPERATION
// enum
And here's the vertex shader in that program:
#version 430 core
uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_model;
uniform mat4 u_lightSpaceMat;
in vec3 a_position;
in vec3 a_normal;
in vec2 a_texture;
out VS_OUT {
vec3 v_fragPos;
vec3 v_normal;
vec2 v_texCoords;
vec4 v_fragPosLightSpace;
} vs_out;
void main()
{
gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0);
vs_out.v_fragPos = (u_model * vec4(a_position, 1.0)).xyz;
vs_out.v_normal = transpose(inverse(mat3(u_model))) * a_normal;
vs_out.v_texCoords = a_texture;
vs_out.v_fragPosLightSpace = u_lightSpaceMat * vec4(vs_out.v_fragPos, 1.0);
}
And the fragment shader in the program:
#version 430 core
uniform sampler2D u_shadowMap;
uniform sampler2D u_diffuseTexture;
uniform vec3 u_lightPos;
uniform vec3 u_viewPos;
in VS_OUT {
vec3 v_fragPos;
vec3 v_normal;
vec2 v_texCoords;
vec4 v_fragPosLightSpace;
} fs_in;
out vec4 fragColor;
float shadowCalculation(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
// perform perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// Get closest depth value from light's perspective (using [0,1] range
// fragPosLight as coords)
float closestDepth = texture(u_shadowMap, projCoords.xy).r;
// Get depth of current fragment from lights perspective
float currentDepth = projCoords.z;
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
// Percentage closer filtering
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(u_shadowMap, 0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float pcfDepth = texture(u_shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
return shadow;
}
void main()
{
vec3 color = texture(u_diffuseTexture, fs_in.v_texCoords).rgb;
vec3 normal = normalize(fs_in.v_normal);
vec3 lightColor = vec3(1.0);
// ambient
vec3 ambient = 0.15 * color;
// diffuse
vec3 lightDir = normalize(u_lightPos - fs_in.v_fragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * lightColor;
// specular
vec3 viewDir = normalize(u_viewPos - fs_in.v_fragPos);
float spec = 0.0;
vec3 halfWayDir = normalize(lightDir + viewDir);
spec = pow(max(dot(normal, halfWayDir), 0.0), 64.0);
vec3 specular = spec * lightColor;
// calculate shadow
float shadow = shadowCalculation(fs_in.v_fragPosLightSpace, normal, lightDir);
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color;
fragColor = vec4(lighting, 1.0);
}
What I'm really confused about is that the program runs when I'm using my local files. But when I pull the files from the Perforce repository and try and run it, then it throws the exception. I checked and all the necessary files are uploaded to Perforce. It would seem that there is something going wrong with which attributes are actually active? I'm not sure. Just scratching my head here...
glBindVertexArray(0);
glDisableVertexAttibArray(a_normalAttribLocation);
glDisableVertexAttribArray modifies the current VAO. You just removed the current VAO, setting it to 0. Which, in a core profile, means no VAO at all. In the compatibility profile, there is a VAO 0, which is probably why it works elsewhere: you're getting the compatibility profile on a different machine.
However, if you're using VAOs, it's not clear why you want to disable an attribute array at all. The whole point of VAOs is that you don't have to call the attribute array functions every frame. You just bind the VAO and go.

GLSL shadow multiplication doesn't work

I'm in front of a very strange problem which seems to originate from a simple multiplication in the fragment shader
I'm trying to calculate shadows using a framebuffer that renders only the depths from "light's perspective" which is a common tecnique for beginners easier to implement
Fragment Shader:
#version 330 core
uniform sampler2D parquet;
uniform samplerCube depthMaps[15];
in vec2 TexCoords;
out vec4 color;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos[15];
uniform vec3 lightColor[15];
uniform float intensity[15];
uniform float far_plane;
uniform vec3 viewPos;
float ShadowCalculation(vec3 fragPos, vec3 lightPost, samplerCube depthMaps)
{
vec3 fragToLight = fragPos - lightPost;
float closestDepth = texture(depthMaps, fragToLight).r;
// original depth value
closestDepth *= far_plane;
float currentDepth = length(fragToLight);
float bias = 0.05;
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
return shadow;
}
void main()
{
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos[0] - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor[0];
float _distance = length(vec3(FragPos - lightPos[0]));
float attenuation = 1.0 / pow(_distance +1, 2);
if(attenuation > 1.0) attenuation = 1.0;
float intens = intensity[0];
if(intensity[0] > 150) intens = 150.0f;
vec3 resulta = (diffuse * attenuation) * intens;
//texture color
vec3 tCol = vec3(texture(parquet, TexCoords));
//gamma correction
tCol.rgb = pow(tCol.rgb, vec3(0.45));
vec3 colors = resulta * tCol * (1.0f - ShadowCalculation(FragPos, lightPos[0], depthMaps[0]));
color = vec4(colors, 1.0f);
}
The last multiplication inside main() behaves strangely, multiplying the result of the diffuse light by the texture color renders nicely (so we have no shadows, just diffuse lightning)
//works
vec3 colors = resulta * tCol;
Multiplying the diffuse light by the shadow results renders also nicely (now we have no textures)
//works
vec3 colors = resulta * (1.0f - ShadowCalculation(FragPos, lightPos[0], depthMaps[0]));
Doing all togheter, renders just a black screen. I've tried all sort of things in the fragment shader, but none worked.
Lastly, here is the fragment shader used to render the cubemap:
#version 330 core
in vec4 FragPos;
uniform vec3 lightPos;
uniform float far_plane;
void main()
{
float lightDistance = length(FragPos.xyz - lightPos);
// map to [0;1] range by dividing by far_plane
lightDistance = lightDistance / far_plane;
gl_FragDepth = lightDistance;
}
Can you spot any logical error? I'm using uniforms array buffers since i'll later need multiple lights at once
After a while trying to visually debug the shader's output I finally found the error, I was binding the depthmap's cubemap texture incorrectly and this caused the strange behaviour I was seeing in the last multiplication
Lesson learned: It' not always fragment's fault

Reflection/refraction with chromatic aberration - eye correction

I am writing a GLSL shader that simulates chromatic aberration for simple objects. I am staying OpenGL 2.0 compatible, so I use the built-in OpenGL matrix stack. This is the simple vertex shader:
uniform vec3 cameraPos;
varying vec3 incident;
varying vec3 normal;
void main(void) {
vec4 position = gl_ModelViewMatrix * gl_Vertex;
incident = position.xyz / position.w - cameraPos;
normal = gl_NormalMatrix * gl_Normal;
gl_Position = ftransform();
}
The cameraPos uniform is the position of the camera in model space, as one might imagine. Here is the fragment shader:
const float etaR = 1.14;
const float etaG = 1.12;
const float etaB = 1.10;
const float fresnelPower = 2.0;
const float F = ((1.0 - etaG) * (1.0 - etaG)) / ((1.0 + etaG) * (1.0 + etaG));
uniform samplerCube environment;
varying vec3 incident;
varying vec3 normal;
void main(void) {
vec3 i = normalize(incident);
vec3 n = normalize(normal);
float ratio = F + (1.0 - F) * pow(1.0 - dot(-i, n), fresnelPower);
vec3 refractR = vec3(gl_TextureMatrix[0] * vec4(refract(i, n, etaR), 1.0));
vec3 refractG = vec3(gl_TextureMatrix[0] * vec4(refract(i, n, etaG), 1.0));
vec3 refractB = vec3(gl_TextureMatrix[0] * vec4(refract(i, n, etaB), 1.0));
vec3 reflectDir = vec3(gl_TextureMatrix[0] * vec4(reflect(i, n), 1.0));
vec4 refractColor;
refractColor.ra = textureCube(environment, refractR).ra;
refractColor.g = textureCube(environment, refractG).g;
refractColor.b = textureCube(environment, refractB).b;
vec4 reflectColor;
reflectColor = textureCube(environment, reflectDir);
vec3 combinedColor = mix(refractColor, reflectColor, ratio);
gl_FragColor = vec4(combinedColor, 1.0);
}
The environment is a cube map that is rendered live from the drawn object's environment.
Under normal circumstances, the shader behaves (I think) like expected, yielding this result:
However, when the camera is rotated 180 degrees around its target, so that it now points at the object from the other side, the refracted/reflected image gets warped like so (This happens gradually for angles between 0 and 180 degrees, of course):
Similar artifacts appear when the camera is lowered/raised; it only seems to behave 100% correctly when the camera is directly over the target object (pointing towards negative Z, in this case).
I am having trouble figuring out which transformation in the shader that is responsible for this warped image, but it should be something obvious related to how cameraPos is handled. What is causing the image to warp itself in this way?
This looks suspect to me:
vec4 position = gl_ModelViewMatrix * gl_Vertex;
incident = position.xyz / position.w - cameraPos;
Is your cameraPos defined in world space? You're subtracting a view space vector (position), from a supposedly world space cameraPos vector. You either need to do the calculation in world space or view space, but you can't mix them.
To do this correctly in world space you'll have to upload the model matrix separately to get the world space incident vector.