Unity着色器训练营(1):入门篇(下)
http://forum.china.unity3d.com/thread-27522-1-1.html


上文我们为大家分享了《Unity 着色器训练营-第一期:着色器入门(上)》,今天将由Unity大中华区技术经理鲍健运继续为大家分享着色器入门下篇内容。教你如何动手修改你的第一个Unity着色器。


动手修改你的第一个Unity着色器

155604t2zur55jagh22uax.png

这是一个最基本的Vertex/Fragment Shader,纯色渲染效果。可以看到上图中,蜘蛛机器人呈现的就是一片纯红色。现在打开对应的Unity着色器文件具体解读一下:

文件头部:

155656vkvy5fkvxsykxyzv.png

我们发现一件很有趣的事,这个一对引号里的Shader命名与地址与实际项目中物理命名与存放该着色器不太一样,这是为什么呢?因为Unity着色器在引擎内部有个自己的取用地址,而这个地址就是文件开头在Shader后面引号里的地址。这个文件的调取可以在检视窗口中材质组件的Shader选项找到,如下:

155726x8eya8aycoy8ce88.png

以后你写的Unity着色器也是遵从这样的规则。

属性:

155744y71ers5rrx517mm1.png

这里声明了一个属性,属性名为“_TintColor”,在检视窗口中显示为“Color”,类型为“颜色”,默认值为RGBA全1,即白色。查看一下检视窗口:

155807tmxhe1xblhmw04m1.png

是有Color这个选项,但是颜色却是红色的?因为这个属性的默认值会在编辑器状态下由设置的手动调整变化而变化,即自己设置了这个颜色。

标签Tags:

155826u1gtp6g6onfbgopp.png

形式:Tags { TagName1 = Value1 TagName2 = Value2 }
作用:控制渲染引擎“何时”、“如何”将子Shader内容进行呈现。
上图中所表示的是“渲染输出的是非透明物体”。
还有一个比较常用的,却容易引起混淆的就是“Queue”渲染队列。Tags { “Queue” = “Opaque” } 表示的是“指定在渲染非透明物体的顺序队列”。其实这两者最主要的区别在于“RenderType”表示的是渲染什么样的物体,而“Queue”表示的是在什么样的实际渲染物体。

Pass的开头部分:

155856pzbp6jvfs6zcwrps.png

CGPROGRAM与下文的ENDCG标记了在两者之间的是一段CG程序。使用的是NVIDIA的CG语言,一种类似于C的语言,其大多数内容基本与微软的HLSL语言是相似的。

#pragma的作用是指示编译对应的着色器函数。#pragma vertex vert 所表示的就是声明一个名为vert的顶点函数(Vertex Function),#pragma fragment frag 所表示的就是声明一个名为frag的片元函数(Fragment Function)。一点实现了这两个函数,实际上就是实现了顶点/片元着色器了。

#include “UnityCG.cginc”作用就是导入Unity通用CG预定义文件,后面的UnityObjectToClipPos函数就是在该文件里定义好的。

输入顶点函数的结构体:

155925oj32eftigtb7bjug.png

appdata结构体只有一个参数,声明了一个名为vertex的四维浮点数,语义为网格的顶点坐标数据。

顶点函数实现:

155948vg8ne8u8rkig0l08.png

声明了vert函数就需要实现它,这里一目了然主要就是做了一件事:就是使用UnityObjectToClipPos方法,将输入网格顶点对象空间转换到屏幕剪裁平面。

顶点输出到片元输入的结构体:

160005awrt2t38yzt2z3w6.png

v2f结构体中也就是一个参数,即网格顶点对应到屏幕上的坐标,而语义上的SV所代表的是System Value(系统值),SV_POSITION对应就是屏幕上的像素位置。

实例化声明:

160020dg6qwizgdmi8i9ze.png

这里可以发现这个float4的变量与属性里的名字是一致了。这个float4变量是将属性(Properties)里的变量在Unity着色器内部进行数据绑定用的,为了CG 程序正常访问属性(Properties)的变量,CG程序中的变量必须使用和之前变量相同的名字进行声明。

片元函数:

160048n9j7699gozv9xs2k.png

SV_Target就是System Value Target,实际就是屏幕的像素,最后frag函数return的就是像素,即RGBA颜色,因此frag返回的类型就是fixed4类型。而这里return的就是_TintColor,含义就是屏幕上每一个像素点返回的都是_TintColor的颜色。

160106nxmdu0qxaqa0xytz.png

修改Color的颜色,在编辑器非运行时状态下,就能看到渲染的即刻变换。

如果要显示蜘蛛机器人的纹理,应该怎么做呢?

第一步:引入蜘蛛机器人的纹理

