主頁 >  其他 > 《Unity Shader入門精要》學習筆記第10章 高級紋理

《Unity Shader入門精要》學習筆記第10章 高級紋理

2021-04-04 15:07:29 其他

本文章用于幫助自己學習,因此只記錄一些個人認為比較重要或者還不夠熟悉的內容,
原作者:http://blog.csdn.net/candycat1992/article/

第十章 高級紋理

10.1 立方體紋理

在圖形學中,立方體紋理(Cubemap)是環境映射(Environment Mapping)的一種實作方法,環境映射可以模擬物體周圍的環境,而使用了環境映射的物體可以看起來像鍍了層金屬一樣 反射岀周圍的環境,
立方體紋理一共包含了6張影像,這些影像對應了一個立方體的6個面,
與之前使用二維紋理坐標不同,對立方體紋理采樣我們需要提供一個三維的紋理坐標,這個三維紋理坐標表示了我們在世界空間下的一個3D方向,這個方向矢量從立方體的中心岀發,當它向外部延伸時就會和立方體的6個紋理之一發生相交,而釆樣得到的結果就是由該交點計算而來的,如圖:
在這里插入圖片描述
使用立方體紋理的好處在于,它的實作簡單快速,而且得到的效果也比較好,但它也有一些缺點,例如當場景中引入了新的物體、光源,或者物體發生移動時,我們就需要重新生成立方體紋理,

10.1.1天空盒子

天空盒子(Skybox)是游戲中用于模擬背景的一種方法,當我們在場景中使用了天空盒子時,整個場景就被包圍在一個立方體內,這個立方體的每個面使用的技術就是立方體紋理映射技術

在Unity中,想要使用天空盒子非常簡單,我們只需要創建一個Skybox材質,再把它賦給該場景的相關設定即可,
我們首先來看如何創建一個Skybox材質,

  1. 新建一個材質,
  2. 在SkyboxMat的Shader下拉選單中選擇Unity自帶的 Skybox,該材質需要6張紋理
  3. 使用6張紋理對第2步中的材質賦值,注意這6張紋理的正確位置(如posz紋理對應了Front [+Z]屬性),為了讓天空盒子正常渲染,我們需要把這6張紋理的Wrap Mode設定為Clamp,以防止在接縫處出現不匹配的現象,

上述步驟得到的材質如下圖所示,
在這里插入圖片描述
然后在Lighting選單中,把SkyboxMat 賦給Skybox 選項即可,效果如圖所示:
在這里插入圖片描述
在Unity中,天空盒子是在所有不透明物體之后渲染的,而其背后使用的網格是一個立方體 或一個細分后的球體,

10.1.2創建用于環境映射的立方體紋理

