关于Unity Shader的学习笔记(七)

本文深入解析Unity Shader中的表面着色器,探讨其与顶点/片元着色器的区别,通过实例代码详细解释表面着色器的编写过程及Unity如何自动生成大量顶点/片元着色器代码。
之前的笔记实际上为表面着色器的学习做了许多的铺垫,从渲染管线起了解shader是如何运作的,从基本的固定管线着色器的编写到顶点片元着色器的编写了解了实际shader的编写内容,一些涉及到的三维数学知识也帮助加深我们对shader的了解。从这篇笔记开始就是正真意义上的UnityShader的学习了,Unity推荐使用表面着色器来编写shader,我们通常在软件中创建的shader就是表面着色器。之前的笔记有讲到表面着色器实际上在编译后还是会生成相应的顶点/片元着色器,表面着色器只是在顶点/片元着色器的基础上的又一层封装。我们可以来深刻的体会一下表面着色器与顶点/片元着色器的差别。首先我们打开Unity的工程,我在这里用的是Unity2019.3.3f1,表面着色器在历来的Unity版本中基本都没改动,除了Unity5以前的版本,这里有不同我会注释说明的。在Assets目录下右击新建一个Standard Surface Shader,之后直接双击用VS编辑器打开它查看它的代码。
以上就是一个标准表面着色器的代码,可能有人会认为这短短50多行的代码不足为惧,那么我们现在来看一下这个标准表面着色器编译成顶点/片元着色器后的代码。还是刚才新创建的Shader,选中它,在Inspector面板中点击Show generated code,之后会自动打开VS编辑器显示代码,如下图: 这时你就可以看到一个简单的50多行标准表面着色器代码对应的一个1600多行的顶点/片元着色器代码,怎么样,晕了吧,晕了吧。。。 这就是Unity表面着色器对顶点/片元着色器的又一层封装,它可以生成大量的可重复的顶点/片元着色器代码,不需要我们自己再去编辑,这确实是便利开发者的,但是如果想要像资深shader人员那样自定义编写的话,你懂的,这还需要点时间。。下面我们就对这个标准表面着色器的代码进行注释,继续开始UnityShader的学习。
在此,为了不用再往前翻我再重新把刚才新建的标准着色器代码的图粘一下 我们先大致浏览下这个标准表面着色器代码,还是和之前笔记写的一样,有标准的Properties,Subshader,FallBack代码块,唯一不同的是SubShader中没有Pass代码块,取而代之的是CGPROGRAM-ENDCG代码块。Yep! 表面着色器在编写SubShader块时是没有Pass块的,相应的,顶点/片元着色器的代码里是有的,之前说了表面着色器在编译后会自动为我们生成顶点/片元着色器的代码,当然也会为我们自动生成Pass通道块。
Properties代码块: 记得在说渲染管线的笔记里有提到,CPU将所需要GPU处理渲染的模型或图片的纹理网格等信息发送给GPU,这里所需要的信息就体现在Properties代码块里。
_Color(”Color”,Color)=(1,1,1,1) 这段代码中的_Color是指颜色值,对应的是Color属性,双引号中的 Color是用来显示在Inspector面板中,等号后面的(1,1,1,1)是对应颜色的RGBA值,这里的意意思是显示白色Alpha值为1。
_MainTex(”Albedo(RGB)”,2D)=”white”{} 这段代码中_MainTex是指主纹理贴图,双引号里的Albedo(RGB)也是用来显示在Inspector面板中,类型是2D类型。等号后面的“white”{}是一个固定写法,一般双引号中要么是空,要么是white、black、gray等内置的纹理名称。
_Glossiness(”Smoothness”,Range(0,1))=0.5 这段代码中_Glossiness是指纹理的高光光泽度,这个可以自己在材质上调节下,看看具体效果加深印象。同样双引号中的Smoothness是用来显示在Inspector面板中,类型是Range,0-1的一个可调节范围,等号后面的0.5是指初始值给定为0.5。
_Metallic(”Metallic”,Range(0,1))=0.0 这段代码中_Metallic是指纹理的金属度,同样可以自己在材质上调节下看看具体效果加深印象。“Metallic”也是用来显示在Inspector面板中,类型也是Range,0-1的一个可调节范围,等号后面是指给定初始值为0。这里要注意下在表示浮点数的时候不能在数字后面加f,要直接写成小数形式。 在Unity中建立一个Cube,新建一个Materal,将新建的标准表面着色器赋给材质,材质赋给Cube,之后可以看到在Inspector面板下显示的Properties下的各个属性值,如下图: 更多属性类型方面可以去回顾一下笔记(四),后面再遇到也会相应的说明一下。
SubShader代码块: Tags{”RenderType”=”Opaque”} 这句代码是SubShader的一个标签,用于对着色器进行分类,这里“RenderType”=“Opaque”是指设定渲染类型为不透明。 LOD 200是指渲染层级,可以提高渲染效率,根据渲染对象距离摄像机的远近不同,采用不同的层次细节处理,距离远渲染比较粗糙,距离近则渲染精细。这个具体在后面再介绍。
CGPROGRAM-ENDCG代码块: 这段代码是用CG语言编写的,用来生成对应的顶点/片元的pass通道。 其中#pragma surface surf Standard fullforwardshadows这句代码在Unity官方文档中有说明。另外插入说明,Unity文档现在有中文版的,虽然还没翻译全,不过相比纯英文已经很不错了。获取方式是在Unity的PackageManager里,具体如下图: 我们来看下文档中对#pragma surface surf Standard fullforwardshadows这句代码的说明,如下图: #pragma target 3.0这句代码在代码的英文注释中也可以了解到,使用shader model3.0可以得到更好的光照。
sampler2D _MainTex;这句代码是对应Properties块中的_MainTex,可以看成是对_MainTex类型的申明,在Properties块中_MainTex对应的类型是2D,而在CG代码中则对应的是sampler2D,这是一个固定写法。
half _Glossiness;这句代码也是对_Glossiness的类型申明,在Properties块中是Range,而在CG代码中是half。
half _Metallic;同理也是对_Metallic的类型申明,在Properties块中是Range,而在CG代码中是half。
fixed4 _Color;同上,对_Color类型申明,Properties中是Color类型,而在CG代码中是fixed4类型。
Struct Input { float2 uv_MainTex; };这段代码描述的是一个结构体,回忆之前的笔记我们知道图像在屏幕上显示是要通过顶点或向量在不同的空间变换后得到的,一个模型或者一张图片是由多个顶点构成的,每个顶点上带有法线、切线,纹理坐标等信息。所以这里我们就用一个结构体来包含这些顶点纹理坐标。还有一点需要注意,在Unity官方中文文档中也有写道,如下图: 如果_MainTex前面不加uv,最终是渲染不出图像的,另外有一点需要注意的是uv_MainTex是指纹理坐标,其对应的类型是float2类型。在Input结构体中可以不只有float2类型的uv_MainTex作为参数,还有float3,float4等类型的参数,可以仔细看上面的文档截图,要注意的是float2,float4等类型都是CG语言里的类型,后面再遇到还会说明。
UNITY_INSTANCING_BUFFER_START(Props) UNITY_INSTANCING_BUFFER_END(Props) 以上两句代码是关于GPUInstancing开和关,可以点击代码注释里面的连接看一下,主要用于渲染优化。要开启的话在Inspector面板中点击Enable GPU Instancing就可以了,如下图:
void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; }这段代码就是对surf这个方法的具体定义了。我们来看第一行代码:void surf(Input IN,inout SurfaceOutputStandard o),其中可以看到surf这个方法的返回值是void,参数是Input类型的一个参数IN和即作为输入参数同时也作为输出参数的SurfaceOutputStandard类型的o。所以这时就可以理解了surf这个方法为什么返回值是void。实际上返回值是用inout来代替了。关于SurfaceOutputStandard这个类型,官方文档也有说明,具体截图如下:
知道这个输出类型的参数含义后,根据以往C#编程经验后面的代码就很好理解了。 fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;这句代码的含义是返回一个fixed4类型的c,从之前的说明知道这个c是指一个颜色,tex2D是一个CG代码中的函数,参数是图像纹理,图像纹理的坐标集,整理下来整句代码的意思大致就是通过传入的纹理得到颜色。
o.Albedo = c.rgb;将颜色的RGB值赋值给输出参数的Albedo(基础颜色) o.Metallic = _Metallic;将金属度赋值给输出参数的Metallic o.Smoothness = _Glossiness;将光泽度赋值给输出参数的Smoothness o.Alpha = c.a;将通过函数得来的颜色的Alpha赋值给输出参数的Alpha 最后FallBack”Diffuse”,前面笔记也有说到,就是降级着色器,针对不同的图形接口(DirectX/OpenGL....)如果以上编码无法对应当前工程的图形接口时就使用通用的Diffuse着色器编码。
以上就是一个标准表面着色器的全部注释了,通过以上的代码梳理我们更加深入的了解了表面着色器是如何工作的,其实整体看下来和我们以往C#代码编写大同小异,Properties代码块就像我们平常定义的字段,Subshader代码块就像我们平常写的类,里面有局部变量的申明,还有方法。总之随着慢慢的深入,一切也没有想象中的那么难。今天是周末,本想好好休息下但是学习不能落下,所以坚持更新,终于更新完了也是心情愉快,悠闲的周末让我们一起摇摆~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值