效果展示
玻璃效果
制作思路
功能概述(视觉组成)
该玻璃效果由 4 个层叠模块构成:
-
基础 MatCap 反射层
- 使用视空间法线与视线计算 MatCap UV
- 提供玻璃外观的主要反射/明暗结构(不依赖场景光源)
-
折射 MatCap 扰动层
- 不是传统意义的屏幕抓取折射(GrabPass/SceneColor),而是第二张 MatCap
- 通过"厚度 thickness"生成 UV 偏移,让 RefractMatCap 产生"错位/扭曲"的观感
-
Fresnel 边缘色
- 通过
NoV(法线与视线点积)做边缘增强 - 与折射层做 lerp,决定边缘区域更偏 Fresnel 还是更偏折射扰动
- 通过
-
贴花 Decal
- 最终叠加层,用 decal 的 alpha 做覆盖混合
- 可用于污渍、裂纹、标签等局部细节
2. 渲染设置与队列行为
2.1 SubShader Tags
"RenderType"="Transparent"
"Queue"="Transparent"
2.2 Pass 状态
Blend SrcAlpha OneMinusSrcAlpha
ZWrite On
Cull Back
ZTest LEqual
- Blend:标准 alpha 混合。
- ZWrite On :透明物体写深度(不常见但有用途)
- 优点:玻璃自身排序/遮挡更稳定,减少"穿插错误"
- 风险:多个透明物体叠加时,后绘制的透明可能被深度挡掉(透明排序问题会更明显)。适合"单层玻璃"或希望玻璃优先占位的场景。
- Cull Back:背面剔除,避免内表面干扰。
- ZTest LEqual:常规深度测试。
3. 顶点阶段(Vertex)输出内容
顶点着色器输出:
positionCS:裁剪空间坐标(用于屏幕栅格化)positionWS:世界坐标(用于视线向量、计算高度)normalWS:世界法线(用于 Fresnel、转换到视空间算 MatCap)uv:模型 UV(用于 dirty/decal)
实现上依赖 URP 的工具函数:
GetVertexPositionInputsGetVertexNormalInputs
4. MatCap 原理与 UV 计算(核心之一)
4.1 为什么 MatCap 适合玻璃"反射外观"
MatCap(Material Capture)是一种"把一个球面在特定光照/环境下拍成贴图"的技术。运行时根据视角和法线查表得到颜色,可快速得到类似反射的明暗变化,成本低且稳定。
它不是真反射:不会反映真实场景,只是"看起来像反射"。
4.2 Shader 中的 MatCap UV 算法
关键代码:
float3 Vws = SafeNormalize(GetWorldSpaceViewDir(i.positionWS));
float3 Vvs = normalize(TransformWorldToViewDir(-Vws));
float3 Nvs = normalize(TransformWorldToViewDir(N));
float3 c = cross(Vvs, Nvs);
float2 matCapUV = float2(-c.y, c.x) * 0.5 + 0.5;
解释:
- 计算世界空间视线
Vws(从像素指向相机)。 - 将视线与法线转换到视空间(View Space),因为 MatCap 本质是"以相机为中心"的查表。
cross(Vvs, Nvs)得到一个与两者垂直的向量,其 x/y 分量用于构造 2D 查表坐标。- 乘 0.5 加 0.5:把 [-1,1] 映射到 [0,1],用于采样贴图。
输出 matcap = SAMPLE_TEXTURE2D(_MatCap, ..., matCapUV) 作为基础反射层。
5. Fresnel(边缘增强)实现
5.1 NoV 的意义
float NoV = dot(N, Vws);
- 当视线接近法线方向(正视表面)时,NoV 趋近 1
- 当视线与表面接近平行(掠射角/边缘)时,NoV 趋近 0
5.2 smoothstep 控制区间
float fres = smoothstep(_Min, _Max, NoV);
_Min/_Max定义 NoV 的映射区间smoothstep输出 0~1 的平滑曲线- 注意:此处 fres 随 NoV 增大而增大,因此:
- 正视区域 fres 较大
- 边缘区域 fres 较小
后续用到了 (1.0 - fres),相当于重新得到"边缘更强"的权重(这是这份 shader 的使用方式)。
6. Thickness(厚度)与高度映射(核心之二)
6.1 设计意图
真实玻璃折射强度与厚度相关。该 shader 用一个厚度标量 thickness 来驱动"折射 MatCap 的 UV 偏移",从而模拟厚薄不均导致的折射扰动。
6.2 ThicknessMap 的 UV 构造:用世界高度做纵向采样
float3 objOriginWS = TransformObjectToWorld(float3(0,0,0));
float h = ((i.positionWS.y - objOriginWS.y) - _ObjectPivotOffset) / max(1e-5, _ObjectHeight);
float2 thicknessUV = float2(0.5, h);
含义:
- 以物体原点(pivot)为基准取世界高度差:
i.positionWS.y - objOriginWS.y _ObjectPivotOffset:手动修正 pivot 不在底部/中心时的高度零点_ObjectHeight:归一化高度范围,避免不同尺寸物体导致同一贴图不适配thicknessUV.x 固定为 0.5:说明厚度图在 x 方向不变化,相当于只取一条竖向渐变(或把贴图当作 1D 曲线)
因此 ThicknessMap 实际是一个"沿高度变化的厚度曲线"。
7. DirtyMask(脏污)如何影响厚度与折射
float2 uvDirty = i.uv * _DirtyMask_ST.xy + _DirtyMask_ST.zw;
float dirtyA = SAMPLE_TEXTURE2D(_DirtyMask, sampler_DirtyMask, uvDirty).a;
-
取 dirty mask 的 alpha 通道作为脏污强度。
-
参与 thickness 计算:
float thickness =
(1.0 - fres) +
SAMPLE_TEXTURE2D(_ThicknessMap, sampler_ThicknessMap, thicknessUV).r +
dirtyA;
分解:
(1 - fres):边缘区域更大 → 边缘更"厚/折射更强"(符合玻璃边缘更明显的主观印象)ThicknessMap.r:提供沿高度变化的基础厚度dirtyA:脏污区域增强 thickness → 让脏污处折射更强、更"糊/扭曲"(模拟指纹、雾化、水渍造成的扰动)
8. 折射 MatCap:UV 偏移与混合逻辑(核心之三)
8.1 偏移量与强度
float refrOffset = thickness * _RefractIntensity;
float refrLerp = saturate(refrOffset);
_RefractIntensity是全局折射强度refrOffset同时也是UV 偏移量 ,且refrLerp用它的大小做混合权重(越偏移越用折射层)
8.2 折射 MatCap 采样
float4 refrMatcap = SAMPLE_TEXTURE2D(_RefractMatCap, sampler_RefractMatCap, matCapUV + refrOffset);
注意:这里 refrOffset 是一个标量,加到 float2 上会同时影响 u、v(等价于 (refrOffset, refrOffset))。这会产生一种"对角线方向统一漂移"的扭曲风格。若你希望更自然的扰动,一般会改成:
matCapUV + normalVS.xy * refrOffset- 或从 noise/flow map 得到 2D 偏移
但当前实现的优点是:简单、稳定、可控。
8.3 FresnelColor 与折射层的混合
float4 fresOrRefr = lerp(_FresnelColor, refrMat, refrLerp);
解释:
- 当
refrOffset小(折射弱)→ 更接近_FresnelColor - 当
refrOffset大(折射强)→ 更接近refrMatcap
这让 FresnelColor 更像是"折射不足时的补色/边缘色",并与厚度共同控制视觉风格。
9. Decal 贴花叠加(最终合成)
float4 col = lerp(matcap + fresOrRefr, decal, decal.a);
- 先把
matcap + fresOrRefr作为玻璃主体颜色 - 再用 decal alpha 做覆盖混合
- decal.a=1:完全显示 decal
- decal.a=0:完全显示玻璃主体
这适合做:裂纹、LOGO、污渍局部贴纸等。
10. Alpha(透明度)生成逻辑
float a = decal.a + saturate(max(matcap.r, thickness));
return half4(col.rgb, a);
含义:
max(matcap.r, thickness):取 MatCap 的红通道或厚度的较大值作为"主体不透明度趋势"saturate限制到 [0,1]- 再加上
decal.a:贴花区域额外提高不透明度(例如裂纹、贴纸更实)
这是一个"风格化透明度"方案:透明度不是物理的 Beer-Lambert 吸收,而是让玻璃在高光强、厚度大、或有贴花时更"实"。
11. 参数说明与调参建议
MatCap 相关
_MatCap:基础反射外观(决定玻璃主质感)_RefractMatCap:折射扭曲外观(决定"扰动的反射色块"长什么样)_RefractIntensity:折强度(同时影响 UV 偏移与折射混合权重)
建议:
_RefractMatCap可以用更"模糊/散"的 MatCap,配合偏移更像折射糊影。
Fresnel
_FresnelColor:折射不足时的边缘色/补色_Min/_Max:控制 NoV 到 fres 的过渡区间_Min小、_Max大:过渡更平缓- 若想"边缘更明显",通常会让
(1-fres)在更宽区间保持较大(可通过调整 min/max 或改成1-smoothstep(...))
Thickness/高度
_ObjectHeight:高度归一化尺度(非常关键,否则 thicknessUV 的 h 会跑飞)_ObjectPivotOffset:修正 pivot 高度零点_ThicknessMap:沿高度控制厚度变化_DirtyMask:局部增强厚度/折射(用 alpha 通道)
Decal
_Decal:贴花内容_Decal_ST:平铺与偏移
12. 已知限制与可扩展方向(工程角度)
限制
- 不是真折射:没有采样 SceneColor,因此不会折射真实背景。
- UV 偏移是标量统一偏移(u、v 同时加同一个值),扰动方向单一。
ZWrite On的透明会带来多层透明叠加不正确的风险。