除了天空盒子,立方體紋理最常見的用處是用于環境映射,通過這種方法,我們可以模擬出金屬質感的材質,
在Unity中,創建用于環境映射的立方體紋理的常用方法有兩種:第一種方法是直接由一些特殊布局的紋理創建;第二種方法是由腳本生成,
如果使用第一種方法,我們需要提供一張具有特殊布局的紋理,例如類似立方體展開圖的交叉布局、全景布局等,然后,我們只需要把該紋理的Texture Type設定為Cubemap即可,Unity 會為我們做好剩下的事情,在基于物理的渲染中,我們通常會使用一張HDR影像來生成高質量的Cubemap,可在官方檔案(http://docs.unity3d.com/Manual/class-Cubemap.html) 中找到更多的資料,
第二種方法,可以根據物體在場景中位置的不同,生成它們各自不同的立方體紋理,這時,我們就可以在Unity中使用腳本來創建,這是通過利用Unity提供的 Camera.RenderToCubemap函式來實作的,Camera.RenderToCubemap函式可以把從任意位置觀察到的場景影像存盤到6張影像中,從而創建出該位置上對應的立方體紋理,
在 Unity 的腳本手冊(http://docs.unity3d.com/ScriptRefbrence/Camera.RenderToCubemap.html) 中給出了如何使用Camera.RenderToCubemap函式來創建立方體紋理的代碼,其中關鍵代碼如下:

void OnWizardCreate()
{
    // 創建臨時攝像機用于渲染
    GameObject go = new GameObject("CubemapCamera");
    go.AddComponent<Camera>();

    //renderFromPosition (由用戶指定)位置處動態創建一個攝像機
    go.transform.position = renderFromPosition.position;
    go.transform.rotation = Quaternion.identity;

    //呼叫Camera.RenderToCubemap函式把從當前位置觀察到的影像渲染到用戶指定的立方體紋理cubemap中
    go.GetComponent<Camera>().RenderToCubemap(cubemap);

    // 銷毀臨時攝像機
    DestroyImmediate(go);
}

之后創建一個Cubemap

  1. 創建一個空的GameObject物件
  2. 新建一個用于存盤的立方體紋理(在Project視圖下單擊右鍵,選擇Create — Legacy — Cubemap來創建),為了讓腳本可以順利將影像渲染到該立方體紋理中,我們需要在它的面板中勾選Readable選項
  3. 從Unity選單欄選擇GameObject -> Render into Cubemap,打開我們在腳本中實作的用于渲染立方體紋理的視窗,并把第1步中創建的GameObject和第2步中創建的Cubemap_0分別拖曳到視窗中的Render From Position和Cubemap選項,然后單擊視窗中的Render!按鈕,就可以把從該位置觀察到的世界空間下的6張影像渲染到 Cubemap_0中,如下圖所示,

在這里插入圖片描述
在這里插入圖片描述

10.1.3反射

想要模擬反射效果很簡單,只需要通過入射光線的方向和表面法線方向來計算反射方向,再利用反射方向對立方體紋理采樣即可,
代碼:



Shader "Unity Shaders Book/Chapter 10/Reflection"
{
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        //控制反射顏色
        _ReflectColor("ReflectionColor",Color) = (1,1,1,1)
        //控制材質反射程度
        _ReflectAmount("Reflect Amount",Range(0,1)) = 1
        //模擬反射的環境映射紋理
        _Cubemap("Reflection Cubemap",Cube) = "_Skybox"{}
    }
    SubShader 
	{
		Tags { "RenderType"="Opaque" "Queue"="Geometry"}
		
		Pass { 
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			fixed4 _ReflectColor;
			fixed _ReflectAmount;
			samplerCUBE _Cubemap;
			
			struct a2v 
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f 
			{
				float4 pos : SV_POSITION;
				float3 worldPos : TEXCOORD0;
				fixed3 worldNormal : TEXCOORD1;
				fixed3 worldViewDir : TEXCOORD2;
				//世界空間下的反射方向
				fixed3 worldRefl : TEXCOORD3;
				SHADOW_COORDS(4)
			};
			
			v2f vert(a2v v) 
			{
				v2f o;
				
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
				
				//計算世界空間下的反射方向
				o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
				
				TRANSFER_SHADOW(o);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target 
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));		
				fixed3 worldViewDir = normalize(i.worldViewDir);		
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
				
				//使用反射方向對Cubemap進行采樣
				fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
				
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				
				//使用_ReflectAmount混合漫反射顏色和反射顏色
				fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;
				
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	}
	FallBack "Reflective/VertexLit"
}

效果如下:
在這里插入圖片描述
在上面的計算中,我們選擇在頂點著色器中計算反射方向,當然,我們也可以選擇在片元著色器中計算,這樣得到的效果更加細膩,但是,對于絕大多數人來說這種差別往往是可以忽略不計的,因此出于性能方面的考慮,我們選擇在頂點著色器中計算反射方向,

10.1.4折射

折射:當光線從一種介質斜射入另一種介質時,傳播方向一般會發生改變,當給定入射角時,我們可以使用斯涅耳定律來計算反射角,當光從介質1沿著表面法線夾角為θ(1)的方向斜射入介質2時,我們可以使用如下公式計算折射光線與法線的夾角θ(2):
在這里插入圖片描述
其中η(1)和η(2) 分別是兩個介質的折射率,例如真空的折射率是1,而玻璃的折射率一般是1.5,下圖給出了這些變數之間的關系,
在這里插入圖片描述
對一個透明物體來說,一種更準確的模擬方法需要計算兩次折射——一次是當光線進它的內部時,而另一次則是從它內部射出時,但是,想要在實時渲染中模擬出第二次折射方向是比較復雜的,因此,在實時渲染中通常僅模擬第一次折射
代碼:

Shader "Unity Shaders Book/Chapter 10/Refraction"
{
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _RefractColor("ReflectionColor",Color) = (1,1,1,1)
        _RefractAmount("Reflect Amount",Range(0,1)) = 1
        //得到不同介質的透射比
        _RefractRatio("Refraction Ratio",Range(0.1,1)) = 0.5
        _Cubemap("Reflection Cubemap",Cube) = "_Skybox"{}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}
		
		Pass { 
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			fixed4 _RefractColor;
			fixed _RefractAmount;
            fixed _RefractRatio;
			samplerCUBE _Cubemap;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldPos : TEXCOORD0;
				fixed3 worldNormal : TEXCOORD1;
				fixed3 worldViewDir : TEXCOORD2;
				fixed3 worldRefr : TEXCOORD3;
                SHADOW_COORDS(4)
            };



            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
                //計算折射方向,
                //第一個引數即為入射光線的方向,這里是視線方向因為光路可逆
                //第二個引數是表面法線,
                //第三個引數是入射光線所在介質的折射率和折射光線所在介質的折射率之間的比值.
                o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);

                TRANSFER_SHADOW(o);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed3 worldViewDir = normalize(i.worldViewDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));

                //使用折射方向對Cubemap進行采樣
				fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;
				
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				
				//使用_ReflectAmount混合漫反射顏色和折射顏色
				fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;

                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
}

