shader-透明效果

透明

     在实时渲染中要实现透明效果,通常在渲染模型时控制它的透明通道(Alpha Channel)。开启透明混合时,当一个物体被渲染到屏幕后时,每个片元除了颜色值和深度值之外,还有另一个属性透明度。透明度为1,完全不透明,为0,完全透明,像素完全不会显示。在unity中实现方法:1.透明度测试(Alpha Test),但是无法实现真正的半透明效果;2.透明度混合(Alpha Blending);

     渲染顺序问题:在渲染不透明(opaque)物体时,不考虑渲染顺序也能正确的排序效果。由于强大的深度缓冲(depth buffer----------> z-buffer)的存在。深度缓冲用于解决可见性(visibility)的问题,决定那个物体的那个部分会被渲染到前面,那些部分会被其他物体遮挡,基本思想:根据深度缓存中的值来判断该片元距离摄像机的距离,当渲染一个片元时,需要把它的深度值和已经存在的深度缓冲值进行比较(如果开启了深度测试),它的值距离摄像机更远,则说明该片元不该被渲染 到屏幕,否则这个片元应该覆盖掉此时的颜色缓存中的像素值。并把它的深度值更新到深度缓冲中(如果开启的深度写入---ZWrite)。

透明测试和透明混合基本原理:

  • 透明测试:只要一个片元不满足条件(小于某个阈值),那么它对应的片元就会被舍弃。被舍弃的片元不会再进行任何处理,也不会对颜色缓冲产生影响;否则就按照不透明物体的处理方式处理它,深度测试、深度写入等。也就是说,透明度测试不需要关闭深度写入,它和其他不透明物体最大的不同就是根据深度值来舍弃一些片元。----造成要么透明,要么不透明的效果。

  • 透明混合:使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是透明度混合需要关闭深度写入,所以要控制渲染的顺序,关闭了深度写入如果不关闭,一个半透明表面背后的表面本来是可以透过它被看到,但是由于深度测试时判断结果是该半透明表面距离摄像机更近,导致后面的表面将会被剔除。渲染顺序就变得及其重要,没关闭深度测试,意味着当使用透明度混合渲染一个片元,还是会比较它的深度值与当前深度缓冲的深度值,如果它的深度值距离摄像机更远,就不再进行混合操作,这一点决定了当一个不透明物体出现在一个透明物体前,先渲染了不透明物体,它仍然可以正常地遮挡住透明物体,所以对于透明混合来说,深度缓冲是只读的。-----能够得到半透明效果。

由于透明混合对深度缓冲是只读的,先渲染透明,后渲染的物体会覆盖颜色缓冲中的颜色值。

      (渲染引擎一般都会先对物体进行排序,再渲染)常用的两种方法

  1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。
  2. 把半透明物体按它们距离摄像机的远近进行排序(多个物体循环重叠无法排序,如果通过网格点来排序,也会出现问题,通常解决办法--分割网格,尽可能让模型是凸表面,减少排序错误;),然后按照从后往前的顺序渲染这些半透明物体,并开启深度测试,但关闭深度写入。

untiy shader渲染队列

 渲染队列(render queue)解决渲染顺序问题,在subshade的Queue标签来决定我们的模型将归于那个渲染队列。在unitu内部使用一系列整数索引表示每个渲染队列,且索引越小越早渲染。

透明度测试

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Unlit/6"
{
    Properties{
        _Color("_Color",Color)=(1,1,1,1)
        _MainTex("_MainTex",2D)="white"{}
        _CutOff("Alpha Test CoutOff",Range(0,1))=0.5
    }
    SubShader{
       //alphaTest测试 Opaque Transparent Geometry Overlay 
       
       //alphaTest 只要有一个片元不满足条件就会被舍弃
       //clip 函数----> 裁剪时使用的标量和矢量条件----》如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色
       // IgnoreProjector 透明物体不受projector(投影器)的影响 ---不接受阴影
       // RenderType 通常用于着色器替换功能 Opaque TransparentCutout
       Tags{ "Queue"="AlphaTest" "IgnoreProjector"="True" 
           "RenderType"="TransparentCutout"}
      
       Pass{
           //LightMode 定义该pass在光照模型中的角色,
           Tags{"LightMode"="ForwardBase"}
           //Cull Off |Front|Back  默认时back 剔除物体背面(相对于摄像机) front 剔除前面 Off关闭剔除----》渲染图元数目会成倍增加。
           //透明度测试关闭---》背面也会渲染---》可以看到内部结构
           Cull Off
           CGPROGRAM
              #pragma vertex vert
              #pragma fragment frag
              #include "Lighting.cginc"
              
              fixed4 _Color;
              sampler2D _MainTex;
              float4 _MainTex_ST;
              fixed _CutOff;
              struct a2v{
                 float4 vertex:POSITION;
                 float4 normal:NORMAL;
                 float4 texcoord:TEXCOORD0;
              };
              struct v2f{
                 float4 pos:SV_POSITION;
                 float3 worldPos:TEXCOORD0;
                 float3 worldNormal:TEXCOORD1;
                 float2 uv:TEXCOORD2;
              };
              v2f vert(a2v a){
                  v2f v;
                  v.pos=UnityObjectToClipPos(a.vertex);
                  v.worldNormal=UnityObjectToWorldNormal(a.normal);
                  v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
                  //v.uv = TRANSFORM_TEX(v.texcoord,_MainTex); 
                  v.uv=a.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                  //根据顶点和纹理得到世界法线、坐标、uv再传递给片元着色器。
                  return v;
              }
              fixed4 frag(v2f v):SV_Target{
                  fixed3 worldLight=normalize(UnityWorldSpaceLightDir(v.worldPos));
                  fixed3 worldNormal=normalize(v.worldNormal);
                  fixed4 texColor=tex2D(_MainTex,v.uv);
                  //alpah test  //为负数 舍弃该片元的输出  最终的结果 要么完全透明或不透明
                  clip(texColor.a - _CutOff);
                  
                  /*if(texColor.a - _CutOff<0){
                      discard;//discard 剔除命令
                  }*/
                  
                  fixed3 albedo=texColor.rgb * _Color.rgb;
                  fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                  fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLight));
                  return fixed4(ambient+diffuse,1.0);
              }
           ENDCG
       }
    
    }
    //如无法在当前显卡上工作时,可以有合适的shader来代替shader,还可以保证透明度测试的物体可以正确地向其他物体投射阴影。
    FallBack "Transparent/Cutout/VertexLit"
