Unity 3D片元着色器中基于相机距离修改颜色Alpha值异常问题
Unity Wireframe着色器距离淡入淡出问题修复
问题描述
新增了
_FadeDistance变量,试图在片元着色器中基于相机距离修改_EdgeColor的Alpha通道以实现透明度变化,但颜色Alpha值始终固定。
问题根源
- 坐标空间错误:
WorldSpaceViewDir函数要求传入世界空间顶点坐标,但当前代码中使用的是裁剪空间的i.vertex(SV_POSITION语义),导致距离计算完全失效。 - 透明度未正确传递:修改
_EdgeColor.w后,片元输出时直接使用1 - t作为Alpha,完全忽略了距离计算的结果。 - 淡入淡出逻辑反向:原逻辑是距离超过阈值后透明度提升,不符合“越远越透明”的常规需求。
修复后的完整代码
Shader "Ogxd/Wireframe" { Properties { _FadeDistance("Fade Distance", Float) = 3.0 _EdgeColor("Edge Color", Color) = (0 ,0 ,0 ,1) _WireframeThickness("Thickness", Range(0.0, 3.0)) = 0.5 [Toggle()] _FixedWidth("Relative to Screen", Float) = 0.0 _WireframeSmoothing("Smoothing", Range(0.0, 3.0)) = 1.0 [Toggle(_QUADIFY)] _Quadify("Quadify", Float) = 0.0 } SubShader { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True" } Pass { Name "Wireframe" Cull Back ZWrite On Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma glsl #pragma vertex vert #pragma geometry geom #pragma fragment frag #pragma multi_compile __ _QUADIFY #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2g { float4 vertex : POSITION; // 世界空间位置 float4 clipPos : TEXCOORD0; // 裁剪空间位置(后续传递给片元) }; struct g2f { float4 vertex : SV_POSITION; float3 bary : TEXCOORD1; float4 worldPos : TEXCOORD2; // 新增:传递世界空间位置到片元 }; v2g vert(appdata v) { v2g o; o.vertex = mul(unity_ObjectToWorld, v.vertex); // 世界空间位置 o.clipPos = UnityObjectToClipPos(v.vertex); // 提前计算裁剪空间位置 return o; } [maxvertexcount(3)] void geom(triangle v2g i[3], inout TriangleStream<g2f> triStream) { float3 param = float3(0.0, 0.0, 0.0); #ifdef _QUADIFY float A = length(i[0].vertex - i[1].vertex); float B = length(i[1].vertex - i[2].vertex); float C = length(i[2].vertex - i[0].vertex); if (A > B && A > C) param.y = 1.0; else if (B > C && B > A) param.x = 1.0; else param.z = 1.0; #endif g2f o; o.vertex = i[0].clipPos; // 使用提前计算的裁剪空间位置 o.bary = float3(1.0, 0.0, 0.0) + param; o.worldPos = i[0].vertex; // 传递世界空间位置 triStream.Append(o); o.vertex = i[1].clipPos; o.bary = float3(0.0, 0.0, 1.0) + param; o.worldPos = i[1].vertex; triStream.Append(o); o.vertex = i[2].clipPos; o.bary = float3(0.0, 1.0, 0.0) + param; o.worldPos = i[2].vertex; triStream.Append(o); } float _WireframeThickness; float _WireframeSmoothing; float _FixedWidth; float _FadeDistance; float4 _EdgeColor; half4 frag(g2f i) : SV_Target { float3 bary = i.bary; float3 deltas = fwidth(bary); float3 smoothing = deltas * _WireframeSmoothing; float3 thickness = deltas * _WireframeThickness / max((1 - _FixedWidth) * i.vertex.w, _FixedWidth); bary = smoothstep(thickness, thickness + smoothing, bary); float t = min(bary.x, min(bary.y, bary.z)); // 计算世界空间下的相机距离 float distanceToCam = length(WorldSpaceViewDir(i.worldPos)); // 计算淡入淡出Alpha:距离小于_FadeDistance时完全不透明,超过后逐渐透明 float fadeAlpha = 1 - clamp(distanceToCam / _FadeDistance, 0.0, 1.0); // 结合线框Alpha和距离淡入淡出Alpha float finalAlpha = (1 - t) * _EdgeColor.a * fadeAlpha; return half4(_EdgeColor.rgb, finalAlpha); } ENDCG } } }
关键修改点
- 在
v2g和g2f结构体中新增世界空间位置的传递,确保WorldSpaceViewDir能正确计算相机距离。 - 调整淡入淡出逻辑为距离越远,透明度越高,符合常规需求。
- 将线框自身的Alpha(
1 - t)、颜色设置的Alpha(_EdgeColor.a)和距离淡入淡出Alpha(fadeAlpha)相乘,得到最终透明度。
内容的提问来源于stack exchange,提问作者Diveye
相关产品推荐
相关产品推荐