在Properties添加一下变量:

160152rnhjoegge6oonhki.png

变量名_MainTex,检视窗口显示“Main Texture”,类型是宽高为2次幂的纹理,默认值为空。保存下看编辑器里的变化。

160216puzqansuarxrun1q.png

在Color下就多了个设置纹理的选项,点击Texture框内的Select按钮选择Bot,即引入了蜘蛛机器人的纹理。

160245ynp9hnsmcsvm4n7c.png

第二步:结构体添加网格和纹理的UV值

UV是什么呢?UV(W) 是纹理空间中的坐标系,值域 0 到 1。这里使用 2D 纹理,因此是二维的

160320xh1paa4dtxwtzxuu.png
160320v7jht8to7sghvw7g.png


分别在appdata和v2f结构体,添加变量uv0,用于记录引入纹理的UV坐标。

第三步:添加纹理的实例化声明

_TintColor后面添加如下新的实例化:

160412fzx6r6q898rx63t3.png

sampler2D是与纹理绑定的数据容器接口,为CG/HLSL中 2D贴图的类型,相应还有sampler1D、sampler3D、samplerCUBE等格式。

第四步:结构体UV赋值

160443m2s2okooo6ok95kh.png

在vert函数中,添加上图中的语句,将获取到的网格数据上的UV信息(网格平铺成二维与纹理的一一对应),赋值给v2f结构体中。

第五步:渲染纹理

160521jzo0vhltlvoz80ee.png

将片元函数做以上修改,使用tex2D方法替代掉原来单纯返回颜色。

现在重新回到编辑器界面并运行:

160542c3fgdih1ix1ihhrd.png

蜘蛛机器人就以对应纹理显示了,从运行状态下可以发现,纹理与网格是完全一致的。

其他有特色的顶点/片元着色器效果介绍:

双纹理混合(Texture Blending - Lerp!)

160625d66o1wmmi86w1rct.png

做双纹理混合肯定需要引入两张不同的纹理,这里分别声明了Main Texture和Second Texture,然后可以通过一个_Blend_Amount参数来调节两个纹理的混合比例。接着,在片元函数部分,分别获取两个纹理对应UV的像素颜色,通过Lerp函数进行混合。

Lerp的功能是基于权重返回两个标量或向量的线性差值。具体在CG中的实现如下:

160754sayy1z95589yi08h.png

调节_Blend_Amount,就可以获得双纹理混合的显示效果:

160944rtt88tnptp66j5tz.png
161009ucwd1wwhcp2p4h1z.png


颜色渐变(Color Ramp - Texture Sample)

161030n644e333ofzvecto.png

在输入参数部分,可以看到[Header(Color Ramp Sample)],它用于在检视窗口中添加一个标签文本。这里是显示Color Ramp Sample。一般引入一个2D的纹理,都会有Tiling和Offset显示,即可以调节纹理的缩放与平移,使用了[NoScaleOffset]就会将这两个参数设置禁用,仅仅获得纹理的原始比例与平移。

161145na6hfoozmfhafffa.png

在检视窗口中的显示如上图所示。

参数_ColorRamp_Evaluation其实是获取这张渐变图的水平中心像素点的位置,从而得到该位置的像素。将主纹理的颜色与渐变纹理的颜色相乘,即可获得混合后的颜色。

161229znhj2l2nyhh26jju.png
161248pucjx4ccvop7hlij.png


颜色与颜色之间可以进行加减乘除进行混合运算。加法可以起到颜色叠加的效果,但是由于颜色值的值域为0~1,相加很容易达到1,就会颜色会愈发明亮,因此叠加建议使用乘法;减法可以进行反色处理,但是同样是值域的原因,数值达到0,颜色就很暗淡,因此要做反色建议使用除法。

纹理剔除(Texture Cutout)

161321djpqlbr3fr5tmtvu.png

在片元函数中使用了Clip方法,该方法的功能是当输入的参数小于等于0时,就会删除对应位置的像素。这里使用了一个有不同颜色分布的纹理作为剔除纹理,使用CutOut Value作为剔除参考值,当该纹理像素某位置上RGB分别减去这个CutOut Value有小于等于0的时候,这个蜘蛛机器人就会有镂空的效果。


161344ya38xi8xmm83iv2u.png
161404sfafkttftfv0sfxt.png


世界坐标-梯度(World Space - Gradient)

161439coedfrj781oedle2.png
161457xsgoznnkc003o722.png

在v2f结构体中,添加了世界坐标。而这个坐标是通过unity_ObjectToWorld,来获取每个顶点在世界空间中的坐标。在输入的部分加入了两个表示高低不同位置的颜色,片元函数中对两者根据踢动值进行了线性插值处理。最后以相乘的方式进行了像素混合,呈现出图中的画面效果。

