Unity Shader 屏幕空间 UVScreen Space UV 完全指南

用裁剪空间坐标除以 w 后重映射到 [0,1],将屏幕纹理"投影"到任意几何体上------ 无需 UV 展开,轻松实现扫描线、水波纹与受击扭曲。

1原理:从裁剪空间到屏幕坐标

在 GPU 管线中,顶点着色器最终输出的是裁剪空间坐标 clipPos (x, y, z, w)。 GPU 在光栅化前会自动执行透视除法 :将 xyz 各分量除以 w, 得到范围在 [-1, 1] 的 NDC(标准化设备坐标)。 屏幕空间 UV 就是在这一步之后,把 NDC 的 xy 重映射到 [0, 1] 的结果。

核心公式只有两行:

cs 复制代码
// 顶点着色器中o.screenPos = ComputeScreenPos(clipPos);

// 片元着色器中float2 screenUV = i.screenPos.xy / i.screenPos.w;

ComputeScreenPos 是 URP 提供的工具函数, 它在顶点阶段把 clipPos 转换为 透视正确 的屏幕坐标(尚未做透视除法), 片元阶段再用 screenPos.xy / screenPos.w 完成透视除法, 得到真正的 [0,1] UV。

为什么要分两步? 在顶点阶段就除以 w 会破坏插值的透视正确性。 把 screenPos.w(即原始 clipPos.w)随 varying 传到片元阶段, 由 GPU 做透视校正插值后,再在片元中相除,结果才是正确的。

2URP 中获取屏幕空间 UV

URP 提供了一张 _CameraOpaqueTexture(需在 URP Asset 中开启 Opaque Texture ),以及深度纹理 _CameraDepthTexture。 下面是完整的 URP Shader 模板,展示如何在不透明物体上读取屏幕颜色。

cs 复制代码
Shader "Custom/URP_ScreenSpaceUV_Base"{    Properties    {        [HideInInspector] _MainTex ("Texture", 2D) = "white" {}    }    SubShader    {        Tags { "RenderType"="Transparent" "Queue"="Transparent" "RenderPipeline"="UniversalPipeline" }

        Pass        {            HLSLPROGRAM            #pragma vertex   vert            #pragma fragment frag            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            // URP 提供的屏幕颜色纹理(需在 URP Asset 中开启 Opaque Texture)            TEXTURE2D(_CameraOpaqueTexture);            SAMPLER(sampler_CameraOpaqueTexture);

            struct Attributes {                float4 positionOS : POSITION; };

            struct Varyings {                float4 positionCS : SV_POSITION;                float4 screenPos  : TEXCOORD0; };

            Varyings vert(Attributes IN) {                Varyings OUT;                OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);                // 关键:在顶点阶段存储带 w 的屏幕坐标                OUT.screenPos = ComputeScreenPos(OUT.positionCS);                return OUT;            }

            half4 frag(Varyings IN) : SV_Target {                // 透视除法:得到真正的 [0,1] 屏幕 UV                float2 screenUV = IN.screenPos.xy / IN.screenPos.w;

                // 直接读取屏幕颜色                half4 sceneColor = SAMPLE_TEXTURE2D(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, screenUV);                return sceneColor;            }            ENDHLSL        }    }}

URP Asset 配置检查: Project Settings → Graphics → URP Asset,确保 Opaque Texture 已勾选,否则 _CameraOpaqueTexture 采样结果为黑色。

Shader Graph 等价节点

如果你使用 Shader Graph,只需在片元中添加 Screen Position 节点(模式选 Default ), 其输出的 xyzw 等价于 HLSL 中 ComputeScreenPos(clipPos) 的结果。 再接一个 Scene Color 节点即可采样 Opaque 纹理。

3实战一 --- 扫描线(Scan Line)

扫描线效果让物体表面叠加一条沿 Y 轴滚动的亮带,常用于科幻 HUD、 能量盾、传送特效。核心思路:对屏幕 UV 的 y 分量做正弦或取模运算, 生成周期性条纹,再乘以遮罩颜色叠加到 BaseColor 上。

参数说明: _ScanDensity 控制条纹数量(越大越密); _ScanSpeed 控制亮带滚动速度; _ScanBandWidth 控制亮带宽度(0.02~0.15 为宜)。

无需 UV 展开的优势

由于 screenUV 完全由屏幕坐标决定, 哪怕是一个没有任何展开 UV 的胶囊体或程序化网格,扫描线也能正确覆盖整个表面, 且条纹始终平行于屏幕水平线,不会随物体旋转而倾斜。

4实战二 --- 全屏水波纹(Screen Ripple)

全屏水波纹常用于水面、镜面折射或技能特效。做法是用正弦波对 screenUV偏移采样 : 把原始 UV 加上一个随时间变化的正弦偏移量,再去采样 _CameraOpaqueTexture, 就得到扭曲的背景投影。

双轴叠加: 将 x 和 y 方向分别用不同频率和相位的正弦波偏移, 可以得到更自然的"随机"水波效果,而非规整条纹。

与透明渲染队列配合

水波纹 Shader 需要读取 Opaque 纹理,因此 Render Queue 必须设为 Transparent (3000) 或更靠后, 确保场景中不透明物体已经渲染完成并写入了 _CameraOpaqueTexture

5实战三 --- 角色受击屏幕扭曲(Hit Distortion)

受击扭曲是一种全屏后处理风格效果:当角色受到攻击时, 屏幕画面短暂发生径向扭曲(以角色为中心向外扩散波纹)。 做法是在一个铺满屏幕的 Quad 上,根据距"冲击中心"的距离, 对 screenUV 施加径向偏移。

6常见问题与注意事项

问题现象 原因 解决方案
采样结果全黑 URP Asset 未开启 Opaque Texture Project Settings → URP Asset → 勾选 Opaque Texture
Y 轴上下颠倒 不同平台 UV 原点不同(DX vs GL) 使用 _ProjectionParams.x:若 < 0 则翻转 y 轴
扭曲效果在边缘拉伸 径向偏移未做边缘衰减 乘以 1 - saturate(dist / _Radius) 衰减因子
在移动端帧率下降 全屏采样带宽开销高 降低效果分辨率,或限制触发频率(节流 throttle)
VR 双眼错位 屏幕空间 UV 在单目渲染下各自独立 使用 unity_StereoEyeIndex 区分左右眼做偏移修正
相关推荐
LF男男5 小时前
TouchManager
unity·c#
mxwin6 小时前
Unity Shader 径向模糊与径向 UV 变形速度感 · 冲击波效果完全指南
unity·游戏引擎·shader·uv
weixin_423995006 小时前
unity 微信开发小游戏,网络资源获取数据
unity·游戏引擎
Yasin Chen7 小时前
Unity TMP_SDF 分析(五)片元着色器
unity·游戏引擎·着色器
mxwin8 小时前
Unity Shader Texture Bombing用随机旋转与偏移的多次采样,打破大地形纹理的
unity·游戏引擎
代数狂人8 小时前
《深入浅出Godot 4与C# 3D游戏开发》第二章:编辑器导航
3d·编辑器·游戏引擎·godot
zcc8580797628 小时前
Unity MVVM UniTask + 轻量级 ReactiveProperty
unity
zcc8580797629 小时前
Unity 自动生成UI绑定+MVVM 架构模板
unity
bigcarp10 小时前
告别pip,拥抱uv
pip·uv