Shader第一讲——从Unlit说起

大家好,我是菜鸟在线的然哥。从今天开始,我将带大家学习课程的重点之一——Shader。Shader中文名“着色器”,是三维计算机图形学最核心的知识点,也是众多Unity老手难啃的一块生肉。如果你已经学到了本课程,恭喜你,你已经开始要往Unity中高级程序员发展了。在学习的过程种会接触到很多数学公式,物理公式,还有可能伴随着很多困惑,迷茫,对前面掌握知识点从自信到质疑,从质疑到顿悟,这些都是很正常的事情。无论遇到什么问题,不要放弃,请相信学完本阶段的课程,你将会有所收获。


首先学习的第一步,我们当然是要创建一个着色器
在下面的图我们可以看到,在UNITY的PROJECT窗口我们右键鼠标,就能看到创建着色器的按钮,我们这里要创建的是Unlit,不受灯光影响的着色器。


为啥要创建这个呢?
因为他是按照顶点与片段着色器的格式写出来的,而我们要学的正是顶点与片段着色器。

Unity的着色器有以下三种:

  • Fixed Function Shader 固定片段着色器
  • Surface Shader 表面着色器
  • Vertex&Fragment Shader 顶点与片段着色器

顶点与片段着色器可以把物体整个渲染管线流程通过代码的方式表现出来,可以说是最接近显卡语言的着色器写法。这里提到渲染管线,其实他是3D图形学的一个非常重要的概念,即一个3D物体如何绘制到屏幕上的显卡渲染流水线的过程。

不要以为这些都是理所当然的事情,没有任何功能是计算机理所当然会的,包括一个3D物体如何显示在屏幕上,是要经过一连串复杂的计算得到的画面效果,而我们学Shader的目的,其实就算去创作,去改变这种效果。

Shader其实就算改变物体在屏幕上的形状和颜色的技术

来看看Shader的真面目,双击进去刚刚创建的shader进来,可以看到一连串的代码,现在这些代码还有点太难了,没关系,我把它简化成下面的代码了,把下面代码拷进去替换即可。

    Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            float4 vert (appdata_base v):POSITION
            {
                return mul(UNITY_MATRIX_MVP,v.vertex);
            }
            
            fixed4 frag () : COLOR
            {
                return fixed4(1,0,0,1);
            }
            ENDCG
        }
    }
    FallBack"Diffuse"
}

我们来一行一行解读一下每句的意思。首先这里可以看到他有四个关键字。

  • Shader
  • Properties
  • SubShader
  • Pass

我们来分开解读一下:

首先是Shader,后面双引号的内容是他在材质球中的位置,首先是根目录,然后是子目录。Unlit/NewUnlitShader就算去材质球找Unlit然后选NewUnlitShader就能选到本shader。

接下来轮到Properties,他记录的是Shader中的变量,这些变量可以表现在Unity中,也可以在Shader里面参与计算,也就是Unity和Shader进行交互的工具。具体变量会在下一篇文章中提到,这里给个截图,大家去体会体会。


然后是SubShader,他是Shader的子着色器,一个Shader可以有多个子着色器,我们会把画面效果写在子着色器里面,每一个子着色器写一种效果,但是他们不能同时执行,一次只有一种效果能实现。

根据上图我们可以看到,子着色器可以有很多个,但是我们具体用哪个呢?其实他们在这里是逻辑或的关系,也就是前面如果有能执行的,后面就不会执行。也就是多种画面效果,只要前面有一种能执行的了,后面的都不会执行。特别适用于选择显卡,比如说我有种画面效果,一个显卡显示不了,没关系,我后面还有很多备用的,一个不行用第二个,再不行用第三个。如果都不行,看到有个Fallback吗?他叫回滚,也就是说所有都不行的情况下调用回滚。通常我们喜欢把画面效果好的写在最上面,然后依次把要求高的SubShader写下来,最后写个回滚,如果全部执行不了就执行回滚,也是最后的保底,通常写最简单通用的着色器。

那SuaShader里面主要有什么呢?

他主要有标签和一些子着色器的关键字还有Pass通道。这里案例里面标签有渲染类型,Opaque表示不透明,就是说在这里把他和不透明物体归到同一类。LOD表示细节层次,这里表示着色器的LOD是100,那么系统的LOD低于这个值,他将不渲染,这些在后面的课程我们会一一提到。

接着我们讲到最后一个关键字,Pass通道。可以从上图看到,他和Subshader一样可以有很多个,但是他们却不是SubShader那样只有一个生效,他们会共同影响最后的画面效果。通常Pass通道写得越多,显示出来的画面效果越多。而我们的CG语法主要就是在Pass通道展开。