法线挤压(Normal Extrusion)

161534jftujodyjotjjjts.png

可以看到在appdata结构体中引入了法线(Normal)。在顶点函数计算的时候,将发现xyz值乘以挤压值(Extrusion Amount),而后叠加到顶点的xyz上,这样就可以根据这个挤压值对于顶点的对象空间位置进行法线相关的偏移处理,最后可以得到“膨胀变胖”、“挤压变瘦”的有趣效果。

161600v1eup1rareih6ppq.png
161603nwaqnikttm1mqrov.png


时间相关(Time)

161645s03z3h20a330ooaw.png

_Time为Unity着色器默认载入UnityShaderVariables.cginc的变量,_Time.y表示游戏自启动开始后的时间。通过波速、波距和波频三个参数是将机器人进行波形化处理。

161707gipo5ql1p9gl10ro.png

漫反射光照(Diffuse Lighting)

161742g6pjr3zdd0kdi6rk.png
161745og2221sgndde82t2.png


漫反射光照牵涉到一些光照运算的内容,首先就需要添加Tags的LightMode为ForwardBase,基于前向的光照模式,还引入了UnityLightingCommon.cginc预定义文件来辅助光照的运算。当然,appdata结构体中不会缺少网格的法线数据。在顶点函数中,通过UnityObjectToWorldNormal获取网格对象在世界坐标中的法线值。接着,通过dot方法得到法线值与世界空间光照坐标的点积值,作为漫反射参考值。然后,将这个漫反射参考值与光照颜色相乘获得光照的漫反射颜色。最后在片元函数中将主纹理颜色与之相乘混合,得到最终的像素颜色。

其他控制Unity着色器的方法

通过动画控制(Animation Clip)

161833khpmi6p5uvivxln5.png

正如上图所示,该示例没有使用额外的代码,仅仅依靠动画片段(Animation Clip)来控制,着色器的参数值。

161854ysokjosssiswudks.png
161910huii4euucpimfjgi.png

Unity着色器依附于材质(Material),而材质需要渲染器(Renderer)使之生效。因此通过动画控制Unity着色器只要找到对应的参数,就可以像制作其他角色动画一样,让策划或美术调整着色器显示的效果。

脚本控制(Scripting)

对于程序员而言,有时候通过代码的手段似乎更为便利,Unity着色器也给这方面的需求提供了方法。也正如前面在叙述动画片段控制Unity着色器的参数,只要通过Renderer → Material → Shader就可以访问到需要的参数。因此,可以通过Get和Set的方式去获取对应参数的值,或者去修改对应参数的值。

161955sv3meommekzbmjkm.png
161957llyko551fyo7ptnp.png


大家如果意犹未尽,可以下载由Unity版PPT生成的单机应用程序和附带的部分场景的工程,进行体验。中间页面的切换通过键盘左右键进行,大多数内容使用到了UGUI。有些可滑动的部分使用鼠标拖动,还有一些内部的切换,需要使用键盘的T键,具体位置详见前文叙述。

常用Unity着色器辅助工具推荐:

1. ShaderlabVS
https://github.com/wudixiaop/ShaderlabVS
开源Visual Studio插件,VS2013、2015、2017均可使用,支持ShaderLab着色器文件的语法高亮补全,支持.shader .cginc .glslinc .compute .cg .hlsl等文件类型。

2. Shader Unity Support
https://marketplace.visualstudio ... itySupport#overview
微软VS插件商店推荐工具,支持多种Unity着色器文件的代码补全。

3. ShaderlabVSCode
https://assetstore.unity.com/pac ... aderlabvscode-94653
如果你使用Mac系统,并安装了Visual Studio Code,这款功能强大的ShaderLab辅助工具会是不错的选择。

4. Unity Shader Intellisense
https://assetstore.unity.com/pac ... -intellisense-28508
这款基于Unity Editor开发的Shader编写插件,可以帮助你无论在那种开发平台上,都能便利的进行着色器代码的开发工作。

结语
相信学会修改自己的第一个Unity着色器是件很酷的事情。希望通过这篇文章的学习,帮助正在使用Unity进行着色器开发的朋友们。我们还将继续分享Unity着色器训练营-第二期直播信息等内容在Unity官方中文社区(unitychina.cn),请保持关注!

大家想要的文件回复可见哦!

davinci8,如果您要查看本帖隐藏内容请回复

转载请注明:来自Unity官方中文社区(forum.china.unity3d.com) 着色器, 开发, Unity锐亚教育

锐亚教育 锐亚科技 unity unity教程