繁体   English   中英

如何在iOS中正确地线性化OpenGL ES中的深度?

[英]How to correctly linearize depth in OpenGL ES in iOS?

我正在尝试使用OpenGL为iOS应用程序渲染forrest场景。 为了使它更好一些,我想在场景中实现深度效果。 但是我需要OpenGL深度缓冲区的线性化深度值才能这样做。 目前我在片段着色器中使用了一个计算(我在这里找到)。

因此我的terrain片段着色器看起来像这样:

#version 300 es

precision mediump float;
layout(location = 0) out lowp vec4 out_color;

float linearizeDepth(float depth) {
    return 2.0 * nearz / (farz + nearz - depth * (farz - nearz));
}

void main(void) {
    float depth = gl_FragCoord.z;
    float linearized = (linearizeDepth(depth));
    out_color = vec4(linearized, linearized, linearized, 1.0);
}

但是,这会产生以下输出:

结果输出 正如你所看到的,你越远“越远”,得到的深度值就越“条纹”(特别是在船后面)。 如果地形图块靠近相机,输出有点可以..

我甚至尝试了另一种计算:

float linearizeDepth(float depth) {
    return 2.0 * nearz * farz / (farz + nearz - (2.0 * depth - 1.0) * (farz - nearz));
}

这导致了一个太高的价值,所以我通过划分来缩小它:

float linearized = (linearizeDepth(depth) - 2.0) / 40.0;

第二个结果输出

然而,它给出了类似的结果。

那么如何在近平面和远平面之间实现平滑,线性的过渡,没有任何条纹? 有没有人有类似的问题?

问题是你存储了被截断的非线性值,因此当你稍后查看深度值时,你会得到波动的结果,因为你失去了准确性,你离znear平面越远。 无论你评价什么,你都不会获得更好的结果,除非:

  1. 精度损失较低

    您可以更改znear,zfar值,使它们更接近。 尽可能多地放大znear,以便更准确的区域覆盖更多的场景。

    另一个选择是每个深度缓冲区使用更多位(16位太低)不确定是否可以在OpenGL ES中执行此操作,但在标准OpenGL中,您可以在大多数卡上使用24,32位。

  2. 使用线性深度缓冲

    因此将线性值存储到深度缓冲区中。 有两种方法。 一个是计算深度,因此在所有基础操作之后,您将获得线性值。

    另一种选择是使用单独的纹理/ FBO并将线性深度直接存储到它。 问题是你不能在同一个渲染过程中使用它的内容。

[Edit1]线性深度缓冲区

要线性化深度缓冲区本身(而不仅仅是从中获取的值),请尝试:

顶点:

varying float depth;
void main()
    {
    vec4 p=ftransform();
    depth=p.z;
    gl_Position=p;
    gl_FrontColor = gl_Color;
    }

分段:

uniform float znear,zfar;
varying float depth; // original z in camera space instead of gl_FragCoord.z because is already truncated
void main(void)
    {
    float z=(depth-znear)/(zfar-znear);
    gl_FragDepth=z;
    gl_FragColor=gl_Color;
    }

在CPU端线性化的非线性深度缓冲区(就像你一样): 中央处理器

线性深度缓冲GPU侧(如您所愿): GPU

场景参数是:

// 24 bits per Depth value
const double zang =   60.0;
const double znear=    0.01;
const double zfar =20000.0;

简单的旋转板覆盖整个深度视野。 展位图像由glReadPixels(0,0,scr.xs,scr.ys,GL_DEPTH_COMPONENT,GL_FLOAT,zed); 并在CPU端转换为2D RGB纹理。 然后渲染为单个QUAD覆盖单位矩阵的整个屏幕......

现在要从线性深度缓冲区获取原始深度值,您只需执行以下操作:

z = znear + (zfar-znear)*depth_value;

我使用古老的东西只是为了保持这个简单,所以把它移到你的个人资料......

请注意,我不在OpenGL ESIOS中进行编码,所以我希望我没有错过与此相关的内容(我习惯于Win和PC)。

为了显示差异,我将另一个旋转的板添加到同一场景(因此它们相交)并使用彩色输出(不再获得深度):

相交

正如您所见,线性深度缓冲区要好得多(对于覆盖大部分深度FOV的场景)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM