返回
Featured image of post URP-可交互护盾

URP-可交互护盾

效果图

思路

这个护盾可以拆分为以下几个效果

  • 旋转UV贴图
  • 菲涅尔反射
  • 深度计算
  • 扫光
  • 点击区域光

首先是旋转的UV贴图,无需过多介绍。

                float2 uv = i.uv;                
                uv.x += _Time.y * 0.2;//滚uv,以每秒钟滚0.2圈的速度旋转
                
                half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * _BaseColor;

其次是菲涅尔反射,公式计算方法在之前的章节也有讲过

接下来是深度空间计算,效果演示中,可以提高深度偏差属性来突出效果

                //开始计算屏幕uv
                i.TtoW0.xy /= i.TtoW0.w;//透除
                
                #ifdef UNITY_UV_STARTS_AT_TOP//判断当前的平台是openGL还是dx
                    i.TtoW0.y = 1 - i.TtoW0.y;
                #endif

                //计算缓冲区深度,模型深度
                float4 depthcolor = tex2D(_CameraDepthTexture, i.TtoW0.xy);
                float depthbuffer = Linear01Depth(depthcolor, _ZBufferParams);//得到线性的深度缓冲
                
                //计算模型深度
                float depth = i.pos.z;
                depth = Linear01Depth(depth, _ZBufferParams);//得到模型的线性深度
                float edge = saturate(depth - depthbuffer + 0.005) * 100 * _depthoffset;//计算接触光
                return edge;

然后是扫光效果

                float flow = saturate(pow(1 - abs(frac(i.TtoW1.y * 0.3 - _Time.y * 0.2) - 0.5), 10) * 0.3);
                float4 flowcolor = flow * _emissioncolor;
                return flowcolor*2;

最后是点击后的圆形区域,点击中心为摄像机发射射线与球形的碰撞盒相交点,由C#监听碰撞检测后将坐标传给材质。

                //计算点击区域
                float4 dt = smoothstep(_MaskRadius + _MaskSmooth, _MaskRadius - _MaskSmooth, distance(i.TtoW1, _InteractPoint)) * _CollisionColor * _Toggle;

最后将这些效果进行叠加,就得到了我们当前的护盾效果

return float4(tex.xyz, tex.w + edge + fre) + flowcolor + dt;

完整代码

C#脚本

using UnityEngine;

public class ShieldClick : MonoBehaviour
{   
    //护盾对象
    public GameObject _gameObject;

    private Material material;
    private Ray ray;
    private RaycastHit hit;

    private void Start()
    {
        material = _gameObject.GetComponent<MeshRenderer>().material;
    }

    private void Update()
    {
        if (Input.GetMouseButton(0))
        {
            // 主相机屏幕点转换为射线
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            //射线碰到了物体
            if (Physics.Raycast(ray, out hit,10000))
            {
                if (hit.collider.gameObject== _gameObject)
                {
                    material.SetVector("_InteractPoint", hit.point);
                    material.SetFloat("_Toggle", 1);//使用toggle控制是否输入顶点有效
                }               
                Debug.Log(hit.collider.gameObject.name);
                Debug.DrawLine(ray.origin, hit.point, Color.red);
            }           
        }
        if (Input.GetMouseButtonUp(0))
        {
            material.SetFloat("_Toggle", 0);
        }
    }   
}

Shader

