网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
从上图可以看出,我们几乎优化了近30%的性能,实际运行中UHD630的性能开销平均大概稳定在20%左右。这样综合评估下来,因为我们的后处理实质上差不多是3Pass处理:一遍水彩画,然后分开水平垂直两个方向的高斯模糊处理,所以这样算来,如果让UHD630满负载运行后处理的大概可以进行15 Pass的后处理,这对复杂甚至更高分辨率的后处理来说已经足够了(当然需要实际的效果测试,并且打开Shader编译优化)。
这也就是为什么我一直热衷于类似我笔记本这种配置情况下的异构多显卡渲染架构的根本原因了。因为现代场景渲染中,后处理其实占用了很大一部分,但其工作量又通常比较固定,所以放到如UHD630这样的核显上来运行是再合适不过了,这样就可以将主显卡从多遍(Pass)后处理中解放出来,从而全力进行复杂的3D场景渲染。这样就尽可能大的发挥了系统的全部性能,并且可以明显看到画质的提升。
3、本章主要Shader
从前一章开始,本系列教程的重点就放到了Shader上来,因此关于本章示例代码中的C++代码部分我就不过多啰嗦了,除非有非常重要的需要注意的地方,大家可以自行从GitHub上下载阅读学习。
在这一章的示例中,由于使用了多遍的后处理,并且多次使用了渲染到纹理的技巧,所以综合下来Shader程序就比较多了。主要有下面几个Shader:
其中MultiThreadAndAdapter.hlsl文件中,主要是进行常规的基本的3D物体渲染,也就是第一遍渲染的Shader程序。例子中只是进行了简单的物体坐标系变换到视锥体空间的变换操作,以及最简单的Sample纹理得到物体像素点基本颜色的操作,这些没什么新奇的,有兴趣的可以自行加入光照、材质、法线纹理等内容,做出基本的光照模型,全当练手。因为这不是我们本章的重点,so 我就没有搞那么复杂了。这里友情提示一下,可以什么都学,但是千万别学我的偷懒!
其余的四个hlsl文件中,主要就是三遍后处理的Shader代码了。因为后处理实际都是针对已经被渲染成2D纹理图像的离屏表面进行的,本质上其实主要是运行Pixel Shader进行图像处理,而所有VS阶段只是简单的绘制一个全屏的矩形,并且不用带坐标系变换操作,所以我将Vertex Shader部分单独抽取出来,放在一个统一的QuadVS.hlsl中,这样后处理的三个PSO对象可以共用这个相同的Vertex Shader代码。其详细代码如下:
struct PSInput
{
float4 m_v4Pos : SV_POSITION;
float2 m_v2UV : TEXCOORD0;
};
PSInput VSMain(float4 v4Pos : POSITION, float2 v2UV : TEXCOORD0)
{
PSInput stResult;
stResult.m_v4Pos =v4Pos;
stResult.m_v2UV = v2UV;
return stResult;
}
这看上去实在是太简单了,仅仅就是简单的值传递操作。而程序中就是传递了一个归一化的单位矩形的顶点,原样输出就是了。
接着11-WaterColourPS.hlsl中和上一章代码中的水彩画特效大同小异,只是将对应的Pixel Shader代码搬到了这个单独的文件中而已。算法的过程就不啰嗦了,大家可以参考前一章。
11-GaussianBlurPS.hlsl就是优化后的简版标准高斯模糊的两遍Pixel Shader的代码了。详细的优化思路稍后会详细介绍。
11-DoNothingPS.hlsl就是最最简单的Pixel Shader,即只是从纹理中采样对应像素点的纹素值作为对应渲染目标像素点的颜色值。
4、水彩画效果
真正的水彩效果在shader中是比较难实现的,它需要进行中值滤波后累加等一些操作,还需要处理NPR(NPR是计算机图形学中的一类,即非真实感绘制Non-photorealistic rendering)中的笔触一类的概念。在本章示例中将绕开这些复杂的概念,只从视觉效果上能尽量模拟出水彩的画的那种感觉来。
水彩画一个最大的特点是水彩在纸上流动扩散后会和周围的颜色搅拌在一起,另外一个特点就是水彩通常会形成一个个的色块,过渡不像照片那样的平滑。针对这两个特点。可以设计这样的一个算法来模拟水彩画的效果。
首先模拟扩散。简单的说,可以通过随机对附近的象素点进行采样来模拟颜色的扩散,而这个随机区域的大小我们可以称为扩散的力度。这在C++代码里应该是非常容易实现的,读者只需要使用rand()函数就可以了。但是HLSL并没有提供这样的函数,怎么办呢?这时可以采用噪声纹理的方式,既事先计算好一个n*n大小的随机数数组,并作为纹理传递给Pixel shader,这样在Pixel Shader里就能获得随机数了。得到随机数后,将随机数映射成纹理坐标的偏移值,就能模拟出色彩的扩散了。典型的噪声纹理是这个样子的:
这像极了古老的阴极射线管电视机在没有信号时的样子,有人说这就是宇宙微波背景辐射的样子!
接下来处理色块,对颜色的RGB值分别进行量化。把RGB分量由原来的8bit量化成比特数更低的值。这样颜色的过渡就会显得不那么的平滑,而是会呈现出一定的色块效果。
通过以上两步处理后,图像依然有非常多的细节,尤其是第一步