做一款基于子弹时间的小游戏(视效篇二)

描边效果

希望游戏的整体风格偏向卡通风格,因此需要对模型进行描边处理。
一开始采用的是双Pass的顶点扩张的方法进行描边,但效果并不理想,因为有些模型的法线并不光滑,导致出现描边断层,比如立方体的描边,由于每个顶点被三个三角面共享,且法线方向并不相同,导致顶点分离:
在这里插入图片描述
而且利用双Pass进行渲染,会导致无法批处理优化,该游戏的场景由瓦片元素生成,虽然数量较多,但模型种类并不多,大多数模型和纹理都是一致的,所以易与批处理。因此描边方案仍旧基于后处理,利用深度差值来判断轮廓。比如一个放置于空场景的模型,模型本身的深度差异并不大,但与无限远的空场景的深度有着明显差异,所以利用像素的深度差值可以判断是否存在轮廓。
由于深度纹理采用物体的阴影投射Pass获得,所以对于那些不希望进行描边的物体,可以删除其阴影投射Pass,比如下图中的狼牙小球。
在这里插入图片描述

步骤

  • 设置开启获得深度纹理
  cam.depthTextureMode |= DepthTextureMode.DepthNormals;
  //开启后会将纹理数据传给shader 的sampler2D _CameraDepthTexture变量
  • 定义参数变量
public Color edgeColor = Color.black;
public float sampleDistance = 1.0f;
public float sensitivityDepth = 1.0f;
  • shader中获取存储纹素大小的变量(需要获取渲染纹理的单个像素在UV坐标的距离)
Half4 _MainTex_TexelSize
  • shader中获取纹理仿射系数
Float4 _MainTex_ST
  • 采样算子
    采用Roberts算子,即计算对角差值
    在这里插入图片描述
    • 实现OnRenderImage
void OnRenderImage(RenderTexture source, RenderTexture destination)	
{
     if(material != null)
     {
        material.SetColor("_EdgeColor", edgeColor);
        material.SetFloat("_SampleDistance", sampleDistance);
        material.SetVector("_Sensitivity", new Vector4(sensitivityNormals, sensitivityDepth, 0.0f, 0.0f));
        Graphics.Blit (source, destination, material);
      }
      else
      {
        Graphics.Blit(source,destination);
     }
}
  • 实现shader
Shader "Unlit/edgeDetectByDepth"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _EdgeColor ("Edge Color", Color) = (0, 0, 0, 1)
        _SampleDistance ("Sample Distance", Float) = 1.0
        _Sensitivity ("Sensitivity", Float) = 1//检测灵敏度
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        CGINCLUDE
        #include "UnityCG.cginc"
        sampler2D _MainTex;
        half4 _MainTex_TexelSize;//x=1/width y=1.0/height z=width  w =height
        float4 _MainTex_ST;
        float _SampleDistance;
        fixed4 _EdgeColor;
        half _Sensitivity;
        sampler2D _CameraDepthTexture;
    
        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };
        struct v2f
        {
            half2 uv[5]: TEXCOORD0;
            float4 vertex : SV_POSITION;
        };
        //利用差值大小判断是否存在边缘
        half isBeEdge(half sample1Depth, half sample2Depth)
        {
            //计算差值
            float diffDepth = abs(sample1Depth - sample2Depth) * _Sensitivity;
            //判断差值,根据距离缩放设置阈值
            int isBeEdgeByDepth = diffDepth < 0.1;//(0.1 * sample1Depth);
            return isBeEdgeByDepth ? 1.0 : 0.0;
        }
         v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            half2 uv = TRANSFORM_TEX(v.uv, _MainTex);
            o.uv[0] = uv;
            //判断纹理坐标空间,如果原点在上面需要变换
            #if UNITY_UV_STARTS_AT_TOP
                if (_MainTex_TexelSize.y < 0)
                uv.y = 1 - uv.y;
            #endif
            //利用roberts算子 获得对角坐标
            o.uv[1] = uv + _MainTex_TexelSize.xy * half2(-1,1) * _SampleDistance;//左上
            o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1,-1) * _SampleDistance;//右下
            o.uv[3] = uv + _MainTex_TexelSize.xy * half2(1,1) * _SampleDistance;//右上
            o.uv[4] = uv + _MainTex_TexelSize.xy * half2(-1,-1) * _SampleDistance;//左下
            return o;
        }
        fixed4 frag (v2f i) : SV_Target
        {
            //采样深度和法线纹理
            half sample1 = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[1]);
            half sample2 = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[2]);
            half sample3 = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[3]);
            half sample4 = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv[4]);
            half isEdge = 1.0;
            isEdge *= isBeEdge(sample1, sample2);
            isEdge *= isBeEdge(sample3, sample4);
            return lerp(_EdgeColor, tex2D(_MainTex, i.uv[0]), isEdge);
        }
        ENDCG
        ZTest Always Cull Off ZWrite Off
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值