效果如下:
在這里插入圖片描述

10.1.5 菲涅耳反射

在實時渲染中,我們經常會使用菲涅耳反射(Fresnel reflection)來根據視角方向控制反射程 度,通俗地講,菲涅耳反射描述了一種光學現象,即當光線照射到物體表面上時,一部分發生反射,一部分進入物體內部,發生折射或散射,被反射的光和入射光之間存在一定的比率關系,這個比率關系可以通過菲涅耳等式進行計算,

計算菲涅耳反射需要使用菲涅耳等式,真實世界的菲涅耳等式是非常復雜的,但在實時渲染中,我們通常會使用一些近似公式來計算,其中一個著名的近似公式就是Schlick菲涅耳近似等式
在這里插入圖片描述
其中,F(0)是一個反射系數,用于控制菲涅耳反射的強度,v是視角方向,n是表面法線,另一個應用比較廣泛的等式是Empricial菲涅耳近似等式
在這里插入圖片描述
其中,bias、scale和power是控制項,
代碼:

Shader "Unity Shaders Book/Chapter 10/Fresnel"
{
    Properties
    {
        _Color("Color Tint",Color) = (1,1,1,1)
        _FresnelScale("Fresnel Scale",Range(0,1)) = 0.5
        _Cubemap("Reflection Cubemap",Cube) = "_Skybox"{}

    }
    SubShader
	{
		Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}

		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM

			#pragma multi_compile_fwdbase

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"
			#include "AutoLight.cginc"

			fixed4 _Color;
			fixed _FresnelScale;
			samplerCUBE _Cubemap;

			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 worldPos : TEXCOORD0;
				fixed3 worldNormal : TEXCOORD1;
				fixed3 worldViewDir : TEXCOORD2;
				fixed3 worldRefl : TEXCOORD3;
				SHADOW_COORDS(4)
			};

			v2f vert(a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);

				o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

				TRANSFER_SHADOW(o);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 worldViewDir = normalize(i.worldViewDir);

				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				//計算陰影和光照衰減
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)

				fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;

				//使用Schlick菲涅耳近似等式來計算fresnel變數
				fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);

				//使用fresnel混合漫反射光照和反射光照
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));

				fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;

				return fixed4(color, 1.0);

			}
			ENDCG
		}
	
	}	
    FallBack "Reflective/VertexLit"
}

