You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 3D片元着色器中基于相机距离修改颜色Alpha值异常问题

Unity Wireframe着色器距离淡入淡出问题修复

问题描述

新增了_FadeDistance变量,试图在片元着色器中基于相机距离修改_EdgeColor的Alpha通道以实现透明度变化,但颜色Alpha值始终固定。

问题根源

  1. 坐标空间错误:WorldSpaceViewDir函数要求传入世界空间顶点坐标,但当前代码中使用的是裁剪空间的i.vertex(SV_POSITION语义),导致距离计算完全失效。
  2. 透明度未正确传递:修改_EdgeColor.w后,片元输出时直接使用1 - t作为Alpha,完全忽略了距离计算的结果。
  3. 淡入淡出逻辑反向:原逻辑是距离超过阈值后透明度提升,不符合“越远越透明”的常规需求。

修复后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:24:50