像素着色器中的碰撞检查

问题描述

我正在使用DirectX 11实现某种3D模型工具,我需要以像素级精度绘制2个网格之间的碰撞区域。

所以我认为我应该在像素着色器中做一些事情,但是我对着色器编程没有足够的经验。

这是我的近似解决方案,我不确定它是否会起作用以及它是否有效。

  1. 一个模型通过inputlayout传递给着色器。

  2. 第二个模型通过结构化缓冲区传递给着色器。

  3. 当像素着色器绘制第一个模型时,在像素着色器中计算碰撞算法

    如果是真的,则将其涂成红色。

  4. 不需要
  5. UAV,因为着色器没有输出。它仅用于像素着色器。

还有其他好方法吗?该解决方案必须检查第二个模型中每个像素的所有三角形。

我知道有计算着色器,但是我以前从未使用过。如果有必要,我将借此机会学习。

解决方法

我认为您应该实施屏幕空间方法。这是一个大概的工作流程。

  1. 在创建渲染目标时,请使用与渲染目标相同或相似的格式创建与深度相同的额外深度缓冲区(例如,如果深度为DXGI_FORMAT_D32_FLOAT_S8X24_UINT,则不需要模具,您只需要DXGI_FORMAT_D32_FLOAT)。创建该缓冲区的两个视图,ID3D11DepthStencilView进行写入,ID3D11ShaderResourceView进行读取。

  2. 在绘制模型A之前,将模型B的仅深度传递渲染到该单独的深度缓冲区中。不要忘记先清除视图。使用通常使用的相同顶点/几何着色器。您不需要任何像素着色器(将nullptr传递到PSSetShader中),并确保取消绑定任何渲染目标,只需要在输出上绑定深度模板即可。完成此操作后,还原到主渲染目标视图和主深度模板视图。

  3. 现在,在渲染模型A时,修改像素着色器,应该是这样的:

     Texture2D<float> modelB_depth: register( t0 );
    
     float4 main( float4 pos: SV_Position ) : SV_Target0
     {
         const int3 depthLoadPosition = int3( (int2)pos.xy,0 );
         const float b_depth = modelB_depth.Load( depthLoadPosition );
         if( b_depth < pos.z )
         {
             // Depth of the model B in this pixel is less than the current position of model A.
             // This means this pixel of model A is occluded by model B.
             // Paint it in red.
         }
         else
         {
             // Paint normally
         }
     }
    

如果您使用MSAA,则需要在此处用Texture2D替换Texture2DMS并相应地调整着色器。

  1. 一旦它开始工作,您会注意到即使模型B不在模型A前面,模型A也会涂成红色,即使没有相交。如果要解决这个问题,您将需要其中2个额外深度缓冲区。将模型B渲染到两个缓冲区中。使用1.0清除它们中的第一个,并使用D3D11_COMPARISON_LESS深度比较进行渲染,使用0清除它们中的第二个,并使用D3D11_COMPARISON_GREATER深度比较进行渲染。渲染到第二个图标时,请不要忘记D3D11_CULL_FRONT。完成后,您将获得2个深度纹理,它们一起告诉您模型B在每个像素处的深度范围。然后,在渲染模型A的三角形时,请同时读取两个深度,并且仅当当前像素的深度在该间隔内时才涂成红色。这仍然不是完美的,当您的模型B是凹面多面体时,在某些情况下可能会失败,但是在性能方面非常有效。 GPU是异步的,模型B的两次深度访问没有依赖关系,将并行运行。通常,只要着色器不太复杂,GPU对三角形的栅格化就会非常快。

更新:此外,还可以通过一次渲染过程来计算最小深度纹理和最大深度纹理。 设置两个渲染目标,并以DXGI_FORMAT_R32_FLOAT类型的纹理作为后盾。使用ClearRenderTargetView()初始化为1.0和0.0。 为该遍设置混合状态,将第一个RT使用D3D11_BLEND_OP_MIN,将第二个使用D3D11_BLEND_OP_MAX。 编写一个像素着色器,将SV_Position的Z分量输出到两个渲染目标中。 不要使用深度缓冲区,将模型B渲染到正面和背面这两个RT中,即D3D11_CULL_NONE。可能会稍快一些,因为这样顶点仅变换一次。