在一次渲染中将图像渲染到完整的三维渲染目标

7
使用DirectX 11,我创建了一个可以绑定为渲染目标的三维体素纹理:
D3D11_TEXTURE3D_DESC texDesc3d;
// ...
texDesc3d.Usage     = D3D11_USAGE_DEFAULT;
texDesc3d.BindFlags = D3D11_BIND_RENDER_TARGET;

// Create volume texture and views
m_dxDevice->CreateTexture3D(&texDesc3d, nullptr, &m_tex3d);
m_dxDevice->CreateRenderTargetView(m_tex3d, nullptr, &m_tex3dRTView);

我现在希望更新整个渲染目标,并用像“全屏通道”一样由像素着色器生成的程序数据填充它。我需要生成数据的所有内容都是有关所涉及像素的UVW坐标。
对于2D,可以构建一个简单的顶点着色器来渲染全屏三角形:
struct VS_OUTPUT
{
    float4 position : SV_Position;
    float2 uv: TexCoord;
};

// input: three empty vertices
VS_OUTPUT main( uint vertexID : SV_VertexID )
{
    VS_OUTPUT result;
    result.uv = float2((vertexID << 1) & 2, vertexID & 2);
    result.position = float4(result.uv * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
    return result;
}

我很难理解如何将这个原则应用于3D。在DirectX 11中是否可能实现,还是必须像这里描述的那样渲染成体纹理的单独切片?


如果我们有CG.SE,这可能会得到更多的关注。哦等等,已经有一个提案了 ;) proposal - Daerst
1
我不确定渲染管线是否适用于这种体积计算,因为其结构专门用于渲染到2D目标。在您的情况下,计算着色器是否更合适? - Gnietschow
@Gnietschow 那真的是一个非常好的想法,我不知道为什么之前完全没有想到 :) - Daerst
1个回答

3
这是一段使用管线版本的示例代码,主要是将N个三角形分批处理,并使用几何着色器将每个实例路由到一个体积切片中。
struct VS_OUTPUT
{
    float4 position : SV_Position;
    float2 uv: TexCoord;
    uint index: SLICEINDEX;
};

VS_OUTPUT main( uint vertexID : SV_VertexID, uint ii : SV_InstanceID )
{
    VS_OUTPUT result;
    result.uv = float2((vertexID << 1) & 2, vertexID & 2);
    result.position = float4(result.uv * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
    result.index= ii;
    return result;
}

现在你需要使用3个顶点和N个实例(其中N是你的体积切片数)调用DrawInstanced函数。

然后像这样将三角形分配给几何着色器:

struct psInput
{
    float4 pos : SV_POSITION;
    float2 uv: TEXCOORD0;
uint index : SV_RenderTargetArrayIndex; //This will write your vertex to a specific slice, which you can read in pixel shader too
};

[maxvertexcount(3)] 
void GS( triangle VS_OUTPUT input[3], inout TriangleStream<psInput> gsout )
{       
psInput output;
for (uint i = 0; i < 3; i++)
{
    output.pos = input[i].pos;
    output.uv = input[i].uv;
    output.index= input[0].index; //Use 0 as we need to push a full triangle to the slice
    gsout.Append(output);
}
gsout.RestartStrip();
}

现在您可以在像素着色器中访问切片索引:
float4 PS(psInput input) : SV_Target
{
//Do something with uvs, and use slice input as Z
}

计算着色器版本(不要忘记为您的体积创建一个UAV),此处的numthreads完全是任意的。

[numthreads(8,8,8)]
void CS(uint3 tid : SV_DispatchThreadID)
{
     //Standard overflow safeguards

     //Generate data using tid coordinates
} 

现在你需要使用 dispatch 函数,它需要的参数是 width/8, height/8, depth/8。

在GS中,output.index=i; 应该改为 output.index=input[i].index,对吗? - miloszmaki
另外,SLICEINDEX是什么?我在支持的HLSL语义列表中没有看到它。是否可以使用自定义语义? - miloszmaki
@miloszmaki 确实,我编辑了答案。对于语义学,是的,你可以使用任何你想要的自定义数据(dx11系统的前缀为SV_,通常人们使用POSITION,NORMAL作为标准值,但你真的可以使用任何你想要的,只要语义名称/类型在阶段之间匹配即可)。 - mrvux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接