Shader "URP/Shield"
{
    Properties
    {
        _MainTex ("MainTex", 2D) = "white" { }
        _BaseColor ("BaseColor", Color) = (1, 1, 1, 1)
        _depthoffset ("深度偏差", float) = 1
        [HDR]_emissioncolor ("EmissionColor", Color) = (1, 1, 1, 1)
        
        _MaskRadius ("遮罩半径", Range(0, 3)) = 0
        _MaskSmooth ("羽化", Range(0, 1)) = 0
        [HDR]_CollisionColor ("碰撞颜色", Color) = (1, 1, 1, 1)
    }
    
    SubShader
    {
        Tags { "RenderPipeline" = "UniversalRenderPipeline" "Queue" = "Transparent" }
        
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        
        CBUFFER_START(UnityPerMaterial)
        float4 _MainTex_ST;
        half4 _BaseColor;
        float _depthoffset;
        float4 _emissioncolor;
        
        float4 _InteractPoint;
        float _Toggle;
        float _MaskSmooth;
        float4 _CollisionColor;
        float _MaskRadius;
        CBUFFER_END
        
        // 声明纹理与采样器
        TEXTURE2D(_MainTex);
        SAMPLER(sampler_MainTex);
        // 声明深度纹理
        SAMPLER(_CameraDepthTexture);
        
        struct a2v
        {
            float4 vertex: POSITION;
            float4 normal: NORMAL;
            float2 texcoord: TEXCOORD;
        };
        
        struct v2f
        {
            float4 pos: SV_POSITION;
            float2 uv: TEXCOORD0;
            float4 TtoW0: TEXCOORD1;
            float3 TtoW1: TEXCOORD2;
            float3 TtoW2: TEXCOORD3;
        };
        
        ENDHLSL
        
        pass
        {
            Tags { "LightMode" = "UniversalForward" "RenderType" = "Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha
            
            HLSLPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            v2f vert(a2v i)
            {
                
                v2f o;
                // 转换到裁剪空间
                o.pos = TransformObjectToHClip(i.vertex.xyz);
                // 使用纹理的属性_ST对顶点纹理进行变化
                o.uv = TRANSFORM_TEX(i.texcoord, _MainTex);
                
                //屏幕坐标TtoW0,xy保存为未透除的屏幕uv,zw不变
                o.TtoW0.xy = o.pos.xy * 0.5 + 0.5 * float2(o.pos.w, o.pos.w);
                o.TtoW0.zw = o.pos.zw;
                
                o.TtoW1 = TransformObjectToWorld(i.vertex.xyz);
                
                o.TtoW2 = normalize(TransformObjectToWorldNormal(i.normal.xyz));
                
                return o;
            }
            
            half4 frag(v2f i): SV_TARGET
            {                
                float2 uv = i.uv;                
                uv.x += _Time.y * 0.2;//滚uv,以每秒钟滚0.2圈的速度旋转
                
                half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv) * _BaseColor;
                //return tex;
             
                //菲涅尔
                half3 worldViewDir = normalize(_WorldSpaceCameraPos - (i.TtoW1));
                float ndotv = dot(i.TtoW2, worldViewDir);
                float fre = (0.2 + 2.0 * pow(1.0 - ndotv, 2.0));
                //return fre;

                //开始计算屏幕uv
                i.TtoW0.xy /= i.TtoW0.w;//透除
                
                #ifdef UNITY_UV_STARTS_AT_TOP//判断当前的平台是openGL还是dx
                    i.TtoW0.y = 1 - i.TtoW0.y;
                #endif

                //计算缓冲区深度,模型深度
                float4 depthcolor = tex2D(_CameraDepthTexture, i.TtoW0.xy);
                float depthbuffer = Linear01Depth(depthcolor, _ZBufferParams);//得到线性的深度缓冲
                
                //计算模型深度
                float depth = i.pos.z;
                depth = Linear01Depth(depth, _ZBufferParams);//得到模型的线性深度
                float edge = saturate(depth - depthbuffer + 0.005) * 100 * _depthoffset;//计算接触光
                //return edge;
                
                //计算扫光,这是一个做特效的通用公式
                float flow = saturate(pow(1 - abs(frac(i.TtoW1.y * 0.3 - _Time.y * 0.2) - 0.5), 10) * 0.3);
                float4 flowcolor = flow * _emissioncolor;
                //return flowcolor*2;
                
                //计算点击区域
                float4 dt = smoothstep(_MaskRadius + _MaskSmooth, _MaskRadius - _MaskSmooth, distance(i.TtoW1, _InteractPoint)) * _CollisionColor * _Toggle;
                //return dt;
                       
                return float4(tex.xyz, tex.w + edge + fre) + flowcolor + dt;
            }
            ENDHLSL
            
        }
    }
    FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}

其他

关于这种可以分开的六边形护盾提供下思路。

在建模时每个六边形的顶点是独立的,而非相邻三个六边形共享一个顶点。当他们顶点相互独立时,我们就可以在顶点着色器中对其进行偏移,以此来达到六边形向外扩展和产生缝隙的效果。

Licensed under CC BY-NC-SA 4.0
0