摄像机与主角之间遮挡显示处理(Unity3D开发之二十八)

今天处理遮挡的时候,本来是想摄像机射线检测,设置建筑半透明效果用来显示被遮挡的角色(有很多游戏也是这样处理的),实现后发现效果实际上不太好。如果被遮挡角色还是敌人或者多个角色时候,不是特别好,比如敌人被遮挡我希望单独区分下。所以改成了现在的直接绘制2D填充色(也就是忽略自身的深度值)来显示。

摄像机与主角之间遮挡显示处理(Unity3D开发之二十八)_第1张图片

主要代码

Properties {
        _NotVisibleColor ("NotVisibleColor (RGB)", Color) = (0.3,0.3,0.3,1)
    }
Pass {
        ZTest Greater
        ZWrite Off
        Lighting Off
        Color [_NotVisibleColor]
    }
  • _NotVisibleColor: 用来绘制挡住时候显示的颜色;
  • ZTest Greater:表示大于深度缓存中的值的时候(也就是被遮挡的时候),将深度缓存中对应像素的颜色值改为自己的颜色(_NotVisibleColor)
  • ZWrite Off:表示关闭深度缓存,也就是在上一步写入像素颜色的时候不写入自身深度值(等于遮挡树上绘制颜色,深度值使用树的)
  • Lighting Off:取消光照,因为我效果希望是2D颜色块的效果。

PlayerDiffuse.shader

Shader "Custom/PlayerDiffuse" {
    Properties {
        _NotVisibleColor ("NotVisibleColor (RGB)", Color) = (0.3,0.3,0.3,1)
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass {
            ZTest Greater
            Lighting Off
            ZWrite Off
            Color [_NotVisibleColor]
        }

CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        fixed4 _Color;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
ENDCG
    } 
    Fallback "Legacy Shaders/VertexLit"
}

渲染深度解释:

参考文章:[UnityShader]渲染队列、ZWrite和ZTest

  1. 什么是深度?
    深度其实就是该像素点在3d世界中距离摄像机的距离。离摄像机越远,则深度值(Z值)越大。

  2. 什么是深度缓存?
    深度缓存中存储着准备要绘制在屏幕上的像素点的深度值。如果启用了深度缓冲区,在绘制每个像素之前,OpenGL会把该像素的深度值和深度缓存的深度值进行比较。如果新像素深度值<深度缓存深度值,则新像素值会取代原先的;反之,新像素值被遮挡,其颜色值和深度将被丢弃。(深度主要起的是比较的作用)

  3. 什么是深度测试?
    在深度测试中,默认情况是将要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新深度缓存中对应像素的颜色值。

  4. 为什么需要深度?
    在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。

那么,在unity中,如果知道了渲染队列,ZWrite,ZTest,如何确定哪个物体先显示呢?

首先,unity先将渲染队列中较前的进行渲染,然后再执行ZWrite,ZTest
ZWrite可以取的值为:On/Off,默认值为On,代表是否要将像素的深度写入深度缓存中(同时还要看ZTest是否通过)。

ZTest可以取的值为:Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off,默认值为LEqual,代表通过比较深度来更改颜色缓存的值。例如当取默认值的情况下,如果将要绘制的新像素的z值小于等于深度缓存中的值,则将用新像素的颜色值更新深度缓存中对应像素的颜色值。

需要注意的是,当ZTest取值为Off时,表示的是关闭深度测试,等价于取值为Always,而不是Never!Always指的是直接将当前像素颜色(不是深度)写进颜色缓冲区中;而Never指的是不要将当前像素颜色写进颜色缓冲区中,相当于消失。


那么,重点来了:
1. 当ZWrite为On时,ZTest通过时,该像素的深度才能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值也会写入颜色缓存。
2. 当ZWrite为On时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
3. 当ZWrite为Off时,ZTest通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值会写入颜色缓存。
4. 当ZWrite为Off时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。

可以看到,像素的深度能否成功写入深度缓存,条件是ZWrite为On,ZTest通过;
写入深度缓存的作用就是为ZTest的比较做准备。


因为ZWrite默认值为On,ZTest默认值为LEqual,所以这很好地解释了为什么在unity中,距离相机近的东西会阻挡住距离相机远的东西。如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这时我们可以通过修改ZWrite和ZTest来改变物体的遮挡关系!


参考链接:
1. [UnityShader]渲染队列、ZWrite和ZTest
2. 建物に遮蔽されたプレイヤーをシルエット表示する

你可能感兴趣的:(Unity3D,Unity3D游戏开发)