【问题标题】:Projecting FBO value to screen-space to read from depth texture将 FBO 值投影到屏幕空间以从深度纹理中读取
【发布时间】:2014-12-21 23:23:16
【问题描述】:

编辑:更新了 JSFiddle 链接,因为它在 Windows 7 上的 Chrome 中无法正确呈现。

上下文

我正在使用 THREE.JS 中的粒子,并使用帧缓冲区/渲染目标(双缓冲)将位置写入纹理。这个贴图受自己的ShaderMaterial影响,然后被PointCloud的ShaderMaterial读取来定位粒子。到目前为止一切都很好;一切都按预期工作。

我现在要做的是使用场景的深度纹理来查看是否有任何粒子与场景的几何体相交。

我做的第一件事是在 PointCloud 的片段着色器中引用我的深度纹理,使用gl_FragCoord.xy / screenResolution.xy 生成我的uv 用于深度纹理查找。

There's a JSFiddle of this here。它运行良好 - 当粒子位于场景中某物的后面时,我会告诉粒子渲染为红色,而不是白色。

当我尝试在位置纹理着色器中进行相同的深度比较时,就会出现我的问题。在绘制片段着色器中,我可以使用gl_FragCoord 的值来获取粒子在屏幕空间中的位置并将其用于深度uv 查找,因为在绘制顶点着色器中我使用modelViewMatrixprojectionMatrix设置gl_Position的值。

我尝试在位置片段着色器中执行此操作,但无济于事。顺便说一句,我的目标是与 GPU 上的场景发生粒子碰撞。

所以...问题(终于!):

  • 给定一个纹理,其中每个像素/纹素都是代表粒子位置的世界空间 3d 矢量,我如何将此矢量投影到片段着色器中的屏幕空间,最终目标是使用 .xy 的属性这个向量作为uv 在深度纹理中的查找?

我的尝试

  • 在位置纹理着色器中,使用与绘图着色器相同的转换,使用模型视图和投影矩阵将粒子的位置转换到(我认为是)屏幕空间:

    // Position texture's fragment shader:
    void main() {
        vec2 uv = gl_FragCoord.xy / textureResolution.xy;
        vec4 particlePosition = texture2D( tPosition, uv );
    
        vec2 screenspacePosition = modelViewMatrix * projectionMatrix * vec4( particlePosition, 1.0 );
        vec2 depthUV = vec2( screenspacePosition.xy / screenResolution.xy );
        float depth = texture2D( tDepth, depthUV ).x;
    
        if( depth < screenspacePosition.z ) {
            // Particle is behind something in the scene,
            // so do something...
        }
    
        gl_FragColor = vec4( particlePosition.xyz, 1.0 );  
    }
    
  • 以上主题的变奏:

    • 通过执行0.5 - depthUV 来抵消深度的uv
    • 使用 tPosition 纹理分辨率而不是屏幕分辨率来缩放 depthUV。
    • 另一个深度uv 变体:做depthUV = (depthUV - 1.0) * 2.0;。这有点帮助,但规模完全没有。

帮助!并提前致谢。

【问题讨论】:

  • 你的深度纹理是否标准化? IE。你应该把screenspacePosition.z除以screenspacePosition.w吗?
  • 是的,深度纹理不是问题(IMO)。将深度纹理渲染到屏幕上的四边形会显示应有的一切(灰度,0.0 到 1.0)。
  • 快速更新:我认为我的问题在于模型视图和/或投影矩阵被三个传递到位置 FBO/RTT 着色器。手动传递正确的矩阵作为制服似乎有点帮助(即它现在可以正确比较深度,但是当使用粒子位置作为 UV 时深度纹理的比例太小了。嗯!
  • 啊哈!我想我已经找到了问题——这与矩阵有关。我将制作一个新的 JSFiddle 并尽可能详细地发布它作为答案,以防万一其他人遇到这个问题......

标签: javascript three.js glsl opengl-es-2.0 webgl


【解决方案1】:

经过大量的实验和研究,我将问题缩小到 modelViewMatrixprojectionMatrix 的值,THREE.js 在创建 THREE.ShaderMaterial 的实例时自动分配。

我想要做的是在我的“绘制”着色器中工作得非常好,其中这些着色器的 modelViewMatrix 设置为(由 THREE.js):

new THREE.Matrix4().multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld)

似乎当创建一个 ShaderMaterial 来将值渲染到纹理(因此不附加到场景/世界中的对象)时,object.matrixWorld 本质上是一个单位矩阵。我需要做的是使我的位置纹理着色器具有与我的绘制着色器(附加到场景/世界中的对象)相同的modelViewMatrix 值。

一旦到位,唯一要做的就是确保我正确地将粒子的位置转换到屏幕空间。为此,我在 GLSL 中编写了一些辅助函数:

    // Transform a worldspace coordinate to a clipspace coordinate
    // Note that `mvpMatrix` is: `projectionMatrix * modelViewMatrix`
    vec4 worldToClip( vec3 v, mat4 mvpMatrix ) {
        return ( mvpMatrix * vec4( v, 1.0 ) );
    }

    // Transform a clipspace coordinate to a screenspace one.
    vec3 clipToScreen( vec4 v ) {
        return ( vec3( v.xyz ) / ( v.w * 2.0 ) );
    }

    // Transform a screenspace coordinate to a 2d vector for
    // use as a texture UV lookup.
    vec2 screenToUV( vec2 v ) {
        return 0.5 - vec2( v.xy ) * -1.0;
    }

I've made a JSFiddle to show this in action, here。我已经评论了它(可能太多了),所以希望它能很好地解释正在发生的事情,让不熟悉这类东西的人能够理解。

关于小提琴的快速说明:它看起来并不那么令人印象深刻,因为我所做的只是模拟 depthTest: true 在 PointCloud 上设置的属性会做什么,尽管在这个例子中我设置了 y与场景几何体碰撞的粒子的位置到70.0,这就是渲染屏幕顶部附近的白色带。最终,我将在速度纹理着色器中进行此计算,以便进行适当的碰撞响应。

希望这可以帮助某人:)

编辑:Here's a version of this 使用(可能有问题的)碰撞响应实现。

【讨论】:

  • 仅供参考:vec3( v.xyz ) / ( v.w ) 从剪辑空间转换为 NDC 空间,并且不存在额外的因子 2。
  • 感谢您的澄清...我仍然对这一切有所了解。如果clipToScreen 中没有因子二,screenToUV 需要更改为以下内容:vec2 screenToUV( vec2 v ) {return vec2( v.xy ) * 0.5 + vec2(0.5);}
猜你喜欢
  • 2013-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多