公司动态
Shader魔法揭秘:3D画面背后的神奇代码
引子Shader听起来好难啊嗨小朋友今天我们要聊一个听起来很酷但很吓人的东西——叫做Shader。Shader是什么——别慌——我用你能听懂的话来说Shader——就是告诉电脑’这个东西看起来该长什么样’的一张纸条打个比方你妈妈做菜要看菜谱**画家画画要看参考图****电脑画3D画面——也要看一张说明书这张说明书——就叫ShaderShader告诉电脑“这个球是红色的”“这个盒子有花纹”“这个水面在波动”“这个火焰在闪烁”没有Shader——电脑就不知道该怎么画你的游戏画面——屏幕上什么都没有今天——我们就来看一份真实的Shader代码——**一行一行拆开——用最简单的话解释——保证你能看懂一、准备工作先看整体骨架先来看看我们今天要研究的Shader——一个给物体染色 贴图的简单ShaderShader MyShader/Simple { Properties { _MainTex (贴图, 2D) white {} _Color (颜色, Color) (1, 1, 1, 1) } SubShader { Tags { RenderType Opaque } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc sampler2D _MainTex; float4 _Color; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; v2f vert(appdata v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; } fixed4 frag(v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv); return col * _Color; } ENDCG } } FallBack Diffuse }看到这一堆代码——你可能吓一跳——“这什么鬼”**别怕——我们把它想象成一个魔法配方——一层一层拆开——你会发现它其实很简单二、第一层这个魔法叫什么名字Shader MyShader/Simple这一行就像给你的魔法起名字**我这个Shader——名字叫MyShader/Simple就像你写作业要写上名字一样**电脑要知道这是哪个Shader——才能用它想象一下妈妈的菜谱本子里——有一页写着红烧肉的做法——这就是这道菜的名字。**这里的MyShader/Simple——就是我们这份Shader的名字。三、第二层可以调节的魔法材料Properties { _MainTex (贴图, 2D) white {} _Color (颜色, Color) (1, 1, 1, 1) }这里叫Properties——中文是属性——就是可以调节的材料**。想象你在做菜一份菜谱说“放多少盐、放多少糖、放什么肉”**这些放多少、放什么**——就是可以调节的每次做菜可以调“今天多放点糖”、“今天用鸡肉”Shader也一样_MainTex——“用什么图片当花纹”**就像用什么肉_Color——“要染成什么颜色”**就像放多少糖)具体来看每一行第一行_MainTex (贴图, 2D) white {}**_MainTex——**这个材料的名字电脑内部叫它这个**贴图——**在Unity里显示的名字给美术姐姐看的**2D——**说这是一张平面的图片**white——**默认给一张白色的图翻译成人话**我需要一张图片作为花纹——默认是白色的。第二行_Color (颜色, Color) (1, 1, 1, 1)**_Color——**颜色材料的名字**颜色——**给美术看的名字**Color——**类型是颜色**(1, 1, 1, 1)——**默认是白色 完全不透明这四个数字什么意思**第1个1——**红色的多少1最红**第2个1——**绿色的多少1最绿**第3个1——**蓝色的多少1最蓝**第4个1——**透明度1完全看得见0完全看不见当红、绿、蓝都是1**——混合出来就是白色**这就是Properties——就是可以调节的材料清单**。四、第三层具体的做法SubShaderSubShader { Tags { RenderType Opaque } // ... }**SubShader——就是具体的做法。为什么可以有好几个SubShader——因为你家有个高级烤箱**——做法可以复杂点、味道更好你奶奶家只有普通锅**——做法要简单点、能做就行一份好的菜谱——要为不同的厨房准备不同做法Shader也一样**好电脑——**用复杂的做法画面漂亮**旧手机——**用简单的做法能跑起来就行Tags给这份做法贴个标签Tags { RenderType Opaque }**Tags——就是标签。RenderType Opaque翻译这个物体是不透明的Opaque的意思就是不透明**。这个标签告诉电脑我不是玻璃、不是水——我是实心的东西**。电脑收到这个标签就知道“哦那我按’画不透明物体’的方式来处理你”。五、第四层真正的魔法配方PassPass { CGPROGRAM // ... 这里面就是真正的魔法 ENDCG }Pass——中文叫一遍——意思是画一遍这个物体。为什么叫一遍——因为**有些复杂物体——要画好几遍才漂亮比如描边效果——先画一遍黑色的外壳——再画一遍正常的物体**——就有描边了**每一遍——就是一个Pass**我们这个简单Shader——只有一个Pass——画一遍就够了。CGPROGRAM 和 ENDCG——“魔法开始和魔法结束”**CGPROGRAM——就像说魔法开始了**ENDCG——就像说魔法结束了**中间的所有东西——都是真正的魔法代码。六、魔法内部告诉电脑两件事**打开魔法的盒子——里面有两个最重要的魔法师#pragma vertex vert #pragma fragment frag这两行是什么意思**vertex——**中文是顶点就是物体的一个个角**fragment——**中文是片段就是屏幕上的一个个小点#pragma vertex vert——顶点魔法师叫vert**#pragma fragment frag——片段魔法师叫frag**这两个魔法师分工不同——我们分别来看看。七、魔法师1号顶点魔法师vertv2f vert(appdata v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; }这个魔法师的工作——处理物体的每一个角。想象你的球——是由很多个三角形拼起来的**每个三角形有3个角**每个角——就是一个顶点顶点魔法师的任务把这些顶点从3D世界变到屏幕上****。打个比方你的球——在游戏世界里比如说位置(10, 5, 3)但屏幕是平的——只有左右和上下没有前后必须有人把3D位置变成屏幕位置**——**这就是顶点魔法师的工作一行一行看v2f o;——创建一个结果盒子等下要把结果放进去o.pos UnityObjectToClipPos(v.vertex);——**这是最重要的一行翻译**v.vertex——**这个顶点原本的位置游戏世界里的3D位置UnityObjectToClipPos(...)——一个神奇的转换器——Unity帮我们写好的函数它能把游戏世界的3D位置——变成屏幕上的2D位置就像有一台神奇的相机——它站在你选的角度——咔嚓一下——把3D世界拍成一张平面照片o.uv v.uv;——把贴图坐标传下去uv是什么——它告诉电脑“这个顶点要贴上图片的哪一部分”**。打个比方你有一张世界地图****你的球体表面——**要贴上这张地图球上的每个点——要对应地图上的某个位置**这个对应关系——**就叫uv顶点魔法师说“记住这个uv等下画颜色的时候用得到”return o;——把结果送出去顶点魔法师的工作到这里结束——它告诉电脑“这个顶点在屏幕上的位置在这里”“它要贴图片的这一块”八、魔法师2号片段魔法师fragfixed4 frag(v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv); return col * _Color; }片段魔法师的工作——处理屏幕上的每一个小点你的手机屏幕——其实是由很多很多小方块组成的**每个小方块——叫一个像素每一个像素——要有一个颜色片段魔法师——就是决定每个像素颜色的人一行一行看fixed4 col tex2D(_MainTex, i.uv);翻译tex2D(...)——从图片里取颜色的函数**_MainTex——**我们的贴图还记得Properties里的吗**i.uv——**顶点魔法师传下来的贴图坐标这行代码在做什么看这个像素的uv坐标去贴图上找对应位置的颜色把那个颜色取出来存到col里打个比方你在一张世界地图**上你想知道北京是什么颜色找到北京的uv坐标看地图上那个位置——是红色**好——**这里就是红色fixed4是什么——它是一种能装4个数字的盒子——装 红、绿、蓝、透明度 四个值。return col * _Color;——返回最终颜色这一行——把两个东西乘起来**col——**从贴图取的颜色**_Color——**Properties里设的颜色为什么要乘起来打个比方贴图是一张白色的猫咪**图你在Properties里设了红色**白色 × 红色 红色的猫咪相当于给贴图染色**如果Properties设的是蓝色**白色的猫咪 × 蓝色 蓝色的猫咪这就是染色的魔法——通过一个乘法就能让一张图变成各种颜色片段魔法师说“这个像素就用这个颜色”九、FallBack Diffuse——“备胎小哥”FallBack Diffuse这一行——是备胎小哥——Shader的救命机制**。它在说什么如果我这个Shader有些事情做不到**——请让Diffuse这个Shader来帮我做。打个比方**你去学校演出——要跳舞但你不会翻跟头**老师说没事你不会翻跟头的部分——让李小明上来翻**李小明——就是你的备胎Shader也一样**我们这个Shader——只会画物体本身但游戏里还需要投射影子——我们没写这个功能FallBack Diffuse——就是说影子的部分让Diffuse来帮我画没有这一行会怎样——你的物体没有影子**加上这一行——电脑会自动去Diffuse里借投影的代码——你的物体就有影子了十、完整流程一个小球的魔法之旅让我们把整个过程串起来——看看一个小球在游戏里是怎么被画出来的第一步Unity打开你的Shader——看到名字MyShader/Simple——“好我要用这个”第二步读取Properties看看用户在Material里设了什么哦贴图是苹果.jpg颜色是红色(1, 0, 0, 1)第三步顶点魔法师上场拿到小球的每一个顶点算出它们在屏幕上的位置传递uv坐标第四步片段魔法师上场对屏幕上小球覆盖的每一个像素**从苹果.jpg里取对应的颜色和红色相乘得到最终颜色第五步画到屏幕上第六步如果需要影子用Diffuse的投影代码画影子**你的红色苹果小球——就出现在屏幕上了十一、举一反三小小的变化能玩出很多花样**理解了这个基础Shader——我们能玩很多花样花样1让它变亮把最后一行改成return col * _Color * 2; // 乘以2结果颜色变成两倍亮——物体像发光一样花样2让它闪烁改成return col * _Color * (sin(_Time.y) 1); // 用时间的sin值_Time是Unity自动给你的时间变量——sin函数会不断变化——物体就会呼吸一样闪烁**花样3让它反色改成return 1 - col * _Color; // 用1减去颜色结果红色变成青色、黑色变成白色——像照片的负片**看到了吗——Shader其实是给电脑写数学**——**只要你会一点数学——就能创造各种视觉效果十二、总结Shader没那么难**回头看看——我们刚才做了什么Shader 告诉电脑怎么画东西的配方Properties “可以调节的材料”SubShader “具体的做法”Pass “画一遍”顶点魔法师 “算出每个角在屏幕上的位置”片段魔法师 “算出每个像素的颜色”FallBack “备胎小哥我不会的它帮我”Shader不是神秘的黑魔法——它是一份写给电脑的说明书**每一行代码——都在说电脑你这样做“用这张贴图”“和这个颜色相乘”“变成屏幕位置”“输出到屏幕上”就这么简单结语一份魔法配方的温暖从一个看起来吓人的Shader代码到把它拆解成配方、“魔法师”、备胎小哥的小学生语言到看清楚每一行在做什么事情——Shader其实是这样一个东西它是给电脑的画画说明书——告诉它这个物体长什么样**Properties——“可以调节的颜料”SubShader——“具体的画法”Pass——“画一遍的动作”顶点魔法师——“把3D变到屏幕”片段魔法师——“给每个像素上色”FallBack——“我不会的让别人帮我”它像一个精心设计的画画流程每一步都清晰每一步都有意义每一步都在为最终画面服务它有小小的Properties——让美术姐姐能调颜色调贴图。它有魔法师的分工——顶点管位置、片段管颜色。它有贴心的FallBack——不会的事情有人帮忙。它有清晰的结构——从名字到属性到画法一目了然。下次当你打开一个Shader文件——请记得那看起来密密麻麻的代码——其实是一份给电脑的说明书**一份画画的配方**一份每一行都在做一件具体事情**的清单每一行float4、return、tex2D——都不神秘——都在说一件很具体的小事。每一个Property——都是一个可以调节的东西。每一个Pass——都是画一遍的动作。每一个函数——都是魔法师的一个小工作。Shader不是天才才能写的——是每个愿意慢慢理解的小朋友都能学会的每一次你多懂一行代码——就多掌握一点给电脑画画的能力。每一次你改动一个数字——就在创造属于自己的视觉魔法**。每一次你写出一个Shader——都是在和电脑聊天**——告诉它画一张这样的画。这——就是Shader的真相——不是高深难懂的黑魔法——而是一份写给电脑的、可以慢慢学、慢慢懂、慢慢玩的画画说明书——是每一位小朋友、每一位新手、每一位好奇的开发者都能一步步掌握的友好的魔法。它不神秘——每一行都能拆开解释。它不复杂——归根到底就是位置颜色两件事。它不遥远——**只要愿意——从今天就能开始学。在这份魔法配方的背后是电脑图形学的扎实基础**是Unity为你提供的贴心工具**是等待你去探索的创造力空间**是从看不懂到能自己写的成长路径**这——就是Shader——一份等你去理解、去玩耍、去创造的友好的魔法**。每一位曾经看代码就头疼的小朋友——只要愿意慢慢拆开——都能像今天这样看懂了。每一份看似复杂的Shader——归根到底就是我们今天讲的这些简单的道理**。每一位怀着好奇心的学习者——都能在Shader的世界里找到自己的乐趣**。这——就是Shader送给每一位小小学习者的、最温柔也最鼓舞人心的友好礼物。 ✨