[英]OpenGL Framebuffer - rendering to texture
I made an application which renders skybox and particles over it. 我制作了一个在其上渲染天空盒和粒子的应用程序。 I want to add some effects and i need to use framebuffers to render skybox, particles color, depth and position to separate textures. 我想添加一些效果,我需要使用帧缓冲区来渲染天空盒,粒子颜色,深度和位置以分离纹理。 Then i want to use simple shader to use values from these textures and mix them in a proper way. 然后,我想使用简单的着色器来使用这些纹理中的值,并以适当的方式混合它们。 I wrote helper classes for textures, framebuffers and screen quad (simple rectangle to render) but unfortunately - nothing renders when i try to use it. 我为纹理,帧缓冲区和屏幕四边形(要渲染的简单矩形)编写了辅助类,但是不幸的是,当我尝试使用它时,没有任何渲染。
When binding framebuffers is commented out, my scene looks like this: 当绑定帧缓冲区被注释掉时,我的场景如下所示:
Modifying shader shows that depth and position values are calculated properly. 修改着色器表明正确计算了深度和位置值。 Therefore problem lays in texture and framebuffers way of using. 因此问题在于纹理和帧缓冲的使用方式。 Time for some code: 时间到一些代码:
Framebuffer helper class (only important methods): 帧缓冲帮助程序类(仅重要方法):
void Framebuffer::init(){
// unbind all textures from openGL
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &framebuffer);
}
void Framebuffer::bind(){
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
void Framebuffer::unbind(){
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Framebuffer::attachTexture(GLuint texture, GLenum attachmentType){
glBindTexture(GL_TEXTURE_2D, texture);
glFramebufferTexture(GL_FRAMEBUFFER, attachmentType, texture, 0);
}
void Framebuffer::drawBuffers(GLsizei n, const GLenum *buffers){
glDrawBuffers(n, buffers);
}
Texture helper class: 纹理助手类:
void Texture::init(GLuint windowWidth, GLuint windowHeight, GLint internalFormat, GLenum format, GLenum type){
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D( GL_TEXTURE_2D, 0, internalFormat , windowWidth, windowHeight, 0, format, type, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture::bind(){
glBindTexture(GL_TEXTURE_2D, texture);
}
void Texture::unbind(){
glBindTexture(GL_TEXTURE_2D, 0);
}
GLuint Texture::getId(){
return texture;
}
ScreenQuad class: ScreenQuad类:
void ScreenQuad::init(void){
vao.createVAO();
vao.bindVAO();
vbo.createVBO();
vbo.addData(vertices, 8*sizeof(GLfloat));
vbo.bindVBO(GL_ARRAY_BUFFER);
vbo.uploadDataToGPU(GL_STATIC_DRAW);
glVertexAttribPointer((GLuint)3, 2, GL_FLOAT, GL_FALSE, 0, NULL);
loadShaders("shaders/basicPostShader.vp", "shaders/basicPostShader.fp");
}
void ScreenQuad::loadShaders(string vsPath, string fsPath){
shaderProgram.createProgram();
shaderProgram.loadVertexShader(vsPath);
shaderProgram.loadFragmentShader(fsPath);
glBindAttribLocation(shaderProgram.getProgramID(), 3, "v_coord");
shaderProgram.linkProgram();
}
void ScreenQuad::draw(GLuint depthTexture, GLuint colorTexture, GLuint positionTexture, GLuint backgroundTexture){
shaderProgram.bindProgram();
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthTexture);
shaderProgram.setUniform("u_depthtex", 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, colorTexture);
shaderProgram.setUniform("u_colortex", 1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, positionTexture);
shaderProgram.setUniform("u_positiontex", 2);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, backgroundTexture);
shaderProgram.setUniform("u_backgroundtex", 3);
glEnableVertexAttribArray(3);
vbo.bindVBO();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
vbo.unbindVBO();
glDisableVertexAttribArray(3);
shaderProgram.unbindProgram();
}
and methods for initialization and rendering scene: 以及初始化和渲染场景的方法:
void OpenGLContext::setupScene(void) {
glClearColor(0.4f, 0.6f, 0.9f, 1.0f);
//FRAMEBUFFERS:
skyboxFramebuffer.init();
skyboxTexture.init(windowWidth, windowHeight, GL_RGBA32F, GL_RGBA, GL_FLOAT);
skyboxFramebuffer.bind();
skyboxFramebuffer.attachTexture(skyboxTexture.getId(), GL_COLOR_ATTACHMENT0);
const GLenum skyboxDrawBuffers[1] = { GL_COLOR_ATTACHMENT0};
skyboxFramebuffer.drawBuffers(1, skyboxDrawBuffers);
skyboxFramebuffer.validate();
skyboxFramebuffer.unbind();
mainFramebuffer.init();
mainColorTexture.init(windowWidth, windowHeight, GL_RGBA32F, GL_RGBA, GL_FLOAT);
mainPositionTexture.init(windowWidth, windowHeight, GL_RGBA32F, GL_RGBA, GL_FLOAT);
mainDepthTexture.init(windowWidth, windowHeight, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_FLOAT);
mainFramebuffer.bind();
mainFramebuffer.attachTexture(mainColorTexture.getId(), GL_COLOR_ATTACHMENT0);
mainFramebuffer.attachTexture(mainPositionTexture.getId(), GL_COLOR_ATTACHMENT1);
mainFramebuffer.attachTexture(mainDepthTexture.getId(), GL_DEPTH_ATTACHMENT);
const GLenum mainDrawBuffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
mainFramebuffer.drawBuffers(2, mainDrawBuffers);
mainFramebuffer.validate();
mainFramebuffer.unbind();
//SKYBOX:
skybox->init("resources/skybox/default/",
"pos_x.tga",
"neg_x.tga",
"pos_y.tga",
"neg_y.tga",
"pos_z.tga",
"neg_z.tga");
//PARTICLES:
particles->init(scene);
//SCREENQUAD:
screenQuad.init();
}
void OpenGLContext::renderScene() {
glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
glViewport(0, 0, windowWidth, windowHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
fpsCounter->calcFPS(1.0, windowName);
if(mode==INPUT_ENABLED_MODE){
updateInputs();
}
projectionMatrix = controls->getProjectionMatrix();
viewMatrix = controls->getViewMatrix();
modelMatrix = glm::mat4(1.0f);
glm::mat4 mvpMatrix = projectionMatrix*viewMatrix*modelMatrix;
//SKYBOX:
skyboxFramebuffer.bind();
skybox->render(mvpMatrix);
skyboxFramebuffer.unbind();
//PARTICLES:
if(scene->tryLockScene()){
if(scene->isSceneUpdated()){
particles->updateParticlesPosition(scene);
scene->setSceneUpdated(false);
}
scene->unlockScene();
}
mainFramebuffer.bind();
particles->draw(modelMatrix, viewMatrix, projectionMatrix);
mainFramebuffer.unbind();
//SCREENQUAD:
screenQuad.draw(mainDepthTexture.getId(), mainColorTexture.getId(), mainPositionTexture.getId(), skyboxTexture.getId());
glfwSwapBuffers(window);
glfwPollEvents();
}
plus screenQuad shaders: vertex: 加上screenQuad着色器:顶点:
#version 430
layout (location = 3) in vec2 v_coord;
layout (binding = 0) uniform sampler2D u_depthtex;
layout (binding = 1) uniform sampler2D u_colortex;
layout (binding = 2) uniform sampler2D u_positiontex;
layout (binding = 3) uniform sampler2D u_backgroundtex;
out vec2 fs_texcoord;
void main(void) {
gl_Position = vec4(v_coord, 0.0, 1.0);
fs_texcoord = (v_coord + 1.0) / 2.0;
}
and fragment: 和片段:
#version 430
layout (binding = 0) uniform sampler2D u_depthtex;
layout (binding = 1) uniform sampler2D u_colortex;
layout (binding = 2) uniform sampler2D u_positiontex;
layout (binding = 3) uniform sampler2D u_backgroundtex;
layout (location = 0) out vec4 out_Color;
in vec2 fs_texcoord;
void main(void) {
float exp_depth = texture(u_depthtex,fs_texcoord).r;
if(exp_depth>0.99f){
out_Color = vec4(texture(u_backgroundtex,fs_texcoord).xyz,1.0f);
return;
}
out_Color = vec4(texture(u_colortex,fs_texcoord).xyz, 1.0f);
}
Shader helper classes, vao and vbo helper classes are fine for sure. 着色器帮助程序类,vao和vbo帮助程序类肯定可以。 No errors occurs in logs. 日志中没有错误发生。
UPDATE: particles vertex shader: 更新:粒子顶点着色器:
#version 430
uniform mat4x4 modelViewMatrix;
uniform mat4x4 projectionMatrix;
uniform float pointRadius; // point size in world space
uniform float pointScale; // scale to calculate size in pixels
layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec4 in_Color;
out vec3 fs_PosEye;
out vec4 fs_Position;
out vec4 fs_Color;
void main(void) {
vec3 posEye = (modelViewMatrix * vec4(in_Position.xyz, 1.0f)).xyz;
float dist = length(posEye);
gl_PointSize = pointRadius * (pointScale/dist);
fs_PosEye = posEye;
fs_Position = modelViewMatrix * vec4(in_Position.xyz, 1.0f);
fs_Color = in_Color;
gl_Position = projectionMatrix * modelViewMatrix * vec4(in_Position.xyz, 1.0f);
}
fragment shader: 片段着色器:
#version 430
uniform mat4x4 modelViewMatrix;
uniform mat4x4 projectionMatrix;
uniform float pointRadius; // point size in world space
uniform float pointScale; // scale to calculate size in pixels
in vec4 fs_Position;
in vec3 fs_PosEye;
in vec4 fs_Color;
layout (location = 0) out vec4 out_Color;
layout (location = 1) out vec4 out_Position;
void main(void)
{
// calculate normal from texture coordinates
vec3 normal;
normal.xy = gl_PointCoord.xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);
float r = dot(normal.xy, normal.xy);
if(r>1.0)
discard;
normal.z = sqrt(1.0-r);
//calculate depth
vec4 pixelPos = vec4(fs_PosEye + normalize(normal)*pointRadius,1.0f);
vec4 clipSpacePos = projectionMatrix * pixelPos;
gl_FragDepth = (clipSpacePos.z / clipSpacePos.w);
out_Color = fs_Color;
out_Position = pixelPos;
}
and Particles.draw() method: 和Particles.draw()方法:
void CParticles::draw(glm::mat4 modelMatrix, glm::mat4 viewMatrix, glm::mat4 projectionMatrix){
shaderProgram.bindProgram();
glm::mat4 modelViewMatrix = viewMatrix*modelMatrix;
shaderProgram.setUniform("projectionMatrix", &projectionMatrix);
shaderProgram.setUniform("modelViewMatrix", &modelViewMatrix);
shaderProgram.setUniform("pointRadius", &pointRadius);
shaderProgram.setUniform("pointScale", &pointScale);
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
glEnable(GL_POINT_SPRITE);
glEnable(GL_PROGRAM_POINT_SIZE);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_POINTS, 0, n);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisable(GL_PROGRAM_POINT_SIZE);
glDisable(GL_POINT_SPRITE);
shaderProgram.unbindProgram();
}
UPDATE2: UPDATE2:
The problem is that textures filled by a particle shader are empty when I try to sample data from them in a screenQuad shader. 问题是当我尝试在screenQuad着色器中采样来自它们的数据时,由粒子着色器填充的纹理为空。 Each depth, position and color texture samplers return zeros. 每个深度,位置和颜色纹理采样器均返回零。 I use same classes and same methods as with a skybox, but skybox texture works fine. 我使用与Skybox相同的类和方法,但Skybox纹理效果很好。
UPDATE3: Random code changes showed me that if I comment line with attaching depth texture to framebuffer, particle color is finally passed to a texture and i can see it on a screen quad (but without any depth test. Red particles (drawed last) are always on the front). UPDATE3:随机代码更改告诉我,如果我在将深度纹理附加到帧缓冲区的注释行中,粒子颜色最终传递给纹理,并且可以在屏幕四边形上看到它(但没有进行任何深度测试。红色粒子(最后绘制)是总是在最前面)。
I guess there is a problem with connecting particle shader with depth texture. 我想将粒子着色器与深度纹理连接起来会有问题。 But still I can't find an exact bug. 但是我仍然找不到确切的错误。 I hope my sugestion will be helpful. 希望我的建议对您有所帮助。
I haven't studied the entire code, but one problem jumps out immediately: 我还没有研究完整的代码,但是一个问题立即跳出来:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, colorTexture);
shaderProgram.setUniform("u_colortex", colorTexture);
The value of a uniform for a texture sampler is not the texture id (aka name). 纹理采样器的统一值不是纹理ID(又名)。 It's the texture unit the texture is bound to. 这是纹理绑定到的纹理单位。 So in this case, since you're using texture unit 1 for this texture, it should be: 因此,在这种情况下,由于您要为此纹理使用纹理单元1,因此它应为:
shaderProgram.setUniform("u_colortex", 1);
The problem was that glDepthMask()
was disabled when i invoked glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
问题是当我调用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
时, glDepthMask()
被禁用了glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
. 。
It needs to be enabled in order to glClear(GL_DEPTH_BUFFER_BIT)
took any effect. 为了使glClear(GL_DEPTH_BUFFER_BIT)
生效,需要启用它。
Plus I needed to add cleaning framebuffers in a proper way as well. 另外,我还需要以适当的方式添加清洗帧缓冲区。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.