效果如下:
在這里插入圖片描述
如圖是Fresnel Scale為0時的效果,只具有邊緣光照,就像現實中的全反射現象,只有在視線方向與表面法線達到一定角度時才會發生,

10.2 渲染紋理

在之前的學習中,一個攝像機的渲染結果會輸出到顏色緩沖中,并顯示到我們的螢屏上,現代的GPU允許我們把整個三維場景渲染到一個中間緩沖中,即渲染目標紋理(Render Target Texture, RTT),而不是傳統的幀緩沖或后備緩沖(back buffer),與之相關的是多重渲染目標 (Multiple Render Target, MRT),這種技術指的是**GPU允許我們把場景同時渲染到多個渲染目 標紋理中,而不再需要為每個渲染目標紋理單獨渲染完整的場景,**延遲渲染就是使用多重渲染目標的一個應用,

Unity為渲染目標紋理定義了一種專門的紋理型別——渲染紋理(Render Texture),在Unity中使用渲染紋理通常有兩種方式:
一種方式是在Project目錄下創建一個渲染紋理,然后把某個攝像機的渲染目標設定成該渲染紋理,這樣一來該攝像機的渲染結果就會實時更新到渲染紋理中, 而不會顯示在螢屏上,使用這種方法,我們還可以選擇渲染紋理的解析度、濾波模式等紋理屬性,
另一種方式是在螢屏后處理時使用GrabPass命令或OnRenderlmage函式來獲取當前螢屏影像, Unity會把這個螢屏影像放到一張和螢屏解析度等同的渲染紋理中,下面我們可以在自定義的Pass 中把它們當成普通的紋理來處理,從而實作各種螢屏特效,

10.2.1 鏡子效果

  1. 在場景中創建6個立方體,并調整它們的位置和大小,使得它們構成圍繞著攝像機的房 間的6面墻,給它們賦予在9.5節中創建的標準材質,向場景中添加3 個點光源,并調整它們的位置,使它們可以照亮整個房間,
  2. 創建3個球體和兩個正方體,調整它們的位置和大小,并給它們賦予在9.5節中創建的標準材質,這些物體將作為房間內的飾品,
  3. 創建一個四邊形(Quad),調整它的位置和大小,它將作為鏡子,把創建的鏡子材質賦給它,
  4. 在Project視圖下創建一個渲染紋理(右鍵單擊Create — Render Texture),命名為MirrorTexture,它使用的紋理設定如下圖所示,
  5. 最后,為了得到從鏡子出發觀察到的場景影像,我們還需要創建一個攝像機,由于這個攝像機不需要直接顯示在螢屏上,而是用于渲染到紋理,因此,我們把第4步中創建的MirrorTexture拖曳到該攝像機的Target Texture上,下圖顯示了攝像機面板和渲染紋理的相關設定,

在這里插入圖片描述
在這里插入圖片描述

鏡子材質代碼:

Shader "Unity Shaders Book/Chapter 10/Mirror"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            sampler2D _MainTex;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };



            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                //翻轉x分量的紋理坐標,
                //因為鏡子里影像左右相反
                o.uv.x = 1 - o.uv.x;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                //對渲染紋理采樣和輸出
                return tex2D(_MainTex,i.uv);
            }
            ENDCG
        }
    }
        FallBack Off
}

保存后回傳場景,并把我們創建的MirrorTexture渲染紋理拖曳到材質的Main Tex屬性中,效果如下:
在這里插入圖片描述

10.2.2玻璃效果

