Greetings!
I have a simple 3D scene with: 2 crates, a plane/floor and an MD2 animated model.
What I want to do is:
- render the crates and plane normally without any effects (a simple pass-through shader that just samples from a color texture, no lighting etc)
- render the model to an offline render texture that gets rendered to a fullscreen quad, apply postprocessing effects on that texture and then render the quad to the screen
The problem that I think I’m having is:
- After rendering the crates and plane normally, and then the postprocessed model on top, the model is overwriting the pixels of the creates and plane
Please see this video demonstrating the problem:
I’m really not sure what’s the cause of this. Is it really because the model is overwriting the other pixels or something else? Because if I move the camera close to the creates/plane I do see them. If the model was really overwriting, why am I seeing the crates/plane, I shouldn’t see them at all even if I move close.
The first thing I thought was a clipping issue, I tried decreasing my camera’s near clipping plane and increasing the far one. Didn’t help.
I then tried clearing the background to yellow in my postprocessor and discarding any pure yellow pixels in the postprocessing shader:
It ‘sort of’ works, but it’s ugly. You can still see yellow pixels on the model’s edges. And there’s also some depth issues with the crates/model as you see in the video.
Here’s my postprocessor code:
void PP_SetupShader(post_processor *P, u32 Shader)
{
P->Shader = Shader;
ShaderUse(Shader);
ShaderSetInt(Shader, "Texture", 0);
r32 Offset = 0.025f;
r32 Offsets[9 * 2] =
{
-Offset, -Offset,
0, -Offset,
+Offset, -Offset,
-Offset, 0,
0, 0,
+Offset, 0,
-Offset, +Offset,
0, +Offset,
+Offset, +Offset,
};
ShaderSetV2s(Shader, "Offsets", 9 * 2, (v2*)Offsets);
GLfloat BlurKernel[] =
{
1.0f, 2.0f, 1.0f,
2.0f, 4.0f, 2.0f,
1.0f, 2.0f, 1.0f
};
ForCount(9) BlurKernel[i] /= 16;
ShaderSetFloats(Shader, "BlurKernel", 9, BlurKernel);
r32 EdgeKernel[] =
{
-1, -1, -1,
-1, +8, -1,
-1, -1, -1
};
ShaderSetFloats(Shader, "EdgeKernel", 9, EdgeKernel);
}
void PP_Initialize(post_processor *P, u32 Shader, u32 Width, u32 Height, r32 (*GetTime)(void), b32 GenDepthBuf)
{
P->GetTime = GetTime;
// Setup VBO and VAO
{
r32 Quad[] =
{
-1.0f, +1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
+1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 1.0f, 0.0f,
+1.0f, +1.0f, 1.0f, 1.0f
};
GLArrayBuffer(sizeof(Quad), &Quad, GL_STATIC_DRAW);
P->VertexArray = GLBeginVertexArray();
{
GLAttribute(0, 2, GL_FLOAT, 4 * sizeof(r32), 0);
GLAttribute(1, 2, GL_FLOAT, 4 * sizeof(r32), 2 * sizeof(r32));
}
GLEndVertexArray();
}
// Setup framebuffer, render texture attachment and depth/stencil render buffer
{
glGenFramebuffers(1, &P->Framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, P->Framebuffer);
glGenTextures(1, &P->RenderTexture);
glBindTexture(GL_TEXTURE_2D, P->RenderTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, P->RenderTexture, 0);
if (GenDepthBuf)
{
u32 DepthRenderBuffer;
glGenRenderbuffers(1, &DepthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, DepthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, Width, Height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, DepthRenderBuffer);
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
Log("Error creating framebuffer in post processor!
");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
PP_SetupShader(P, Shader);
}
u32 PP_BeginUsing(post_processor *P)
{
glBindFramebuffer(GL_FRAMEBUFFER, P->Framebuffer);
glClearColor(1, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
return 1;
}
u32 PP_EndUsing(post_processor *P)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ShaderUse(P->Shader);
ShaderSetInt(P->Shader, "EffectsMask", P->EffectsMask);
ShaderSetFloat(P->Shader, "Time", P->GetTime());
GLBeginRenderGroup(P->VertexArray, P->RenderTexture);
glDrawArrays(GL_TRIANGLES, 0, 6);
GLEndRenderGroup();
return 0;
}
Here’s the rendering code:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
m4 ModelToWorld;
m4 WorldToView;
m4 ViewToProjection;
CameraUpdate(&Data->Camera, &Platform->Input, DeltaTime, &WorldToView, &ViewToProjection);
u32 Shader = Data->Shaders.Main;
ShaderUse(Shader);
ShaderSetM4(Shader, "view", &WorldToView);
ShaderSetM4(Shader, "projection", &ViewToProjection);
GLBeginRenderGroup(Data->Models.Floor.VAO, Data->Models.Floor.Textures.Color);
{
ModelToWorld = m4_Identity;
ShaderSetM4(Shader, "model", &ModelToWorld);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
GLEndRenderGroup();
GLBeginRenderGroup(Data->Models.Crate.VAO, Data->Models.Crate.Textures.Color);
{
ModelToWorld = m4_Translate(&m4_Identity, V3(-3.0f, 0.0f, -1.0f));
ShaderSetM4(Shader, "model", &ModelToWorld);
glDrawArrays(GL_TRIANGLES, 0, 36);
ModelToWorld = m4_Translate(&m4_Identity, V3(3.0f, 0.0f, 0.0f));
ShaderSetM4(Shader, "model", &ModelToWorld);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
GLEndRenderGroup();
PP_BeginUsing(&Data->PostProcessor)
{
MD2UpdateAndRender(&Data->DrFreak, DeltaTime, Shader, V3(0, 1.95f, 0), 0.1f);
}
PP_EndUsing(&Data->PostProcessor);
Note that I’m really not doing anything special inside MD2UpdateAndRender besides just a call to glDrawArrays. I can verify that the problem doesn’t have to do with the MD2 model. I tried replacing the model render call with one of the crates draw calls and it’s still the same issue. But if anybody’s still curious about how I’m rendering the model let me know and I’ll add the code, just didn’t want to add a lot of code.
Here are some of the helpers I’ve been using:
// note: internal is just 'static'
internal inline void
GLBeginRenderGroup(u32 VAO, r32 Texture)
{
glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_2D, Texture);
}
internal inline void
GLEndRenderGroup()
{
glBindVertexArray(0);
}
internal inline void
GLSetArrayBuffer(u32 ArrayBuffer, u32 Size, void *Data)
{
glBindBuffer(GL_ARRAY_BUFFER, ArrayBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, Size, Data);
}
internal u32
GLArrayBuffer(u32 SizeInBytes, void *Data, u32 Mode)
{
u32 ArrayBuffer;
glGenBuffers(1, &ArrayBuffer);
glBindBuffer(GL_ARRAY_BUFFER, ArrayBuffer);
glBufferData(GL_ARRAY_BUFFER, SizeInBytes, Data, Mode);
return (ArrayBuffer);
}
internal inline void
GLAttribute(u32 Index, u32 Count, u32 Type = GL_FLOAT, u32 Stride = 0, u32 Offset = 0)
{
glEnableVertexAttribArray(Index);
glVertexAttribPointer(Index, Count, Type, GL_FALSE, Stride, (char *)0 + Offset);
}
internal inline u32
GLBeginVertexArray()
{
u32 VertexArray;
glGenVertexArrays(1, &VertexArray);
glBindVertexArray(VertexArray);
return (VertexArray);
}
internal inline void
GLEndVertexArray()
{
glBindVertexArray(0);
}
How can achieve my rendering setup successfully? I’m pretty sure I’m missing something stupid. Note I know that I could use a shader on each object I want to pixelate etc but it’s more efficient to render them all to a texture and apply the effects only once on that texture.
Any help/advice/pointers is appreciated!
Thanks!
-Keithster