词汇
渲染到纹理:render to texture 模糊核:blur krenel 高斯模糊:Gaussian blur 可分离性:separable
输出合并:Output Merger 渲染到离屏纹理:render-to-off-screen-texture
渲染到纹理:render-to-texutre 阴影贴图:shadow mapping
屏幕空间环境光遮蔽:screen space ambient occlusion,SSAO
动态反射与立方体图:dynamic reflections with cube maps
内容
13.7图像模糊演示程序
本节学习适用计算着色器实现图像模糊的算法并讨论渲染到纹理技术。
13.7.1 图像模糊理论
-
图像模糊算法概括如下:对原图像中每一个像素 P i j P_{ij} Pij,计算以它为中心的 m × n m\times n m×n矩阵的加权平均值,此加权平均值便是经模糊处理后图像中第i行第j列的像素颜色。公式就不写了。将m和n强制为奇数来保证矩阵总有中心项,各设a为垂直模糊半径,b为水平半径,若a=b则只需指定模糊半径即可确定矩阵大小。 m × n m\times n m×n权值矩阵称为模糊核,权值之和必为1,若权值之和小于1,则模糊后的图像会因颜色缺失而显得更暗,若大于1则会显得更亮。
-
保证权值为1的前提下可以运用多种不同方法计算,大多数图像编辑软件中,能发现一种广为人知的模糊运算:高斯模糊。
G ( x ) = e x p ( − x 2 2 σ 2 ) G(x)=exp(-\frac {x^2}{2\sigma^2}) G(x)=exp(−2σ2x2) -
高斯模糊最著名的在于它的可分离性,据此可以如下将它分为两个1D模糊过程。
- 通过1D横向模糊将输入的图像I进行模糊处理 I H = B l u r H ( I ) I_H=Blur_H(I) IH=BlurH(I)
- 对上一步输出的结果再次进行1D纵向模糊处理 B l u r I = B l u r V ( I H ) Blur_I=Blur_V(I_H) BlurI=BlurV(IH)
- 简化后得:
B
l
u
r
(
I
)
=
B
l
u
r
V
(
B
l
u
r
H
(
I
)
)
Blur(I)=Blur_V(Blur_H(I))
Blur(I)=BlurV(BlurH(I))
拾取样本是代价高昂的操作,因此通过分离模糊过程较少纹理采样操作是一种常用的优化手段。
13.7.2 渲染到纹理技术
目前为止一直在程序中向后台缓冲区渲染数据,实际上后台缓冲区是一种位于交换链中的纹理
Microsoft::WRL::ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRtvHeap->GetCPUDescriptorHandleForHeapStart());
for (UINT i = 0; i < SwapChainBufferCount; i++)
{
ThrowIfFailed(mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
rtvHeapHandle.Offset(1, mRtvDescriptorSize);
}
通过将后台缓冲区渲染目标视图与渲染流水线的输出合并(Output Merger,OM)阶段想绑定,使Direct3D将数据渲染至后台缓冲区内:
//指定即将被渲染的缓冲区
mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());
在通过IDXGISwapChain::Present方法呈现后台缓冲区时,其中数据便会显示在屏幕上。
用作渲染目标的纹理一定要以D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET标志创建。
还可以创建另一个纹理,再为它创建渲染目标视图,并将它绑定到渲染流水线的OM阶段之上。由此就能将数据绘制到不同的离屏纹理之中。这就是渲染到离屏纹理技术,渲染到纹理。这种纹理与后台缓冲区唯一不同之处在于在执行Present过程中,它无法显示在屏幕上。
渲染到纹理执行完毕后,还可以将后台缓冲区重新绑定到OM阶段从而继续讲几何图形绘制到后台缓冲区之中。并且还能够用渲染到纹理期间所生成的纹理为几何贴图,可以实现各种特殊效果。比如把鸟瞰图绘制到 场景纹理之上,以模拟雷达系统。其它常见的用途:
1. 阴影贴图
2. 屏幕空间环境光遮蔽
3. 动态反射与立方体图
若使用渲染到纹理技术,则在GPU上实现的模糊算法将如下操作:把演示程序中的场景按寻常方式渲染到离屏纹理上,该纹理会被输入至计算着色器并执行模糊算法,模糊处理完成后会将所得的纹理回执未全屏四边形并传送到后台缓冲区,由此便可根据模糊效果校验模糊实现,实现步骤如下:
1. 绘制场景到一个离屏纹理
2. 通过计算着色器程序对纹理进行模糊处理
3. 将后台缓冲区恢复为渲染目标,并以模糊后的纹理绘制全屏四边形。
使用渲染到纹理技术实现模糊效果,并且需要在场景渲染为与后台缓冲区大小相同的纹理中时也是理想的选择。假设离屏纹理与后台缓冲区大小及格式相匹配,就能采用间接绘制到纹理的方式取代,先将纹理渲染至后台缓冲区,再把后台缓冲区的内容用CopyResource方法复制到离屏纹理,再将模糊后的纹理绘制为全屏西边形后送至后台缓冲区,生成最终的屏幕输出。
//将input复制到BlurMap0
cmdList->CopyResource(mBlurMap0.Get(),input);
上述处理过程需要先用普通的渲染流水线绘制,然后切换到 计算着色器执行计算任务,最后再切换回普通渲染流水线,一般来讲,应该尝试避免在渲染流水线与计算工作之间的反复切换,因为上下文context的切换会产生开销,在每一帧中应试者先完成所有计算工作,再执行全部的渲染人物,然而这是无法实现的。比如在上述处理过程中,我们需要把场景渲染到一个纹理,使它在计算着色器中进行模糊处理,再将处理结果绘制出来,虽无法完全避免,但也应尝试尽量将切换次数降到最低