在Unity中,我們還可以在Unity Shader中使用一種特殊的Pass來完成獲取螢屏影像的目的,這就是GrabPass
當我們在Shader中定義了一個GrabPass后,Unity會把當前螢屏的影像繪制在一張紋理中,以便我們在后續的Pass中訪問它,
我們通常會使用GrabPass來實作諸如玻璃等透明材質的模擬,與使用簡單的透明混合不同,使用GrabPass可以讓我們對該物體后面的影像進行更復雜的處理,例如使用法線來模擬折射效果,而不再是簡單的和原螢屏顏色進行混合

需要注意的是,在使用GrabPass的時候,我們需要額外小心物體的渲染佇列設定,正如之前所說,GrabPass通常用于渲染透明物體,盡管代碼里并不包含混合指令,但我們往往仍然需要把物體的渲染佇列設定成透明佇列(即"Queue"=“Transparent”),這樣才可以保證當渲染該物體時, 所有的不透明物體都已經被繪制在螢屏上,從而獲取正確的螢屏影像,
代碼:


Shader "Unity Shaders Book/Chapter 10/Glass Refraction" 
{
	Properties 
	{
		_MainTex ("Main Tex", 2D) = "white" {}
		_BumpMap ("Normal Map", 2D) = "bump" {}
		_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
		//控制折射時影像扭曲程度
		_Distortion ("Distortion", Range(0, 100)) = 10
		//控制折射程度(值為0時該玻璃只包含反射效果,值為1時只包括折射效果)
		_RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0
	}
	SubShader 
	{
		//Queue設定成Transparent可以確保該物體渲染時,
		//其他所有不透明物體都已經被渲染到螢屏上了
		Tags { "Queue"="Transparent" "RenderType"="Opaque" }
		
		//定義一個抓取螢屏圖形的Pass
		//該字串內部的名稱決定了抓取得到的螢屏影像將會被存入哪個紋理中
		GrabPass { "_RefractionTex" }
		
		Pass 
		{		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			samplerCUBE _Cubemap;
			float _Distortion;
			fixed _RefractAmount;
			//對應使用GrabPass時指定的紋理名稱
			sampler2D _RefractionTex;
			//_TexelSize可以得到該紋理的紋素大小,
			//例如一個大小為256X512的紋理,它的紋素大小為(1/256, 1/512),
			//我們需要在對螢屏影像的釆樣坐標進行偏移時使用該變數
			float4 _RefractionTex_TexelSize;
			
			struct a2v 
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT; 
				float2 texcoord: TEXCOORD0;
			};
			
			struct v2f 
			{
				float4 pos : SV_POSITION;
				float4 scrPos : TEXCOORD0;
				float4 uv : TEXCOORD1;
				float4 TtoW0 : TEXCOORD2;  
			    float4 TtoW1 : TEXCOORD3;  
			    float4 TtoW2 : TEXCOORD4; 

			};
			
			v2f vert (a2v v) 
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				//呼叫函式得到頂點坐標對應被抓取的螢屏影像的采樣坐標
				o.scrPos = ComputeGrabScreenPos(o.pos);
				
				o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
				
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
				fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
				fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
				
				//切線空間到世界空間變換矩陣,w分量存盤世界空間頂點坐標
				o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
				o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
				o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  
				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target 
			{		
				float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
				fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				
				//得到切線空間法線方向
				fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));	
				
				//對螢屏影像的采樣坐標進行便宜,模擬折射效果
				float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
				//offset乘以scrPos的z分量,z分量越大折射效果越明顯
				i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
				//對scrPos進行齊次除法得到真正的螢屏坐標,
				//然后用該坐標對抓取的螢屏影像進行采樣,得到折射顏色
				fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
				
				//把法線方向變換到世界空間下
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
				fixed3 reflDir = reflect(-worldViewDir, bump);
				fixed4 texColor = tex2D(_MainTex, i.uv.xy);
				//得到反射顏色
				fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
				
				fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;
				
				return fixed4(finalColor, 1);
			}
			
			ENDCG
		}
	}
	
	FallBack "Diffuse"
}

