Unity3D ShaderLab 物体相交

物体相交

利用深度图来做效果,不提供数学原理,只提供Shader脚本实现。

思路

已知需要渲染物体的深度信息,通过与_CameraDepthTexture纹理中的深度值进行一些计算或者判定即可完成相交的效果。

计算方式:在视图(View)坐标系下,假设物体深度值为 e y e Z eyeZ eyeZ,深度缓存中的深度值为 s c e n e Z sceneZ sceneZ,相交宽度为 I n t e r s e c t i o n W i d t h IntersectionWidth IntersectionWidth;通过 a b s ( e y e Z − s c e n e Z ) / I n s t e r s e c t i o n W i d t h abs(eyeZ - sceneZ)/InstersectionWidth abs(eyeZsceneZ)/InstersectionWidth可以得到 [ 0 , n ] [0, n] [0,n]的一个值,将 [ 0 , n ] [0, n] [0,n]截断到 [ 0 , 1 ] [0, 1] [0,1],最后使用lerp函数实现一个过渡。

_CameraDepthTexture纹理记录的是NDC空间下的深度信息,因此需要转到视图(View)空间下。

实现

Shader "Hidden/IntersectionHighlight"
{
    Properties
    {
        _IntersectionColor("Intersection Color", Color) = (1,1,0,0)
        _IntersectionWidth("Intersection Width", Range(0, 2)) = 0.1
    }
    SubShader
    {
        Pass
        {
            // 关闭深度写入, 否则渲染会出现问题
            ZWrite Off
            // 关闭背面剔除, 渲染双面
            Cull Off
            // 开blend, 在片段后会进行blend混合
            Blend SrcAlpha One
            
            Tags {
                "RenderType" = "Opaque"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 screenPos : TEXCOORD1;
                float eyeZ : TEXCOORD2;
            };

            // 深度缓存图
            sampler2D _CameraDepthTexture;
            
            // 相交颜色
            fixed4 _IntersectionColor;
            // 相交宽度
            float _IntersectionWidth;

            v2f vert (appdata v)
            {
                v2f o;
                // o.vertex为裁剪空间中的点
                o.vertex = UnityObjectToClipPos(v.vertex);
                // 返回齐次坐标系下的点, 范围是[0, w]; 其中,w不是viewport的width (ps: 需要与tex2Dproj函数配套使用)
                o.screenPos = ComputeScreenPos(o.vertex);
                // 将v.vertex与model和view矩阵相乘, 得到相机到物体的z坐标, 为view坐标系下
                COMPUTE_EYEDEPTH(o.eyeZ);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // UNITY_PROJ_COORD(a)应该是预留的一个接口, 官方解释为大部分平台将返回入参值
                // SAMPLE_DEPTH_TEXTURE_PROJ(tex, uv)内部调用tex2Dproj(tex, uv), tex2Dproj会将(uv = uv / w)
                // LinearEyeDepth(depth)将返回depth在view坐标系下的表示, 源码中的_ZBufferParams是相机中far和near两个参数表达式的结果值
                float screenZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));
                // 相交计算, abs(物体的深度值 - 深度缓存的深度值) / 长度 => [0, n]的范围 => saturate限制到[0, 1]
                float diff = saturate(abs(i.eyeZ - screenZ) / (_IntersectionWidth / 2));
                // (0.0 -> 1.0) => (最亮 -> 最暗), diff => color
                return lerp(_IntersectionColor, float4(0.0, 0.0, 0.0, 0.0), diff);
            }
            ENDCG
        }
    }
}

效果

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值