|
前言
我们来加深一下目前理解到的内容,并且着手解决一些在学习过程中遇到的疑惑(如果有疑惑但我没提到欢迎在评论留下我会随后加上)。
首先,以往我们写程序是在CPU上跑,但写shader则是在GPU上跑,我们会用两对好兄弟,分别是vertex shader和fragment shader。
vertex shader是每个顶点都会调用一次的程序,它可以访问顶点的位置、法线、纹理等信息,这些值在经过一些处理之后,会先跑一个叫做“光栅化(Rasterization)”的过程。我们都知道,显示器是由红绿蓝 LED 组成的显示单元,因此单元之间是离散的,而“连续”的三维图像要渲染到屏幕上,就要经过一系列的修改,称之为光栅化。
此时得到就是片元(fragment),我之前说过片元与最终的图像单位是非常接近的(我甚至鼓励大家以图形的方式来理解它们实在是抱歉),但它们之间仍存在着微妙的差别。这是因为片元需要先通过一些测试,比如深度测试、透明度测试等,以决定它们是需要被使用还是被抛弃。其实在这里并不需要太纠结片元到底是不是像素,因为这不是这个系列的重点。
而由每个片元分别调用一次的就是fragment shader,它可以获取片元的二维、深度、颜色等信息。注意,fragment shader获取的片元颜色是根据片元的三个顶点颜色插值计算后得到的,也就是说如果三个顶点颜色并不相同,那么最终颜色便是渐变的。
shader的计算是在CGPROGRAM内完成的,并且应该符合以下结构
- struct appdata{}; // 获取模型数据
- struct v2f{}; // 存放计算结果
- v2f vert(appdata v){} // 拿取模型数据计算后传给fragment shader
- float4 frag(v2f i) : SV_Target{} // 拿取计算结果作用于目标
复制代码
注意,在结构体内定义的输入输出是由语义限定的,举个栗子:
- struct appdata{
- float4 vertex : POSITION; // 把模型顶点坐标填充到vertex中
- float2 uv : TEXCOORD1; // 第一组纹理坐标
- float3 normal : NORMAL; // 把法线方向填充到normal中
- };
- struct v2f{
- float4 vertex : SV_POSITION; // 输出到裁剪空间中的顶点坐标
- float3 color : COLOR; // 输出颜色
- };
- float4 frag(v2f i) : SV_Target{ // frag输出值直接用于渲染
- return float4(i.color, 1.0);
- }
复制代码
语义是很聪明的一种设计,它能够让Shader知道从哪里获取数据,并且还知道把输出的数据放到哪里(或者怎么放)
问题1:顶点位置是一种坐标系,uv也是一种坐标系,既然都是坐标为什么不能只算一个多方便啊?
好问题!(拍大腿)这两种坐标系的应用是不一样的,顶点是一个和网格(mesh)相关的局部点,以xyz为坐标轴;而uv则是贴图映射到模型表面的依据,以uvw为坐标轴,或者可以将uv看作一张纹理查找表格。因为xyz轴已经被网格占用了,所以贴图用的坐标轴就只能用uvw了,简称uv。很多同学看了一些shader教程经常见到它们两一齐出现,自己写的时候不知所以索性就一齐计算了。
5.4 鲁迅名言
问题2:有些shader程序的frag函数输出的COLOR,而另一些则是SV_TARGET,有什么区别啊?
没啥不同,SV开头的是Direct3D定义的一种System Value语义,SV_TARGET对应的就是COlOR,但有些平台支持SV语义,而有些则不支持。但这并不重要,因为编译器会自动帮我们做转换,它们两的功能是一样的。类似还有SV_POSITION与POSITION,SV_DEPTH与DEPTH等等。反正无脑写SV_Target就没错了(狗头)
自我小测:
在写过一些shader之后,我们已经是个shader程序员了,当然要学会自己写shader咯,下面是一些可以努力的方向,并获取图中的渲染效果(因为是读者自我测验,因此笔者不会提供答案,但如果实在想不明白可以私信笔者问思路)
练习1 : 新增一个_Color属性,并将其与主纹理的颜色相乘
注:颜色在属性区块内是以Color类型存在,在CGPROGRAM使用内需被预定义为float4类型
练习2 : 用第二章讲到,用uv值计算颜色值的方法,将其结果与主纹理的颜色想乘,获得下面的效果
练习3 : 使用两张纹理,以及第五章讲到的拖动条,当移动滑杆时在两张纹理中进行插值
练习4 : 在图片的设置面板内将图片的wrap mode设为repeat。在对纹理采样时,将uv乘2
练习5 : 使用明亮度(luminance)公式来算出一个明亮度值,然后将红绿蓝通道都设置为明亮度值,透明度通道则用对图片采样获得的透明度,最终获得灰度效果
注: luminance = 0.2125 * Red + 0.07154 * Green + 0.0721 * Blue
练习6 : 将练习5的返回颜色与自定义的颜色属性相乘
相关阅读:
Shader从入门到跑路:朴实无华的图形学基础
Shader从入门到跑路:自定义纹理输入
Shader从入门到跑路:屏幕后处理效果
Shader从入门到跑路:实作屏幕扭曲效果
作者:俊铭
专栏地址:https://zhuanlan.zhihu.com/p/86192364
|
|