9.Transparent shaders

本文详细介绍了如何在Unity3D中创建透明材质和玻璃效果的着色器。通过修改渲染类型和队列,利用Alpha通道控制透明度,以及结合反射、折射和屏幕抓取技术来实现空间海盗变为透明玻璃的效果。关键概念包括Alpha测试、透视除法、反射函数和tex2Dproj纹理采样。
摘要由CSDN通过智能技术生成

9. Transparent shaders

48. Alpha transparency in a surface shader

贯穿全章节的是不要忘记首先改变渲染类型和队列

48.1 Using the alpha channel for transparency

Alpha Chanel 的 含义:

  • Black is totally transparent 黑的透明,也就是alpha = 0
  • White is opaque白的不透明,也就是alpha = 1
  • distance ordering is used for transparent surfaces,transparent queue can not write z buffer
    这里彩色玻璃窗的alpha通道是根据图像的灰度生成的。因此我们想要不透明的地方反而是黑色的,这就会导致生成的alpha通道是翻转的,所以我们需要再翻转。
    我们把这个思想带入到下面的shader中。
Shader "NiksShaders/Shader65Lit"
{
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _AlphaTest ("Alpha Test", Float) = 0.7
    }

    SubShader {
      
      Tags { "RenderType" = "Transparent" "Queue" = "Transparent"  }//改变标签
      ZWrite Off//关闭深度写入
      CGPROGRAM
      
      #pragma surface surf Lambert alpha:fade//透明,表面着色器必须要有这一句才起作用。
      
      // Use shader model 3.0 target, to get nicer looking lighting
      #pragma target 3.0

      struct Input {
          float2 uv_MainTex;
      };
      
      sampler2D _MainTex;
      
      void surf (Input IN, inout SurfaceOutput o) {
          fixed4 col = tex2D (_MainTex, IN.uv_MainTex);
          o.Albedo = col.rgb;
          o.Alpha  = 1 - col.a;//这里就是上面的,因为灰度图生成的,所以需要翻转
      }
      
      ENDCG
      //这个用来生成阴影
      ColorMask 0  
      CGPROGRAM
      
      #pragma surface surf Lambert alpha:fade alphatest:_AlphaTest addshadow 
      
      // Use shader model 3.0 target, to get nicer looking lighting
      #pragma target 3.0

      struct Input {
          float2 uv_MainTex;
      };
      
      sampler2D _MainTex;
      
      void surf (Input IN, inout SurfaceOutput o) {
          fixed4 col = tex2D (_MainTex, IN.uv_MainTex);
          o.Albedo = col.rgb;
          o.Alpha  = 1 - col.a;
      }
      
      ENDCG
    } 

    Fallback "Diffuse"
}

49. Space Pirate turns to glass

把海盗变成透明材质。

这里我们要思考的问题在于,我们说海盗是透明材质了,那我们为什么还能看见透明的海盗呢?

所以这里,我称之为,伪透明。

什么叫伪透明呢?就是我们可以通过其他的方式看见这个物体,比如我们看见玻璃的方式。

我们怎么看见玻璃?

玻璃反射光线(环境照到玻璃上发生反射)+玻璃折射背景和自身光线(透过物体看到的背景的颜色在物体内部发生折射)。

我们来想象一下,这两个哪个可以缺少?

如果不反射环境光,那么也就是不反射环境光,可能就是不那么华丽了。

如果不会折射背景和自身光线,意味着,自身颜色会直接输出,不会经过折射,那就不会有透明效果。

这里有个tips加法是没有关系的颜色之间的叠加,而乘法是模拟光的照射过程,比如自发光(emissive),环境光(ambient),漫反射(diffuse),高光反射(specular) 这四个之间没有关系,就是加法,而这里自发光的颜色除了纹理,还有背景的折射,所以就是乘以。

We will grab the screen behind the space pirate character

Add GrabPass{"_GrabTexture"}before the pass line

49.1 ComputeGrabScreenPos(float4)

Input is clip space position. They return float4 where the final coordinate to sample texture with can be computed via perspective division (for example xy/w).它们返回 float4,其中用于纹理采样的最终坐标可以通过透视除法(例如 xy/w)计算得出。

之前,ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。那么为什么Unity要这么做呢?Unity的本意是希望你把该坐标值用作tex2Dproj指令的参数值,tex2Dproj会在对纹理采样前除以w分量。

49.2 reflect 函数

o.reflect = reflect(-viewDir,worldNormal);reflect函数,这里的第一个参数是入射光线,是从某个地方入射到球上,第二个是从球上出发的法线,返回一个反射光线。

49.3 tex2Dproj

纹理进行采样之前,tex2Dproj将输入的UV xy坐标除以其w坐标。

  • 透视除法:这对透视相机有用,而正交相机w本来就是1,除了没除都一样,作用是将坐标从剪裁空间(相机的)转换到屏幕空间。
  • 为什么只有ComputeScreenPos和ComputeGrabScreenPos才使用透视除法呢?因为Unity在顶点着色器中的顶点只要转换到剪裁空间,之后就会在片元着色器中自动转换到屏幕空间。
  • unity之所以不在顶点着色器中除以w分量,是因为要留到光栅化阶段进行线性插值后再除以w,这样得到的结果是正确的。如果提前除以w再经过光栅化线性插值后,得到的结果就不准确,因为投影空间不是线性空间
tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.screenPosition)).r;
//等同于
tex2D(_GrabTexture, UNITY_PROJ_COORD(i.screenPosition.xy / i.screenPosition.w)).r;

49.4 宏UNITY_PROJ_COORD(a)

UNITY_PROJ_COORD(a) 给定一个 4 分量矢量(比如抓取的屏幕纹理),此宏返回一个适合投影纹理读取的纹理坐标。

Shader "NiksShaders/Shader66Glass" 
{
    Properties 
    {
        _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
        _Colour ("Colour", Color) = (1,1,1,1)
        _BumpMap("Bump Map",2D) = "bump"{}
        _Magnitude("Magnitude",Range(0,1)) = 0.05
        _TintStrength("Tint Strength", Range(0,1)) = 0.3 
        _EnvironmentMap ( "Environment Map", CUBE) ="cube" {}
        _ReflectionStrength ("Reflection",Range(0,1)) = 0.3 
    }
 
    SubShader
    {
        Tags{"Queue" = "Transparent"}
        Tags {"RenderType"="Transparent" }
        GrabPass{"_GrabTexture"}
        Pass 
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            sampler2D _MainTex;
            fixed4 _Colour;
            sampler2D _BumpMap;//法线
            float _Magnitude;//大小
            float _TintStrength;//颜色强度
            samplerCUBE _EnvironmentMap;//环境贴图
            float _ReflectionStrength;//反射光强
            sampler2D _GrabTexture;//抓取的材质
            
            struct appdata
            {
                float4 vertex : POSITION;
                float4 color : COLOR;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
 
            struct v2f
            {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
                float2 uv : TEXCOORD0;
                float4 uvgrab : TEXCOORD1;//uvgrab算出来为什么是float4
                float3 reflect : TEXCOORD2;//反射光线是float3
            };
 
            // Vertex function 
            v2f vert (appdata v)
            {
                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = v.color;
                o.uv = v.texcoord;
                o.uvgrab = ComputeGrabScreenPos(o.vertex);//计算抓取的帧的屏幕uv
                
                float3 viewDir = WorldSpaceViewDir(v.vertex);//视线,这里计算出来的是从表面到摄像机
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);//法线
                o.reflect = reflect(-viewDir,worldNormal);//reflect函数,这里的第一个参数是入射光线,是从某个地方入射到球上
                return o;
            }
 
            // Fragment function
            fixed4 frag (v2f i) : COLOR
            {
                fixed4 maincol = lerp(tex2D(_MainTex,i.uv),_Colour,_TintStrength);//为什么是越靠近白色越透明呢?
                half3 bump = UnpackNormal(tex2D(_BumpMap,i.uv));//这里使用i.uv,实际上法线和maintex常常用一样的uv
                half2 distortion = bump.rg;//扭曲,法线然后取出xy值,也就是切线和副切线
                i.uvgrab.xy += distortion * _Magnitude;//将截取的纹理进行扭曲放缩,也就是法线折射
                //和tex2D功能基本一致,就是增加了一个变换到透视投影的功能。
                //裁剪空间的坐标经过缩放和偏移后就变成了(0,w),而当分量除以分量W以后,就变成了(0,1),这样在计算需要返回(0,1)值的时候,就可以直接使用tex2Dproj了
                //这里要返回屏幕上的颜色
               fixed4 grabcol = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD (i.uvgrab));//透视除法
                fixed4 reflcol = texCUBE(_EnvironmentMap,i.reflect);//环境光反射贴图
                reflcol *= maincol.a;
                reflcol *= _ReflectionStrength;
                return grabcol * maincol * _Colour + reflcol;
            }
 
            ENDCG
        } 
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值