24.总结
24.1 核心要点速览
下面按课题写:每个效果尽量拆成「在做什么 → 原理 → 实现或易错」几块,长句压成条,复习时先扫四级标题再回实现篇对代码。
流光效果
在做什么
亮纹来自贴图本身,时间只改往哪采:给 uv.x 加上 时间 × 速度,等于把纹理沿 U 轴平移,屏幕上亮区就沿表面走。
原理
- 顶点:
MVP、TRANSFORM_TEX照旧;动画放片元,改 UV 不用每帧动网格。 - 片元:
uv偏移后tex2D,再乘_Color控制叠色强度。
混合与队列
Blend One One:dst = src + dst,越叠越亮,像往背景上「加一盏灯」;和SrcAlpha OneMinusSrcAlpha那种「按透明度盖上去」不是同一套语义。Queue = Transparent:和同屏其它透明物一起排序,模型穿插时前后可能不符合直觉,要实机看一眼。Cull Off:薄片双面都画;封闭壳体若只关心外表面,可开回剔除减 overdraw。
实现顺序(对照 Lesson02_MovingLight)
- Unlit,主纹理 + 空片元先跑通。
Properties:_MainTex、_Color、_Speed。- Tags:
RenderType/Queue→ Transparent;Blend One One;按需Cull Off。 - 片元里推 UV,乘色输出。
| 项 | 含义 | 复习提示 |
|---|---|---|
_Time |
常见分量为 t/20、t、2t、3t |
.x 偏慢,.y 偏快;换分量等于换时间缩放,记得同步调 _Speed |
Blend One One |
加法叠色 | 多层易曝、易糊;玻璃/UI 类透明不要用这套 |
Queue |
Transparent | 排序靠画家算法,别假设永远正确 |
Cull Off |
双面 | 能单面就关,省片元 |
float2 uv = float2(i.uv.x + _Time.x * _Speed, i.uv.y);
return tex2D(_MainTex, uv) * _Color;
例:闪电贴图里一条高亮横带,U 向滚动时整条亮带会沿剑身移动;觉得慢先改 _Speed,再考虑换 _Time 分量。
模型描边效果
在做什么
先画一层 沿法线外扩的壳(纯色),再画 未变形的本体(主纹理)。轮廓来自两层深度关系 + 颜色,不是单 Pass 勾线。
原理
- Pass1:顶点
vertex + normalize(normal) * _OutLineWidth→UnityObjectToClipPos,片元输出_OutLineColor。 - Pass1 通常 **
ZWrite Off**:壳若写深度,Pass2 画真表面时可能被自己的深度挡死,出现自遮挡或层次乱。 - Pass2:正常采样主纹理,深度由场景与 Pass2 共同决定。
队列与标签
Queue提到 Transparent:解决「本体盖住描边」一类排序问题;真正画多晚 看Queue,不是看RenderType。RenderType仍可标 Opaque:给替换 Shader、部分工具做分类用;接手的人容易误解,材质或 Wiki 里写一句。
易错
_OutLineWidth是 对象空间 单位,模型放大 10 倍,同样数值会显得更细。- 硬边:法线在边上不连续,外扩可能 断线,这是法线扩法固有代价。
| 项 | 要点 |
|---|---|
| 外扩 | 对象空间沿法线推顶点 |
Pass1 ZWrite Off |
避免第二 Pass 被壳的深度挡死 |
Queue |
Transparent 时与 RenderType 不一致要文档说明 |
Fallback |
"Diffuse" 等,低端回退 |
建议搭法:单 Pass 主色 → 复制 Pass 做外扩 + ZWrite Off → 第二 Pass 主纹理 → 改队列 + Fallback。
遮挡半透明效果
在做什么
第一遍专门画 「深度上已经比前景更远」 的那片表面(被挡层),第二遍画 正常可见 表面。合起来:正面看清,被挡处半透明或 X 光感。
原理(对照深度缓冲)
- 缓冲里存的是 当前最近深度。默认
ZTest LEqual:更近或相等才过。 - Pass1:
ZTest Greater+ZWrite Off→ 只画 比缓冲里更远 的片元,即被前景挡在后面的表面;不写深度 免得破坏后续物体。 - Pass2:默认
LEqual,画正常可见部分。 Blend SrcAlpha OneMinusSrcAlpha:常规透明合成,不是加法发亮。
两个 Shader 差在哪
| Pass1 颜色 / Alpha | Pass2 | |
|---|---|---|
OcclusionTransparent1 |
主纹理 RGB,A 用统一 _Alpha |
主纹理可见部分 |
OcclusionTransparent2 |
A 用菲涅耳近似,掠射角更「透」 | 同上 |
菲涅耳(文中形式):_FresnelScale + (1 - _FresnelScale) * pow(1 - dot(V, N), _FresnelN),V、N 单位化,pow 指数调边缘软硬。
例子:球前放一个立方体,球 被立方体挡住的那块球面 仍能着色,看起来像隔着玻璃看后面。
物体切割效果
在做什么
每个片元判断:在世界空间某一轴上,自己在 切割平面哪一侧;不在保留侧就 clip 掉。双面用 不同贴图 时靠 VFACE 分面。
原理
step得到 0/1;_Invert翻转保留侧。clip(v):只有v < 0才丢;v == 0仍会画,所以cutValue == 0时要clip(-1)这类写法。- 轴:
0/1/2→x/y/z与_CuttingPos比较;_CuttingPos常绑定空物体,脚本每帧SetVector。
数据流
- 顶点输出
worldPos(如mul(unity_ObjectToWorld, vertex).xyz),片元里做比较。 VFACE:face > 0→_MainTex,否则_BackTex;需要Cull Off、#pragma target 3.0。
易错(脚本)
sharedMaterial改的是 资产:同材质多物体 切割面会联动。正式用material拷贝或MaterialPropertyBlock。[ExecuteAlways]:编辑模式也能预览,调场景方便。
| 属性 | 含义 |
|---|---|
_CuttingDir |
0/1/2 → x / y / z |
_Invert |
是否反转保留侧 |
_CuttingPos |
世界空间参考点 |
翻页效果
在做什么
在 模型空间 里模拟一张纸:先平移到装订边,再弯折,再绕局部 Z 轴翻页,最后平移回去。波纹与旋转的顺序决定「弯折是不是还在同一页面上」。
顶点顺序(别乱改)
vertex += float3(_MoveDis, 0, 0):把旋转中心挪到纸边。- Y 向
sin、X 向收缩:中间拱起,模拟书页弯曲。 - 绕 Z 轴
sincos旋转_AngleProgress。 - 减回平移,到裁剪空间。
若把 sin 挪到旋转 之后,弯折作用在已旋转的坐标系上,波纹方向会跟着歪,和课文效果不一致。
角度权重
weight = 1 - abs(90 - _AngleProgress) / 90:0°、180° 为 0,90° 为 1 → 中间翻得最弯,合上时平。
贴图
VFACE+Cull Off+target 3.0,与切割篇双面套路相同。
| 属性 | 作用 |
|---|---|
_AngleProgress |
0~180° |
_WeightY / _WeightX |
起伏/收缩强度,乘 weight |
_WaveLength |
控制 sin 波纹密度 |
_MoveDis |
铰链在纸的哪一侧 |
卡通风格渲染
在做什么
漫反射用 Ramp 纹理 把连续明暗切成色带;阴影用 Built-in 宏接到 半兰伯特 上;描边用 Cull Front + 外扩;高光可改成 硬阈值 做块面高光。
漫反射与 Ramp
- 半兰伯特标量进
tex2D(_RampTex, float2(halfLambertNum, halfLambertNum)),法线仍走切线空间 + 法线贴图那条链。 - 本质:用纹理当 LUT,替代连续
pow漫反射。
阴影(Built-in 前向)
ForwardBase、multi_compile_fwdbase,Lighting.cginc+AutoLight.cginc。SHADOW_COORDS/TRANSFER_SHADOW/UNITY_LIGHT_ATTENUATION:halfLambertNum *= atten再 进 Ramp,否则投影进不了色带。Fallback "Diffuse":顺带吃内置 ShadowCaster 链。
描边与另一篇描边的区别
- 本篇:
Cull Front+ 外扩,描边 Pass 可以写深度;主 PassCull Back,靠深度挡掉壳的内侧,只留外圈。 - 「双 Pass 描边关 ZWrite」那套:第一遍不写深度,动机不同,不要混用两套假设。
高光
step阈值:spec = step(_SpecularNum, dot(N, halfVec))→ 高光整块亮/灭,赛璐璐感强,边缘不如pow平滑。
SubShader 顺序:OUTLINE → ForwardBase。
素描风格渲染
在做什么
用 6 张 素描纹理 + 顶点算出的权重,按明暗层级混排线;阴影来时 整幅 乘 atten;描边 复用 Lesson12 的 OUTLINE Pass。
权重与 diff * 7
- 兰伯特
diff映射到[0,7),拆成 6 段,对应_Sketch0~_Sketch5。 - 段内相邻两张用 互补权重(如
x = diff - k、y = 1 - x),比单层权重少一块死白。
与卡通的差异(记一句)
- 素描:
sketchColor.rgb * atten * _Color,阴影里 整张贴图一起暗。 - 卡通:
halfLambertNum先乘atten再 Ramp,高光路径 单独,暗部里高光相对更跳。
UsePass 条件
Lesson12里 Pass 必须Name "OUTLINE"。- 本 Shader 的
Properties仍要有_OutLineColor、_OutLineWidth,否则引用 Pass 读不到参数。
噪声素材与工具链
美术(PS)
- 灰度噪声:固定尺寸(如
512×512)→ 云彩 → 自动色调 → 导出,给clip、溶解阈值用。 - 一维渐变条:RGB 画布 + 渐变工具直线拖(
Shift约束),导出后 Shader 里用value当 UV 的一维坐标,常做 消融烧边。
程序(Mathf.PerlinNoise)
- 每个像素:
(x/width)*scale、(y/height)*scale喂给 Perlin;**scale大** → 相邻采样步长大 → 图更 碎;**scale小** → 更平滑。 SetPixel→Apply→EncodeToPNG→AssetDatabase.Refresh。
工具
PerlinNoiseTextureTool:EditorWindow调宽高、scale、文件名,把上面流程固化。
消融效果
在做什么
按噪声值与溶解进度比较 clip 掉片元;边界用 渐变条 上色;阴影 Pass 必须与主 Pass 同一套 clip,否则影子是完整剪影。
溶解
clip(noise.r - _Dissolve):噪声低于进度就消失。- 坑:
clip只对<0生效;_Dissolve == 1时若noise.r == _Dissolve仍会留点,要clip(-1)分支强制全清。
烧边
smoothstep出边界value,采_Gradient;lerp时用step(0.000001, _Dissolve)避免进度为 0 时整面误混渐变。
阴影
ShadowCaster里 同样clip条件,否则光眼里还是整块模型,与画面上的洞对不上。
水波效果
在做什么
背后屏幕:GrabPass + 透明队列晚画,抓到的才是「身后的场景」。折射用 法线扰动后的屏幕偏移;反射用 Cubemap;菲涅耳决定 反射 vs 折射 比例。
法线
- 同一张
BumpMap采两次:uv + speed与uv - speed,再normalize。 - 直觉:单向滚像「一条纹在跑」,反向叠加后 更乱、更像水;归一化保证后续反射/菲涅耳单位向量可靠。
折射与菲涅耳
grabPos.xy += bump.xy * _Distortion * grabPos.z(实现形式以课文为准),再透视除法采_GrabTexture。- Schlick:
fresnel = _FresnelScale + (1-_FresnelScale)*pow(1-dot(V,N),5),color = refl * fresnel + grab * (1-fresnel);主纹理 UV 可加speed做细节流动。
工程提醒
GrabPass:多水面 ≈ 多次拷屏,带宽贵。- 排序错了:Grab 到的不是你以为的那层背景。
- 换 URP/HDRP:Grab 行为与 Built-in 不一定一致,要查文档。
噪声雾效
在做什么
全屏后处理:用 深度 还原 worldPos,先算 高度雾 基线,再用 滚动噪声 把雾浓做空间起伏,最后 lerp 到雾色。
管线要点
- 深度:
LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture));射线插值还原worldPos(课文中的Lesson23_NoiseFog写法)。 - 高度雾:
f = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart)。 - 噪声:
noise = (tex2D(...).r - 0.5) * _NoiseAmount,可正可负;f = saturate(f * _FogDensity * (1 + noise))→ 雾 一块浓一块淡;saturate防止lerp因子越界。
C#
- 继承雾效基类;除雾参外设
_Noise、滚动速度;相机depthTextureMode |= DepthTextureMode.Depth。
24.2 面试题精选
基础题
1. Unity Shader 里 _Time 各分量是什么,流光示例为何常用 .x
题目
说明 _Time 四个分量的含义,并联系「U 轴偏移做流光」解释用 _Time.x 和用 _Time.y 时观感差异。
深入解析
- Unity 内置
float4 _Time,文档与教程中常见写法为四个分量对应 **t/20、t、2t、3t**,其中t为场景加载后的秒级时间。 .x为t/20,同样_Speed下周期更长、滚动更慢;**.y** 为t,变化更快。- 片元里
uv.x + _Time.x * _Speed只是把「时间因子」乘到偏移上,换分量本质是换时间缩放,需与美术节奏一起调_Speed。
答题示例
_Time是内置时间向量,四个分量通常对应t/20、t、2t、3t,t是场景时间。流光用 U 偏移时,乘
.x相当于用较慢的t/20,同样速度参数下动得比.y更柔和;要快一点就改用.y或调大_Speed。
参考文章
- 2.流光效果-具体实现
2. 模型描边第一个 Pass 为什么要关 ZWrite
题目
双 Pass 描边里,外扩那一 Pass 为何要 ZWrite Off?若打开深度写入,第二 Pass 正常画模型时容易出现什么现象?
深入解析
- Pass1 把网格沿法线放大,若写入深度,缓冲里会留下「更靠外」的深度;Pass2 画真实表面时,深度测试可能认为本体在扩面之后,导致表面被错误遮挡或描边层次错乱。
- 关掉 Pass1 的写入后,深度缓冲主要反映场景里其他物体与第二 Pass的贡献,第二 Pass 才能稳定画上纹理表面,描边由第一 Pass 的颜色与队列排序呈现。
答题示例
第一个 Pass 只是扩出去画一条边,如果它还写深度,会把「假的大壳」写进深度缓冲。
第二个 Pass 画真模型时深度对不过,表面可能被挡掉或描边关系乱掉。所以外扩 Pass 一般关
ZWrite,让第二 Pass 正常盖在正确的深度关系上。
参考文章
- 4.模型描边效果-具体实现
3. ZTest Greater 在遮挡半透明里画的是哪一块
题目
遮挡半透明第一个 Pass 使用 ZTest Greater 且 ZWrite Off,相对默认的 LEqual,多画了场景里的哪类片元?为什么要配 Blend SrcAlpha OneMinusSrcAlpha?
深入解析
- 默认通过测试的是「更近或相等」;Greater 通过的是「比缓冲里已存深度更远」的片元,在遮挡关系下对应已被前面几何挡住、在深度上处于后方的表面区域。
ZWrite Off避免这一半透明层破坏后续物体的深度一致性。Alpha 混合用标准SrcAlpha OneMinusSrcAlpha把被挡区域与背景按透明度合成。
答题示例
Greater表示只有比深度缓冲里更远的片元才通过,所以画的是「在深度上已经落在遮挡物后面」的那部分表面。关掉写入是避免半透明乱改深度。混合用 SrcAlpha OneMinusSrcAlpha 做正常透明叠色。
参考文章
- 6.遮挡半透明效果-具体实现
4. 翻页顶点里 weight 与 sin 起伏的关系
题目
文中用 weight = 1 - abs(90 - _AngleProgress) / 90 再乘 _WeightY 去做 Y 向 sin 偏移。0°、90°、180° 时 weight 各为多少,对翻页弯曲感有什么影响?
深入解析
- 代入得 0°、180° 时 weight=0,90° 时 weight=1,与正文「两端不需要起伏、中间最弯」一致。
sin(appdata_base.vertex.x * _WaveLength) * weight * _WeightY在 weight 为 0 时整项为 0,页面贴合;90° 时起伏幅度由_WeightY与波长共同决定。
答题示例
weight 在 0 度和 180 度是 0,在 90 度是 1。
所以页面在合上、完全翻开时是平的,翻到中间弯得最厉害,正好配合 sin 做波浪形变。
参考文章
- 10.翻页效果-具体实现
5. 卡通描边 Pass 为什么用 Cull Front 加法线外扩
题目
Lesson12_ToonShader 里描边 Pass 使用 Cull Front 并把顶点沿法线外扩。这样画出来的是什么几何,与主 Pass 的 Cull Back 如何分工?
深入解析
Cull Front丢弃正面、只光栅化背面;外扩后这些背面片元在屏幕边缘形成一圈比本体略大的壳,片元着色器输出纯色即轮廓。- 主 Pass
Cull Back只画正面,正常深度测试下内层扩面被挡,只留下外缘可见的描边颜色。
答题示例
Cull Front 只画背面,把模型沿法线撑大一圈再只渲染背面,边缘就会露出一圈颜色当描边。
主 Pass Cull Back 画正面,靠深度把里面那层扩出去的壳挡住,外面只看到一圈边线。
参考文章
- 12.卡通风格渲染-具体实现
6. 素描里 diff * 7.0 与多张纹理权重在做什么
题目
Lesson14_Sketch 把兰伯特结果乘 7 再分支写 sketchWeights0/1,片元里六张图按权重相加。为什么要放大到 0~7,而不是直接用 0~1 采一张渐变?
深入解析
- 0~7 把明暗划成多段,对应 6 张素描贴图之间的过渡;段内用小数在相邻两张之间分权重,得到离散笔触层级。
- 多张贴图比单张 Ramp 更易做出笔触方向与疏密变化;修正版让相邻权重和为 1,减轻大块留白。
答题示例
乘 7 是把亮度切成多段,每段对应不同素描纹理,用权重在两张之间混。
比一张渐变更像手绘排线,改算法是为了少出一片死白。
参考文章
- 14.素描风格渲染-具体实现
7. 消融里 clip 与 _Dissolve == 1 为什么要特殊处理
题目
clip(noise.r - _Dissolve) 为何在 _Dissolve == 1 时改成 clip(-1)?clip 对 0 的行为会带来什么问题?
深入解析
clip(x)仅在x < 0时丢弃;**x == 0仍通过**,进度拉满时可能残留noise.r == _Dissolve的片元。- 进度为 1 时强制传入负值可保证全部剔除。
答题示例
clip 只扔掉小于 0 的,等于 0 还会画。
溶解到 1 时噪声若刚好等于阈值会留渣,所以 dissolve 为 1 时直接 clip 负数全扔掉。
参考文章
- 19.噪声-消融效果-具体实现
8. Mathf.PerlinNoise 里 scale 起什么作用
题目
工具用 (x/width)*scale 作为 Perlin 输入。增大 scale 时生成图更「碎」还是更「平」?
深入解析
- 坐标乘
scale使相邻像素在噪声场上步进更大,高频相对增强,视觉上更细碎;scale 小则采样更缓、更平滑。
答题示例
scale 大相当于 UV 拉得更开,相邻像素差得多,图就更花更碎;scale 小就平。
参考文章
- 17.噪声-制作纹理-柏林噪声纹理生成工具
进阶题
1. 流光里用 Blend One One 与常规 Alpha 混合各适合什么
题目
本系列流光 Shader 使用 Blend One One。若改成典型的 SrcAlpha OneMinusSrcAlpha 一类 Alpha 混合,表现与适用场景会有什么不同?
深入解析
Blend One One:src + dst,不断向背景加亮,适合闪电、能量、高光扫过等「发光体」;不保留物理上可信的覆盖率,透明语义弱,叠多了易曝、糊。- Alpha 混合:按源 Alpha 与背景插值,适合玻璃、UI、需要正确遮挡与层次的透明材质。
- 工程上还要结合
Queue与同屏其他透明物体的排序,加法混合往往更「抢眼」,也更容易和后期 Bloom 等一起把画面洗白。
答题示例
One One是加法,颜色往背景上叠亮,适合做流光、闪电这种发亮特效,但容易过亮、多层叠在一起很糊。Alpha 混合按透明度与背景插值,更适合常规透明材质。选哪种要看是要「发亮」还是要「正确的透明遮挡关系」。
参考文章
- 2.流光效果-具体实现
2. 遮挡半透明的两个 Pass 如何分工
题目
OcclusionTransparent1 中第一个 Pass 与第二个 Pass 在深度测试与视觉效果上各负责什么?Pass 顺序能否对调,为什么?
深入解析
- Pass1:
ZTest Greater专门渲染被遮挡部分并带透明度;Pass2:默认LEqual(未改即前向默认),渲染正常可见部分。 - 若简单对调:先画可见再画被挡,需仍满足「被挡层」用 Greater 才能出现;通常教程顺序是先 Greater 半透明层、再正常层,与深度缓冲更新节奏一致;对调后若仍先写入了某些深度会改变结果,工程上应遵循「谁依赖当前缓冲状态」的顺序设计。
答题示例
第一个 Pass 用 Greater 把被挡区域半透叠上去;第二个 Pass 用默认深度测试画没被挡到的正常表面。
不能随意对调而不改状态,因为 Greater 那层依赖当前缓冲里已经是「前景挡在后面」的深度关系,顺序错了效果就不对。
参考文章
- 6.遮挡半透明效果-具体实现
3. 物体切割里 step、_Invert 与 clip 如何配合
题目
切割 Shader 里用 step 得到 cutValue,再经 _Invert 与 clip(-1) 决定是否显示片元。说明 step 各参数含义及 clip 丢弃条件。
深入解析
- 文中实现为
cutValue = step(_CuttingPos.axis, worldPos.axis):HLSLstep(edge, x)在x >= edge时为 1,否则 0;即比较切割位置与片元世界坐标在选定轴上的前后关系。 _Invert为真时cutValue = 1 - cutValue翻转保留侧。**clip(v)** 在v < 0时丢弃片元,故clip(-1)用于丢弃。
答题示例
step拿切割参考坐标和片元世界坐标比,大于等于边的一侧返回 1,另一侧返回 0。
_Invert把保留侧翻过来。clip(-1)小于零直接扔掉片元,实现切开效果。
参考文章
- 8.物体切割效果-具体实现
4. 翻页顶点着色器里平移、起伏与旋转的先后顺序
题目
Lesson10_PageTurning 顶点里先 vertex += (_MoveDis,0,0),再做 sin 起伏与 X 收缩,再乘 Z 旋转矩阵,最后减回平移。若把「起伏」挪到旋转之后,效果可能有什么不同?
深入解析
- 当前顺序把形变定义在翻页前的局部坐标系里:起伏沿「页」的局部 X 起伏、绕 Z 旋转模拟装订边,数学上与美术预期的「先弯纸再绕轴翻」一致。
- 若先旋转再起伏,sin 作用的轴已随旋转改变,波纹方向与弯曲轴会跟教程效果不一致,调参难度上升。
答题示例
现在是先平移、在局部空间里做弯折和压缩,再绕 Z 旋转,最后移回去。
如果旋转后再 sin,弯折作用在已经转过的坐标上,波纹方向会跟着变,就不像在同一页纸上折了。
参考文章
- 10.翻页效果-具体实现
5. 卡通里 halfLambertNum 为什么要乘 UNITY_LIGHT_ATTENUATION
题目
加入阴影接收后,代码在采样 Ramp 前令 halfLambertNum *= atten。这样做的渲染语义是什么?
深入解析
halfLambertNum表征 N·L 明暗;**atten** 含 阴影与距离衰减,乘进去后 Ramp 的 UV 也会随投影区域与光衰变暗,被照亮与进影的区域在色带上拉开。- 若不乘,渐变纹理只反映法线与光方向关系,阴影贴图不会参与卡通明暗分带。
答题示例
atten 里有阴影和衰减,乘到半兰伯特上以后,进阴影的地方整体会变暗,Ramp 采出来也会掉到更暗的色带。
不乘的话卡通分带跟阴影对不齐,看起来会像没吃到投影。
参考文章
- 12.卡通风格渲染-具体实现
6. UsePass "Unlit/Lesson12_ToonShader/OUTLINE" 要能工作需满足什么
题目
素描 Shader 用 UsePass 引用卡通课里的描边 Pass。若 Lesson12 里该 Pass 没有 Name "OUTLINE" 会怎样?材质上还要暴露哪些属性?
深入解析
UsePass路径依赖对方 Shader 里 Pass 的Name;未命名则无法稳定引用。- 描边用到的
_OutLineColor、_OutLineWidth须在本材质上可见,通常写在当前 Shader 的 Properties 中供两 Pass 共享。
答题示例
UsePass 是按名字抓 Pass 的,卡通那边必须先给描边 Pass 起名叫 OUTLINE。
轮廓颜色和宽度要在当前材质的 Properties 里声明,外轮廓 Pass 才能读到。
参考文章
- 14.素描风格渲染-具体实现
7. 消融的 ShadowCaster Pass 为什么要对噪声再做 clip
题目
主 Pass 已按噪声溶解,阴影投射 Pass 若不做相同剔除会出现什么现象?
深入解析
- 阴影图记录的是「光源视角下哪些片元存在」;若投射 Pass 不
clip,已溶解掉的区域仍写深度,阴影会呈完整剪影,与肉眼溶解不一致。 - 同步
clip(_Dissolve == 1 ? -1 : noise.r - _Dissolve)后,阴影轮廓随溶解收缩。
答题示例
阴影 Pass 不写 clip 的话,物体在光眼里还是整块,影子不会跟着溶掉。
在 ShadowCaster 里用同样的 clip,影子才会和画面上的洞对齐。
参考文章
- 19.噪声-消融效果-具体实现
8. 水波里对法线纹理做 +speed 与 -speed 两次采样再归一化的目的
题目
bump1、bump2 取相反方向滚动后 normalize(bump1+bump2),相比单次采样有什么好处?
深入解析
- 双向滚动叠加可打破单一方向的条纹感,得到更各向、更乱序的法线扰动,水纹更自然。
- 归一化保持法线近似单位长度,后续算反射向量与菲涅耳更稳。
答题示例
一个往正方向滚一个往反方向滚,叠在一起比单次扫动更不像一条纹在跑。
normalize 一下让法线还是单位向量,反射和菲涅耳才不会漂。
参考文章
- 21.噪声-水波效果-具体实现
深度题
1. Transparent 队列下的流光与排序、开销
题目
该 Shader 将 RenderType 与 Queue 设为 Transparent,并配合 Blend One One。从渲染顺序与性能角度,可能遇到哪些问题?可以怎样缓解?
深入解析
- 排序:透明队列通常按深度等规则排序,穿插模型时顺序不总符合直觉,加法混合又会改写颜色,错误排序时局部会异常亮或异常暗。
- Overdraw:
Cull Off双面 + 全屏或大面片特效时,片元执行次数上升;One One对 HDR 与 Bloom 管线还可能放大「亮斑」。 - 缓解思路:能单面则恢复剔除;控制面片与全屏范围;需要稳定层次时对特效用独立 Queue 偏移或与美术约定相机角度;与 URP/HDRP 时还要考虑相机栈与透明 Pass 的实际行为差异(以当前管线文档为准)。
答题示例
走 Transparent 队列就要面对透明排序问题,加法混合又对顺序特别敏感,叠错会显得怪。
双面和全屏会拉高 overdraw,加法还容易和 Bloom 一起把画面打曝。可以从能剔则剔、控制几何范围、调队列或与美术定相机来减轻。
参考文章
- 1.流光效果-基本原理
- 2.流光效果-具体实现
2. RenderType 为 Opaque 而 Queue 为 Transparent 时排序上要注意什么
题目
描边 Shader 将 RenderType 标为 Opaque、Queue 设为 Transparent。这与「通体透明材质」在排序与批处理上的直觉差异是什么?可能带来什么副作用?
深入解析
- RenderType 影响材质替换、部分后效或替代 Shader 的分类;Queue 决定大致绘制顺序。二者不一致时,物体进入透明排序区间,可能与大量透明物体一起排序,却仍按不透明方式声明,易造成文档与工具链认知不一致。
- 副作用包括:与纯不透明物体分批、排序规则不同;调试时 RenderType 与表现不一致;部分管线优化路径可能按 RenderType 假设队列。
答题示例
RenderType 更多是分类标签,Queue 才真正决定大概什么时候画。标 Opaque 却进 Transparent 队列,会跟透明物体一起排序,但材质分类上仍叫 Opaque,容易让接手的人误判。
可能带来排序穿插、批处理行为与「看起来不透明」预期不一致,文档和调试要说明白。
参考文章
- 4.模型描边效果-具体实现
3. 用 sharedMaterial 传切割位置有什么工程风险
题目
示例在 Update 里对 Renderer.sharedMaterial 取材质并 SetVector("_CuttingPos", ...)。若场景里多个 Renderer 共用同一材质资产,会出现什么?更稳妥的写法是什么?
深入解析
- sharedMaterial 指向资源上的共享材质,一改所有引用该材质的实例都会受影响,切割平面会「联动」。
- 更稳妥:运行时
material属性取实例副本再改,或MaterialPropertyBlock按 Renderer 写属性,避免污染共享资产。
答题示例
sharedMaterial改的是资源本体,所有用这个材质的物体切割位置都会变成同一个。单物体演示可以,正式项目要么用
material实例化一份再改,要么用 MaterialPropertyBlock 按物体写参数。
参考文章
- 8.物体切割效果-具体实现
4. 卡通 Outline 与主 Pass 的深度关系及可能穿帮
题目
完整 Lesson12_ToonShader 先画外扩描边再画 ForwardBase 主表面,且描边 Pass 未像另一篇描边那样关 ZWrite。依赖什么测试挡住内层扩面?近裁剪、薄模型或宽度极大时可能出什么问题?
深入解析
- 依赖主 Pass 正常深度写入与
Cull Back只画正面,使相机侧可见的扩面内侧被后续深度挡住,只留外缘。 - 过宽的
_OutLineWidth、近裁剪面相交、深度精度不足时,可能出现轮廓厚度不均、局部闪烁或穿帮,需调宽度、模型厚度或队列。
答题示例
描边 Pass 把模型撑大一圈,主 Pass 再画正面并写深度,把里面那层挡掉,只留下外边一圈颜色。
描边太宽、模型太薄或者深度打架时,可能看到轮廓断掉或闪,要调宽度和模型。
参考文章
- 12.卡通风格渲染-具体实现
5. 用 step 做硬高光与用 pow 做连续高光各适合什么
题目
同一卡通 Shader 前期用 pow(max(0, dot(N, halfA)), _SpecularNum),后期把 _SpecularNum 改成阈值并用 step。两者在画面与调参上各有什么取舍?
深入解析
pow:高光平滑过渡,_SpecularNum大则锐利但仍连续,适合偏 PBR 或柔和卡通。step:高光二值化,阈值以下全黑、以上整块亮,块面感强,符合赛璐璐硬高光;但会失去柔和过渡,角度稍变高光可能整坨跳变。
答题示例
pow 是连续变化,高光边缘还是渐变的,调指数是在调锐利程度。
step 是硬切,高光要么整块亮要么没有,更像平面卡通,但边缘会跳,不如 pow 好混自然光。
参考文章
- 12.卡通风格渲染-具体实现
6. 素描把 atten 乘在最终 sketchColor 上与卡通只乘 halfLambertNum 有何观感差异
题目
Lesson14 返回 sketchColor.rgb * atten * _Color,而卡通实现把衰减主要乘进 Ramp 的输入。对阴影内高光、环境光分量可能产生什么不同?
深入解析
- **整幅乘
atten**:Lesson14_Sketch定稿片元为sketchColor.rgb * atten * _Color,没有再在片元里拆布林冯;笔触层与whiteColor所代表的亮部都乘同一atten,进影后整体变暗。 - 卡通:
halfLambertNum *= atten后再采_RampTex,高光仍用pow/step路径且未再乘atten(Lesson12定稿);与素描「整张贴图混合结果统一乘衰减」不同,进影后高光相对更亮、与漫反射分阶的耦合更弱。
答题示例
素描最后是加权素描纹理和白色一起乘 atten,进影子整幅一起暗。
卡通先把半兰伯特乘 atten 再查 Ramp,高光单独算且没乘 atten,阴影里高光往往还相对更跳一点。
参考文章
- 14.素描风格渲染-具体实现
7. GrabPass 水波在性能与平台上要注意什么
题目
水波 Shader 每物体 GrabPass 抓屏,再加立方体反射与菲涅耳混合。主要瓶颈与常见坑是什么?
深入解析
- GrabPass 易触发额外全屏拷贝,多水面、高分辨率下带宽与填充率压力大;透明队列排序错误会导致抓到错误背景。
- 移动平台、SRP 下 GrabPass 行为与 Built-in 可能不一致,需按目标管线查文档或改 Blit/相机颜色目标 方案。
答题示例
GrabPass 相当于多一次拷屏,水面一多就很贵。
排序不对抓到的就不是你想折射的那层;换 URP 以后行为也可能不一样,不能当内置管线照搬。
参考文章
- 21.噪声-水波效果-具体实现
8. 噪声雾效里 f * _FogDensity * (1 + noise) 再 saturate 的语义
题目
noise 由 tex2D - 0.5 缩放得到,可正可负。乘在雾因子上会如何改变雾浓?saturate 最后起什么作用?
深入解析
1 + noise在noise为负时压低雾因子,为正时抬高,形成空间不均匀的雾浓。saturate把混合因子钳在 0~1,避免lerp第三分量越界产生意外外推。
答题示例
noise 正的地方雾更浓,负的地方更淡,所以雾一块一块不均匀。
saturate 把因子卡在 0 到 1,防止 lerp 混出怪色。
参考文章
- 23.噪声-噪声雾效-具体实现
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com