unity 模型描边中间透明(shader)

1.记录找到的一个模型描边中间透明的shader,大概就是下图Cube这样.

2.脚本

Shader "Unlit/Shaer"
{
    Properties{
    _Color("Main Tint", Color) = (1, 1, 1, 1)
        _MainTex("Texture", 2D) = "white" {}
    _OutlineWidth("OutlineWidth", Range(0, 1)) = 0.1
        _OutlineColor("OutlineColor", Color) = (1, 1, 1, 1)
        _AlphaBlend("AlphaBlend", Range(0, 1)) = 0.8  // 用于在透明纹理的基础上控制整体的透明度
        _AlphaTest("AlphaTest", Range(0, 1)) = 0.5
}
SubShader
{
    // RenderType标签可以让Unity把这个Shader归入到提前定义的组(Transparent)用于指明该Shader是一个使用了透明度混合的Shader
    // IgnoreProjector=True这意味着该Shader不会受到投影器(Projectors)的影响
    // 为了使用透明度混合的Shader一般都应该在SubShader中设置这三个标签
    Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }


    //第一个Pass,开启深度写入,不输出颜色,处理AlphaTest部分
    Pass{
        // 仅仅是把模型的深度信息写入深度缓冲中从而剔除模型中被自身遮挡的片元


        ZWrite On


        // 用于设置颜色通道的写掩码(Wirte Mask)ColorMask RGB|A|0|(R/G/B/A组合)
        // 当为0时意味着该Pass不写入任何颜色通道,也就不会输出任何颜色
        //也可以不用,这样就能刻意控制AlphaTest导致的发丝边缘的颜色
        ColorMask 0


        CGPROGRAM


        #pragma vertex vert  
        #pragma fragment frag  
        #include "Lighting.cginc"  


        sampler2D _MainTex;
        float4 _MainTex_ST;
        fixed _AlphaTest;


        struct a2v {
        float4 vertex : POSITION;
        float4 texcoord : TEXCOORD0;
        };


        struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD2;
        };


        v2f vert(a2v v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
        return o;
        }


        fixed4 frag(v2f i) : SV_Target{
        fixed4 texColor = tex2D(_MainTex, i.uv);

        clip(texColor.a - _AlphaTest); //进行AlphaTest     //clip函数,但参数为负数则舍弃该片元输出

        return fixed4(0,0,0,1);
        }
        ENDCG
        }
    //第二个Pass,关闭深度写入,处理AlphaBlend半透明,处理光照等部分
    Pass{
            // 向前渲染路径的方式
            Tags{ "LightMode" = "ForwardBase" }

            Cull Off //关闭剔除,让头发双面显示

            ZWrite Off //关闭深度写入  
            //透明度混合需要关闭深度写入    
            //Orgb = SrcAlpha * Srgb + OneMinusSrcAlpha * Drgb
            //Oa = SrcAlpha * Sa + OneMinusSrcAlpha * Da
            //将本Shader计算出的颜色值(源颜色值) * 源Alpha值 + 目标颜色值(可以理解为背景色) * (1-源Alpha值),从而让源物体展示出了(1-alpha)的透明度。
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM

            #pragma vertex vert  
            #pragma fragment frag  
            #include "Lighting.cginc"  

            fixed4  _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaBlend;

            struct a2v {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
            };

            struct v2f {
            float4 pos : SV_POSITION;
            float3 worldNormal : TEXCOORD0;
            float3 worldPos : TEXCOORD1;
            float2 uv : TEXCOORD2;
            };

            v2f vert(a2v v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.worldNormal = UnityObjectToWorldNormal(v.normal);
            o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            return o;
            }

            fixed4 frag(v2f i) : SV_Target{
            fixed3 worldNormal = normalize(i.worldNormal);
            fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
            fixed4 texColor = tex2D(_MainTex, i.uv);
            fixed3 albedo = texColor.rgb * _Color;
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
            fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
            // 设置透明度通道的值
            return fixed4(ambient + diffuse, texColor.a * _AlphaBlend);
            }
            ENDCG
            }
            //第三个Pass,处理描边
            Pass{
            Cull Front //剔除正面 

            CGPROGRAM
            #pragma vertex vert  
            #pragma fragment frag         
            #include "UnityCG.cginc"  


            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _AlphaTest;
            float _OutlineWidth;
            float4 _OutlineColor;

            struct a2v
            {
            float4 vertex : POSITION;
            float4 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
            };

            struct v2f
            {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD2;
            };
            v2f vert(a2v v)
            {
            v2f o;
            //o.pos = mul(UNITY_MATRIX_MVP, v.vertex);    
            //float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal); 
            //float2 offset = TransformViewToProjection(normal.xy);
            //o.pos.xy += offset *o.pos.z * _OutlineWidth;

            //方法二:
            //o.pos = UnityObjectToClipPos(v.vertex);
            //float3 normal = UnityObjectToWorldNormal(v.normal);
            //normal = mul(UNITY_MATRIX_VP, normal);
            //o.pos.xyz += normal * _Outline;
            float4 pos = mul(UNITY_MATRIX_MV, v.vertex);   // viewPos,顶点变换到视角空间
            float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);  // viewNormal,UNITY_MATRIX_IT_MV这个针对方向向量,法线变换
            normal.z = -0.5;   // viewNormal 对顶点法线的z分量进行处理,使他们等于一个定值
            float4 newNoraml = float4(normalize(normal), 0); //归一化后的noraml
            pos = pos + newNoraml * _OutlineWidth;  // viewPos 沿法线方向对顶点进行扩大
            o.pos = mul(UNITY_MATRIX_P, pos);
            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            return o;
            }


            fixed4 frag(v2f i) : SV_Target
            {
            fixed4 texColor = tex2D(_MainTex, i.uv);
            //AlphaTest为了让描边也能根据透贴部分来勾边,也可以不加
            clip(texColor.a - _AlphaTest);

            return _OutlineColor;
            }
            ENDCG
            }
}
FallBack "Diffuse"
}

 3.使用方法

