I'm having a little trouble on implementing Shadow Mapping in the Engine I'm doing. I'm following the LearnOpenGL's tutorial to do so, and more or less it "works", but there's something I'm doing wrong, like if something in the shadowmap was reverted or something, check the next gifs: gif1, gif2
In those gifs, there is a simple scene with a directional light (which has an orthogonal frustum to make the shadow calculations and to ease my life), which has to cast shadows. Then, at the right there is a little window showing the "shadow map scene", the scene rendered from light's point of view only with depth values.
Now, about the code, it pretty follows the guidelines from the mentioned tutorial. I have a ModuleRenderer and I first create the framebuffers with the textures they have to have:
glGenTextures(1, &depthMapTexture);
glBindTexture(GL_TEXTURE_2D, depthMapTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, App->window->GetWindowWidth(), App->window->GetWindowHeight(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
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);
glGenFramebuffers(1, &depthbufferFBO);
glBindFramebuffer(GL_FRAMEBUFFER, depthbufferFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMapTexture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
Then, in the Module Renderer's post update, I make the 2 render passes and draw the FBOs:
// --- Shadows Buffer (Render 1st Pass) ---
glBindFramebuffer(GL_FRAMEBUFFER, depthbufferFBO);
SendShaderUniforms(shadowsShader->ID, true);
DrawRenderMeshes(true);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// --- Standard Buffer (Render 2nd Pass) ---
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
SendShaderUniforms(defaultShader->ID, false);
DrawRenderMeshes(false);
// --- Draw Lights ---
std::vector<ComponentLight*>::iterator LightIterator = m_LightsVec.begin();
for (; LightIterator != m_LightsVec.end(); ++LightIterator)
(*LightIterator)->Draw();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// -- Draw framebuffer textures ---
DrawFramebuffer(depth_quadVAO, depthMapTexture, true);
DrawFramebuffer(quadVAO, rendertexture, false);
The DrawRenderMeshes() function, basically gets the list of meshes to draw, the shader it has to pick, and sends all needed uniforms. It's a huge function to put here, but for a normal mesh, it gets a shader called Standard and sends all it needs. For the shadow map, it sends the texture attached to the depth FBO:
glUniform1i(glGetUniformLocation(shader, "u_ShadowMap"), 4);
glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, depthMapTexture);
In the standard shader, for the vertex, I just pass the uniform for lightspace (the light's frustum projection x view matrices) to calculate the fragment position in light space (the next is done in the vertex's main):
v_FragPos = vec3(u_Model * vec4(a_Position, 1.0));
v_FragPos_InLightSpace = u_LightSpace * vec4(v_FragPos, 1.0);
v_FragPos_InLightSpace.z = (1.0 - v_FragPos_InLightSpace.z);
gl_Position = u_Proj * u_View * vec4(v_FragPos, 1.0);
And for the fragment, I calculate, with that value, the fragment's shadowing (the diffuse+specular values of light are multiplied by the result of that shadowing function):
float ShadowCalculation()
{
vec3 projCoords = v_FragPos_InLightSpace.xyz / v_FragPos_InLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
float closeDepth = texture(u_ShadowMap, projCoords.xy).z;
float currDept = projCoords.z;
float shadow = currDept > closeDepth ? 1.0 : 0.0;
return (1.0 - shadow);
}
Again, I'm not sure what can be wrong, but I can guess that something is kind of inverted? Not sure... If anyone can imagine something and let me know, I would appreciate a lot, thanks you :)
Note: For the first render pass, in which all scene is rendered only with depth values, I use a very simple shader that just puts objects in their position with the common function (in the vertex shader):
gl_Position = u_Proj * u_View * u_Model * vec4(a_Position, 1.0);
And the fragment doesn't do anything, is an empty main(), since it's the same than doing what we want for shadows pass
gl_FragDepth = gl_FragCoord.z;
Related
The goal:
Effectively read and write to the same texture, like how Shadertoy does their buffers.
The setup:
I have a basic feedback system with 2 textures each connected to a framebuffer. As I render to frame buffer 1, I bind Texture 2 for sampling in the shader. Then, as I render to frame buffer 2, I bind texture 1 for sampling, and repeat. Finally, I output texture 1 to the whole screen with the default frame buffer and a sperate shader.
The issue:
This almost works as intended as I'm able to read from the texture in the shader and also output to it, creating the desired feedback loop.
The problem is that the frame buffers do not clear completely to black it seems.
To test, I made a simple trailing effect.
In shadertoy, the trail completely disappears as intended:
Live in shadertoy
But in my app, the trail begins to disappear, but leaves a small amount behind:
My thoughts are I'm not clearing the frame buffers correctly or I am not using GLFW's double buffering correctly in this instance. I've tried every combination of clearing the framebuffers but I must be missing something here.
The code:
Here is the trailing effect shader with a moving circle (Same as above images)
#version 330
precision highp float;
uniform sampler2D samplerA; // Texture sampler
uniform float uTime; // current execution time
uniform vec2 uResolution; // resolution of window
void main()
{
vec2 uv = gl_FragCoord.xy / uResolution.xy; // Coordinates from 0 - 1
vec3 tex = texture(samplerA, uv).xyz;// Read ping pong texture that we are writing to
vec2 pos = .3*vec2(cos(uTime), sin(uTime)); // Circle position (circular motion around screen)
vec3 c = mix(vec3(1.), vec3(0), step(.0, length(uv - pos)-.07)); // Circle color
tex = mix(c, tex, .981); // Replace some circle color with the texture color
gl_FragColor = vec4(tex, 1.0); // Output to texture
}
Frame buffer and texture creation:
// -- Generate frame buffer 1 --
glGenFramebuffers(1, &frameBuffer1);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer1);
// Generate texture 1
glGenTextures(1, &texture1);
// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, texture1);
// Create an empty image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);
// Nearest filtering, for sampling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// Attach output texture to frame buffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
// -- Generate frame buffer 2 --
glGenFramebuffers(1, &frameBuffer2);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer2);
// Generate texture 2
glGenTextures(1, &texture2);
// Bind the newly created texture
glBindTexture(GL_TEXTURE_2D, texture2);
// Create an empty image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);
// Nearest filtering, for sampling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// Attach texture 2 to frame buffer 2
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
Main loop:
while(programIsRunning){
// Draw scene twice, once to frame buffer 1 and once to frame buffer 2
for (int i = 0; i < 2; i++)
{
// Start trailing effect shader program
glUseProgram(program);
glViewport(0, 0, platform.windowWidth(), platform.windowHeight());
// Write to frame buffer 1
if (i == 0)
{
// Bind and clear frame buffer 1
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer1);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Bind texture 2 for sampler
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(uniforms.samplerA, 0);
}
else // Write to frame buffer 2
{
// Bind and clear frame buffer 2
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer2);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Bind texture 1 for sampler
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(uniforms.samplerA, 0);
}
// Render to screen
glDrawArrays(GL_TRIANGLES, 0, 6);
}
// Start screen shader program
glUseProgram(screenProgram);
// Bind default frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, platform.windowWidth(), platform.windowHeight());
// Bind texture 1 for sampler (binding texture 2 should be the same?)
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(uniforms.samplerA, 0);
// Draw final rectangle to screen
glDrawArrays(GL_TRIANGLES, 0, 6);
// Swap glfw buffers
glfwSwapBuffers(platform.window());
}
If this is an issue with clearing I would really like to know why. Changing which frame buffer gets cleared doesn't seem to change anything.
I will keep experimenting in the meantime.
Thank you!
The problem is that you are creating a texture with too little precision for your exponential moving average computations to ultimately discretize to zero.
In your call to:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1920, 1080, 0, GL_RGBA, GL_FLOAT, 0);
you are using the unsized internal format GL_RGBA (third argument), which will very likely ultimately result in the GL_RGBA8 internal format actually being used. So, all channels will have a precision of 8 bits.
You probably believed that using GL_FLOAT as the argument for the type parameter results in a 32-bit floating-point texture being allocated: It does not. The type parameter is used to indicate to OpenGL how it should interpret your data (last parameter of the function) when/if you actually specify data to be uploaded. You use 0/NULL so the type parameter really does not influence the call, as there is no memory to be interpreted as float values to be uploaded.
So, your texture will have a precision of 8 bits per channel and therefore each channel can hold at most 256 different values.
Given that in your shown RGB image the RGB value is 24 for each channel, we can do the math how OpenGL gets to this value and why it won't get any lower than that:
First, let's do another round of your exponential moving average between (0, 0, 0) and (24, 24, 24)/255 with a factor of your 0.981:
d = (24, 24, 24)/255 * 0.981
If we had infinite precision, this value d would be 0.09232941176.
Now, let's see what RGB value within the representable range [0, 255] this comes close to: 0.09232941176 * 255 = 23.5439999988.
So, this value is actually (when correctly rounded to the nearest representable value within the [0, 255] discretization) 24 again. And that's where it stays.
In order to fix this, you likely need to use a higher precision internal texture format, such as GL_RGBA32F (which is actually what ShaderToy itself uses).
Open GL 3.3
My textures suddenly became black after working for many days
Pretty much all the posts that had a similiar issue were about
incorrect or absent use of glTexParameteri or incorrect texture loading but i seem to be doing everything correctly regarding that,
the vector containing the data is 1024 bytes (16 pixels x 16 pixels x 4 bytes) so that's good,
after the issue arose i made a test texture just to make shure everything about that was right.
also saw that many posts issues were incomplete texture but here im using glTexImage2D passing the data so the texture has to be complete, also am not creating mipmaps, i disabled them for testing. Altough they were on and working before this bug.
Also im calling glGetError quite frequently and there are no errors
Here is the texture creation code:
unsigned int testTexture;
unsigned long w, h;
std::vector<byte> data;
std::vector<byte> img;
loadFile(data, "./assets/textures/blocks/brick.png");
decodePNG(img, w, h, &data[0], data.size());
glGenTextures(1, &testTexture);
glBindTexture(GL_TEXTURE_2D, testTexture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,w,h,0,GL_RGBA, GL_UNSIGNED_BYTE,&img[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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
data.clear();
img.clear();
And here is where i setup my Uniforms:
glUseProgram(worldShaderProgram);
glUniform1f(glGetUniformLocation(worldShaderProgram, "time"), gameTime);
glUniformMatrix4fv(glGetUniformLocation(worldShaderProgram, "MVP"), 1, GL_FALSE, &TheMatrix[0][0]);
glUniform1i(glGetUniformLocation(worldShaderProgram, "texAtlas"), testTexture);
glUniform1f(glGetUniformLocation(worldShaderProgram, "texMult"), 16.0f / 256.0f);
glUniform4f(glGetUniformLocation(worldShaderProgram, "fogColor"), fogColor.r, fogColor.g, fogColor.b, fogColor.a);
Also Here Is The Fragment Shader
#version 330
in vec4 tex_color;
in vec2 tex_coord;
layout(location = 0) out vec4 color;
uniform sampler2D texAtlas;
uniform mat4 MVP;
uniform vec4 fogColor;
const float fogStart = 0.999f;
const float fogEnd = 0.9991f;
const float fogMult = 1.0f / (fogEnd - fogStart);
void main() {
if (gl_FragCoord.z >= fogEnd)
discard;
//color = vec4(tex_coord.x,tex_coord.y,0.0f,1.0f) * tex_color; // This Line Does What Its Supposed To
color = texture(texAtlas,tex_coord) * tex_color; // This One Does Not
if (gl_FragCoord.z >= fogStart)
color = mix(color,fogColor,(gl_FragCoord.z - fogStart) * fogMult);
}
If i use this line color = vec4(tex_coord.x,tex_coord.y,0.0f,1.0f) * tex_color;
Instead of this line color = texture(texAtlas,tex_coord) * tex_color;
To show the coord from witch it would be getting its color from the texture, the result is what you would expect: (Currenlty only testing it with the top faces)
Image Link Cause I Cant Do Images But Please Click
That Proves That The Vertex Shader Is Working Corretly
(The sampler2D is obtained from a uniform at the fragment shader)
Main Loop Rendering Code
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, textures.textureId);
glUseProgram(worldShaderProgram);
wm.render();
// wm.render() calls lots of meshes to render themselves
// just wanted to point out each one of them has their own
// vertex arry buffer, vertex buffer, and index buffer
// to render i bind the vertex array buffer with glBindVertexArray(vertexArrayBuffer);
// then i call glDrawElements();
Also here is the OpenGL Initialization Code
if (!glfwInit()) // Initialize the library
return -1;
window = glfwCreateWindow(wndSize.width, wndSize.height, "Minecraft", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // Make the window's context current
glfwSetWindowSizeCallback(window,resiseEvent);
glfwSwapInterval(1);
if (glewInit() != GLEW_OK)
return -1;
glClearColor(fogColor.r, fogColor.g, fogColor.b, fogColor.a);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST); // Enable depth testing for z-culling
glEnable(GL_CULL_FACE); // Orientation Culling
glDepthFunc(GL_LEQUAL); // Set the type of depth-test (<=)
glShadeModel(GL_SMOOTH); // Enable smooth shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nice perspective corrections
glLineWidth(2.0f);
You wrongly set the texture object to the texture sampler uniform. This is wrong:
glUniform1i(glGetUniformLocation(worldShaderProgram, "texAtlas"), testTexture);
The binding point between the texture object and the texture sampler uniform is the texture unit. When glBindTexture is invoked, then the texture object is bound to the specified target and the current texture unit. The texture unit can be chosen by glActivTexture. The default texture unit is GL_TEXTURE0.
Since your texture is bound to texture unit 0 (GL_TEXTURE0), you have set the value 0 to the texture sampler uniform:
glUniform1i(glGetUniformLocation(worldShaderProgram, "texAtlas"), 0);
Note that your code worked before by chance. You just had 1 texture object or testTexture was the first texture name created. Hence the value of testTexture was 0. Now the value of testTexture is no longer 0, causing your code to fail.
I generate a PointCloud in my program, and now, I want to be able to click on a point in this point cloud rendered to my screen using OpenGL.
In order to do so, I used the trick of giving to each pixel in an offscreen render a colour based on its index in the VBO. I use the same camera for my offscreen render and my onscreen render so they move together, and when I click, I get values of my offscreen render to retrieve the position in the VBO to get the point I clicked on. This is the theory since when I click, I have only (0,0,0). I believe that means my FBO is not well renderer but I'm not sure whether it is that or if the problem comes from somewhere else...
So here are the steps. clicFBO is the FBO I'm using for offscreen render, and clicTextureColorBuf is the texture in which I write in the FBO
glGenFramebuffers(1, &clicFBO);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glGenTextures(1, &clicTextureColorBuf);
glBindTexture(GL_TEXTURE_2D, clicTextureColorBuf);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, clicTextureColorBuf, 0);
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers);
After that, I wrote a shader that gives to each point the color of its index in the VBO...
std::vector<cv::Point3f> reconstruction3D; //Will contain the position of my points
std::vector<float> indicesPointsVBO; //Will contain the indexes of each point
for (int i = 0; i < pts3d.size(); ++i) {
reconstruction3D.push_back(pts3d[i].pt3d);
colors3D.push_back(pt_tmp);
indicesPointsVBO.push_back(((float)i / (float)pts3d.size() ));
}
GLuint clicVAO, clicVBO[2];
glGenVertexArrays(1, &clicVAO);
glGenBuffers(2, &clicVBO[0]);
glBindVertexArray(clicVAO);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[0]);
glBufferData(GL_ARRAY_BUFFER, reconstruction3D.size() * sizeof(cv::Point3f), &reconstruction3D[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glEnable(GL_PROGRAM_POINT_SIZE);
glBindBuffer(GL_ARRAY_BUFFER, clicVBO[1]);
glBufferData(GL_ARRAY_BUFFER, indicesPointsVBO.size() * sizeof(float), &indicesPointsVBO[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
and the vertex shader:
layout (location = 0) in vec3 pos;
layout (location = 1) in float col;
out float Col;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform int pointSize;
void main()
{
gl_PointSize = pointSize;
gl_Position = projection * view * model * vec4(pos, 1.0);
Col = col;
}
And the Fragment:
#version 330 core
layout(location = 0) out vec4 FragColor;
in float Col;
void main()
{
FragColor = vec4(Col, Col, Col ,1.0);
}
And this is how I render this texture:
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 1.0f, 100.0f);
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
clicShader.use();
glDisable(GL_DEPTH_TEST);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
clicShader.setMat4("projection", projection);
clicShader.setMat4("view", view);
model = glm::mat4();
clicShader.setMat4("model", model);
clicShader.setInt("pointSize", pointSize);
glBindVertexArray(clicVAO);
glDrawArrays(GL_POINTS, 0, (GLsizei)reconstruction3D.size());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And then, when I click, I Use this piece of Code:
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
int width = 11, height = 11;
std::array<GLfloat, 363> arry{ 1 };
glReadPixels(Xpos - 5, Ypos - 5, width, height, GL_RGB, GL_UNSIGNED_BYTE, &arry);
for (int i = 0; i < 363; i+=3) { // It's 3 time the same number anyways for each number
std::cout << arry[i] << " "; // It gives me only 0's
}
std::cout << std::endl << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, clicFBO);
I know the error might be really stupid but I still have some problems with how OpenGL works.
I put what I thought was necessary to understand the problem (without extending too much), but if you need more code, I can write it too.
I know this is not a question in which you can say Yes or No and it's more like debugging my program, but since I really don't find from where the problem comes from, I'm looking toward someone who can explain to me what I did wrong. I do not necessarily seek the solution itself, but clues that could help me understand where my error is ...
Using a framebuffer object FBO to store a "object identifier" is a cool method. But also want to see the objects, right? Then you must render also to the default frame buffer (let me call it "defFB", which is not a FBO).
Because you need to render to two different targets, you need one of these techniques:
Draw objects twice (e.g. with two glDrawArrays calls), one to the FBO and a second one to the defFB.
Draw to two FBO's images at once and later blit one of then (with colors) to the defFB.
For the first technique you may use a texture attached to a FBO (as you currently do). Or you can use a "Renderbuffer" and draw to it.
The second approach needs a second "out" in the fragment shader:
layout(location = 0) out vec3 color; //GL_COLOR_ATTACHMENT0
layout(location = 1) out vec3 objID; //GL_COLOR_ATTACHMENT1
and setting the two attachments with glDrawBuffers.
For the blit part, read this answer.
Note that both "out" have the same format, vec3 in this example.
A fail in your code is that you set a RGB texture format and also use this format at glReadPixels, but your "out" in the FS is vec4 instead of vec3.
More concerns are:
Check the completeness with glCheckFramebufferStatus
Using a "depth attachment" to the FBO may be needed, even it will not be used for reading.
Disabling the depth test will put all elements if the frame. Your point-picking will select the last drawn, not the nearest.
I found the problem.
There were 2 failures in my code :
The first one is that in OpenGL, there is an Y inversion between the image and the framebuffer. So in order to pick the good point, you have to flip Y using the size of the viewport : I did it like this :
GLint m_viewport[4];
glGetIntegerv(GL_VIEWPORT, m_viewport);
int YposTMP = m_viewport[3] - Ypos - 1;
The second one is the use of
glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]);, the 6th parameter must be GL_FLOAT since the datas i'm returning are float.
Thanks all!
Best regards,
R.S
I'm working on shadow casting using this lovely tutorial. The process is, we render the scene to a frame buffer, attached to which is a cubemap to hold the depth values. Then, we pass this cubemap to a fragment shader which samples it and gets the depth values from there.
I took a slight deviation from the tutorial in that instead of using a geometry shader to render the entire cubemap at once, I instead render the scene six times to get the same effect - largely because my current shader system doesn't support geometry shaders and for now I'm not too concerned about the performance hit.
The depth cubemap is being drawn to fine, here's a screenshot from gDEBugger:
Everything seems to be in order here.
However, I'm having issues in my fragment shader when I attempt to sample this cubemap. After the call to glDrawArrays, a call to glGetError returns GL_INVALID_OPERATION, and as best as I can tell, it's coming from here: (The offending line has been commented)
struct PointLight
{
vec3 Position;
float ConstantRolloff;
float LinearRolloff;
float QuadraticRolloff;
vec4 Color;
samplerCube DepthMap;
float FarPlane;
};
uniform PointLight PointLights[NUM_POINT_LIGHTS];
[...]
float CalculateShadow(int lindex)
{
// Calculate vector between fragment and light
vec3 fragToLight = FragPos - PointLights[lindex].Position;
// Sample from the depth map (Comment this out and everything works fine!)
float closestDepth = texture(PointLights[lindex].DepthMap, vec3(1.0, 1.0, 1.0)).r;
// Transform to original value
closestDepth *= PointLights[lindex].FarPlane;
// Get current depth
float currDepth = length(fragToLight);
// Test for shadow
float bias = 0.05;
float shadow = currDepth - bias > closestDepth ? 1.0 : 0.0;
return shadow;
}
Commenting out the aforementioned line seems to make everything work fine - so I'm assuming it's the call to the texture sampler that's causing issues. I saw that this can be attributed to using two textures of different types in the same texture unit - but according to gDEBugger this isn't the case:
Texture 16 is the depth cube map.
In case it's relevant, here's how I'm setting up the FBO: (called only once)
// Generate frame buffer
glGenFramebuffers(1, &depthMapFrameBuffer);
// Generate depth maps
glGenTextures(1, &depthMap);
// Set up textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, depthMap);
for (int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT,
ShadowmapSize, ShadowmapSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// Set texture parameters
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
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);
// Attach cubemap to FBO
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFrameBuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
ERROR_LOG("PointLight created an incomplete frame buffer!\n");
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Here's how I'm drawing with it: (called every frame)
// Set up viewport
glViewport(0, 0, ShadowmapSize, ShadowmapSize);
// Bind frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFrameBuffer);
// Clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
// Render scene
for(int i = 0; i < 6; ++i)
{
sh->SetUniform("ShadowMatrix", lightSpaceTransforms[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, depthMap, 0);
Space()->Get<Renderer>()->RenderScene(sh);
}
// Unbind frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
And here's how I'm binding it before drawing:
std::stringstream ssD;
ssD << "PointLights[" << i << "].DepthMap";
glActiveTexture(GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap()); // just returns the ID of the light's depth map
shader->SetUniform(ssD.str().c_str(), i + 4); // just a wrapper around glSetUniform1i
Thank you for reading, and please let me know if I can supply more information!
It is old post, but i think it may be useful for other people from the search.
Your problem here:
glActiveTexture(GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap());
This replacement should fix problem:
glActiveTexture(GL_TEXTURE4 + i);
glUniform1i(glGetUniformLocation("programId", "cubMapUniformName"), GL_TEXTURE4 + i);
glBindTexture(GL_TEXTURE_CUBE_MAP, pointlights[i]->DepthMap());
It set texture unit number for shader sampler
The Problem
I have been trying to implement shadows in OpenGL for some time. I have finally gotten it to a semi-working state in that the shadow appears but covers the scene in strange places [i.e - it is not relative to the light]
To further explain the above gif: As I move the light-source further away from the scene (to the left) - the shadow stretches further. Why? If anything, it should show more of the scene.
Update - I messed around with the lights position and am now being given this result (confusing):
Depth Map
Here it is:
The Code
Because this is a difficult issue to pinpoint - I will post a large chunk of the code I am using in this application.
The Framebuffer and Depth Texture - The first thing I needed was a framebuffer to record the depth values of all the drawn objects and then I needed to dump these values into a depth texture (the shadow-map):
// Create Framebuffer
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
// Create and Load Depth Texture
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
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 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
//Attach Texture To Framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
//Check for errors
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
Falcon::Debug::error("ShadowBuffer [Framebuffer] could not be initialized.");
Rendering The Scene - First I do the shadow-pass which just runs through some basic shaders and outputs to the framebuffer and then I do a second, regular pass that actually draws the scene and does GLSL shadow-map sampling:
//Clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Select Main Shader
normalShader->useShader();
//Bind + Update + Draw
/* Render Shadows */
shadowShader->useShader();
glBindFramebuffer(GL_FRAMEBUFFER, Shadows::framebuffer());
//Viewport
glViewport(0,0,640,480);
//GLM Matrix Definitions
glm::mat4 shadow_matrix_view;
glm::mat4 shadow_matrix_projection;
//View And Projection Calculations
shadow_matrix_view = glm::lookAt(glm::vec3(light.x,light.y,light.z), glm::vec3(0,0,0), glm::vec3(0,1,0));
shadow_matrix_projection = glm::perspective(45.0f, 1.0f, 0.1f, 1000.0f);
//Calculate MVP(s)
glm::mat4 shadow_depth_mvp = shadow_matrix_projection * shadow_matrix_view * glm::mat4(1.0);
glm::mat4 shadow_depth_bias = glm::mat4(0.5,0,0,0,0,0.5,0,0,0,0,0.5,0,0.5,0.5,0.5,1) * shadow_depth_mvp;
//Send Data To The GPU
glUniformMatrix4fv(glGetUniformLocation(shadowShader->getShader(),"depth_matrix"), 1, GL_FALSE, &shadow_depth_mvp[0][0]);
glUniformMatrix4fv(glGetUniformLocation(normalShader->getShader(),"depth_matrix_bias"), 1, GL_FALSE, &shadow_depth_bias[0][0]);
renderScene();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
/* Clear */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* Shader */
normalShader->useShader();
/* Shadow-map */
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Shadows::shadowmap());
glUniform1f(glGetUniformLocation(normalShader->getShader(),"shadowMap"),0);
/* Render Scene */
glViewport(0,0,640,480);
renderScene();
Fragment Shader - This is where I calculate the final color to be output and do the depth texture / shadow-map sampling. It could be the source of where I am going wrong:
//Shadows
uniform sampler2DShadow shadowMap;
in vec4 shadowCoord;
void main()
{
//Lighting Calculations...
//Shadow Sampling:
float visibility = 1.0;
if (texture(shadowMap, shadowCoord.xyz) < shadowCoord.z){
visibility = 0.1;
}
//Final Output
outColor = finalColor * visibility;
}
Edits
<1> AMD Hardware Issue - It was also suggested that this could be an issue of the GPU but I find this hard to believe given that it's a Radeon HD 6670. Would it be worth putting in a Nvidia card in to test this theory?
<2> Suggest Changes - I made some suggested changes from the comments and answers:
Firstly, I changed the light's perspective projection to an ortho one which gave me the accuracy I needed in the shadow-map so that now I can see the depth clearly (i.e -> it's not all white). In addition, it removes the need for the perspective division so I am using 3-dimensional coordinates for testing this. Below is a screenshot:
Secondly, I changed my texture sampling to this: visibility = texture(shadowMap,shadowCoord.xyz); which now always returns 0 and thus I cannot see the scene as it is considered ENTIRELY shadowed.
Thirdly and finally, I made a swap from GL_LEQUAL to GL_LESS as suggested an no changes occurred.
There is something fundamentally wrong with your shader:
uniform sampler2DShadow shadowMap; // NOTE: Shadow samplers perform comparison !!
...
if (texture(shadowMap, shadowCoord.xyz) < shadowCoord.z)
You have texture compare vs. reference enabled. That means that the 3rd texture coordinate is going to be compared by the texture (...) function and the returned value is going to be the result of the test function (GL_LEQUAL in this case).
In other words, texture (...) will return either 0.0 (fail) or 1.0 (pass) by comparing the looked up depth at shadowCoord.xy to the value of shadowCoord.z. You are doing this test twice.
Consider using this altered code instead:
float visibility = texture(shadowMap, shadowCoord.xyz);
That is not going to produce quite the results you want because your comparison function is GL_LEQUAL, but it is a start. Consider changing the comparison function to GL_LESS to get an exact functional match.