深度贴图的提取深度值
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度贴图的提取深度值相关的知识,希望对你有一定的参考价值。
Depth map 深度图是一张2D图片,每个像素都记录了从视点(viewpoint)到遮挡物表面(遮挡物就是阴影生成物体)的距离,这些像素对应的顶点对于观察者而言是“可见的”。Depth map中像素点记录的深度值为lenth1;然后从视点出现,计算物体顶点v到视点的距离,记为lenth2;比较二者大小,来确定“v”是否被遮挡。该术语的同义词有depth buffer, Z-buffer, Z-buffering 和 Z-depth。这里的"Z"是相对于相机(即视点)视图中心轴线而言的,也就是相机的z轴线,而不是场景的绝对坐标中的z轴线。
下面是一个立方框的深度图示例:
Shadow texture 阴影贴图,就是日常所见的阴影保存为纹理图片;
Depth texture保存的是“从视点到物体顶点的距离,通常称为深度值”。
Shadow map 以depth map 为技术基础,通过比较“光源可见点到光源的深度”和“任何点到光源的深度”来判断点是否被物体遮挡;
shadow texture 技术,将生成的阴影图形作为投影纹理来处理,也就是将一张阴影图投影映射到一个物体上(阴影接收体)。这种方法的缺点在于:设计者必须确认哪个物体是遮挡物,哪个物体是阴影接受体,并且不能产生自阴影现象(按照常识,物体的阴影不会投射到自己身上)。
生成深度贴图的流程
以光源所在位置为相机位置,光线发射方向为观察方向进行相机参数设置;
将世界视点投影矩阵 worldViewProjMatrix 传入顶点着色程序中,并在其中计算每个点的投影坐标,投影坐标的Z 值即为深度值(将Z 值保存为深度值只是很多方法中的一种)。在片段shadow 程序中将深度值进行归一化,即转化到【0,1】区间。然后将深度值赋给颜色值
从 frame buffer 中读取颜色值,并渲染到一张纹理上,就得到了depthmap。
注意:在实际运用中,如果遇到动态光影,则depth map 通常是实时计算的,这就需要场景渲染两次,第一次渲染出depth map,然后基于depth map 做阴影渲染。
用途
模拟在一个场景中的密度均匀的半透明介质效果-如雾,烟或大量的水;
模拟场景表面的深度域(depth of field (DOF));
可用于高效的变形体碰撞检测。 参考技术A 通过设置一个onmouse时间,当触发点击事件后,可以获取到点击点所在图像的长宽位置,再根据该位置提取深度值
无法使用深度附件渲染到 FBO
【中文标题】无法使用深度附件渲染到 FBO【英文标题】:Not being able to render into FBO with Depth Attachment 【发布时间】:2014-02-04 19:56:28 【问题描述】:我正在尝试实施阴影贴图几天,我想我开始看到手头的真正问题:我有一个附加了深度纹理的 FBO,用于阴影贴图的光通道,但是里面没有渲染任何东西。甚至没有硬编码值。
我已经仔细检查了可视化深度纹理的程序部分,它正在使用常规纹理,而且我没有看到我的场景中出现阴影,所以我认为肯定有问题。
初始化:
//shadow FBO and texture
depthFBO = new FrameBufferObject().create().bind();
depthTexture = new Texture2D().create().bind()
.storage2D(12, GL_DEPTH_COMPONENT32F, 4096, 4096)
.minFilter(GL_LINEAR)
.magFilter(GL_LINEAR)
.compareMode(GL_COMPARE_REF_TO_TEXTURE)
.compareFunc(GL_LEQUAL);
depthFBO.texture2D(GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, depthTexture, 0)
.checkStatus().unbind();
depthTexture.unbind();
渲染:
depthFBO.bind();
glViewport(0, 0, 4096, 4096);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(4.0f, 4.0f);
glDrawBuffer(GL_NONE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepthf(1f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Drawable.drawAllLightPass(drawables, lightNormalProgram, lightTessellationProgram);
glDisable(GL_POLYGON_OFFSET_FILL);
depthFBO.unbind();
直接gl*
调用中的代码。
初始化:
int frameBufferObjectId = GL30.glGenFramebuffers();
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferObjectId);
int textureId = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
GL42.glTexStorage2D(GL11.GL_TEXTURE_2D, 12, GL_DEPTH_COMPONENT32F, 4096, 4096);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL11.GL_TEXTURE_2D, textureId, 0);
if (GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER) != GL30.GL_FRAMEBUFFER_COMPLETE)
throw new RuntimeException(this + " is not complete.");
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
渲染:
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferObjectId);
glViewport(0, 0, 4096, 4096);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(4.0f, 4.0f);
glDrawBuffer(GL_NONE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepthf(1f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//appropiate calls to glDraw* are being done, this is confirmed to work as it works for displaying data
glDisable(GL_POLYGON_OFFSET_FILL);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
现在最重要的着色器:
light-normal.vs.glsl
#version 430 core
layout(location = 4) uniform mat4 light_model_matrix;
layout(location = 5) uniform mat4 light_view_matrix;
layout(location = 6) uniform mat4 light_projection_matrix;
layout(location = 0) in vec4 position;
void main(void)
gl_Position = light_projection_matrix * light_view_matrix * light_model_matrix * position;
light-normal.fs.glsl
#version 430 core
layout(location = 0) out float depth;
void main(void)
depth = gl_FragCoord.z;
甚至将depth
硬编码为0.0f
、0.5f
或1.0f
都不起作用。
只是为了澄清我的期望是否正确:当我将相机靠近屏幕上正在渲染的对象时,深度缓冲区的颜色应该改变吗?那么深度会接近0.0f,而远离1.0f?
还有一个说明:我所看到的深度值是一个到处都是白色的矩形。
根据要求,着色器将深度纹理数据可视化:
depth-box.vs.glsl
#version 430 core
void main(void)
const vec4 vertices[6] = vec4[](
vec4(-1.0, -1.0, 1.0, 1.0),
vec4(-1.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
vec4(1.0, 1.0, 1.0, 1.0),
vec4(1.0, -1.0, 1.0, 1.0),
vec4(-1.0, -1.0, 1.0, 1.0)
);
gl_Position = vertices[gl_VertexID];
depth-box.fs.glsl
#version 430 core
layout(location = 7) uniform int screen_width;
layout(location = 8) uniform int screen_height;
layout(binding = 0) uniform sampler2D shadow_tex;
out vec4 color;
void main(void)
vec2 tex_coords = vec2(
(gl_FragCoord.x - 100) / (screen_width / 5),
(gl_FragCoord.y - (screen_height - (screen_height / 5) - 100)) / (screen_height / 5)
);
color = vec4(vec3(texture(shadow_tex, tex_coords).r), 1.0);
【问题讨论】:
【参考方案1】:这不起作用,您忘记禁用纹理比较。除了GL_NONE
之外,您不能使用sampler2D
对纹理进行采样。 结果将是不确定的。
sampler2DShadow
是读取启用比较的纹理所必需的。当你这样做时,前两个纹理坐标正常工作,但第三个是要比较的深度值,texture (...)
的返回是一个浮点值(1.0 表示通过,0.0 表示失败)。如果你有一个线性过滤器,它可以介于两者之间。
【讨论】:
更新了,可惜还是不行。将sampler2D
与GL_NONE
一起使用时。
还通过将render
中的glClearDepthf(1f);
更改为例如0.5f
来确认它确实做了一些事情(即变成灰色),所以没有任何东西被绘制到纹理中?
至于在你的着色器中输出depth
,那是完全没有意义的。我开始讨厌你得到这些着色器的书。您正在将窗口空间深度写入颜色缓冲区,但您有glDrawBuffers (GL_NONE)
。您的着色器甚至不应该有一个名为 depth
的输出,为了您自己的利益将其删除并使用一个空的片段着色器来创建阴影贴图。
如果你真的想在片段着色器中写一些东西来影响深度,你可以写gl_FragDepth
。但是,不要这样做除了调试之外,因为光栅器已经生成了这个值,如果你覆盖它,你将失去几个硬件优化。
那么,总结一下,写一个空的着色器更好,这样驱动程序/硬件可以优化,并提供一致性?是的,我也不再那么喜欢这本书了,但是网上的解释更难找到。以上是关于深度贴图的提取深度值的主要内容,如果未能解决你的问题,请参考以下文章