> 文章列表 > Direct3D 12——计算着色器——利用索引对纹理进行采样

Direct3D 12——计算着色器——利用索引对纹理进行采样

Direct3D 12——计算着色器——利用索引对纹理进行采样

利用索引纹理进行采样

纹理元素可以借助2D索引加以访问。在计算着色器中,我们基于分派的线程ID来索引 纹理。而每个线程都要被指定一个唯一的调度ID (调度标识符)。

[numthreads(16, 16, 1]
void CS(int3 dispatchThreadID : SV_DispatchThreadID)
{
//对两个纹理中横纵坐标分别为x、y处的纹素求和,并将结果存至相应的gOutput纹素中
gOutput[dispatchThreadID.xy] = glnputA[dispatchThreadID.xy] + glnputB[dispatchThreadID.xy];
}

假设我们为处理纹理而分发了足够多的线程组(即利用一个线程来处理一个单独的纹素),那么这段 代码会将两个纹理图像的对应数据进行累加,再将结果存于纹理gOutput中。

系统对计算着色器中的索引越界行为有着明确的定义。越界的读操作总是返回0,而向越 界处写入数据时却不会实际执行任何操作。

由于计算着色器运行在GPU上,因此便可以将它作为访问GPU的一般工具,特别是在通过纹理过 滤来对纹理进行采样的时候。但是,这个过程中还存在两点问题。

第一个问题是,我们不能使用Sample 方法,而必须采用SampleLevel方法。与Sample相比,SampleLevel需要获取第三个额外的参数, 以指定纹理的mipmap层级。0表示mipmap的最高级别,1是第二级,并以此类推。若此参数存在小数 部分,则该小数将用于在开启mipmap线性过滤的两个mipmap层级之间进行插值。至于Sample方法, 它会根据屏幕上纹理所覆的像素数量而自动选择最佳的mipmap层级。因为计算着色器不可直接参与渲 染,它便无法知道Sample方法自行选择的mipmap层级,所以我们必须在计算着色器中以 SampleLevel方法来显式(手动)指定mipmap的层级。

第二个问题是,当我们对纹理进行釆样时, 会使用范围为[0,1]2的归一化纹理坐标,而非整数索引。此时,我们便可以将纹理的大小(width, height) (即纹理的宽度与高度)设置为一个常量缓冲区变量,再利用整数索引(x,y)来求取归一化纹理坐标:

u=x/width v=y/height

下列代码展示了一个使用整数索引的计算着色器,而第二个功能相同的版本则釆用了纹理坐标与 SampleLevel函数。这里我们假设纹理的大小为512x512,且仅使用最高的mipmap层级:

版本1:使用整数索引
cbuffer cbUpdateSettings{float gWaveConstantO; float gWaveConstantl;float gWaveConstant2;float gDisturbMag; int2 gDisturblndex; 
};RWTexture2D<float> gPrevSolInput : register(u0); 
RWTexture2D<float> gCurrSolInput : register(ul); 
RWTexture2D<float> gOutput	: register(u2);[numthreads(16, 16, 1)]
void CS(int3 dispatchThreadlD : SV_DispatchThreadID)
{int x = dispatchThreadlD.x;int y = dispatchThreadlD.y;gOutput[int2(xz y)] =gWaveConstantO * gPrevSolInput[int2(x,y)].r + gWaveConstantl * gCurrSolInput[int2(x,y)].r + gWaveConstant2 * (gCurrSolInput[int2(x, y+1) ] .r + gCurrSolInput[int2(x, y-1)] .r + gCurrSolInput[int2(x+1, y)].r +gCurrSolInput[int2(x-1,y)].r);
}
版本2:使用函数SampleLevel与纹理坐标
cbuffer cbUpdateSettings
(float gWaveConstantO;float gWaveConstantl;float gWaveConstant2;float gDisturbMag;int2 gDisturblndex;
};
SamplerState samPoint : register(s0);RWTexture2D<float> gPrevSolInput : register(u0);
RWTexture2D<float> gCurrSolInput : register(ul);RWTexture2D<float> gOutput	: register(u2);[numthreads(16, 16, 1)]
void CS(int3 dispatchThreadID : SV_DispatchThreadID){//相当于以SampleLevel()取代运算符[]int x = dispatchThreadID.x;int y = dispatchThreadID.y;float2 c = float2(x,y)/512.0f; float2 t = float2(x,y-l)/512.0;float2 b = float2(x,y+l)/512.0; float2 l = float2(x-1,y)/512.0; float2 r = float2(x+1,y)/512.0;gNextSolOutput[int2(x,y)]=        gWaveConstantsO*gPrevSolInput.SampleLevel(samPoint, c,0.0f).r+gWaveConstantsl*gCurrSolInput.SampleLevel(samPoint, c,0.0f).r+gWaveConstants2*( gCurrSolInput.SampleLevel(samPoint,b,0.0f).r+ gCurrSolInput.SampleLevel(samPoint,t,0.0f).r+ gCurrSolInput.SampleLevel(samPoint,r,0.0f).r+ gCurrSolInput.SampleLevel(samPoint,l,0.0f).r+ }