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:
Related
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 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.
Right now I am working at creating a heightmap-based terrain grid, similar to the Lighthouse 3D Terrain Tutorial, except that I am using VBO's and EBO's. All has been going well until I have tried to texture my grid. Currently I am applying one texture that spans the entire grid. Using Window 7's sample Jellyfish picture, I end up with this:
For those familiar with the picture, you can see that it is being repeated several times throughout the terrain grid. This led me to believe that my UV coordinates were being corrupted. However, if I use a function that always returns 0 to determine the height at each grid vertex, I end up with this:
Now I am thoroughly confused, and I can't seem to find any other resources to help me.
My code is as follows:
generate_terrain() function:
QImage terrainImage;
terrainImage.load(imagePath.data());
int width = terrainImage.width();
int height = terrainImage.height();
float uStep = 1.0f / width;
float vStep = 1.0f / height;
grid = new std::vector<float>;
indices = new std::vector<unsigned short>;
for (int i = 0; i <= height-1; ++i) {
for (int j = 0; j <= width-1; ++j) {
QVector3D vertex1{j, heightFunction(terrainImage.pixel(j, i)), i};
QVector3D vertex2{j, heightFunction(terrainImage.pixel(j, i+1)), i+1};
QVector3D vertex3{j+1, heightFunction(terrainImage.pixel(j+1, i+1)), i+1};
QVector3D edge1 = vertex2 - vertex1;
QVector3D edge2 = vertex3 - vertex1;
QVector3D normal = QVector3D::crossProduct(edge1, edge2);
normal.normalize();
grid->push_back(vertex1.x());
grid->push_back(vertex1.y());
grid->push_back(vertex1.z());
grid->push_back(normal.x());
grid->push_back(normal.y());
grid->push_back(normal.z());
grid->push_back(j * uStep);
grid->push_back(i * vStep);
}
}
for (int i = 0; i < height-1; ++i) {
for (int j = 0; j < width-1; ++j) {
indices->push_back(i * width + j);
indices->push_back((i+1) * width + j);
indices->push_back((i+1) * width + (j+1));
indices->push_back((i+1) * width + (j+1));
indices->push_back(i * width + (j+1));
indices->push_back(i * width + j);
}
}
vertices = grid->size()/8;
indexCount = indices->size();
Texture Loading:
f->glGenTextures(1, &textureId);
f->glBindTexture(GL_TEXTURE_2D, textureId);
QImage texture;
texture.load(texturePath.data());
QImage glTexture = QGLWidget::convertToGLFormat(texture);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexture.width(), glTexture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture.bits());
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
Drawing:
f->glActiveTexture(GL_TEXTURE0);
f->glBindTexture(GL_TEXTURE_2D, textureId);
program->setUniformValue(textureUniform.data(), 0);
f->glBindBuffer(GL_ARRAY_BUFFER, vbo.bufferId());
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo.bufferId());
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
f->glDisableVertexAttribArray(2);
f->glDisableVertexAttribArray(1);
f->glDisableVertexAttribArray(0);
Shaders:
Vertex:
attribute vec3 vertex_modelspace;
attribute vec3 normal_in;
attribute vec2 uv_in;
uniform mat4 mvp;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 lightPosition;
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
void main(void)
{
gl_Position = projection * view * model * vec4(vertex_modelspace, 1);
uv = uv_in;
normal = normal_in;
fragPos = vec3(model * vec4(vertex_modelspace, 1));
}
Fragment:
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
uniform sampler2D texture;
uniform vec3 lightPosition;
void main(void)
{
vec3 lightColor = vec3(0.6, 0.6, 0.6);
float ambientStrength = 0.2;
vec3 ambient = ambientStrength * lightColor;
vec3 norm = normalize(normal);
vec3 lightDirection = normalize(lightPosition - fragPos);
float diff = max(dot(norm, lightDirection), 0.0);
vec3 diffuse = diff * lightColor;
vec3 color = texture2D(texture, uv).rgb;
vec3 result = (ambient + diffuse) * color;
gl_FragColor = vec4(result, 1.0);
}
I am completely stuck, so any suggestions are welcome :)
P.S. I am also working at trying to get my lighting to look better, so any tips on that would be welcome as well.
Your code is assuming values for the attribute locations, which are the values used as the first argument to glVertexAttribPointer() and glEnableVertexAttribArray(). For example here:
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
you're assuming that the positions have location 0, the normals location 1, and the texture coordinates location 2.
This is not guaranteed by anything you have currently in your code. The order of the attribute declarations in the GLSL code does not define the location assignment. For example from the OpenGL 3.2 spec:
When a program is linked, any active attributes without a binding speciļ¬ed through BindAttribLocation will be automatically be bound to vertex attributes by the GL.
Note that this does not specify how the automatic assignment of the locations is done. This means that it's implementation dependent.
To fix this, there are two approaches:
You can call glBindAttribLocation() for all your attributes before the shader program is linked.
You can query the automatically assigned locations by calling glGetAttribLocation() after the program is linked.
In newer OpenGL versions (GLSL 3.30 and later, which is the version matching OpenGL 3.3), you also have the option to specify the location directly in the GLSL code, using qualifiers of the form layout(location=...).
None of these options has any major advantages over the others. Just use the one that works best based on your preferences and software architecture.
I implemented a new rendering pipeline in my engine and rendering is broken now. When I directly draw a texture of the G-Buffer to screen, it shows up correctly. So the G-Buffer is fine. But somehow the lighting pass makes trouble. Even if I don't use the resulting texture of it but try to display albedo from G-Buffer after the lighting pass, it shows a solid gray color.
I can't explain this behavior and the strange thing is that there are no OpenGL errors at any point.
Vertex Shader to draw a fullscreen quad.
#version 330
in vec4 vertex;
out vec2 coord;
void main()
{
coord = vertex.xy;
gl_Position = vertex * 2.0 - 1.0;
}
Fragment Shader for lighting.
#version 330
in vec2 coord;
out vec3 image;
uniform int type = 0;
uniform sampler2D positions;
uniform sampler2D normals;
uniform vec3 light;
uniform vec3 color;
uniform float radius;
uniform float intensity = 1.0;
void main()
{
if(type == 0) // directional light
{
vec3 normal = texture2D(normals, coord).xyz;
float fraction = max(dot(normalize(light), normal) / 2.0 + 0.5, 0);
image = intensity * color * fraction;
}
else if(type == 1) // point light
{
vec3 pixel = texture2D(positions, coord).xyz;
vec3 normal = texture2D(normals, coord).xyz;
float dist = max(distance(pixel, light), 1);
float magnitude = 1 / pow(dist / radius + 1, 2);
float cutoff = 0.4;
float attenuation = clamp((magnitude - cutoff) / (1 - cutoff), 0, 1);
float fraction = clamp(dot(normalize(light - pixel), normal), -1, 1);
image = intensity * color * attenuation * max(fraction, 0.2);
}
}
Targets and samplers for the lighting pass. Texture ids are mapped to attachment respectively shader location.
unordered_map<GLenum, GLuint> targets;
targets.insert(make_pair(GL_COLOR_ATTACHMENT2, ...)); // light
targets.insert(make_pair(GL_DEPTH_STENCIL_ATTACHMENT, ...)); // depth and stencil
unordered_map<string, GLuint> samplers;
samplers.insert(make_pair("positions", ...)); // positions from G-Buffer
samplers.insert(make_pair("normals", ...)); // normals from G-Buffer
Draw function for lighting pass.
void DrawLights(unordered_map<string, GLuint> Samplers, GLuint Program)
{
auto lis = Entity->Get<Light>();
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glUseProgram(Program);
int n = 0; for(auto i : Samplers)
{
glActiveTexture(GL_TEXTURE0 + n);
glBindTexture(GL_TEXTURE_2D, i.second);
glUniform1i(glGetUniformLocation(Program, i.first.c_str()), n);
n++;
}
mat4 view = Entity->Get<Camera>(*Global->Get<unsigned int>("camera"))->View;
for(auto i : lis)
{
int type = i.second->Type == Light::DIRECTIONAL ? 0 : 1;
vec3 pos = vec3(view * vec4(Entity->Get<Form>(i.first)->Position(), !type ? 0 : 1));
glUniform1i(glGetUniformLocation(Program, "type"), type);
glUniform3f(glGetUniformLocation(Program, "light"), pos.x, pos.y, pos.z);
glUniform3f(glGetUniformLocation(Program, "color"), i.second->Color.x, i.second->Color.y, i.second->Color.z);
glUniform1f(glGetUniformLocation(Program, "radius"), i.second->Radius);
glUniform1f(glGetUniformLocation(Program, "intensity"), i.second->Intensity);
glBegin(GL_QUADS);
glVertex2i(0, 0);
glVertex2i(1, 0);
glVertex2i(1, 1);
glVertex2i(0, 1);
glEnd();
}
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
I found the error and it was such a stupid one. The old rendering pipeline bound the correct framebuffer before calling the draw function of that pass. But the new one didn't so each draw function had to do that itself. Therefore I wanted to update all draw function, but I missed the draw function of the lighting pass.
Therefore the framebuffer of the G-Buffer was still bound and the lighting pass changed its targets.
Thanks to you guys, you had no change to find that error, since I hadn't posted my complete pipeline system.