//    SubShader{
//        Tags{"Queue"="Transparent"}
//        Pass{
//           ZWrite Off //关闭深度写入
//        }
//    }
}

透明度混合

Shader "Unlit/7"
{
   Properties{
       _Color("_COlor",Color)=(1,1,1,1)
       _MainTex("_MainTex",2D)="white"{}
       _AlphaScale("_AlphaScale",Range(0,2))=0.5
   }
   SubShader{
     //表明使用了透明混合的shader
      Tags{"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
      //透明度混合,使用当前片元透明度作为透明因子,与已经存储在颜色缓冲中的颜色值进行混合得到新的颜色。需要关闭深度写入
      //Blend 混合模式 1.Off关闭 
      //2.SrcFactor DstFactor 开启混合并设置混合因子,源颜色(该片元产生的颜色)乘以SrcFactor,而且目标颜色(已经存在于颜色缓冲的颜色)会乘以DstFactor,最后相加再写入颜色缓冲。  
      // 3. SrcFactor DstFactor,SrcFactorA DstFactorA 与2相同,只是使用不同的因子来混合
      // 4. BlendOp BlendOperation 并非是把源颜色和目标颜色简单相加后混合,而是使用 BlendOperation进行其他操作。
      //最终的颜色 = SrcAlpha x SrcColor + (1-SrcAlpha)x DstColor
      Pass{
          Tags{"LightMode"="ForwardBase"}
          ZWrite Off
          //BlendOp 默认是Add
          //正常混合
          Blend SrcAlpha OneMinusSrcAlpha
          //一个pass渲染前面 一个pass渲染后面,就得到了双面渲染的效果---》可以看到内部结构
          Cull Front //渲染前面  
        // 柔和相加
        //Blend OneMinusDstColor One
        //正片叠底
        //Blend DstColor Zero
        //两倍相乘
        //Blend DstColor SrcColor
        // 变暗
        //BlendOp Min
        //Blend One One	// 使用min或max,会忽略混合因子
        // 线性减淡
        //Blend One One
        //变亮B
        //BlendOp Max
        //Blend One One 
        // 滤色
        //Blend OneMinusDstColor One
        // Or
        //Blend One OneMinusSrcColor
        
          CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "Lighting.cginc"
             fixed4 _Color;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             fixed _AlphaScale;
             struct a2v{
                float4 vertex:POSITION;
                float3 normal:NORMAL;
                float4 texcoord:TEXCOORD0;
             };
             struct v2f{
                float4 pos:SV_POSITION;
                float3 worldPos:TEXCOORD0;
                float3 worldNormal:TEXCOORD1;
                float2 uv:TEXCOORD2;
             };
             v2f vert(a2v a){
               v2f v;
               v.pos = UnityObjectToClipPos(a.vertex);
               v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
               v.worldNormal=UnityObjectToWorldNormal(a.vertex).xyz;
               v.uv=TRANSFORM_TEX(a.texcoord,_MainTex);
               return v;
             }
             fixed4 frag(v2f v):SV_Target{
                fixed3 worldNormal=normalize(v.worldNormal);
                fixed3 worldLight=normalize(UnityWorldSpaceLightDir(v.worldPos));
                fixed4 texColor=tex2D(_MainTex,v.uv);
                fixed3 albedo=texColor.rbg * _Color.rgb;
                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLight));
                
                return fixed4(ambient+diffuse,texColor.a*_AlphaScale);
             }
          ENDCG
      }
      Pass{
          Tags{"LightMode"="ForwardBase"}
          ZWrite Off
          //BlendOp 默认是Add
          //正常混合
          Blend SrcAlpha OneMinusSrcAlpha
          Cull Back //渲染后面
        // 柔和相加
        //Blend OneMinusDstColor One
        //正片叠底
        //Blend DstColor Zero
        //两倍相乘
        //Blend DstColor SrcColor
        // 变暗
        //BlendOp Min
        //Blend One One	// 使用min或max,会忽略混合因子
        // 线性减淡
        //Blend One One
        //变亮B
        //BlendOp Max
        //Blend One One 
        // 滤色
        //Blend OneMinusDstColor One
        // Or
        //Blend One OneMinusSrcColor
        
          CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "Lighting.cginc"
             fixed4 _Color;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             fixed _AlphaScale;
             struct a2v{
                float4 vertex:POSITION;
                float3 normal:NORMAL;
                float4 texcoord:TEXCOORD0;
             };
             struct v2f{
                float4 pos:SV_POSITION;
                float3 worldPos:TEXCOORD0;
                float3 worldNormal:TEXCOORD1;
                float2 uv:TEXCOORD2;
             };
             v2f vert(a2v a){
               v2f v;
               v.pos = UnityObjectToClipPos(a.vertex);
               v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
               v.worldNormal=UnityObjectToWorldNormal(a.vertex).xyz;
               v.uv=TRANSFORM_TEX(a.texcoord,_MainTex);
               return v;
             }
             fixed4 frag(v2f v):SV_Target{
                fixed3 worldNormal=normalize(v.worldNormal);
                fixed3 worldLight=normalize(UnityWorldSpaceLightDir(v.worldPos));
                fixed4 texColor=tex2D(_MainTex,v.uv);
                fixed3 albedo=texColor.rbg * _Color.rgb;
                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLight));
                
                return fixed4(ambient+diffuse,texColor.a*_AlphaScale);
             }
          ENDCG
      }
   }
   FallBack "Transparent/VertexLit"
}

