如何将顶点着色器的曲面修改传递给Unity表面着色器
实现Subway Surfer风格的曲面效果(结合Unity Standard表面着色器)
嘿,你已经找对路子了!用表面着色器的vertex:vert指令自定义顶点变换,同时保留Standard光照管线的效果,这个思路完全正确。你的测试代码里给X轴加偏移的逻辑没问题,只需要把原来的曲面计算逻辑正确迁移到顶点函数里,再注意几个关键细节就能搞定。
修正后的完整Shader代码
Shader "Custom/CurvedSurface" { Properties{ _Color("Color", Color) = (1,1,1,1) _MainTex("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 _QOffset("Curve Offset (X,Y,Z)", Vector) = (0,1,0,0) // 默认Y轴偏移,匹配Subway Surfer地面向上弯曲的效果 _Dist("Curve Distance Factor", Float) = 100.0 } SubShader{ Tags{ "RenderType" = "Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows vertex:vert addshadow #pragma target 3.0 sampler2D _MainTex; half _Glossiness; half _Metallic; fixed4 _Color; float4 _QOffset; float _Dist; struct Input { float2 uv_MainTex; }; void vert(inout appdata_full v) { // 1. 把模型空间顶点转换到视图空间(和你找到的原始Shader逻辑对齐) float4 worldPos = mul(unity_ObjectToWorld, v.vertex); float4 viewPos = mul(UNITY_MATRIX_V, worldPos); // 2. 计算基于深度的二次偏移因子——距离相机越远,弯曲程度越大 float zOff = viewPos.z / _Dist; viewPos += _QOffset * zOff * zOff; // 3. 把修改后的视图空间位置转换回模型空间,赋值给顶点 v.vertex = mul(unity_WorldToObject, mul(UNITY_MATRIX_V_INV, viewPos)); } void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
核心细节解释
- 空间转换的重要性:原始Shader是在视图空间计算弯曲的,这样效果会跟随相机视角(地面越远越向上弯),完美匹配Subway Surfer的视觉体验。如果直接修改模型空间的顶点,弯曲效果会和模型绑定,而不是相机视角,这就不符合需求了。
- 阴影正确显示:一定要加上
addshadow指令!它会让Unity自动生成适配弯曲顶点的阴影投射器,不然阴影会还是原来的平面形状,穿帮感拉满。 - 参数调整技巧:
_QOffset的Y值设为正数时,地面向上弯;调X值可以左右弯,按需调整。_Dist越小,弯曲效果越强烈,你可以根据场景的大小慢慢试出合适的值。
踩坑提醒
- 必须用透视相机!正交相机下视图空间的Z值变化逻辑不一样,弯曲效果会完全不对。
- 这个Shader只给场景静态物体(地面、轨道)用就行,别给玩家角色、道具用,不然它们也会跟着弯曲,显得很奇怪。
- 如果你的场景里有动态阴影(比如实时光影),
fullforwardshadows指令已经帮你处理了大部分情况,不用额外写阴影代码。
内容的提问来源于stack exchange,提问作者Carlos Hdez Barbera
相关产品推荐
相关产品推荐

