根据软件渲染器生成的 2D 视觉填充深度缓冲区

Populating the depth buffer based on a 2D visual produced from a software renderer

我的情况是这样的:作为 2D 纹理的 2D 图像是从实际说明“3D”视觉效果的软件渲染器生成的。 OpenGL 基本上只用于显示此 2D 纹理。因此,尽管渲染了看似 3D 视觉效果的内容,但无论我使用着色器渲染深度缓冲区可能做出的任何努力,都无法完成,因为那里实际上什么也没有。我想访问深度缓冲区以启用此类着色器。

所以,我想以某种方式根据我的图像填充深度缓冲区。我认为这是可行的,因为所讨论的软件渲染器可以生成 "depth map" 图像及其 "regular" 图像作为渲染模式——深度图图像看起来就像深度缓冲区的渲染(灰度,靠近相机的物体是黑色的)。所以我想我的问题是:我是否可以将表示深度的 "pre-rendered" 图像转换为深度缓冲区?我该怎么做呢?

编辑:如果这有帮助,我正在专门使用 OpenGL 3.3。


编辑 2:继续研究我在这里可以做的事情,我发现 this discussion 这表明我 "either use framebuffer objects or a fragment shader which writes to gl_FragDepth." 但是我认为讨论很快变得有点难以消化,我想我了解写入 gl_FragDepth 的片段着色器的概念,但是这实际上是如何工作的?

我在想我会做类似以下伪代码的事情吗?

program = createProgram(); //write to gl_FragDepth in the frag shader
glUseProgram(program);

glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
glEnable(GL_DEPTH_TEST);
glGenTextures(1, &depth_texture);
glBindTexture(GL_TEXTURE_2D, depth_texture);

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, depth->width, depth->height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, depth->pixels)
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, 0);

我需要启用深度测试吗?


编辑 3:

如果我理解正确,在阅读更多内容后,我认为我需要执行以下操作,但我无法相当 让它工作。这里的东西看起来明显不正确吗?我发现发生的事情是在片段着色器中,sampler2Ds tex0tex1 以某种方式包含相同的值,因此,我能够将颜色值写入 gl_FragDepth 或颜色的深度值会产生有趣但无用的结果。

片段着色器总结:

out vec4 color;
uniform sampler2D tex0; // color values
uniform sampler2D tex1; // depth values

void main(void) {
    color = texture(tex0, uv);
    gl_FragDepth = texture(tex1, uv).z;
}

OpenGL 总结:

// declarations
static GLuint vao;
static GLuint texture = 1;
static GLuint depth_texture = 2;

// set up shaders
program = createProgram();
glUseProgram(program); //verified that this is working

// enable depth testing
glEnable(GL_DEPTH_TEST);

// prepare dummy VAO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

// prepare texture for color values
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

// prepare texture for depth values
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &depth_texture);
glBindTexture(GL_TEXTURE_2D, depth_texture);

// disable depth mask while working with color values
glDepthMask(GL_FALSE);

// select GL_TEXTURE0 and bind the color values
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// specify texture image for colorvalues
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, TEX_FORMAT, TEX_TYPE, fb->pixels);

// enable depth mask while working with depth values
glDepthMask(GL_TRUE);

// select GL_TEXTURE1 and bind the depth values
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, depth_texture);

// specify texture image for depth values
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, TEX_FORMAT, TEX_TYPE, fb->depth);

// draw
glViewport(win_x, win_y, win_width, win_height);
glDrawArrays(GL_TRIANGLES, 0, 3);

我认为您对纹理有点困惑。这不是教程网站,所以我不会过多描述。

OpenGL 的目标是将像素绘制到 window(或内部帧缓冲区)中。 window 是一个二维颜色容器。每种颜色都是 {0,1} 范围内的 4 值 (RGBA)(它会以某种方式转换为 {0,255} 范围)。

因为多个 基元(点、线或三角形)可能绘制在相同的 {x,y} 位置,所以第 3 个坐标(z 又名 "depth") 被使用,并存储在所谓的 "depth buffer".

片段着色器 输出具有 {x,y,z} 坐标的像素时,深度测试 完成根据深度缓冲区中的当前值对 z 值进行片段化。根据用于此测试的函数,此当前值会被 z 值替换或不替换。例如,这种方式允许的典型情况是像素靠近相机 "survive",其余像素为 "occluded"(阅读:忘记)。