开启深度写入的半透明

两个pass,一个pass仅仅将模型的深度值写入深度缓冲,这样可以得到正确的深度值,第二个pass进行正常的透明度混合。

Shader "Unlit/8"
{
    Properties{
       _Color("_COlor",Color)=(1,1,1,1)
       _MainTex("_MainTex",2D)="white"{}
       _AlphaScale("_AlphaScale",Range(0,2))=0.5
   }
    SubShader
    {
        Tags{"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
        //关闭深度写入而造成的错误排序,一种解决方式--使用两个pass来渲染模型,第一个pass开启深度写入。但不输出颜色,仅仅把深度值写入深度缓冲中。第二个pass正常混合。
        //第一个pass就得到了正确的深度值。后续的就可以按照像素级别深度排序进行透明渲染。缺点在于会对性能造成一定的影响。
        Pass{
          ZWrite On
          // ColorMask 用于设置颜色通道的写掩码(write mask) ColorMask RGB |A|0 其他任何R、G、B、A的组合
          //为0时,意味这该pass不写入任何颜色通道,既不会输出任何颜色。
          ColorMask 0
        }
        Pass{
          Tags{"LightMode"="ForwardBase"}
          ZWrite Off
          //想要使用混合,必须首先开启它
          // SrcFactor DstFactor,SrcFactorA DstFactorA 与2相同,只是使用不同的因子来混合
          //rgb=SrcFactor * S+DstFactor*D
          //a=SrcAlpha * Sa +DstFactorA *Da
          Blend SrcAlpha OneMinusSrcAlpha,One Zero
          CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "Lighting.cginc"
             fixed4 _Color;
             sampler2D _MainTex;
             float4 _MainTex_ST;
             fixed _AlphaScale;
             struct a2v{
                float4 vertex:POSITION;
                float3 normal:NORMAL;
                float4 texcoord:TEXCOORD0;
             };
             struct v2f{
                float4 pos:SV_POSITION;
                float3 worldPos:TEXCOORD0;
                float3 worldNormal:TEXCOORD1;
                float2 uv:TEXCOORD2;
             };
             v2f vert(a2v a){
               v2f v;
               v.pos = UnityObjectToClipPos(a.vertex);
               v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
               v.worldNormal=UnityObjectToWorldNormal(a.vertex).xyz;
               v.uv=TRANSFORM_TEX(a.texcoord,_MainTex);
               return v;
             }
             fixed4 frag(v2f v):SV_Target{
                fixed3 worldNormal=normalize(v.worldNormal);
                fixed3 worldLight=normalize(UnityWorldSpaceLightDir(v.worldPos));
                fixed4 texColor=tex2D(_MainTex,v.uv);
                fixed3 albedo=texColor.rbg * _Color.rgb;
                
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLight));
                
                return fixed4(ambient+diffuse,texColor.a*_AlphaScale);
             }
          ENDCG
      }
   }
   FallBack "Diffuse"
}

双面渲染的透明

使用Cull Back|off|Front 命令进行控制剔除。默认剔除Back。透明度测试,直接关闭掉剔除 cull Off ,当关闭时,图元数据会成倍增加; 透明混合则一个pass 是由Cull Front渲染前面,一个pass使用 Cull Back渲染后面。

 

 

 

发布了24 篇原创文章 · 获赞 1 · 访问量 1247

猜你喜欢

转载自blog.csdn.net/birdswillbecomdragon/article/details/105175481