unity 3d物体描边效果_Unity Shader 用深度图、边缘检测实现类似无主之地描边

本文介绍了如何使用边缘检测和深度图技术在漫画风格渲染中实现物体边缘的描边效果。通过卷积计算进行边缘检测,以及在后期处理中利用深度图检测物体边缘,调整阈值可优化效果。文中提供了具体的代码示例和对比图,展示了在不同场景下的应用,特别适合纹理单一的区域,但在复杂脸部细节处可能效果不佳。
摘要由CSDN通过智能技术生成

从下边两张图来看,这种漫画风格的线条出现在贴图存在线条的地方,同时物体的边缘也有这种描边效果。前者可以用边缘检测,后者可以用深度图。

2acbcab917ca44a7f3bd05dac4e127aa.png

64cf94963af78fbda3c7d931d571f440.png

边缘检测

边缘检测可以用卷积进行计算,具体可以参考这篇文章

https://gameinstitute.qq.com/community/detail/121016​gameinstitute.qq.com
b72322bb35200c00ede0ad8c71a6b458.png
            fixed luminance(fixed4 color){
                return color.r * 0.33 + color.g * 0.33 + color.b * 0.33;
            }

            half edge(half2 uv){
                const half Gx[9] = {-1,  0,  1,
                                -2,  0,  2,
                                -1,  0,  1};
                const half Gy[9] = {-1, -2, -1,
                                        0,  0,  0,
                                        1,  2,  1}; 
                half dx = 0, dy = 0;
                half center = luminance(tex2D(_MainTex, uv));
                for (int i = -1;i < 2;i++){
                    for (int j = -1;j < 2;j++){
                        half x = _MainTex_TexelSize.x * i;
                        half y = _MainTex_TexelSize.y * j;
                        half color = luminance(tex2D(_MainTex, uv + half2(x, y)));
                        dx += Gx[(i + 1) * 3 + j + 1] * color;
                        dy += Gy[(i + 1) * 3 + j + 1] * color;
                    }
                }
                return (abs(dx) + abs(dy)) * 10;
            }

            fixed4 frag (v2f i) : SV_Target{
                // -------------------
                float rate = 1;
                if (edge(i.uv) > _Edge)
                    rate = 0;

                col = col * diff * _Diffuse + spec * _Specular;
                return col * rate;
            }

放个对比图

38757fd22cfb8c5da845d218c8a33e28.png
整容前

857b48bb0db4e9cd30618549c908a10a.png
整容后

从上图来看,像胸部这些纹理较为单一的贴图部位描边的效果极好。就是脸部比较惨不忍睹。当然,可以通过调节阈值来控制效果。

深度图检测物体边缘

深度图的利用是在相机渲染时调用的,相当于一个后处理特效。

[ExecuteInEditMode]
public class PostEffect : MonoBehaviour
{
    public Material mat;
    private void OnRenderImage(RenderTexture source, RenderTexture destination) {
        Graphics.Blit(source, destination, mat);
    }
}

将这个脚本挂载到主摄像机上,并添加一个材质。在预览时便可以查看效果

Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Edge ("Edge", Range(0, 0.2)) = 0.1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float2 dxy : TEXCOORD1;
            };

            sampler2D _MainTex;
            sampler2D _CameraDepthTexture;
            float2 _MainTex_TexelSize;
            float _Edge;

            fixed luminance(fixed4 color){
                return color.r * 0.33 + color.g * 0.33 + color.b * 0.33;
            }

            half edge(half2 uv, float2 xy){
                half offset = 0;
                half center = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, uv)));
                for (int i = -1;i < 2;i++){
                    for (int j = -1;j < 2;j++){
                        offset += center - 
                        Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, uv + 
                        half2(i * xy.x, j * xy.y))));
                        //offset += abs(center - luminance(tex2D(_MainTex, uv + half2(i, j))));
                    }
                }
                return abs(offset) * 10000;
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.dxy = _MainTex_TexelSize.xy;

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);//fixed4(depth, depth, depth, 1);
                float rate = saturate(edge(i.uv, i.dxy));
                
                rate = rate > _Edge ? 0 : 1;
                
                return color * rate;// fixed4(rate, rate, rate, 1);
            }
            ENDCG
        }
    }

此处有几个关键点,一是深度值的获取,分别是深度图的声明、采样、非线性转换;二是像素点对应 UV 坐标的获取,这里使用了_MainTex_TexelSize,对应的是单位像素的长宽,需要先声明。深度图是自动生成的,使用前需要先声明,名字固定为_CameraDepthTexture,采样之后再使用UNITY_SAMPLE_DEPTH采样,最后用Linear01Depth将深度值转换到 0 ~ 1 区间。检测方法也很简单将周围的像素深度值分别去减当前像素深度值,如果当前像素周围很平滑,那么结果必然趋近于零,反之则变大,尤其是物体最外侧的像素点。

效果如下图

763c4ecf7b75bf91e40f35ae7e5e260b.png

有意思的是,在将阈值调低之后,可以看见模型的三角形

f7824920e5d37be07eabe18e25fa68ca.png
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值