在unity里面创建一个Shader,把上面的脚本复制粘贴进去.然后创建一个材质球,把我们前面创建好的shader贴上去,最后把他给到我们的模型就行了,

可以在里面调节描边的颜色,和描边的线条的宽度.

4.最后:非原创,

 

### Unity透明 3D 物体遮挡剔除问题分析 在 Unity 的场景渲染中,处理透明材质的物体可能会遇到一些特殊的挑战。当涉及到 **Occlusion Culling**(遮挡剔除)时,由于其核心逻辑依赖于不透明几何体来判断可见性,因此对于透明对象的行为可能不符合预期。 #### 遮挡剔除的工作原理 Unity 提供了一些工具帮助开发者理解遮挡剔除的结果。通过启用 Occlusion Window 中的 Visualization 功能并选择相机,可以可视化哪些部分被标记为不可见[^1]。然而,默认情况下,这些工具主要针对的是不透明的对象,而透明对象通常不会参与此过程中的计算。 #### 处理透明物体会带来的问题 透明材质本身并不适合传统的遮挡剔除算法,因为它们允许光线穿过表面从而影响其他物体的可见度。如果尝试直接应用标准的遮挡剔除到具有半透效果或者完全透明区域上的模型上,则可能导致错误裁剪或不必要的绘制调用: - 半透明物体无法正确阻挡视线路径下的后续图元。 - 如果设置不当,某些本应显示的部分也可能因误判而消失。 为了改善这种情况,在项目开发过程中需要注意以下几点建议: #### 解决方案一:调整层掩码配置 可以通过修改摄像机视锥内的分组策略以及每类资源所属层次结构关系来进行优化管理。具体操作如下所示: ```csharp // 设置特定LayerMask只作用于非透明物件 Camera.main.cullingMask &= ~(1 << LayerMask.NameToLayer("Transparent")); ``` 上述脚本片段展示了如何利用 `Culling Mask` 属性排除掉指定标签名为 `"Transparent"` 的所有游戏对象参与最终帧缓冲区更新流程[^2]。 #### 解决方案二:自定义Shader实现更精确控制 创建一个新的着色器程序文件(.shader),其中加入额参数选项用于区分不同类型的像素贡献权重值,并将其应用于目标网格组件之上。下面是一个简单的例子演示了基于距离衰减因子调节Alpha通道强度的方法: ```hlsl Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader{ Tags { "Queue"="Transparent" } Blend SrcAlpha OneMinusSrcAlpha Pass{ CGPROGRAM #pragma vertex vert #pragma fragment frag sampler2D _MainTex; struct appdata_t { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; fixed fog : COLOR0; }; v2f vert (appdata_t v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); // Calculate distance-based alpha modulation here... half dist = length(o.pos.xy / o.pos.w * _ScreenParams.xy - mul(UNITY_MATRIX_MV,v.vertex).xy); o.fog = saturate(dist*0.01); return o; } fixed4 frag (v2f i):SV_Target{ fixed4 col = tex2D(_MainTex,i.uv); col.a *= lerp(1.,col.a*(1.-i.fog),_FogDensity); return col; } ENDCG } } ``` 该 HLSL 脚本实现了根据屏幕空间位置动态改变颜色透明度的功能,使得远处的小细节逐渐淡出视野范围之而不至于突然截断边界线轮廓形状。 #### 结论 尽管 Unity 默认提供的遮挡剔除机制难以完美支持复杂场景下含有多重叠加效应的透明材质表现形式,但借助合理规划资产分类体系加上定制化图形管线设计思路仍可有效缓解此类难题所带来的困扰。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值