openGL 绘制深度到纹理

前几天想写一个描边的效果,当然在unity中非常容易实现,但是换到基于openGL底层接口的时候却犯了难,本人已经不写openGL半年了,有些都忘了差不多了,再重新拾忆起来,调试了半天终于搞定。记录一下,以作备忘。

凡事都有个大思路:
描边其实很简单,判断相邻像素点的Z值和该像素所属面片的法向量只差。若大于一个阈值则判断为边界。
正所谓理论到实践总有一条漫长坎坷的路要走。因为fragment shader只能获取到上层vertex shader输出的值,而vertex shader又只能获得当前处理面片的信息,所以单单从渲染的路径中是获取不到相邻面片的法向量。故必须以另外的方式事先储存每个像素点所对应的面片法向量值,然后在渲染的路径中去提取这些信息才行。这时候只有纹理贴图能帮忙了。

思路大概是这样的:
第一步从视角拍摄一张纹理贴图,从vertex shader中获取顶点法向量,也是面片法向量,传递给fragment shader,然后fragment shader把法向量作为rgb值,像素深度值作为alpha值储存在一个纹理贴图中。
第二步以正常的渲染路径出发,在fragment shader中获取先前具有像素法向量和深度信息的纹理单元,因为我们有了当前处理像素的坐标,只要移动这个像素的坐标,并获取贴图中的相应坐标即可获得该像素的法向量和深度值,突破了渲染路径中不能获取其他面片信息的障碍。

上述还只是理论,但好歹给了我们一个努力的方向。
下面开始具体实践了。
首先我们需要一个fbo来储存texture的操作信息。

初始化init

    //创建一个framebuffer object
    glGenFramebuffers(1, &depth_fbo);
    //绑定framebuffer object到GL_FRAMEBUFFER对象
    glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);
glGenTextures(1, &depth_debug_tex);
    glBindTexture(GL_TEXTURE_2D, depth_debug_tex);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, DEPTH_TEXTURE_SIZE, DEPTH_TEXTURE_SIZE); //注意这里的GL_RGBA32F参数值必须和所要储存的数据结构相对应,否则数据会被抛弃或者加零。比如GL_RGA32F的话我们从fragment shader中output一个vec4的color值,则rgb会被保留而alpha值则被抛弃
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //depth_debug_tex以第一个GL_COLOR_ATTACHMENT0绑定到GL_FRAMEBUFFER(所对应fbo)中去
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, depth_debug_tex, 0);
    //此时fbo已经对应texture 脱离绑定 
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

渲染到纹理贴图

    static const GLfloat zero[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    static const GLfloat ones[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    //先绑定fbo,由于初始化这时fbo已经知道要给depth_debug_tex赋值了
    glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo);
    glViewport(0, 0, DEPTH_TEXTURE_SIZE, DEPTH_TEXTURE_SIZE);
    //glEnable(GL_POLYGON_OFFSET_FILL);
    //glPolygonOffset(4.0f, 4.0f);
    //depth_texture_program中vertex shader是很简单的传递面片法向量
    //fragment shader很简单的把法向量作为rgb,Z深度作为alpha值输出
    glUseProgram(depth_texture_program);
    static const GLenum buffs[] = { GL_COLOR_ATTACHMENT0 };
    //绑定渲染输出到GL_COLOR_ATTACHMENT0 而不是窗口
    glDrawBuffers(1, buffs);
    glClearBufferfv(GL_COLOR, 0, zero);
    glClearBufferfv(GL_DEPTH, 0, ones);

然后渲染物体。这时候物体的渲染都会从该shader pass中经过,所有的信息被绘制到depth_debug_tex纹理贴图中

最后别忘了取消绑定fbo
        glDisable(GL_POLYGON_OFFSET_FILL);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

至此信息全部被输出到纹理贴图中。
当然如果我们要看纹理贴图就要写一个shader吧纹理输出到屏幕

        glDisable(GL_DEPTH_TEST);
        glBindVertexArray(quad_vao);
        glUseProgram(depth_render_program);
        glBindTexture(GL_TEXTURE_2D, depth_debug_tex);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

其中depth_render_program就是把贴图输出的shader。
我们来看下该shader

//depth_render_program /vertex shader.glsl
#version 420 core 

void main(void)                                                
{                                                              
    const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.5, 1.0), 
                                   vec4( 1.0, -1.0, 0.5, 1.0), 
                                   vec4(-1.0,  1.0, 0.5, 1.0), 
                                   vec4( 1.0,  1.0, 0.5, 1.0));

    gl_Position = vertices[gl_VertexID];
}                           
//depth_render_program /fragment shader.glsl
#version 420

layout (binding = 0) uniform sampler2D tex_depth;

layout (location = 0) out vec4 color;

void main(void)
{
    float r = texelFetch(tex_depth, ivec2(gl_FragCoord.xy * 3.0) + ivec2(450, 850), 0).r;
    float g = texelFetch(tex_depth, ivec2(gl_FragCoord.xy * 3.0) + ivec2(450, 850), 0).g;
    float b = texelFetch(tex_depth, ivec2(gl_FragCoord.xy * 3.0) + ivec2(450, 850), 0).b;
    float a = texelFetch(tex_depth, ivec2(gl_FragCoord.xy * 3.0) + ivec2(450, 850), 0).a;
    r = (r - 0.95) * 15.0;
    g = (g - 0.95) * 15.0;
    b = (b - 0.95) * 15.0;
    color = vec4(a);
}

在上面我只输出了深度数据并放大了数值… 15倍
openGL 绘制深度到纹理_第1张图片

你可能感兴趣的:(OpenGL)