当前位置: 首页 > news >正文

魔法水晶Shader全解析:从呼吸到发光

引子:从"简单"到"进阶",向前一大步

嗨,小朋友!

上次我们看了一个"简单的Shader"**——只会做两件事

  • 贴图
  • 染色

你可能觉得:"这也太简单了吧!——游戏里那些炫酷的火焰、流动的水面、发光的宝剑——是怎么做出来的?"

答案是——它们的Shader比我们上次看的复杂得多**!

但别怕——“复杂"不代表"看不懂”

  • 它只是"用了更多小技巧"
  • **每个小技巧——都能拆开慢慢看
  • 拆开之后——你会发现"哦,原来是这样啊!"

今天——我们要看一份真正的进阶Shader——一个会"呼吸、发光、摆动"的"魔法水晶"Shader

**看完这个——你就能理解"游戏里那些炫酷特效的原理"**了!

一、先看看今天的"主角"

今天的Shader要做出这样的效果

  • 🌊 会摆动——像水晶在轻轻晃动
  • ✨ 会发光——边缘有一圈光晕
  • 💗 会呼吸——亮度一亮一暗地变化
  • 🌈 有花纹——表面有滚动的能量纹路

听起来很酷对不对?——这就是一个"魔法水晶"Shader

完整代码在这里(先别怕,我们会一点点拆开看):

Shader "MyShader/MagicCrystal" { Properties { _MainTex ("主贴图", 2D) = "white" {} _NoiseTex ("能量纹路", 2D) = "white" {} _BaseColor ("基础颜色", Color) = (0.2, 0.5, 1.0, 1) _RimColor ("边缘光颜色", Color) = (0.5, 0.8, 1.0, 1) _RimPower ("边缘光强度", Range(0.1, 10)) = 3.0 _WaveSpeed ("摆动速度", Range(0, 5)) = 1.0 _WaveStrength ("摆动幅度", Range(0, 0.5)) = 0.1 _ScrollSpeed ("纹路滚动速度", Range(0, 5)) = 1.0 _PulseSpeed ("呼吸速度", Range(0, 5)) = 2.0 } SubShader { Tags { "RenderType" = "Transparent" "Queue" = "Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; sampler2D _NoiseTex; float4 _BaseColor; float4 _RimColor; float _RimPower; float _WaveSpeed; float _WaveStrength; float _ScrollSpeed; float _PulseSpeed; struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1; float3 viewDir : TEXCOORD2; }; v2f vert(appdata v) { v2f o; // 顶点摆动 float wave = sin(_Time.y * _WaveSpeed + v.vertex.y * 3) * _WaveStrength; v.vertex.x += wave; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.worldNormal = UnityObjectToWorldNormal(v.normal); float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.viewDir = normalize(_WorldSpaceCameraPos - worldPos); return o; } fixed4 frag(v2f i) : SV_Target { // 主贴图 fixed4 mainCol = tex2D(_MainTex, i.uv); // 滚动的能量纹路 float2 scrollUV = i.uv + float2(_Time.y * _ScrollSpeed, 0); fixed4 noiseCol = tex2D(_NoiseTex, scrollUV); // 边缘光 float rim = 1 - saturate(dot(i.worldNormal, i.viewDir)); rim = pow(rim, _RimPower); // 呼吸 float pulse = (sin(_Time.y * _PulseSpeed) + 1) * 0.5; pulse = lerp(0.5, 1.0, pulse); // 合成最终颜色 fixed3 finalColor = mainCol.rgb * _BaseColor.rgb; finalColor += noiseCol.rgb * 0.3; finalColor += _RimColor.rgb * rim; finalColor *= pulse; return fixed4(finalColor, mainCol.a * _BaseColor.a); } ENDCG } } FallBack "Diffuse" }

**看到这一大堆——你可能又开始晕了——别慌

深呼吸——我们一块一块拆开看——每一块都不难

二、Properties:这次的"材料清单"更丰富了

Properties { _MainTex ("主贴图", 2D) = "white" {} _NoiseTex ("能量纹路", 2D) = "white" {} _BaseColor ("基础颜色", Color) = (0.2, 0.5, 1.0, 1) _RimColor ("边缘光颜色", Color) = (0.5, 0.8, 1.0, 1) _RimPower ("边缘光强度", Range(0.1, 10)) = 3.0 _WaveSpeed ("摆动速度", Range(0, 5)) = 1.0 _WaveStrength ("摆动幅度", Range(0, 0.5)) = 0.1 _ScrollSpeed ("纹路滚动速度", Range(0, 5)) = 1.0 _PulseSpeed ("呼吸速度", Range(0, 5)) = 2.0 }

**上次只有2个材料——这次有9个

打个比方

  • 上次做菜——只放盐、糖
  • 这次做菜——盐、糖、辣椒、酱油、醋、姜、蒜、料酒、花椒——做一道大餐**!

看看每个材料

  • _MainTex——主贴图(水晶本身的样子)
  • _NoiseTex——能量纹路(会滚动的花纹)
  • _BaseColor——基础颜色(水晶是什么色的)
  • _RimColor——边缘光颜色(边上光晕什么色)
  • _RimPower——边缘光有多亮
  • _WaveSpeed——摆得多快
  • _WaveStrength——摆得多厉害
  • _ScrollSpeed——纹路滚多快
  • _PulseSpeed——呼吸多快

看到Range(0.1, 10)了吗?

  • 这个意思是"可以在0.1到10之间调"**
  • 在Unity里——会显示成一个滑块——美术姐姐可以拖着调

这就是Shader的贴心之处——把所有"可以调"的东西都摆到Inspector里**——美术不用改代码就能调效果

三、SubShader的"新东西":让物体半透明

SubShader { Tags { "RenderType" = "Transparent" "Queue" = "Transparent" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off // ... }

**这里有几个新东西——都和"透明"有关

新东西1:"RenderType" = "Transparent"

上次是Opaque(不透明)——这次是Transparent(透明)

告诉Unity:**"我是透明物体——请按透明物体的方式对待我"

就像贴标签

  • 上次的标签是"实心物体"**
  • 这次的标签是"半透明水晶"**

新东西2:"Queue" = "Transparent"

这是"渲染队列"——决定"先画谁后画谁"

打个比方

  • 画画的时候——"先画背景,再画前面的东西"
  • 不然前面的物体会被背景盖住
  • Queue——就是"画的顺序"

Transparent队列——排在后面——**保证先把不透明物体画完——再画透明物体

为什么要这样?——因为透明物体要"混合"背景色——必须先有背景才行

新东西3:Blend SrcAlpha OneMinusSrcAlpha

这就是"透明混合"的魔法

**看起来很吓人——别慌——它的意思是

“把我的颜色 和 后面的背景 混合起来”

具体怎么混合?

  • SrcAlpha——我的透明度是多少(比如0.5,就是半透明)
  • OneMinusSrcAlpha——1减去我的透明度(0.5,就是背景占一半)

混合公式

最终颜色 = 我的颜色 × 0.5 + 背景颜色 × 0.5

结果就是我一半、背景一半——看起来半透明

这就是玻璃、水、透明水晶的效果

新东西4:ZWrite Off

“ZWrite"是"深度写入”——**关掉它——让别的物体能透过我被看见

打个比方

  • ZWrite On——“我很霸道,我挡住谁谁就看不见”
  • ZWrite Off——“我很客气,你们随便被看见”

透明物体一般都要ZWrite Off——不然透明后面的物体会被"莫名挡住"

新东西5:Cull Off

“Cull"是"剔除”——“要不要剔除背面”**。

普通物体——只画正面(Cull Back——背面看不到反正

透明水晶——**能看到里面——内外都要画(Cull Off

这三个设置Blend + ZWrite Off + Cull Off——就是"半透明物体的标配"**!

四、顶点魔法师升级:让物体摆动!

v2f vert(appdata v) { v2f o; // 顶点摆动 float wave = sin(_Time.y * _WaveSpeed + v.vertex.y * 3) * _WaveStrength; v.vertex.x += wave; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.worldNormal = UnityObjectToWorldNormal(v.normal); float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.viewDir = normalize(_WorldSpaceCameraPos - worldPos); return o; }

**这次的顶点魔法师——比上次多了几个新技能

新技能1:摆动魔法

float wave = sin(_Time.y * _WaveSpeed + v.vertex.y * 3) * _WaveStrength; v.vertex.x += wave;

**这两行——就让水晶摆动起来了

让我们拆开看

_Time.y是什么?——Unity送给你的"时间"变量

  • **游戏一直在跑——时间一直在增加
  • _Time.y就是"现在过了多少秒"**

sin(...)是什么?——数学里的"正弦函数"

  • 它的值——-1到1之间来回摇摆**
  • 像一个"来回荡的秋千"**
  • 时间不断变化——sin的值不断在-1到1之间跳

打个比方

  • 想象一个钟摆**——**左右左右不停摆
  • **左边最远——值是-1
  • **中间——值是0
  • **右边最远——值是1
  • **sin就是这个钟摆!

v.vertex.y * 3是什么?——“位置越高,摆得越不一样”

  • 让每个位置的顶点——摆动稍微不一样
  • 不然整个物体就像"僵尸一样一起动"**——很假

* _WaveStrength——“摆多厉害由你决定”

  • **_WaveStrength = 0.1——摆一点点
  • **_WaveStrength = 0.5——摆很大

v.vertex.x += wave;——把这个"摆动值"加到x坐标上

  • 每一帧——顶点位置稍微不一样
  • 看起来就像——水晶在左右摆动

这就是"顶点动画"的魔法——通过修改顶点位置,让物体"动起来"

很多游戏里的旗帜、草、水面、果冻**——都是用这个原理做的

新技能2:算出"世界法线"

o.worldNormal = UnityObjectToWorldNormal(v.normal);

"法线"是什么?——它是"每个顶点朝哪个方向"**:

打个比方

  • 想象你在墙上贴一张贴纸
  • 贴纸的"正面"朝哪个方向?
  • "离开墙"的方向——这就是"法线"

球的每一个点——都有一个"法线"——"远离球心"的方向

为什么要算这个?——用来做"边缘光"**(下面会用到)!

UnityObjectToWorldNormal(...)——**Unity帮我们写好的函数——把"模型自己坐标系的法线"变成"游戏世界里的法线"

新技能3:算出"看的方向"

float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.viewDir = normalize(_WorldSpaceCameraPos - worldPos);

“viewDir"是"视线方向”——“摄像机在看这个点的方向”

打个比方

  • **你的眼睛——**摄像机
  • **一个物体上的点
  • **从物体指向你眼睛——**就是"视线方向"

这两行的意思

  • **worldPos——**这个顶点在游戏世界里的位置
  • **_WorldSpaceCameraPos——**摄像机的位置(Unity送的)
  • **两者相减——**就是"看向摄像机的方向"

为什么要算这个?——还是为了"边缘光"**!

五、片段魔法师升级:颜色的"大合成"!

fixed4 frag(v2f i) : SV_Target { // 主贴图 fixed4 mainCol = tex2D(_MainTex, i.uv); // 滚动的能量纹路 float2 scrollUV = i.uv + float2(_Time.y * _ScrollSpeed, 0); fixed4 noiseCol = tex2D(_NoiseTex, scrollUV); // 边缘光 float rim = 1 - saturate(dot(i.worldNormal, i.viewDir)); rim = pow(rim, _RimPower); // 呼吸 float pulse = (sin(_Time.y * _PulseSpeed) + 1) * 0.5; pulse = lerp(0.5, 1.0, pulse); // 合成最终颜色 fixed3 finalColor = mainCol.rgb * _BaseColor.rgb; finalColor += noiseCol.rgb * 0.3; finalColor += _RimColor.rgb * rim; finalColor *= pulse; return fixed4(finalColor, mainCol.a * _BaseColor.a); }

这次片段魔法师——做了"四件事"——每一件都很酷

事情1:取主贴图颜色(老朋友)

fixed4 mainCol = tex2D(_MainTex, i.uv);

**这个我们上次学过——从贴图上取颜色

事情2:滚动的能量纹路(新魔法!)

float2 scrollUV = i.uv + float2(_Time.y * _ScrollSpeed, 0); fixed4 noiseCol = tex2D(_NoiseTex, scrollUV);

"让贴图动起来"的魔法

核心i.uv + float2(_Time.y * _ScrollSpeed, 0)

  • 原来的uv——固定的位置
  • 加上"时间×速度"——位置一直在移动

打个比方

  • 你在窗户外——**看外面的风景
  • **窗户不动——**风景不动
  • **窗户往右移——**风景就"往左滚过"你的视线!

Shader里

  • uv不变——贴图不动
  • uv随时间加一个值——贴图滚动

结果能量纹路一直在物体表面"流动"——像水晶里能量在涌动

这个技巧——在游戏里用得超级多

  • 传送带的效果
  • 能量护盾的花纹
  • 瀑布的水流
  • 火焰的动态

事情3:边缘光(最酷的魔法!)

float rim = 1 - saturate(dot(i.worldNormal, i.viewDir)); rim = pow(rim, _RimPower);

**这两行——做出了"边缘发光"的效果——就是那种"物体边上有一圈光晕"的效果

让我们拆开看

dot(i.worldNormal, i.viewDir)是什么?

  • **dot——**数学里的"点积"(不用管公式,只要知道它的意义)
  • 它算的是"两个方向有多像"**

具体意义

  • 法线朝着摄像机——dot值≈1
  • 法线垂直摄像机——dot值≈0(这就是"边缘")
  • 法线背对摄像机——dot值≈-1

1 - dot——反过来

  • 中间的地方(法线朝摄像机)——rim值≈0(不发光)
  • 边缘的地方(法线垂直摄像机)——rim值≈1(发光!)

这就是"边缘光"的原理

打个比方

  • **想象一个球
  • **正中间——**你看到的是"球的正面"——很清楚
  • **球的边上——**几乎是"侧着的"——**你只能勉强看到

**“越侧的地方”——**就"越是边缘"——就"越亮"——这就是边缘光

pow(rim, _RimPower)——让边缘光更集中

  • **_RimPower = 1——**光很散
  • **_RimPower = 5——**光只在很窄的边缘
  • **_RimPower = 10——**光只在最边最边

**很多游戏里"发光的水晶、护盾、幽灵"——都是用这个原理

事情4:呼吸效果(可爱的魔法!)

float pulse = (sin(_Time.y * _PulseSpeed) + 1) * 0.5; pulse = lerp(0.5, 1.0, pulse);

又见到我们的老朋友sin——这次让物体"一亮一暗"地呼吸**。

sin(_Time.y * _PulseSpeed)

  • 值在-1到1之间

+1变成0到2之间
* 0.5变成0到1之间

lerp(0.5, 1.0, pulse)

  • lerp是"在两个数之间平滑过渡"
  • 让pulse从"0到1"——变成"0.5到1"
  • 保证物体永远不会完全变黑**——最暗也是"一半亮"

结果亮度在"一半亮"到"完全亮"之间来回变化——"呼吸"**!

大合成:把所有效果混在一起!

fixed3 finalColor = mainCol.rgb * _BaseColor.rgb; finalColor += noiseCol.rgb * 0.3; finalColor += _RimColor.rgb * rim; finalColor *= pulse;

**四行代码——做四件事

第一行主贴图 × 基础颜色——“给贴图染色”
第二行加上0.3的能量纹路——“加入流动的花纹”
第三行加上边缘光——“边缘发光”
第四行乘以呼吸值——“一亮一暗”

打个做菜的比方

  • 先炒主菜**(主贴图×基础颜色)
  • 加点调料**(能量纹路)
  • 撒点装饰**(边缘光)
  • 最后大火收汁**(呼吸变亮暗)

四步下来——一份炫酷的魔法水晶就做好了**!

最后一行:输出!

return fixed4(finalColor, mainCol.a * _BaseColor.a);

**fixed4(...)——把颜色打包成4个数

  • **前面finalColor——**红绿蓝三个值
  • **后面mainCol.a * _BaseColor.a——**透明度

“透明度也要乘”——因为要让物体半透明**!

六、完整流程回顾:一个像素的"魔法之旅"

**让我们跟着一个"水晶边缘"的像素——看它是怎么被画出来的

Step 1顶点魔法师算它的位置

  • **加上sin摆动——它稍微偏了一点
  • 算出屏幕位置
  • 算出法线、视线方向

Step 2片段魔法师上场

  • 从主贴图取颜色——得到淡蓝色
  • _BaseColor相乘——变成深蓝色

Step 3取能量纹路

  • **uv加上时间——采样滚动的位置
  • 得到一个"能量花纹"的颜色**
  • 加到基础色上——变成"有花纹的深蓝色"

Step 4算边缘光

  • **这是边缘像素——dot值≈0
  • **1-dot ≈ 1——rim值很大
  • 加上边缘光颜色——变成"发光的深蓝色"

Step 5呼吸

  • **假设当前sin=0.5——**pulse≈0.75
  • **颜色乘以0.75——稍微暗一点
  • **下一帧——**pulse可能=1——又亮一点

Step 6输出

  • 加上透明度
  • 和背景混合
  • 屏幕上出现——一个摆动、发光、呼吸的魔法水晶的边缘像素

七、这个Shader的"精妙之处"

看完这个例子——能提炼几个Shader的核心技巧

技巧1:用_Time让物体动起来

**只要有_Time.y——任何东西都能动

  • **顶点位置动——**摆动、飘扬
  • **uv动——**滚动、流动
  • **颜色动——**呼吸、闪烁

技巧2:用sin做周期性变化

sin是"来回摇摆"的魔法

  • 摆动
  • 呼吸
  • 闪烁
  • 波浪

**几乎所有"周期性动画"——都能用sin做

技巧3:用dot(法线, 视线)做边缘光

这是"边缘检测"的经典技巧——**几乎所有:

  • 护盾
  • 幽灵
  • 能量物体
  • 卡通描边

——都会用到这个原理

技巧4:颜色层层叠加

复杂的效果——都是简单效果的叠加

  • 基础色 × 主贴图
  • 加上滚动纹路
  • 加上边缘光
  • 乘以呼吸

**一层一层——堆出复杂效果

结语:一份"魔法配方"的进阶之旅

从上次的"简单Shader"(只会贴图染色),
到这次的
"魔法水晶"(会摆动、会呼吸、会发光、会流动)——

我们做了一次"从新手到进阶"的完整旅程

  • 多学了"透明混合"**——让物体半透明
  • 多学了"顶点动画"**——让物体摆动
  • 多学了"uv滚动"**——让贴图流动
  • 多学了"边缘光"**——让物体发光
  • 多学了"呼吸变化"**——让物体呼吸
  • 多学了"多层合成"**——堆出复杂效果

它像是一份"进阶菜谱"**:

  • 上次的菜谱——“白饭配盐”**
  • 这次的菜谱——“海鲜大餐”**
  • 原料多了几倍
  • 步骤多了几倍
  • 每一步都能理解**!

它有Properties里丰富的"可调参数"——让美术能调出各种风格的水晶
它有渲染状态的"透明配置"——让物体像水晶一样半透明
它有顶点着色器的"摆动魔法"——让物体动起来
它有片段着色器的"四种效果合成"——滚动+边缘光+呼吸+染色
它有sin、dot、pow这些"数学工具"——让复杂效果变简单

下次当你看到游戏里的"炫酷特效"——请记得**:

那看起来"神奇的画面"——背后是

  • 一堆Properties让美术能调
  • 一些顶点动画让物体动
  • 一些uv滚动让贴图流
  • 一些sin/dot运算让效果酷
  • 一些颜色叠加让画面丰富

每一个"炫酷效果"——归根到底就是我们今天讲的这些基础技巧的组合
每一份"复杂Shader"——都可以拆成几十个"小技巧"慢慢理解
每一位曾经"看代码就晕"的学习者——只要肯拆开慢慢看——都能像今天这样"看懂了"

每一个_Time.y——都是"时间的魔法棒"——让物体动起来
每一个sin(...)——都是"摇摆的秋千"
——让效果周期变化
每一个dot(法线, 视线)——都是"边缘的检测器"——让物体发光
每一次+叠加——都在"堆积炫酷"
——从简单到复杂

这——就是Shader进阶的秘密

  • 不是"神秘的黑魔法"
  • 而是"几个基础技巧的组合"**
  • 不是"天才才能写"
  • 而是"愿意慢慢学的人都能掌握"**
  • 不是"看代码就头疼的东西"
  • 而是"拆开就能理解的清晰配方"**

在这份"魔法配方"的背后

  • 是Unity为你准备的丰富工具箱**(_Timesinpowdot……)
  • 是数学与美学的巧妙结合**
  • 是简单技巧堆积成复杂效果的智慧路径**
  • 是每一位创作者都能实现的炫酷梦想**

这——就是Shader的进阶之旅——不是"越来越难"——而是"越来越好玩"——每多学一个技巧,就多一份创造的能力——是每一位怀着好奇心的学习者,都能一步步走完的"魔法之路"

每一位读到这里的小朋友——都已经掌握了游戏Shader的很多核心秘密
每一次你多看一个Shader——都会发现"哦,这也是我学过的技巧!"**
每一位持续探索的学习者——总有一天能写出属于自己的炫酷Shader**!

这——就是Unity送给每一位小小学习者的、最鼓舞人心的"进阶礼物"。 🌟✨🧙‍♂️

加油——你已经在Shader的路上走得比想象中远了! 💪🎨

http://www.jsqmd.com/news/1338198/

相关文章:

  • 虚拟机CPU超配问题解析:从超量分配到性能调优的实战指南
  • UnityLive2DExtractor:从AssetBundle无损提取Live2D Cubism 3模型资源
  • KKCE: 网站测速分段计时与多节点实战 -快快测
  • 海归求职辅导怎么选?留学生真实体验总结分享
  • 为什么你的AI账号总被限流?深度溯源抖音推荐系统底层逻辑与3步权重修复法
  • Sysmon部署与配置实战:从日志黑洞到精细化Windows系统监控
  • 三方聊天软件工具|IM即时通讯系统定制开发与私有化部署
  • 企业级IM聊天软件定制开发|打造专属即时通讯平台
  • 帖子上了首页以后,我没急着做新功能,先回头改了反应测试
  • DeepSeek V4 Flash量化版本地部署指南:从环境搭建到性能调优
  • KKCE: 多运营商对照式网站测速与教育网移动 IPv6 专项排查-快快测
  • Flutter跨平台地图引擎鸿蒙适配实战
  • PyInstaller打包Python程序为EXE:从原理到实战的完整指南
  • UE5.4 C++项目创建失败:.NET SDK与MSVC工具链配置全解析
  • KKCE: 网站测速多节点实战 -快快测
  • 解决UE5.5.4 C++项目创建失败:MSVC编译器版本冲突深度解析
  • Linux与Windows文件压缩解压命令详解
  • Cloudflare Workers AI 实践指南:边缘部署 Kimi 与 GLM 大模型
  • CAD快速标注全攻略:从样式设置到批量操作提升绘图效率
  • 广西企业员工AI技能团训哪里有机构
  • Vivado内存溢出(OOM)全解析:从根因到实战解决方案
  • IDM v6.43.6.2深度解析:从多线程下载原理到高效工作流配置
  • 集合的线程不安全问题
  • 电源EMI传导测试:从噪声根源到滤波器设计的实战指南
  • 内存地址与容量计算:从比特到寻址,掌握计算机底层核心逻辑
  • 零跑分、零论文:如何理性评估 Qoder Cantus 模型的真实能力?
  • 非线性优化在三维重建三角化中的应用:从重投影误差到LM算法
  • 2026阜南县别墅大门源头厂家推荐与选购指南 - 品牌优推
  • MP3文件格式深度解析:从ID3标签到音频帧的完整结构指南
  • DSB-SC解调器噪声性能评估:从理论推导到实测分析