• UnityShader实例04:遮挡透明材质


    遮挡透明材质

    在3D游戏中,经常会出现控制的角色被房子或者墙壁之类的挡住,一些游戏会把挡住角色的物体透明化显示(这个应该需要脚本配合shader实现,因此不在本文讨论范围),如藏地传奇;而一些游戏会做一些特殊效果把角色被挡住的部分显示出来,如火炬之光这样(如下图),下面就用shader实现这一效果:



    shader实现原理:


    分析上图的效果,角色被前面墙体挡住的部分显示的类似xray效果(代码就直接从前面拿了,哈哈),没被挡住的部分显示贴图的正常效果,因此可以知道这个shader需要两个pass来实现。其中一个pass正常显示贴图,另外一个pass实现xray效果。当然不止这么简单,要实现上图的效果还需要Ztest,Zwrite,以及渲染队列等知识。

    准备知识:


    Rendering Order渲染队列


    渲染队列,从字面的意思理解就是渲染的顺序,即对象渲染谁先谁后,如图你画画的时候,如果画画的时候每一笔的顺序一样,如果第一笔画红色,后面一笔画黑色,最终黑色会把红色覆盖掉。Unity里面我们可以通过SubShader Tags中的Queue Tag控制,unity提供了一些内置的渲染队列:

    渲染队列渲染队列描述渲染队列值
    Background这个队列被最先渲染。它被用于skyboxes等。1000
    Geometry这是默认的渲染队列。它被用于绝大多数对象。不透明几何体使用该队列。2000
    AlphaTest通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效。2450
    Transparent该渲染队列在Geometry和AlphaTest队列后被渲染。任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和粒子效果。3000
    Overlay该渲染队列是为覆盖物效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕。4000

    在关闭深度测的情况下,可以看出处于background队列的物体会最先被渲染,如果没有深度测试,他将现实画面的最后面,如果前面geometry队列的物体,就会优先显示geometry的物体,我想一般都会有这个问题:如果属于同一个渲染队列该怎么决定谁先谁后呢,所以unity还有其他的方式决定物体能根据实际深度正常显示,如深度测试。可以这么说,渲染队列相当于把物体进行了大致的排序,如果需要精确的话,还需要其他的操作。

    Depth Testing深度测试


    Depth Testing:深度测试,也叫深度缓冲。用来确定物体的遮挡关系。只有最靠近观察者的物体会被绘制。深度即Z,该值越小表示离观察者越近,该值越大表示离观察者越远。


    Shader里默认有如下代码
    ZWrite On
    ZTest LEqual

    意思即使Shader里没有写任何关于Depth Testing的代码,Shader也会执行深度测试。

    ZWrite:是否此物体的像素深度会被记录(默认记录),
    ZWrite On  深度记录(默认On),此对象的深度会根据实际情况进行记录。
    ZWrite Off  不记录此深度,通常用于半透明物体,

    ZTest Less | Greater | LEqual | GEqual | Equal | NotEqual | Always

    默认是 LEqual。 即当深度小于或者等于 深度最小值时,渲染物体,即渲染最近的物体。


       根据上面的知识我们可以整理个思路,在unity默认情况下物体深度缓存都是打开的,除了半透明物体外,大部分物体都会根据实际情况渲染,显示正确的遮挡关系,即离观察者越近的物体会挡住离观察较远的物体。因此要实现本文的效果,使的被挡住的部分现实出来,我们需要关闭深度缓存(不是关闭ztest),自己控制渲染队列来使被遮挡物体的部分显示到遮挡物的前面来,具体就是在shader中,首先将subshader Tag改成如下形式:
    Tags { "Queue" = "Geometry+500" "RenderType"="Opaque" }  

       这样物体将在不透明物体(墙壁)之后渲染,同时定义两个pass,一个输出xray效果,一个输出贴图颜色,将第一个pass设置成Zwitre off,关闭深度缓存,并将Ztest 参数设置成Greater,第二个pass使用默认设置 即Zwrite on , Ztest LEqual。

       那么执行的时候,如果对象没有被墙壁挡住时,先执行的pass输出xray效果,后执行的pass输出了贴图颜色,最终xray被替换成正常贴图颜色。而如果对象被墙壁遮挡时,第一个pass不将它的深度值写入【深度缓存】,此时Ztest参数为Greater ,显然当前pass的深度值("Geometry+500")是大于墙壁深度值的,所以最终显示了当前pass的颜色,第二个pass是默认设置,并写入了【深度缓存】,深度值是小于墙壁深度值的,根据ztest LEqual第二个pass的贴图颜色被剔除,最终的颜色就会根据blend公式,显示第一个pass和墙壁颜色的混合结果:

    Blend  SrcAlpha   One

    最终颜色=xray效果RGB*xray效果Alpha+墙壁RGB*1


    对于这个效果可以总结出个简单的代码模式:

    [csharp]  view plain  copy
     print ?
    1. Shader "…………"     
    2. {    
    3.     Properties     
    4.     {     
    5.         ……………………  
    6.     }    
    7.         
    8.     SubShader     
    9.     {    
    10.           
    11.         LOD 300    
    12. //更改渲染队列,调整绘制顺序,使物体绘制到其他物体之前  
    13.     Tags { "Queue" = "Geometry+500" "RenderType"="Opaque" }  
    14. //第一个pass输出xray部分,两个pass的顺序不能改变  
    15. Pass  
    16.     {  
    17.         Blend SrcAlpha One//设置颜色混合结果  
    18.         ZWrite off  //关闭深度缓存,重写对象的绘制顺序  
    19.         ztest greater//当深度大于最小值时,渲染对象  
    20.         …………………………  
    21.     }  
    22. //第二个pass正常输出贴图颜色  
    23.         pass    
    24.         {    
    25.                 ZTest LEqual//缺省值,可以不写,当深度小于或者等于深度最小值时,渲染对象,即渲染最近的物体。     
    26.                 …………………………   
    27.         }     
    28. }  





    VF版本代码01:

    [csharp]  view plain  copy
     print ?
    1. Shader "PengLu/OccTransVF"     
    2. {    
    3.     Properties     
    4.     {     
    5.         _MainTex ("Base (RGB)", 2D) = "white" {}    
    6.         _RimColor("RimColor",Color) = (0,1,1,1)  
    7.         _RimPower ("Rim Power", Range(0.1,8.0)) = 1.0  
    8.     }    
    9.         
    10.     SubShader     
    11.     {    
    12.           
    13.         LOD 300    
    14.         Tags { "Queue" = "Geometry+500" "RenderType"="Opaque" }   
    15.         Pass  
    16.         {  
    17.             Blend SrcAlpha One  
    18.             ZWrite off  
    19.             Lighting off  
    20.   
    21.             ztest greater  
    22.   
    23.             CGPROGRAM  
    24.             #pragma vertex vert  
    25.             #pragma fragment frag  
    26.             #include "UnityCG.cginc"  
    27.   
    28.             float4 _RimColor;  
    29.             float _RimPower;  
    30.               
    31.             struct appdata_t {  
    32.                 float4 vertex : POSITION;  
    33.                 float2 texcoord : TEXCOORD0;  
    34.                 float4 color:COLOR;  
    35.                 float4 normal:NORMAL;  
    36.             };  
    37.   
    38.             struct v2f {  
    39.                 float4  pos : SV_POSITION;  
    40.                 float4  color:COLOR;  
    41.             } ;  
    42.             v2f vert (appdata_t v)  
    43.             {  
    44.                 v2f o;  
    45.                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);  
    46.                 float3 viewDir = normalize(ObjSpaceViewDir(v.vertex));  
    47.                 float rim = 1 - saturate(dot(viewDir,v.normal ));  
    48.                 o.color = _RimColor*pow(rim,_RimPower);  
    49.                 return o;  
    50.             }  
    51.             float4 frag (v2f i) : COLOR  
    52.             {  
    53.                 return i.color;   
    54.             }  
    55.             ENDCG  
    56.         }  
    57.         pass    
    58.         {    
    59.             ZWrite on  
    60.             ZTest less   
    61.   
    62.             CGPROGRAM    
    63.             #pragma vertex vert    
    64.             #pragma fragment frag    
    65.             sampler2D _MainTex;    
    66.             float4 _MainTex_ST;  
    67.                 
    68.             struct appdata {    
    69.                 float4 vertex : POSITION;    
    70.                 float2 texcoord : TEXCOORD0;    
    71.             };    
    72.               
    73.             struct v2f  {    
    74.                 float4 pos : POSITION;    
    75.                 float2 uv : TEXCOORD0;    
    76.             };    
    77.               
    78.             v2f vert (appdata v)   
    79.             {    
    80.                 v2f o;    
    81.                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);    
    82.                 o.uv = v.texcoord;    
    83.                 return o;    
    84.             }   
    85.                
    86.             float4 frag (v2f i) : COLOR    
    87.             {    
    88.                 float4 texCol = tex2D(_MainTex, i.uv);    
    89.                 return texCol;    
    90.             }    
    91.             ENDCG    
    92.         }    
    93.     }   
    94.     FallBack "Diffuse"   
    95. }    


    VF版本代码01效果

  • 相关阅读:
    《现代操作系统》精读与思考笔记 第一章 引论
    笔试面试中常见的位运算用法
    Linux磁盘概念及其管理工具fdisk
    linux选择sdb sdb4 fat32 还是sda分区
    Linux系统 fdisk命令 创建新分区
    Java----------JMX规范
    Java------------JVM(Java虚拟机)优化大全和案例实战
    Java关键字-----------------java中synchronized关键字的用法
    SQL-----------------------之ON和WHERE的区别
    C语言之单元测试
  • 原文地址:https://www.cnblogs.com/jrmy/p/14316291.html
Copyright © 2020-2023  润新知