Unity 对选中物体实现描边效果(方法1:指定layer的外轮廓渲染)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l773575310/article/details/78701756

Unity 基于屏幕后处理的描边效果(指定Layer的物体)

Unity Shader 学习笔记(19) 屏幕后处理效果、调整亮度、饱和度、对比度
Unity Shaderlab: Object Outlines
Unity Shader-描边效果
完整源码:https://github.com/ZeroChiLi/ShaderTest


原理

  添加额外相机(位置等信息等同原相机),将指定Layer的可见物体渲染出物体所占的面积,并把渲染的纹理存到一个临时渲染纹理(RenderTexture)中,再通过这个纹理扩展出外边,最后将这个外边和原场景纹理混合即可。

DrawOutline类继承于PostEffectsBase类,介绍见这里


实现


原始场景:包含左边一个有剔除片元的立方体,右边半透明立方体,以及没有在”Outline”层的右边小球。

1. 初始化配置额外相机


额外相机不需要enable,渲染处理都在代码中实现


额外相机默认渲染效果

DrawOutline类,添加在主相机对象。在对象Awake时初始化额外相机(不需要active 或 enable)。

private void Awake()
{
    additionalCamera.CopyFrom(MainCamera);
    additionalCamera.clearFlags = CameraClearFlags.Color;
    additionalCamera.backgroundColor = Color.black;
    additionalCamera.cullingMask = 1 << LayerMask.NameToLayer("Outline");       // 只渲染"Outline"层的物体
}

2. 渲染物体所占面积

小技巧:直接Fallback OFF。

DrawOutline类中,OnRenderImage接口实现屏幕后处理。

void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (TargetMaterial != null && drawOccupied != null && additionalCamera != null)
        {
            RenderTexture TempRT = RenderTexture.GetTemporary(source.width, source.height, 0);
            TempRT.Create();
            additionalCamera.targetTexture = TempRT;

            // 额外相机中使用shader,绘制出物体所占面积
            additionalCamera.RenderWithShader(drawOccupied, "");

            Graphics.Blit(TempRT, destination);
            TempRT.Release();
        }
        else
            Graphics.Blit(source, destination);
    }

绘制所在面积的shader:DrawOccupied.shader。

Shader "Custom/DrawOccupied"
{
    FallBack OFF
}

3. 绘制外边与混合原场景


基于上面面积,扩展每个像素的颜色(类似高斯模糊),黑色值全为0,所以无法扩展


最后将黑色部分,和原占面积(粉红色部分),渲染为原场景。

在上一步绘制完面积后,添加修改如下代码:

// TargetMaterial为含有描绘混合shader的材质
// 设置相关属性,source为接口传入的原场景纹理
TargetMaterial.SetTexture("_SceneTex", source);
TargetMaterial.SetColor("_Color", outlineColor);
TargetMaterial.SetInt("_Width", outlineWidth);
TargetMaterial.SetInt("_Iterations", iterations);

// 使用描边混合材质实现描边效果
Graphics.Blit(TempRT, destination, TargetMaterial, 0);

DrawOutline.shader:绘制描边效果的shader


Shader "Custom/DrawOutline"
{
    Properties
    {
        _MainTex("Main Texture",2D)="black"{}           // 绘制完物体面积后纹理
        _SceneTex("Scene Texture",2D)="black"{}         // 原场景纹理
        _Color("Outline Color",Color) = (0,1,0,1)       // 描边颜色
        _Width("Outline Width",int) = 4                 // 描边宽度(像素级别)
        _Iterations("Iterations",int) = 3               // 描边迭代次数(越高越平滑,消耗越高,复杂度O(n^2))
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM

            sampler2D _MainTex;
            float2 _MainTex_TexelSize;
            sampler2D _SceneTex;
            fixed4 _Color;
            float _Width;
            int _Iterations;

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

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

            v2f vert (appdata_base v) 
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord.xy;  
                return o;
            }

            half4 frag(v2f i) : COLOR 
            {
                // 迭代为奇数,保证对称
                int iterations = _Iterations * 2 + 1;
                float ColorIntensityInRadius;
                float TX_x = _MainTex_TexelSize.x * _Width;
                float TX_y = _MainTex_TexelSize.y * _Width;

                // 类似高斯模糊,只是这里的核权重不重要,只要最后计算大于0,说明该像素属于外边范围内。
                for(int k = 0;k < iterations;k += 1)
                    for(int j = 0;j < iterations;j += 1)
                        ColorIntensityInRadius += tex2D(_MainTex, i.uv.xy + float2((k - iterations/2) * TX_x,(j - iterations/2) * TX_y));

                // 如果该像素有颜色(原来所占面积),或者该像素不在外边范围内,直接渲染原场景。否则就渲染为外边颜色。
                if(tex2D(_MainTex,i.uv.xy).r > 0 || ColorIntensityInRadius == 0)
                    return tex2D(_SceneTex, i.uv);
                else
                    return _Color.a * _Color + (1 - _Color.a) * tex2D(_SceneTex, i.uv);// 通过输入颜色的透明度来混合原场景。
            }
            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/l773575310/article/details/78701756