I am trying to implement shadow maps for point lights. Basically I'm creating a framebuffer and then render all shadow casters on each side of a cubemap texture (which is 6 times) and then read it in the regular rendering pass and determine which pixel is in shadow. I have several questions:
Why do I have to include a color attachment in addition to a depth component in order for my cubemap to get anything rendered to? I tried it without the color attachment and it did not work.
After adding the color attachment, I can see my shadow casters in the cubemap but it seems the shadow comparison is wrong. I am suspecting that one is in NDC while the other isn't.
Here's how I initialize my framebuffer containing the shadow cubemap:
// Create the depth buffer
glGenTextures(1, &mDepthTextureID);
glBindTexture(GL_TEXTURE_2D, mDepthTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
//Create the cubemap texture
glGenTextures(1, &mCubemapTextureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemapTextureID);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
for (GLuint i = 0; i < 6; ++i)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, 0);
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
//Create the framebuffer and attach the cubemap texture to it
glGenFramebuffers(1, &mFrameBufferObjectID);
glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferObjectID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mDepthTextureID, 0);
//Disable writes to the color buffer
glDrawBuffer(GL_NONE);
//Disable reads from the color buffer
glReadBuffer(GL_NONE);
GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (Status != GL_FRAMEBUFFER_COMPLETE)
{
switch(Status)
{
case GL_FRAMEBUFFER_UNSUPPORTED:
printf("FrameBuffer unsupported error");
return false;
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
printf("FrameBuffer incomplete attachement");
return false;
break;
default:
printf("GLShadowCubemap error, status: 0x%x\n", Status);
return false;
}
}
//Unbind this
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Here's my shadow's vertex shader: (Only the Position attribute is used)
#version 330 core
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Normal;
layout (location = 2) in vec2 TexCoord;
layout (location = 3) in vec3 Tangent;
uniform mat4 gModelMatrix;
uniform mat4 gModelViewProjectionMatrix;
out vec3 WorldPosition;
/*
* Below needs a GS and using layered rendering
void main()
{
gl_Position = gModelMatrix * vec4(Position, 1.0);
}
*/
void main()
{
vec4 pos4 = vec4(Position, 1.0);
gl_Position = gModelViewProjectionMatrix * pos4;
WorldPosition = (gModelMatrix * pos4).xyz;
}
Here's my shadow fragment shader:
#version 330 core
in vec3 WorldPosition;
uniform vec3 gLightPosition;
out float Fragment;
void main()
{
// get distance between fragment and light source
float dist_to_light = length(WorldPosition - gLightPosition);
//gl_FragDepth = dist_to_light;
Fragment = dist_to_light;
}
Additional question here:
I saw that many have said that overriding gl_FragDepth is a bad idea. I kind of know why but what's strange here is that if I were to override the gl_FragDepth manually, nothing gets written to the cubemap. Why?
Here's how I render all the regular stuff (the variable i is an index to my lights array)
mShadowCubemapFBOs[i].ViewportChange();
mShadowMapTechnique.SetLightPosition(light.Position);
const float shadow_aspect = (static_cast<float>(mShadowWidth) / mShadowHeight);
const mat4 shadow_projection_matrix = glm::perspective(90.f, shadow_aspect, 1.f, mShadowFarPlane);
const vector<MeshComponent>& meshes = ComponentManager::Instance().GetMeshComponentPool().GetPool();
for(int layer = 0; layer < 6; ++layer)
{
GLenum cubemap_face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
mShadowCubemapFBOs[i].Bind(cubemap_face);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
for(const MeshComponent& mesh : meshes)
{
//the transform_component is referenced ahead of time.
const mat4 model_transform = transform_component->GetTransformMatrix();
mShadowMapTechnique.SetModelViewProjectionMatrix(light.Position, cubemap_face, shadow_projection_matrix, model_transform);
mShadowMapTechnique.SetModelMatrix(model_transform);
mesh.Render();
}
}
Finally here's the regular rendering shader:
#version 330 core
const int MAX_LIGHTS = 8;
const int LIGHT_TYPE_DIRECTIONAL = 0;
const int LIGHT_TYPE_POINT = 1;
const int LIGHT_TYPE_SPOT = 2;
in vec2 TexCoord0;
in vec3 WorldNormal0;
in vec3 WorldPos0;
in vec3 WorldTangent0;
out vec4 FragmentColor;
struct Material
{
vec4 Emissive;
vec4 Ambient;
vec4 Diffuse;
vec4 Specular;
float SpecularPower;
bool UseTexture;
};
struct Light
{
vec3 Position;
vec3 Direction;
vec4 Color; //RGBA
float SpotAngle;
float ConstantAttenuation;
float LinearAttenuation;
float QuadraticAttenuation;
int LightType;
samplerCube ShadowMap; //Cubemap shadows
bool Enabled;
};
struct LightingResult
{
vec4 Diffuse;
vec4 Specular;
};
uniform Material gMaterial;
uniform Light gLights[MAX_LIGHTS];
uniform sampler2D gTextureSampler0;
uniform sampler2D gNormalMap;
uniform bool gEnableNormalMap;
uniform vec3 gEyeWorldPos;
float CalculateShadowFactor(vec3 frag_pos, Light light)
{
vec3 fragment_to_light = frag_pos - light.Position;
float sample_distance = texture(light.ShadowMap, fragment_to_light).r;
float distance = length(fragment_to_light);
if (distance < sample_distance + 0.001)
{
return 1.0; // Inside the light
}
else
{
return 0.5; // Inside the shadow
}
}
//L - Light direction vector from pixel to light source
//N - Normal at the pixel
vec4 CalculateDiffuse(Light light, vec3 L, vec3 N)
{
float n_dot_l = max(0, dot(N, L));
return light.Color * n_dot_l;
}
//V - View vector
//L - Light direction vector from pixel to light source
//N - Normal at the pixel
vec4 CalculateSpecular(Light light, vec3 V, vec3 L, vec3 N)
{
//Phong lighting
vec3 R = normalize(reflect(-L, N));
float r_dot_v = max(0, dot(R, V));
return light.Color * pow(r_dot_v, max(0.4, gMaterial.SpecularPower));
}
float CalculateAttenuation(Light light, float distance)
{
return 1.0 / (light.ConstantAttenuation + light.LinearAttenuation * distance + light.QuadraticAttenuation * distance * distance);
}
//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculatePointLight(Light light, vec3 V, vec3 P, vec3 N)
{
LightingResult result;
result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
result.Specular = vec4(0.0, 0.0, 0.0, 1.0);
vec3 L = light.Position - P;
float distance = length(L);
L = normalize(L);
float attenuation = CalculateAttenuation( light, distance );
result.Diffuse = CalculateDiffuse(light, L, N) * attenuation;
result.Specular = CalculateSpecular(light, V, L, N) * attenuation;
return result;
}
//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateDirectionalLight(Light light, vec3 V, vec3 P, vec3 N)
{
LightingResult result;
result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
result.Specular = vec4(0.0, 0.0, 0.0, 1.0);
vec3 L = -light.Direction;
result.Diffuse = CalculateDiffuse(light, L, N);
result.Specular = CalculateSpecular(light, V, L, N);
return result;
}
//L - Light vector
//Smoothness increases as angle gets larger
float CalculateSpotCone(Light light, vec3 L)
{
//cos are in radians
float min_cos = cos(light.SpotAngle);
float max_cos = (min_cos + 1.0f) / 2.0f;
float cos_angle = dot(light.Direction, -L); //negated L such that as we move towards the edge, intensity decreases
return smoothstep(min_cos, max_cos, cos_angle);
}
//V - View vector
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateSpotLight(Light light, vec3 V, vec3 P, vec3 N)
{
LightingResult result;
result.Diffuse = vec4(0.0, 0.0, 0.0, 1.0);
result.Specular = vec4(0.0, 0.0, 0.0, 1.0);
vec3 L = light.Position - P;
float distance = length(L);
L = normalize(L);
float attenuation = CalculateAttenuation(light, distance);
float spot_intensity = CalculateSpotCone(light, L);
result.Diffuse = CalculateDiffuse(light, L, N) * attenuation * spot_intensity;
result.Specular = CalculateSpecular(light, V, L, N) * attenuation * spot_intensity;
return result;
}
//P - Position of pixel
//N - Normal of pixel
LightingResult CalculateLighting(vec3 P, vec3 N)
{
vec3 V = normalize(gEyeWorldPos - P);
LightingResult total_result;
total_result.Diffuse = vec4(0, 0, 0, 1.0);
total_result.Specular = vec4(0, 0, 0, 1.0);
for(int i = 0; i < MAX_LIGHTS; ++i)
{
if(!gLights[i].Enabled)
{
continue;
}
LightingResult result;
result.Diffuse = vec4(0, 0, 0, 1.0);
result.Specular = vec4(0, 0, 0, 1.0);
float shadow_factor = 1.0;
switch(gLights[i].LightType)
{
case LIGHT_TYPE_DIRECTIONAL:
result = CalculateDirectionalLight(gLights[i], V, P, N);
break;
case LIGHT_TYPE_POINT:
result = CalculatePointLight(gLights[i], V, P, N);
shadow_factor = CalculateShadowFactor(P, gLights[i]);
break;
case LIGHT_TYPE_SPOT:
result = CalculateSpotLight(gLights[i], V, P, N);
shadow_factor = CalculateShadowFactor(P, gLights[i]);
break;
}
total_result.Diffuse += (result.Diffuse * shadow_factor);
total_result.Specular += (result.Specular * shadow_factor);
}
total_result.Diffuse = clamp(total_result.Diffuse, 0, 1);
total_result.Specular = clamp(total_result.Specular, 0, 1);
return total_result;
}
vec3 CalculateNormalMapNormal()
{
vec3 normal = normalize(WorldNormal0);
vec3 tangent = normalize(WorldTangent0);
tangent = normalize(tangent - dot(tangent, normal) * normal); //remove components from the normal vector. This is needed for non-uniform scaling
vec3 bi_tangent = cross(tangent, normal);
vec3 bump_map = texture(gNormalMap, TexCoord0).xyz;
bump_map = 2.0 * bump_map - vec3(1.0, 1.0, 1.0); //Remaps the values
mat3 TBN = mat3(tangent, bi_tangent, normal);
vec3 actual_normal = TBN * bump_map;
return normalize(actual_normal);
}
void main()
{
vec3 pixel_normal = normalize(WorldNormal0);
vec4 texture_color = vec4(0, 0, 0, 1);
if(gMaterial.UseTexture)
{
texture_color = texture( gTextureSampler0, TexCoord0 );
}
if(gEnableNormalMap)
{
pixel_normal = CalculateNormalMapNormal();
}
LightingResult light_result = CalculateLighting(WorldPos0, pixel_normal);
vec4 diffuse_color = gMaterial.Diffuse * light_result.Diffuse;
vec4 specular_color = gMaterial.Specular * light_result.Specular;
FragmentColor = (gMaterial.Emissive + gMaterial.Ambient + diffuse_color + specular_color) * texture_color;
//FragmentColor = texture_color;
//temp test
//vec3 fragment_to_light = WorldPos0 - gLights[1].Position;
//FragmentColor = vec4(vec3(texture(gLights[1].ShadowMap, fragment_to_light).r / gFarPlane), 1.0);
}
What am I doing wrong? I see that I am storing the distance from fragment to light in world space and it is written to a color buffer (not the depth buffer) and so it shouldn't be in NDC. Finally when I am comparing it, it's also in world space .... Why are the shadows off? It appears as if the shadows are way larger than they should be so the entire scene is covered with shadow and it appears that what should be the size of shadow is actually covered in light.
Picture of the shadow cubemap:
Picture of the scene (only the helicopter will cast shadow):
Thanks!
After some debugging, I found out my problems:
glPerspective takes fov as radians, not degrees even though it's documentation says it's only in radians if FORCE_RADIANS is defined (I did not define that)
The cubemap for shadow require the clear color to be (FLT_MAX, FLT_MAX, FLT_MAX, 1.0) such that everything is NOT in shadow by default.
Related
Here is my code for generating the texture(MRE):
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
if(readAlpha)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
Here is how Tex Coords are generated:
for (int y = 0; y < resolution; y++)
{
for (int x = 0; x < resolution; x++)
{
int i = x + y * resolution;
glm::vec2 percent = glm::vec2(x, y) / ((float)resolution - 1);
glm::vec3 pointOnPlane = (percent.x - .5f) * 2 * right + (percent.y - .5f) * 2 * front;
pointOnPlane *= scale;
vertices[i] = Vert();
vertices[i].position = glm::vec3(0.0f);
vertices[i].position.x = (float)pointOnPlane.x;
vertices[i].position.y = (float)pointOnPlane.y;
vertices[i].position.z = (float)pointOnPlane.z;
vertices[i].texCoord = glm::vec2(percent.x, percent.y)*textureScale;
vertices[i].normal = glm::vec3(0.0f);
if (x != resolution - 1 && y != resolution - 1)
{
inds[triIndex] = i;
inds[triIndex + 1] = i + resolution + 1;
inds[triIndex + 2] = i + resolution;
inds[triIndex + 3] = i;
inds[triIndex + 4] = i + 1;
inds[triIndex + 5] = i + resolution + 1;
triIndex += 6;
}
}
}
Here is the shader:
VERT:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNorm;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 _PV;
uniform mat4 _Model;
out DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_out;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
data_out.FragPos = aPos;
data_out.Normal = aNorm;
data_out.TexCoord = aTexCoord;
data_out.PV = _PV;
}
GEOM:
#version 330 core
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
in DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_in[];
void main()
{
gl_Position = data_in[0].PV * gl_in[0].gl_Position;
Normal = data_in[0].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[0].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[1].gl_Position;
Normal = data_in[1].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[1].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[2].gl_Position;
Normal = data_in[2].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[2].FragPos;
EmitVertex();
EndPrimitive();
}
FRAG:
#version 330 core
out vec4 FragColor;
uniform vec3 _LightPosition;
uniform vec3 _LightColor;
uniform sampler2D _Diffuse;
//unifrom float _UseTexutres;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
void main()
{
//vec3 objectColor = vec3(0.34f, 0.49f, 0.27f);
vec3 objectColor = vec3(1, 1, 1);
objectColor = texture(_Diffuse, TexCoord).xyz;
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(_LightPosition - FragPos);
float diff = max(dot(norm, lightDir), 0.0f);
vec3 diffuse = diff * _LightColor;
vec3 result = (vec3(0.2, 0.2, 0.2) + diffuse) * objectColor;
FragColor = vec4(result, 1.0);
}
I am getting pixilated texture even thought I am using a 8K texture.
If you want to see the entire source : https://github.com/Jaysmito101/TerraGen3D
Here is the result:
Your geometry shader does not make sense:
First of all, you use the same data_in.TexCoords[0] for all 3 vertices of of the output triangle, which means that all fragments generated for this triangle will sample the exact same location of the texture, resulting in the exact same output color, so the "pixelated" structure of the image emerges. Like you do already for Normal and FragPos, you should forward the data for each vertex. This already should solve your issue.
However, there are more issues with your approach. You do forward mat4 PV as per-Vertex data from the VS to the GS. However, the data you forward is an uniform, so this is a waste of resources. Every shader stage has access to all of the uniforms, so there is no need to forward this data per vertex.
But the real elephant in the room is what this geometry shader is supposed to be doing. The actual transformation with the uniform matrices can - and absolutely should - be carried out directly in the vertex shader. And the rest of your geometry shader is basically an attempt at a pass-through implementation (just a faulty one). So what do you need this shader for? You
can do the transformation in the VS and completely remove the geometry shader. And performance-wise, this will also be a win as geometry shaders are rather inefficent and should be avoided if not absolutely needed.
I have been trying to do basic Shadow Mapping in my custom Engine using LearnOpenGL as the source. The link for the exact tutorial can be found: here.
I have been debugging this bug for around two weeks, researching the internet, and even trying to wrap my head around this, but all I can say is that the shadow almost never appears, and when it appears it is where the light is Pos is terms of x and z. I tried to do everything exactly like in the tutorial around 10 times, I also tried to check this website for similar questions but for every way I found, it was not my case.
findings
In this Image(1) you can see that the shadow is not visible when the light is on top of it, but it is then visible on this Image (2) when the lightPos.x variable is around -4.5 or 4.5, this is so for the lightPos.z variable too. The shadow when appearing is being drawn where the lightPos is, where in the pictures it is circled by a red line.
I use multiple shaders, one for the light and shadow calculations (ShadowMapping) one for a basic depth mapping (ShadowMapGen)
Here is my ShadowMapping shader:
ShadowMapping Vertex
version 460
in vec3 vertexIn;
in vec3 normalIn;
in vec2 textureIn;
out vec3 FragPos;
out vec3 normalOut;
out vec2 textureOut;
out vec4 FragPosLightSpace;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightSpaceMatrix;
void main()
{
textureOut = textureIn;
FragPos = vec3(model * vec4(vertexIn, 1.0));
normalOut = mat3(transpose(inverse(model))) * normalIn;
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
gl_Position = projection * view * model * vec4(vertexIn, 1.0);
}
ShadowMapping Frag
out vec4 FragColor;
in vec3 FragPos;
in vec3 normalOut;
in vec2 textureOut;
in vec4 FragPosLightSpace;
uniform sampler2D diffuseTexture;
uniform sampler2D shadowMap;
uniform vec3 lightPos;
uniform vec3 viewPos;
float ShadowCalculation(vec4 fragPosLightSpace, 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(shadowMap, projCoords.xy).r;
// get depth of current fragment from light's perspective
float currentDepth = projCoords.z;
// check whether current frag pos is in shadow
float bias = max(0.05 * (1.0 - dot(normalOut, lightdir)), 0.005);
// check whether current frag pos is in shadow
// float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
// // PCF
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
for(int x = -1; x <= 1; ++x)
{
for(int y = -1; y <= 1; ++y)
{
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
// keep the shadow at 0.0 when outside the far_plane region of the light's frustum.
if(projCoords.z > 1.0)
shadow = 0.0;
return shadow;
}
void main()
{
vec3 color = texture(diffuseTexture, textureOut).rgb;
vec3 normal = normalize(normalOut);
vec3 lightColor = vec3(1.0f);
// ambient
vec3 ambient = 0.30 * color;
// diffuse
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * lightColor;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, normal);
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(FragPosLightSpace, lightDir);
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color;
FragColor = vec4(lighting, 1.0);
}
ShadowMapGen Vertex
Fragment Shader is empty for this shader
version 460
in vec3 vertexIn;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;
void main()
{
gl_Position = model * lightSpaceMatrix * vec4(vertexIn, 1.0);
}
Variable initialisation
lightPos = glm::vec3(-2.0f, 4.0f, -1.0f);
near_plane = 1.0f;
far_plane = 7.5f;
//SAMPLE 2D Uniform binding
TheShader::Instance()->SendUniformData("ShadowMapping_diffuseTexture", 0);
TheShader::Instance()->SendUniformData("ShadowMapping_shadowMap", 1);
Depth Map Framebuffer Generation
This is how I generate my depth map/ shadow map texture in the constructor of my scene:
glGenFramebuffers(1, &depthMapFBO);
//Create depth texture
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); // Height and Width = 1024
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0, 1.0, 1.0, 1.0 };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
//Attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Then in an Update() function that runs in the While loop of the engine I firstly do:
Render Objects from light's perspective
//Light Projection and view Matrix
m_lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane);
m_lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
//Calculate light matrix and send it.
m_lightSpaceMatrix = m_lightProjection * m_lightView;
TheShader::Instance()->SendUniformData("ShadowMapGen_lightSpaceMatrix", 1, GL_FALSE, m_lightSpaceMatrix);
//Render to Framebuffer depth Map
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
//Set current Shader to ShadowMapGen
m_floor.SetShader("ShadowMapGen");
m_moon.SetShader("ShadowMapGen");
//Send model Matrix to current Shader
m_floor.Draw();
m_moon.Draw();
//Set current Shader back to ShadowMapping
m_moon.SetShader("ShadowMapping");
m_floor.SetShader("ShadowMapping");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Render Objects from Camera's perspective
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Update Camera and Send the view and projection matrices to the ShadowMapping shader
m_freeCamera->Update();
m_freeCamera->Draw();
//Send Light Pos
TheShader::Instance()->SendUniformData("ShadowMapping_lightPos", lightPos);
//Send LightSpaceMatrix
TheShader::Instance()->SendUniformData("ShadowMapping_lightSpaceMatrix", 1, GL_FALSE, m_lightSpaceMatrix);
//Activate Shadow Mapping texture
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depthMap);
//Send model Matrix to ShadowMapping shaders
m_moon.Draw();
m_floor.Draw();
I hope someone will see this, thank you for your time.
I tried to do everything exactly like in the tutorial around 10 times
Well, you seem to have missed at least one obvious thing:
m_lightSpaceMatrix = m_lightProjection * m_lightView;
So far, so good, but in your "ShadowMapGen" vertex shader, you wrote:
gl_Position = model * lightSpaceMatrix * vec4(vertexIn, 1.0);
So you end up with model * projection * view multiplication order, which does not make sense no matter which conventions you adhere to. Since the tutorial uses default GL conventions, you always need projection * view * model * vertex multiplication order, which the tutorial also correctly uses.
Following the learnopengl tutorial (https://learnopengl.com/Advanced-Lighting/Deferred-Shading)
the author leaves fixed the amount of light (32 lights) as shown by the GLSL:
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
struct Light {
vec3 Position;
color;
};
const int NR_LIGHTS = 32;
uniform Light lights [NR_LIGHTS];
uniform vec3 viewPos;
void main ()
{
// retrieve data from G-buffer
vec3 FragPos = texture (gPosition, TexCoords) .rgb;
vec3 Normal = texture (gNormal, TexCoords) .rgb;
vec3 Albedo = texture (gAlbedoSpec, TexCoords) .rgb;
float Specular = texture (gAlbedoSpec, TexCoords) .a;
// then calculate lighting as usual
vec3 lighting = Albedo * 0.1; // hard-coded ambient component
vec3 viewDir = normalize (viewPos - FragPos);
for (int i = 0; i <NR_LIGHTS; ++ i)
{
// diffuse
vec3 lightDir = normalize (lights [i] .Position - FragPos);
vec3 diffuse = max (dot (Normal, lightDir), 0.0) * Albedo * lights [i] .Color;
lighting + = diffuse;
}
FragColor = vec4 (lighting, 1.0);
}
And when it comes to applying the lights:
glBindFramebuffer (GL_FRAMEBUFFER, 0);
// 2. lighting pass: calculate lighting by iterating over screen filled quad pixel-by-pixel using the gbuffer's content.
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderLightingPass.use ();
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, gPosition);
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, gNormal);
glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, gAlbedoSpec);
// send light relevant uniforms
for (unsigned int i = 0; i <lightPositions.size (); i ++)
{
shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Position", lightPositions [i]);
shaderLightingPass.setVec3 ("lights [" + std :: to_string (i) + "] .Color", lightColors [i]);
// update attenuation parameters and calculate radius
const float constant = 1.0; // note that we do not send this to the shader, we assume it is always 1.0 (in our case)
const float linear = 0.7;
const float quadratic = 1.8;
shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Linear", linear);
shaderLightingPass.setFloat ("lights [" + std :: to_string (i) + "] .Quadratic", quadratic);
}
shaderLightingPass.setVec3 ("viewPos", camera.Position);
// finally render quad
renderQuad ();
but I would like to be able to add as many lights as I want, because my project will have countless lights (laser guns, bonfire, blast), so I made some changes:
GLSL:
uniform Light light;
uniform vec3 viewPos;
void main()
{
// retrieve data from gbuffer
vec3 FragPos = texture(gPosition, TexCoords).rgb;
vec3 Normal = texture(gNormal, TexCoords).rgb;
vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb;
float Specular = texture(gAlbedoSpec, TexCoords).a;
// then calculate lighting as usual
vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
vec3 viewDir = normalize(viewPos - FragPos);
// diffuse
vec3 lightDir = normalize(light.Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * light.Color;
// specular
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
vec3 specular = light.Color * spec * Specular;
// attenuation
float distance = length(light.Position - FragPos);
float attenuation = 1.0 / (1.0 + light.Linear * distance + light.Quadratic * distance * distance);
diffuse *= attenuation;
specular *= attenuation;
lighting += diffuse + specular;
FragColor = vec4(lighting, 1.0);
}
And then I passed the values one by one and rendered a quad:
for (unsigned int i = 0; i < lightPositions.size(); i++)
{
shaderLightingPass.use();
shaderLightingPass.setInt("gPosition", 0);
shaderLightingPass.setInt("gNormal", 1);
shaderLightingPass.setInt("gAlbedoSpec", 2);
shaderLightingPass.setVec3("light.Position", lightPositions[i]);
shaderLightingPass.setVec3("light.Color", lightColors[i]);
const float constant = 1.0; // note that we don't send this to the shader, we assume it is always 1.0 (in our case)
const float linear = 0.7;
const float quadratic = 0.08;
shaderLightingPass.setFloat("light.Linear", linear);
shaderLightingPass.setFloat("light.Quadratic", quadratic);
shaderLightingPass.setVec3("viewPos", camera.Position);
renderQuad();
glUseProgram(-1);
}
and also added a new shader to render the framebuffer on the screen:
screenShader.use();
renderQuad();
but my code renders only the first light:
Result
could anyone tell me what I am doing wrong and how to add the lights in the end result?
Please include code like below
void renderDeferredPass(int i)
{
glUseProgram(ps[Passes::Deferred]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, g_fbo);
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
//mat4 model = glm::scale(mat4(1.0f), vec3(3.1f, 3.1f, 3.1f));
model = glm::translate(mat4(1.0f), vec3(-150.0f, -600.0f, -800.0f+camera));
model = glm::rotate(model, 30.0f, vec3(0.0f, 1.0f, 0.0f));
mat4 view = glm::lookAt(glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 0.0, -5.0), glm::vec3(0.0, 1.0, 0.0));
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, &model[0][0]);
glUniformMatrix4fv(viewLocation, 1, GL_FALSE, &view[0][0]);
glUniformMatrix4fv(projLocation, 1, GL_FALSE, &projection[0][0]);
glUniform1i(textureLocation, 0);
quad->Render();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glUseProgram(0);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
}
And
void renderLightPass()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glUseProgram(ps[Passes::LightPass]);
glBindVertexArray(quadVAO);
bindUbo();
for (unsigned int i = 0; i < NUM_GBUFFER_TEXTURES; i++)
{
glActiveTexture(GL_TEXTURE1 + i);
glBindTexture(GL_TEXTURE_2D,
g_textures[POSITION_TEXTURE + i]);
}
glUniform1i(mapLocations[POSITION_TEXTURE], 1);
glUniform1i(mapLocations[DIFFUSE_TEXTURE], 2);
glUniform1i(mapLocations[NORMAL_TEXTURE], 3);
glUniform1i(mapLocations[TEXCOORD_TEXTURE], 4);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
glUseProgram(0);
glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
And your draw function should look like
void display()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glGenerateMipmap(GL_TEXTURE_2D);
glEnable(GL_MULTISAMPLE);
//for (int i = 0; i < quad->m_Entries.size(); i++)
{
renderDeferredPass(0);
renderLightPass();
}
glutSwapBuffers();
glutPostRedisplay();
}
For complete implementation refer following
https://github.com/PixelClear/Deferred-renderer
I have above code where we store light information in SSBO so this demo has 32 lights but can easily extended to many.
The problem is due to the fixed "ambient" term being repeated for the # of lights that overlap the scene.
The shader contains:
vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
This effectively re-adds albedo everytime a light overlaps.
The old code had the following sum:
vec3 lighting = Diffuse * 0.1;
foreach (Light l : lights)
lighting += Diffuse * (l's diffuse lighting)
But now with additive blending you have:
foreach (Light l : lights)
lighting += Diffuse * 0.1;
lighting += Diffuse * (l's diffuse lighting)
As such you got the overbrightening of ambient in https://i.ibb.co/gMBtM6c/With-Blend.png
To fix this you need to separate the (Diffuse * 0.1) term into a separate shader. You would have 1 draw call to apply ambient, then n draw calls for n lights.
The algorithm on the C++ side would then look like:
Make sure you have additive blend still.
Clear Screen
Set Ambient shader, Draw Quad.
Set Light shader, Do your lighting loop and Draw n Quads for n lights.
EDIT: Also it looks like you aren't reading the right Albedo texture based off of your screenshots. It looks like you are reading the position buffer based off of the colors you're getting.
I'm doing Dual depth peeling. I want to ask you, how to properly. I have algorithm like this.
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, dualDepthFBOID);
// Render targets 1 and 2 store the front and back colors
// Clear to 0.0 and use MAX blending to filter written color
// At most one front color and one back color can be written every pass
glDrawBuffers(2, &drawBuffers[1]);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
GL_CHECK_ERRORS
// Render target 0 stores (-minDepth, maxDepth, alphaMultiplier)
glDrawBuffer(drawBuffers[0]);
glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendEquation(GL_MAX);
DrawScene(MVP, initShader);
// 2. Depth peeling + blending pass
glDrawBuffer(drawBuffers[6]);
glClearColor(bg.x, bg.y, bg.z, bg.w);
glClear(GL_COLOR_BUFFER_BIT);
int numLayers = (NUM_PASSES - 1) * 2;
int currId = 0;
for (int layer = 1; bUseOQ || layer < numLayers; layer++) {
currId = layer % 2;
int prevId = 1 - currId;
int bufId = currId * 3;
glDrawBuffers(2, &drawBuffers[bufId+1]);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glDrawBuffer(drawBuffers[bufId+0]);
glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Render target 0: RG32F MAX blending
// Render target 1: RGBA MAX blending
// Render target 2: RGBA MAX blending
glDrawBuffers(3, &drawBuffers[bufId+0]);
glBlendEquation(GL_MAX);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, depthTexID[prevId]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, texID[prevId]);
DrawScene(MVP, dualPeelShader, true,true);
// Full screen pass to alpha-blend the back color
glDrawBuffer(drawBuffers[6]);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (bUseOQ) {
glBeginQuery(GL_SAMPLES_PASSED_ARB, queryId);
}
GL_CHECK_ERRORS
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, backTexID[currId]);
blendShader.Use();
DrawFullScreenQuad();
blendShader.UnUse();
if (bUseOQ) {
glEndQuery(GL_SAMPLES_PASSED);
GLuint sample_count;
glGetQueryObjectuiv(queryId, GL_QUERY_RESULT, &sample_count);
if (sample_count == 0) {
break;
}
}
GL_CHECK_ERRORS
}
GL_CHECK_ERRORS
glDisable(GL_BLEND);
// 3. Final render pass
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK_LEFT);
glBindTexture(GL_TEXTURE_RECTANGLE, colorBlenderTexID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_RECTANGLE, depthTexID[currId]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, texID[currId]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_RECTANGLE, colorBlenderTexID);
finalShader.Use();
DrawFullScreenQuad();
finalShader.UnUse();
I'm doing lighting in dualPeelShader, where are lighting each pass. This is resulting to extremly bright object. Should I do lighting in finalShader?
---EDIT----
Peel Fragment Shader
#version 330 core
layout(location = 0) out vec4 vFragColor0;
layout(location = 1) out vec4 vFragColor1;
layout(location = 2) out vec4 vFragColor2;
uniform vec4 vColor;
uniform float isObject;
uniform vec3 LightPosition;
uniform sampler2DRect depthBlenderTex;
uniform sampler2DRect frontBlenderTex;
in vec4 vOutColor;
in vec3 position;
in vec3 normal;
in vec3 eyeDirection;
in vec3 lightDirection;
#define MAX_DEPTH 1.0
vec4 Lighted()
{
vec3 LightColor = vec3(1.0,1.0,1.0);
float LightPower = 50;
// Material properties
vec3 MaterialDiffuseColor = vOutColor.rgb;
vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);
// Distance to the light
float distance = length( LightPosition - position );
// Normal of the computed fragment, in camera space
vec3 n = normalize( normal );
// Direction of the light (from the fragment to the light)
vec3 l = normalize( lightDirection);
// Cosvaryinge of the angle between the normal and the light direction,
// clamped above 0
// - light is at the vertical of the triangle -> 1
// - light is perpendicular to the triangle -> 0
// - light is behvaryingd the triangle -> 0
float cosTheta = clamp( dot( n,l ), 0,1 );
// Eye vector (towards the camera)
vec3 E = normalize(eyeDirection);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l,n);
// Cosvaryinge of the angle between the Eye vector and the Reflect vector,
// clamped to 0
// - Lookvaryingg varyingto the reflection -> 1
// - Lookvaryingg elsewhere -> < 1
float cosAlpha = clamp( dot( E,R ), 0,1 );
return vec4(MaterialAmbientColor + MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance),vOutColor.a);
}
void main(void)
{
float fragDepth = gl_FragCoord.z;
vec2 depthBlender = texture(depthBlenderTex, gl_FragCoord.xy).xy;
vec4 forwardTemp = texture(frontBlenderTex, gl_FragCoord.xy);
// Depths and 1.0-alphaMult always increase
// so we can use pass-through by default with MAX blending
vFragColor0.xy = depthBlender;
// Front colors always increase (DST += SRC*ALPHA_MULT)
// so we can use pass-through by default with MAX blending
vFragColor1 = forwardTemp;
// Because over blending makes color increase or decrease,
// we cannot pass-through by default.
// Each pass, only one fragment writes a color greater than 0
vFragColor2 = vec4(0.0);
float nearestDepth = -depthBlender.x;
float farthestDepth = depthBlender.y;
float alphaMultiplier = 1.0 - forwardTemp.w;
if (fragDepth < nearestDepth || fragDepth > farthestDepth) {
// Skip this depth in the peeling algorithm
vFragColor0.xy = vec2(-MAX_DEPTH);
return;
}
if (fragDepth > nearestDepth && fragDepth < farthestDepth) {
// This fragment needs to be peeled again
vFragColor0.xy = vec2(-fragDepth, fragDepth);
return;
}
// If we made it here, this fragment is on the peeled layer from last pass
// therefore, we need to shade it, and make sure it is not peeled any farther
vFragColor0.xy = vec2(-MAX_DEPTH);
vec4 Color;
if(isObject == 0.0)
Color = vColor;
else
Color = Lighted();
if (fragDepth == nearestDepth) {
vFragColor1.xyz += Color.rgb * Color.a * alphaMultiplier;
vFragColor1.w = 1.0 - alphaMultiplier * (1.0 - Color.a);
} else {
vFragColor2 += Color;
}
}
Blend Fragment Shader
#version 330 core
uniform sampler2DRect tempTexture;
layout(location = 0) out vec4 vFragColor;
void main(void)
{
vFragColor = texture(tempTexture, gl_FragCoord.xy);
if(vFragColor.a == 0)
discard;
}
And this is result when I invert the tangent vector right after transferring it to vertex shader:
The "shadow" is in the wrong place.
(And it works only when I rotate it through Y axis so the last image seem to present a good parallax mapped cube)
IM SURE IT IS NOT A TANGENT VECTOR OR TEXTURE COORDINATES PROBLEM
Because
I used exactly the same tangent calculation functions and exactly the same cube position, normal and texture coordinate data as in working demo.
After all, I exported arrays with position/texcoord/normal/tangent data into a .txt file and I saw what I exactly expected (and what I expected is the same pos/tex/norm data as in working demo, including calculated tangents which I managed to export from working demo).
The next argument is, I copied my shader code to a working demo and it still works.
Other one is, I tried multiple ways to render this cube.
I tried VBO with glVertexAttribPointer, I tried VBO with saving tangent as other texture coordinate (as in the demo), I tried DisplayList with glVertexAttrib4f. Result is... EXACTLY THE SAME.
Height map is loading correctly, I tried to set it as a diffuse map and it looked OK.
glGetError() gives me No Errors and shader compile logs says so.
It is probably something with camera or init states.
Maybe posting an init code will help.
void CDepthBase::OpenGLSet() {
glEnable( GL_TEXTURE_2D );
glShadeModel( GL_SMOOTH );
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepth( 1.0f );
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glDepthFunc( GL_LEQUAL );
glEnable(GL_DEPTH_TEST);
glBlendFunc( GL_ONE, GL_ONE );
GLfloat ratio;
glViewport(0, 0, ResolutionWidth, ResolutionHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, ResolutionWidth / (float)ResolutionHeight, 0.1f, 900.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (GLEW_OK != glewInit()) {
MBX("Failed to init GLEW.", "Error");
}
if (glewIsSupported("GL_ARB_vertex_buffer_object")) {
VBO_supported = true;
} else VBO_supported = false;
glHint( GL_FOG_HINT, GL_DONT_CARE );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
glShadeModel(GL_SMOOTH);
glAlphaFunc(GL_ALWAYS, 0);
}
By the way, I'm using GL Extension Wrangler with extensions.
Shader code & log (this exported file contains code which was directly passed to glShaderSource):
Vertex shader was successfully compiled to run on hardware.
Fragment shader was successfully compiled to run on hardware.
Fragment shader(s) linked, vertex shader(s) linked.
------------------------------------------------------------------------------------------
varying vec3 lightDir;
varying vec3 viewDir;
attribute vec4 tangent;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
vec3 vertexPos = vec3(gl_ModelViewMatrix * gl_Vertex);
vec3 tn = tangent.xyz;
vec3 n = normalize(gl_NormalMatrix * gl_Normal);
vec3 t = normalize(gl_NormalMatrix * tangent.xyz);
vec3 b = cross(t, n) * -tangent.w;
mat3 tbnMatrix = mat3(t.x, b.x, n.x,
t.y, b.y, n.y,
t.z, b.z, n.z);
lightDir = (gl_LightSource[0].position.xyz - vertexPos) / 100.0;
lightDir = tbnMatrix * lightDir;
viewDir = -vertexPos;
viewDir = tbnMatrix * viewDir;
}
-----------------------------------------------------------------------------------------
varying vec3 lightDir;
varying vec3 viewDir;
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform sampler2D heightMap;
uniform float scale;
uniform float bias;
void main()
{
vec3 v = normalize(viewDir);
vec2 TexCoord = gl_TexCoord[0].st;
{
float height = texture2D(heightMap, gl_TexCoord[0].st).r;
height = height * scale + bias;
TexCoord = gl_TexCoord[0].st + (height * v.xy);
}
vec3 l = lightDir;
float atten = max(0.0, 1.0 - dot(l, l));
l = normalize(l);
vec3 n = normalize(texture2D(normalMap, TexCoord).rgb * 2.0 - 1.0);
vec3 h = normalize(l + v);
float nDotL = max(0.0, dot(n, l));
float nDotH = max(0.0, dot(n, h));
float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess);
vec4 ambient = gl_FrontLightProduct[0].ambient * atten;
vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL * atten;
vec4 specular = gl_FrontLightProduct[0].specular * power * atten;
vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular;color *= texture2D(diffuseMap,TexCoord);
gl_FragColor = color ;
}
Uniforms are working correctly because results are the same if I switch them with constant values.
Compiling shader:
void __Shader::import(){
if(imported) __Shader::~__Shader();
v = glCreateShader(GL_VERTEX_SHADER);
f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v, 1, (const GLchar **)&vsrc.cstr,NULL);
glShaderSource(f, 1, (const GLchar **)&fsrc.cstr,NULL);
glCompileShader(v);
glCompileShader(f);
p = glCreateProgram();
glAttachShader(p,v);
glAttachShader(p,f);
if(_flags & NORMAL_MAPPING)
glBindAttribLocation(p, ATTRIB_TANGENT, "tangent");
glLinkProgram(p);
if(_flags & DIFFUSE_MAPPING)
diffuseUni.loc = glGetUniformLocation(p, "diffuseMap");
if(_flags & NORMAL_MAPPING)
normalUni.loc = glGetUniformLocation(p, "normalMap");
if(_flags & PARALLAX_MAPPING)
heightUni.loc = glGetUniformLocation(p, "heightMap");
if(_flags & SPECULAR_MAPPING)
specularUni.loc = glGetUniformLocation(p, "specularMap");
imported = true;
}
Setting attribute in VBO:
if(tangents.size() > 0){
buffered |= 3;
glGenBuffers(1, &VBO_tangent);
glBindBuffer(GL_ARRAY_BUFFER, VBO_tangent);
glBufferData(GL_ARRAY_BUFFER, tangents.size()*sizeof(tangent), tangents.get_ptr(), GL_STATIC_DRAW);
}
// and in draw:
if(buffered & 3) {
glBindBuffer(GL_ARRAY_BUFFER, VBO_tangent);
glVertexAttribPointer(__Shader::ATTRIB_TANGENT, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(__Shader::ATTRIB_TANGENT);
}
and a small note
for(int i = 0; i < responders.size(); ++i)
if(strstr(responders[i].idea, "tangent problem"))
responders[i].please_dont_talk();
Just tell me your other ideas about what can be the reason of those bad results.
Wheew... already solved it. The problem was with loading texture files even though I did not see any disorders with diffuse mapping or even with diffuse+normal mapping. I was using IMG_Load from SDL, maybe I used it wrong way but it did not work for me. It was probably normal map messed up.
bad texture import code:
if(imported || filenamez.length() < 1) return;
SDL_Surface* surface = 0;
surface = IMG_Load(filenamez.c_str());
if (surface) {
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool endianess = filenamez.substr(filenamez.length()-4) == ".jpg";
glTexImage2D(GL_TEXTURE_2D, 0, 3, surface->w, surface->h, 0,
(endianess ? GL_RGB : GL_BGR), GL_UNSIGNED_BYTE, surface->pixels);
}
BEWARE !
I'm now using HBITMAP-based texture loading taken from dhpoware demo which I was talking about. And it works fine.
peace.
After 2-3 days of hard debugging, let me feel a little bit of euphoria.
Oh, I'd forget, the final result: