前言
借由 Unity Shader 实战学习 Shader !
带完整注释,一起学习 💪
效果
环绕物体的水波纹特效,被神秘力量的光晕环绕,光晕从底部升起,向上逐渐消失
👉 展示备份(国内节点)
👇 效果展示 (科学上网)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| Shader "Unlit/AloeaShader03" { Properties { _Color ("Color", Color) = (0,0,0,0)
} SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Pass { Cull Off ZWrite off Blend One One
CGPROGRAM #pragma vertex vert #pragma fragment frag
#include "UnityCG.cginc"
#define TAU 6.283185
float4 _Color;
struct appdata { float4 vertex : POSITION; float3 normal: NORMAL; float2 uv : TEXCOORD0; };
struct v2f { float4 vertex : SV_POSITION; float3 normal: TEXCOORD0; float2 uv : TEXCOORD1; };
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.normal = UnityObjectToWorldNormal(v.normal); o.uv = v.uv; return o; }
fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * TAU * 8) * 0.02; float t = cos((i.uv.y + offset - _Time.x * 2) * 30) * 0.5 + 0.5; float topBottomRemover = (abs(i.normal.y) < 0.999); float4 outColor = _Color * t * topBottomRemover; return outColor; } ENDCG } } }
|
细节
🤔效果调试过程
1 2 3 4 5 6
| fixed4 frag (v2f i) : SV_Target { float t = cos(i.uv.y * 30); return float4(t, 0, 0, 1); }
|
1 2 3 4 5 6 7
| fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * 40) * 0.02; float t = cos((i.uv.y + offset) * 30); return float4(t, 0, 0, 1); }
|
1 2 3 4 5 6 7 8
| fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * 40) * 0.02; float t = cos((i.uv.y + offset) * 30); t *= 1 - i.uv.y; return float4(t, 0, 0, 1); }
|
1 2 3 4 5 6
| fixed4 frag (v2f i) : SV_Target { float topBottomRemover = abs(i.normal.y); return float4(topBottomRemover, 0, 0, 1); }
|
1 2 3 4 5 6 7 8 9 10 11
| fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * 40) * 0.02; float t = cos((i.uv.y + offset) * 30) * 0.5 + 0.5; t *= 1 - i.uv.y; float topBottomRemover = (abs(i.normal.y) < 0.999); return float4(t * topBottomRemover, 0, 0, 1); }
|
1 2 3 4 5 6 7 8 9 10 11
| fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * 40) * 0.02; float t = cos((i.uv.y + offset) * 30) * 0.5 + 0.5; t *= 1 - i.uv.y; float topBottomRemover = (abs(i.normal.y) < 0.999); float4 outColor = _Color * t * topBottomRemover; return outColor; }
|
1 2 3 4 5 6 7 8 9 10 11
| fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * 40) * 0.02; float t = cos((i.uv.y + offset - _Time.x * 2) * 30) * 0.5 + 0.5; t *= 1 - i.uv.y; float topBottomRemover = (abs(i.normal.y) < 0.999); float4 outColor = _Color * t * topBottomRemover; return outColor; }
|
_Time 是内置变量,_Time.xyzw 对应不同的时间分量。t 是开始渲染至今的时间,单位为秒,我们在上面约束在了 cos 函数内,这意味着采用 .x 的动画速度是慢于 .y 20倍的,但用哪个并不重要,因为都是根据效果调试出来的。
_Time.x = t / 20
_Time.y = t
_Time.z = t * 2
_Time.w = t * 4
现在,整个效果已经出来了! 😝
我们检查下细节发现,在圆柱一圈的连接处(uv 的边缘)上,出现了断横,就像我们廉价的条纹毛线衣一样不够优雅。
1 2 3 4 5 6 7 8 9 10 11 12
| #define TAU 6.283185
fixed4 frag (v2f i) : SV_Target { float offset = cos(i.uv.x * TAU * 8) * 0.02; float t = cos((i.uv.y + offset - _Time.x * 2) * 30) * 0.5 + 0.5; float topBottomRemover = (abs(i.normal.y) < 0.999); float4 outColor = _Color * t * topBottomRemover; return outColor; }
|
完美 ~ 😝😝
🤔剔除 Cull
引擎在处理剔除的时候,默认摄像机看不到的面是不会渲染的,这意味着物体的背面是不会被渲染的,这个场景下会产生我们想要的透明物体只剩下前面而看不到后面的问题,所以我们需要关闭剔除。
Cull 有三种选项,
Back:摄像机看不到的那一面剔除,默认值,常规情况下(非透明)确实应该这么处理以提高性能,毕竟看不到嘛
Front:摄像机看得到的那一面剔除,看不到的那一面展示,做一些特殊效果时可以用到
Off:不管摄像机看不看的面都展示,不需要计算剔除
注意这里的 Cull 指的是物体不同面在摄像机方位下的剔除规则,如果物体根本就超出了视锥体/视野范围,那么当然还是会直接剔除的,这不是 Shader 部分处理的事。
文档:https://docs.unity3d.com/Manual/SL-Cull.html
🤔混合透明 Alpha Blend
我们在这个案例中采用 Alpha Blend 这种方式实现的半透明效果。之后我可以单独整理下半透明的实现原理和方式。
指令使用上我们用到 Blend sourceFactor destinationFactor
。
表示最终颜色 = 物体颜色 * sourceFactor + 屏幕已有颜色 * destinationFactor,而 One 对应的是 1,这样就实现了透明效果。
注意这里不能用 Zero,因为除了 0,0,0,0 的部分要去除,正常的绿色波纹部分我们是不希望去除的,只需要用 0,0,0,0 去乘以 1 就可以实现去除了。
其他还有一些指令,下次用到再展开聊聊不同的效果
文档:https://docs.unity3d.com/Manual/SL-Blend.html
🤔渲染顺序 RenderQueue
默认情况下, Unity 根据对象距离摄像机的距离由远及近依次渲染,以保证我们看到的物体先后关系是正确。但一些情况则需要开发者特别设置,比如当对象是透明的..
我们可以通过设置 Queue Tag 改变渲染的顺序,当不同的实体(SubShader)采用不同的类就决定了它们的先后。
注意指定渲染顺序生效,需要声明关闭写入深度缓存 ZWrite Off
, 否则依然以Unity的距离计算为准。
1 2 3
| Tags { "Queue"="Overlay" } Tags { "Queue"="Overlay-10" } Tags { "Queue"="Overlay+10" }
|
|
|
Background |
这个队列是最先渲染的,比如天空盒一类用它 |
Geometry |
默认渲染队列,大部分实体几何体用它 |
AlphaTest |
开启透明度测试的对象 |
GeometryLast |
所有 Geometry 和 AlphaTest 渲染完成后 |
Transparent |
所有 Geometry 和 AlphaTest 渲染完成后,再按照从远往近顺序进行渲染,透明对象用它 |
Overlay |
所有叠加效果一类用它 |
文档:https://docs.unity3d.com/ScriptReference/Rendering.RenderQueue.html