什么你问我CG语法是啥???

CG(Nvida公司开发的c for graphics计算机图形编程C语言超集):CG程序可以根据运行时的需要或者事先编译成GPU汇编代码;
HLSL(基于DirectX的High Level Shading Language):只能供微软的Direct3D以及XNA使用,移植性差;
GLSL(基于OpenGL的OpenGL Shading Language):供OpenGL使用,具有良好的跨平台和移植特性。
CG和HLSL的语法类似。GLSL语法体系自成一家。
CG是一个可以被OpenGL和Direct3D广泛支持的图形处理器编程语言。CG语言和OpenGL、DirectX并不是同一层次的语言,而是OpenGL和DirectX的上层,即CG程序是运行在OpenGL和 DirectX标准顶点和像素着色的基础上的。

咳咳,简单说,CG就是C For Graphic,是Nvidia的一套显卡语言,他可以像雇佣军一样嵌套在DX和OpenGL里面使用,是移植性非常好的显卡语言。我们的ShaderLab是啥语言?他是隶属于DX的,也就是说他其实是在DX基础上的一个框架,那么他内部也是有CG这个雇佣军的。怎么看出来的?从Pass通道里面的CGPROGRAM开始,我们就看到CG语法的影子了。

注意从这里开始变成了CG语法,其实就是C语言,他嵌套在ShaderLab里面,那么上面的其实是ShaderLab的框架。细心的同学会发现,上面从来没有出现过分号,下面开始出现分号了,这也是他俩区别的标识

那我们来看CG语法:

 #pragma vertex vert
 #pragma fragment frag

这两句主要是用你自己写的顶点函数和片段函数去替换系统自带的顶点函数和片段函数。

  • 顶点函数是啥?现在和你说太多没用,你记住他是负责处理物体形状的。
  • 片段函数呢?他是用来负责处理物体在屏幕的颜色的。

这里vert和frag是你自己定义的名字,可以改变,但是改变的话,下面必须声明两个同名的函数,否则编译器算你耍赖,要替换的函数找不到,就会报错。

float4 vert (appdata_base v):POSITION
{
    return mul(UNITY_MATRIX_MVP,v.vertex);
}

这个就是你要实现的顶点函数,首先我们返回的顶点要是float4,4唯的向量,函数名必须和上面的一样,appdata_base 是CPU传过来的数据块,也就是一次Draw Call传过来的数据,里面有物体的法线,物体的本地顶点,物体的贴图信息等等,最后的是语义,这里表示返回值是用来显示形状的,不是用来显示颜色的。为啥要有语义呢?因为返回的float4可以做任何事情,可以用来处理形状,也可以表示颜色,你给他语义,他就知道他要用来做啥了。那么我们这个函数什么时候调用呢?不需要关心,我们上面写好替换了,程序会自动调用。

如何理解mul(UNITY_MATRIX_MVP,v.vertex)这个代码呢?这个后面渲染管线我们会讲,这里其实是把物体的顶点坐标通过3D矩阵计算换算成屏幕投影的坐标,显示在屏幕上。

接下来是

fixed4 frag () : COLOR
{
    return fixed4(1,0,0,1);
}

这个是我们实现的片段函数,返回的是fixed4,和上面float有点不同,fixed也是浮点,不过取值范围在-2-2,为啥要用fixed呢?首先我们知道这是片段函数,是用来处理颜色的。颜色有RGBA四个值,刚好对应fixed4?那为啥用fixed不用float呢?因为显卡的显存很珍贵,他发现颜色都是从0-1(其实把0~255做了个映射)的,那么就不需要用字节比较大的float.后面是COLOR语义,表示返回值是用来显示颜色的,不是处理形状的。

如何理解return fixed4(1,0,0,1);这个代码呢?其实就算返回一个颜色值,射回画笔工具里面的值就是,255,0,0,255,也就是红色,最后一个255表示透明度里面的不透明,与之相反的0就代表全透明。

最后最后有个ENDCG说明CG语言到底了,执行完了,回到ShaderLab的怀抱。

那我们这个Shader的作用其实就是,显示一个正常的物体,形状没有改变,但是颜色会变色纯色的红色,因为最后返回的是1,0,0,1.这是我们写的第一个着色器,虽然没有什么用,但是我们后面会在这个简单的着色器基础上进行扩展,做出各种不同效果的着色器。

下一章节我们会学习如何UnityShader中变量的知识,并学习如何通过变量改变物体的形状或颜色。


(程序是个永无止境的东西,必须每天保持学习)

版权所有,违责必究

菜鸟在线 林老师 2017.8.14

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容