效果如下:
在這里插入圖片描述
實際上,GrabPass支持兩種形式:

  • 直接使用GrabPass { },然后在后續的Pass中直接使用_GrabTexture來訪問螢屏影像,對于每一個使用它的物體,Unity都會為它單獨進行一次昂貴的螢屏抓取操作,這種方法可以讓每個物體得到不同的螢屏影像,這取決于它們的渲染佇列及渲染它們時當前的螢屏緩沖中的顏色,
  • 使用GrabPass ( ''TextureName" ),正如本節中的實作,我們可以在后續的Pass中使用TextureName來訪問螢屏影像,使用這種方法同樣可以抓取螢屏,但Unity只會在每一幀時為第一個使用名為TextureName的紋理的物體執行一次抓取螢屏的操作,而這個紋理同樣可以在其他Pass中被訪問,這種方法更高效,因為不管場景中有多少物體使用了該命令,每一幀中Unity都只會執行一次抓取作業,但這也意味著所有物體都會使用同一張螢屏影像,不過,在大多數情況下這已經足夠了,

10.2.3渲染紋理 vs. GrabPass

盡管GrabPass和之前使用的渲染紋理+額外的攝像機的方式都可以抓取螢屏影像,但它們還是有一些不同的,GrabPass的好處在于實作簡單,我們只需要再Shader中寫幾行代碼就可以實作抓取螢屏的目的,而要使用渲染紋理的話,我們首先需要創建一個渲染紋理和一個額外的攝像機,再把該攝像機的Render Target 設定為新建的渲染紋理物件,最后把該渲染紋理傳遞給回應的Shader,

但從效率上來說,使用渲染紋理的效率往往要好于GrabPass,尤其在移動設備上,使用渲染紋理我們可以自定義渲染紋理的大小,盡管這種方法需要把部分場景再次渲染一遍,但我么可以通過調整攝像機的渲染層來減少二次渲染時的場景大小,或使用其他方法來控制攝像機是否需要開啟,而使用GrabPass獲取到的影像解析度和顯示螢屏是一致的,這意味著在一些高解析度的設備上可能會造成嚴重的帶寬影響,而且在移動設備上,GrabPass雖然不會重新渲染場景,但它往往需要CPU直接讀取后備緩沖中的資料,破壞了CPU和GPU之間的并行性,這是比較耗時的,甚至在一些移動設備上這是不支持的,

10.3程式紋理

程式紋理(Procedural Texture)指的是那些由計算機生成的影像,我們通常使用一些特定的演算法來創建個性化圖案或非常真實的自然元素,例如木頭、石子等,
使用程式紋理的好處在于我們可以使用各種引數來控制紋理的外觀,而這些屬性不僅僅是那些顏色屬性,甚至可以是完全不同型別的圖案屬性,這使得我們可以得到更加豐富的影片和視覺效果,

10.3.1在Unity中實作簡單的程式紋理

代碼:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;

[ExecuteInEditMode]
public class ProceduralTextureGeneration : MonoBehaviour {

	//宣告一個材質,
	//這個材質將使用腳本中生成的程式紋理
	public Material material = null;

	//宣告紋理引數
	#region Material properties
	[SerializeField, SetProperty("textureWidth")]
	private int m_textureWidth = 512;
	public int textureWidth {
		get {
			return m_textureWidth;
		}
		set {
			m_textureWidth = value;
			_UpdateMaterial();
		}
	}

	[SerializeField, SetProperty("backgroundColor")]
	private Color m_backgroundColor = Color.blue;
	public Color backgroundColor {
		get {
			return m_backgroundColor;
		}
		set {
			m_backgroundColor = value;
			_UpdateMaterial();
		}
	}

	[SerializeField, SetProperty("circleColor")]
	private Color m_circleColor = Color.yellow;
	public Color circleColor {
		get {
			return m_circleColor;
		}
		set {
			m_circleColor = value;
			_UpdateMaterial();
		}
	}

	[SerializeField, SetProperty("blurFactor")]
	//宣告模糊因子
	private float m_blurFactor = 2.0f;
	public float blurFactor {
		get {
			return m_blurFactor;
		}
		set {
			m_blurFactor = value;
			_UpdateMaterial();
		}
	}
	#endregion

	//宣告紋理變數保存生成的紋理
	private Texture2D m_generatedTexture = null;

	//在Start函式中進行檢查,得到需要使用該程式紋理的材質
	void Start () {
		if (material == null) {
			//如果材質為空,
			//就從使用該腳本的物體上得到相應的材質
			Renderer renderer = gameObject.GetComponent<Renderer>();
			if (renderer == null) {
				Debug.LogWarning("Cannot find a renderer.");
				return;
			}

			material = renderer.sharedMaterial;
		}

		_UpdateMaterial();
	}

	private void _UpdateMaterial() {
		if (material != null) {
			//呼叫函式生成程式紋理,賦給m_generatedTexture變數
			m_generatedTexture = _GenerateProceduralTexture();
			//把生成的紋理賦給材質
			material.SetTexture("_MainTex", m_generatedTexture);
		}
	}

	private Color _MixColor(Color color0, Color color1, float mixFactor) {
		Color mixColor = Color.white;
		mixColor.r = Mathf.Lerp(color0.r, color1.r, mixFactor);
		mixColor.g = Mathf.Lerp(color0.g, color1.g, mixFactor);
		mixColor.b = Mathf.Lerp(color0.b, color1.b, mixFactor);
		mixColor.a = Mathf.Lerp(color0.a, color1.a, mixFactor);
		return mixColor;
	}

	private Texture2D _GenerateProceduralTexture() {
		//初始化一張二維紋理
		Texture2D proceduralTexture = new Texture2D(textureWidth, textureWidth);

		//定義圓與圓之間的間距
		float circleInterval = textureWidth / 4.0f;
		//定義圓的半徑
		float radius = textureWidth / 10.0f;
		//定義模糊系數
		float edgeBlur = 1.0f / blurFactor;

		for (int w = 0; w < textureWidth; w++) {
			for (int h = 0; h < textureWidth; h++) {
				//使用背景顏色進行初始化
				Color pixel = backgroundColor;

				//依次畫9個圓
				for (int i = 0; i < 3; i++) {
					for (int j = 0; j < 3; j++) {
						//計算當前所絵制的圓的圓心位置
						Vector2 circleCenter = new Vector2(circleInterval * (i + 1), circleInterval * (j + 1));

						//計算當前像素與圓心的距離
						float dist = Vector2.Distance(new Vector2(w, h), circleCenter) - radius;

						//模糊圓的邊界
						Color color = _MixColor(circleColor, new Color(pixel.r, pixel.g, pixel.b, 0.0f), Mathf.SmoothStep(0f, 1.0f, dist * edgeBlur));

						//與之前得到的顏色進行混合
						pixel = _MixColor(pixel, color, color.a);
					}
				}

				proceduralTexture.SetPixel(w, h, pixel);
			}
		}

		//把像素值寫入紋理中
		proceduralTexture.Apply();

		return proceduralTexture;
	}
}

效果如下:
在這里插入圖片描述

10.3.2Unity的程式材質

在Unity中,有一類專門使用程式紋理的材質,叫做程式材質(Procedural Materials),這類材質和我們之前使用的那些材質在本質上是一樣的,不同的是,它們使用的紋理不是普通的紋理, 而是程式紋理,需要注意的是,程式材質和它使用的程式紋理并不是在Unity中創建的,而是使用了一個名為Substance Designer的軟體在Unity外部生成的,

Substance Designer是一個非常出色的紋理生成工具,很多3A的游戲專案都使用了由它生成 的材質,我們可以從Unity的資源商店或網路中獲取到很多免費或付費的Substance材質,這些材質都是以.sbsar為后綴的,我們可以直接把這些材質像其他資源一樣拖入Unity專案中,
當把這些檔案匯入Unity后,Unity就會生成一個程式紋理資源(Procedural Material Asset),

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/272322.html

標籤:其他

上一篇:javascript操作行內樣式和獲取元素當前樣式以及兼容性問題

下一篇:CodeForces - 731A

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more