将FBO值投影到屏幕空间以从深度纹理中读取

时间:2014-10-26 18:30:35

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

编辑:更新了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矢量,如何将此向量投影到片段着色器中的屏幕空间,最终目标是使用{ {1}}此向量的属性为深度纹理中的.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 ); }
    • 抵消深度uv
    • 使用tPosition纹理分辨率代替屏幕分辨率来缩放depthUV。
    • 另一个深度0.5 - depthUV变体:做uv。这有点帮助,但规模完全关闭。

帮助!并提前感谢。

1 个答案:

答案 0 :(得分:0)

经过大量的实验和研究后,我将问题缩小到modelViewMatrixprojectionMatrix的值,当THREE.ShaderMaterial创建modelViewMatrix的实例时,THREE.js会自动指定这些值。 / p>

我想做的就是在我画画时做得非常好。着色器,这些着色器的new THREE.Matrix4().multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld)设置为(由THREE.js):

object.matrixWorld

看来,当一个人创建一个ShaderMaterial来向纹理渲染值(因而没有附加到场景/世界中的一个对象)时,modelViewMatrix本质上就是一个单位矩阵。我需要做的是让我的位置纹理着色器与我的绘制着色器(附加到场景/世界中的对象)具有相同的 // 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; } 值。

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

depthTest: true

I've made a JSFiddle to show this in action, here。我已经对它进行过评论(可能太多了),所以希望它能够为那些不熟悉这类内容的人们解释什么是正常的。

关于小提琴的快速说明:它看起来并不令人印象深刻,因为我所做的只是模仿70.0在PointCloud上设置的属性所做的事情,尽管在这个例子中我将与场景几何体碰撞的粒子的y位置设置为{{1}},以便白色条带靠近渲染屏幕的顶部。最后,我将在速度纹理着色器中进行此计算,因此我可以进行适当的碰撞响应。

希望这有助于某人:)

编辑:Here's a version of this实现了(可能有错误的)碰撞响应。