一个纹理只不过是一个缓冲区,它的特点是它的值可以通过坐标而不是索引来获取。这个过程叫做"sampling".

纹理可以是 1/2/3 维缓冲区,每个维度在 {0, 1} 范围内。您可以指定当样本坐标与纹理中的 "cell" 不完全匹配时要执行的操作。这称为 "minification/magnification"。

作为缓冲区,纹理可以存储许多不同类型的值,从单个 'byte' 到组合的 'RGBA' 值。可以存储 深度分量 ,但不要将它与 帧深度缓冲区 混淆。
您必须说明纹理是如何填充的(例如读取 4 个浮点值,其他例如读取 1 个字节)以及它们是如何在内部存储和采样的。参见 doc

现在我们正在接近解决您的问题。您可以拥有一个存储颜色的 2D 纹理和另一个存储 z 值的 2D 纹理。在片段着色器中,您 采样 并为 {x,y} 颜色和 gl_FragDepth.

写入输出

任何数据都可能来自顶点到片段插值器,或来自纹理,或来自其他中间着色器或其他缓冲区。上面有两个纹理的段落只是一个例子。
重点是您知道数据的存储内容和存储位置,以及如何检索和使用它来输出 {x,y,z} 值。


编辑,在你的第 3 版之后

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,..., fb->depth)

GL_RGBA 类型应匹配(或至少易于转换)fb->depth 数据。例如,如果您有 'float' 值,则可以将它们内部存储在纹理的 'red' 通道中,宽度为 32 位:

glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F,..., fb->depth)

fb->depth 中的 float 值意味着 TEX_FORMAT 应该是唯一的频道,例如 GL_REDTEX_TYPEGL_FLOAT.
如果您有整数值(例如在 {0, 255} 范围内),请使用不同的参数或只是 标准化 它们:除以 255 以获得 {0,1} 范围值。

在片段着色器中读取您在 glTexImage2D 中设置的相同通道。所以,对于 fb->depth:

gl_FragDepth = texture(tex1, uv).r;

不要忘记定义 uv 纹理坐标的获取方式。可能它们是顶点着色器的"out",FS中的"in"。

我想指出 Ripi2 的回答确实引导我解决了这个问题,但是我想我应该写一个答案来解决我的问题、我最终需要做什么以及我的问题有什么问题,现在已经找到了解决这个问题的方法。

首先,我的问题有什么问题?我对 OpenGL 纹理有一些误解,但最根本的是。我将纹理误解为图像的 "container",我现在的理解是它是一个缓冲区并且可以包含除简单 "an image" 之外的信息(例如,纹理可以存储 z 深度数据)。

据我所知,我的问题仍然存在:我有两张预渲染图像,一张说明颜色数据,另一张说明深度缓冲区数据。为了解决这个问题,首先我必须了解如何管理两个纹理:一个包含颜色数据,一个包含深度数据,这样我就可以对它们进行采样。

我的 OpenGL 代码(来自我问题中的第三次编辑)基本上缺少以下内容:

// get the uniform variables location
depthValueTextureLocation = glGetUniformLocation(program, "DepthValueTexture");
colorValueTextureLocation = glGetUniformLocation(program, "ColorValueTexture");

// specify the shader program to use
glUseProgram(program);

// bind the uniform variables locations
glUniform1i(depthValueTextureLocation, 0);
glUniform1i(colorValueTextureLocation, 1);

然后我的片段着色器采样器最终看起来像这样匹配:

 uniform sampler2D ColorValueTexture;
 uniform sampler2D DepthValueTexture;

只有在这一点上,我 A) 现在不仅有了纹理,而且了解了如何在我的着色器和 B)[ 中对其进行采样=34=] 将我的数据放在正确的位置,这样我就可以了解我在绘图时到底发生了什么。我看到一个令人困惑的结果,其中一个纹理的数据似乎出现在另一个纹理中,我也能够通过将我的 "drawing phase" 分成两个较小的阶段来解决这个问题:

首先我使用颜色纹理绘制:

// select the color value binding
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, texture);

// draw
glDrawArrays(GL_TRIANGLES, 0, 3);

然后我使用深度纹理绘制:

// select the depth value binding
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, depth_texture);

// draw
glEnable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisable(GL_DEPTH_TEST);

请务必注意,我专门只在使用深度纹理时启用了深度测试!