【问题标题】:OpenGL Framebuffer - rendering to textureOpenGL Framebuffer - 渲染到纹理
【发布时间】:2014-07-22 18:51:05
【问题描述】:

我制作了一个应用程序,在它上面渲染天空盒和粒子。我想添加一些效果,我需要使用帧缓冲区来渲染天空盒、粒子颜色、深度和位置以分离纹理。然后我想使用简单的着色器来使用这些纹理中的值并以适当的方式混合它们。我为纹理、帧缓冲区和屏幕四边形(要渲染的简单矩形)编写了辅助类,但不幸的是 - 当我尝试使用它时,什么都没有渲染。

当绑定帧缓冲被注释掉时,我的场景是这样的:

修改着色器显示深度和位置值计算正确。因此问题在于纹理和帧缓冲区的使用方式。一些代码的时间:

Framebuffer 辅助类(仅重要方法):

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);
}

纹理辅助类:

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 类:

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();
}

以及初始化和渲染场景的方法:

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();
}

加上 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;
}

和片段:

#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);
}

着色器辅助类、vao 和 vbo 辅助类肯定没问题。日志中没有错误发生。

更新: 粒子顶点着色器:

#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);

}

片段着色器:

#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;
}

和 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();
}

更新2:

问题是当我尝试在 screenQuad 着色器中从它们中采样数据时,由粒子着色器填充的纹理是空的。每个深度、位置和颜色纹理采样器都返回零。我使用与天空盒相同的类和相同的方法,但天空盒纹理效果很好。

更新3: 随机代码更改告诉我,如果我将深度纹理附加到帧缓冲区的注释行,粒子颜色最终会传递给纹理,我可以在屏幕四边形上看到它(但没有任何深度测试。红色粒子(最后绘制)总是打开前)。

我猜想将粒子着色器与深度纹理连接起来有问题。但我仍然找不到确切的错误。我希望我的建议会有所帮助。

【问题讨论】:

  • 一方面,GL_DEPTH_TEXTURE_MODE 在 GLSL 4.30 着色器中毫无意义。从 GLSL 1.30 开始,采样深度纹理始终返回:vec4 (r, r, r, 1.0),无论您为 GL_DEPTH_TEXTURE_MODE 设置了什么(已从核心 GL 3.2+ 中删除,并且可能会在运行时生成 GL_INVALID_ENUM 错误)。同样,纹理环绕模式:GL_CLAMP 在核心 3.2+ 中也被删除 - 您应该使用 GL_CLAMP_TO_EDGEvarying 不应该在 GLSL 4.30 着色器中,texture2Dgl_FragColor 也不应该。如果您要使用这些,请使用#version 430 compatibility
  • 谢谢,我已经解决了这些问题,但这仍然不是重点。什么都没有渲染。我会在一分钟内更新代码。
  • 感谢您的建议,我又修复了一个错误并取得了进展(我忘了将 loadShaders() 添加到 init() )。现在它渲染了一个黑色矩形,但上面仍然没有纹理。
  • 我还注意到,如果我写 out_Color = vec4(1.0, 0.5, 0.5, 1.0) 而不是从纹理中读取,矩形会改变颜色。所以这是纹理采样器中的数据问题。我还可以从天空盒纹理中采样正确的数据,所以这是粒子纹理的问题。我会附上粒子着色器。

标签: c++ opengl textures framebuffer


【解决方案1】:

我没有研究完整的代码,但是马上跳出来一个问题:

glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, colorTexture);
shaderProgram.setUniform("u_colortex", colorTexture);

纹理采样器的制服值不是纹理 ID(也称为名称)。它是纹理绑定到的纹理单元。所以在这种情况下,由于您为此纹理使用纹理单元 1,它应该是:

shaderProgram.setUniform("u_colortex", 1);

【讨论】:

  • 谢谢。无论如何,矩形仍然没有渲染。
  • 能否将问题简化为更小的示例?老实说,详细研究有点多。我注意到的另一件小事是 internalFormat 和其中一个纹理 (GL_RGB32F, GL_RGBA) 的格式可能不匹配,但这可能不是主要问题。
  • 好吧,我不确定错误在哪里。我可以说你不应该专注于着色器的数学部分。这肯定很好。屏幕渲染显示粒子的位置、颜色和深度计算良好(我尝试将它们放入out_Color 并且渲染良好)。但是当我尝试将其渲染为纹理并将其传递给 screenQuad 着色器时,一切都是黑色的。
  • 天空盒纹理很好,如果我写 out_Color = vec4(texture(u_backgroundtex,fs_texcoord).xyz, 1.0f); 而不是 out_Color = vec4(texture(u_colortex,fs_texcoord).xyz, 1.0f); 我可以看到天空盒。我尝试使用与天空盒纹理相同的格式和内部格式,但纹理还是空的。
【解决方案2】:

问题是当我调用glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);glDepthMask() 被禁用。

需要启用它才能使glClear(GL_DEPTH_BUFFER_BIT)生效。

另外,我还需要以适当的方式添加清理帧缓冲区。

【讨论】:

  • 你不会相信我在这个问题上浪费了多少时间!启用深度写入掩码,一切都按我的预期工作。干杯! :D
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-03
  • 2013-10-05
  • 1970-01-01
  • 2013-12-27
相关资源
最近更新 更多