webgl(和 threejs)中的深度剥离不变性

depth peeling invariance in webgl (and threejs)

我在看我认为是什么 the first paper for depth peeling (the simplest algorithm?) 我想用 webgl 实现它,使用 three.js

我想我理解了这个概念并且能够制作多个果皮,一些逻辑看起来像这样:

render(scene, camera) {

    const oldAutoClear = this._renderer.autoClear
    this._renderer.autoClear = false

    setDepthPeelActive(true) //sets a global injected uniform in a singleton elsewhere, every material in the scene has onBeforeRender injected with additional logic and uniforms

    let ping
    let pong

    for (let i = 0; i < this._numPasses; i++) {
        const pingPong = i % 2 === 0

        ping = pingPong ? 1 : 0
        pong = pingPong ? 0 : 1

        const writeRGBA = this._screenRGBA[i]
        const writeDepth = this._screenDepth[ping]

        setDepthPeelPassNumber(i) //was going to try increasing the polygonOffsetUnits here globally, 

        if (i > 0) {
            //all but first pass write to depth
            const readDepth = this._screenDepth[pong]
            setDepthPeelFirstPass(false)
            setDepthPeelPrevDepthTexture(readDepth)
            this._depthMaterial.uniforms.uFirstPass.value = 0
            this._depthMaterial.uniforms.uPrevDepthTex.value = readDepth
        } else {
            //first pass just renders to depth
            setDepthPeelFirstPass(true)
            setDepthPeelPrevDepthTexture(null)
            this._depthMaterial.uniforms.uFirstPass.value = 1
            this._depthMaterial.uniforms.uPrevDepthTex.value = null
        }

        scene.overrideMaterial = this._depthMaterial 

        this._renderer.render(scene, camera, writeDepth, true)

        scene.overrideMaterial = null
        this._renderer.render(scene, camera, writeRGBA, true)
    }

    this._quad.material = this._blitMaterial
    // this._blitMaterial.uniforms.uTexture.value = this._screenDepth[ping]
    this._blitMaterial.uniforms.uTexture.value = this._screenRGBA[
        this._currentBlitTex
    ]

    console.log(this._currentBlitTex)

    this._renderer.render(this._scene, this._camera)

    this._renderer.autoClear = oldAutoClear
}

我正在使用 gl_FragCoord.z 进行测试,并将深度打包到 8 位 RGBA 纹理中,着色器如下所示:

float depth = gl_FragCoord.z;

vec4 pp = packDepthToRGBA( depth );

if( uFirstPass == 0 ){

    float prevDepth = unpackRGBAToDepth( texture2D( uPrevDepthTex , vSS));

    if( depth <= prevDepth + 0.0001) {
        discard;
    }

}

gl_FragColor = pp;

投影后在顶点着色器中计算变化 vSS

vSS.xy = gl_Position.xy * .5 + .5;

基本想法似乎可行,我得到了果皮,但前提是我使用软糖因素。看起来它失败了,但随着角度变得更钝(这就是为什么 polygonOffset 需要因子和单位来解释斜率?)。

我完全没看懂不变性是怎么解决的。我不明白提到的扩展是如何被使用的,除了它似乎覆盖了片段深度,但是用什么?

我必须承认,我什至不确定这里指的是哪个插值,因为每个像素都是对齐的,我只是使用最近的过滤。

我确实看到了一些关于深度缓冲区精度的提示,但并没有真正理解这个问题,我想尝试将深度打包到三个通道中,看看会发生什么。

有这么小的软糖因素让它有点工作告诉我很可能所有这些采样和计算的深度似乎确实存在于同一个 space 中。但这似乎与使用 gl.EQUAL 进行深度测试是同一个问题?对于狗屎和咯咯笑声,我试图在打包后立即用未打包的深度覆盖深度,但它似乎没有做任何事情。

编辑

增加每次剥离的多边形偏移似乎已经成功。虽然我在线条上遇到了一些问题,但我认为这是因为我已经在使用偏移来绘制它们并且我需要将其包含在剥离偏移中。我仍然很想更多地了解这个问题。

深度缓冲区存储深度 :) 根据 'far' 和 'near' 平面,透视投影往往会在 "stacked" 的一小部分中设置点的深度缓冲。它在 z 中不是线性的。您可以根据深度自己设置不同的颜色并渲染一些占用大部分近远距离的三角形。

阴影贴图存储深度(到光的距离)...在投影后计算。稍后,在第二遍或后续遍中,您将比较这些深度,它们是 "stacked",这使得一些比较失败,因为它们是非常相似的值:危险方差。

您可以使用更细粒度的深度缓冲区,24 位而不是 16 或 8 位。这可能会解决部分问题。

还有一个问题:透视分割 或 z/w,需要获得规范化设备坐标 (NDC)。它发生在顶点着色器之后,因此 gl_FragDepth = gl_FragCoord.z 受到影响。

另一种方法是存储在一些space中计算的深度,它不会受到"stacking"和透视除法的影响。相机 space 是一个。也就是说,可以在vertex shader中计算depth undoing projection

您 link 阅读的文章适用于旧的固定管道,没有着色器。它显示了处理这些差异的 NVIDIA 扩展。