Chapter15_使用噪聲

消融(dissolve)效果:

原理:噪聲紋理+透明度測試

  • 1.使用噪聲紋理的採樣結果和某個閾值比較,小於閾值,使用clip函數把它對應的像素裁剪掉,這對應了圖中的燒燬區域。
  • 2.燒焦效果則是將兩種顏色混合,再用pow函數處理後,與原紋理顏色混合後的結果。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Chan/Chapter15_Dissolve" {
	Properties
	{
		//控制消融程度
		_BurnAmount("Burn Amount",Range(0.0,1.0)) = 0.0
		//控制模擬燒焦的線寬,值越大,火焰蔓延的範圍越廣
		_LineWidth("Burn Line Width",Range(0.0,0.2)) = 0.1
		_MainTex("Main Tex",2D) = "white"{}
		_BumpMap("Normal Map",2D) = "bump"{}
		//火線邊緣的兩種顏色
		_BurnFirstColor("Burn First Color",Color) = (1,0,0,1)
		_BurnSecondColor("Burn Second Color",Color) = (1,0,0,1)
		//噪聲紋理
		_BurnMap("Burn Map",2D) = "white"{}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" "Queue"="Geometry"}

		Pass
		{
			Tags{"LightMode" = "ForwardBase"}
			//消融效果會導致裸露模型內部的構造,如果只渲染正面會出現錯誤 因此關閉面片剔除
			Cull Off

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

			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag

			fixed _BurnAmount;
			fixed _LineWidth;

			sampler2D _MainTex;
			sampler2D _BumpMap;
			sampler2D _BurnMap;
			float4 _MainTex_ST;
			float4 _BumpMap_ST;
			float4 _BurnMap_ST;

			fixed4 _BurnFirstColor;
			fixed4 _BurnSecondColor;

			struct a2v
			{
				float4 vertex:POSITION;
				float4 texcoord:TEXCOORD0;
				//TANGENT_SPACE_ROTATION 計算需要
				float3 normal:NORMAL;
				float4 tangent:TANGENT;
			};

			struct v2f
			{
				float4 pos:SV_POSITION;
				float2 uvMainTex:TEXCOORD0;
				float2 uvBumpMap:TEXCOORD1;
				float2 uvBurnMap:TEXCOORD2;
				float3 lightDir:TEXCOORD3;
				float3 worldPos:TEXCOORD4;
				SHADOW_COORDS(5)
			};

			v2f vert(a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);

				//獲取三個紋理的紋理(uv)座標
				o.uvMainTex = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uvBumpMap = TRANSFORM_TEX(v.texcoord,_BumpMap);
				o.uvBurnMap = TRANSFORM_TEX(v.texcoord,_BurnMap);

// 				TANGENT_SPACE_ROTATION 宏 相當於嵌入如下兩行代碼:得到模型空間像切線空間的轉置矩陣rotation
//   			float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;
//   			float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal);

				TANGENT_SPACE_ROTATION;
				//光源方向從模型空間轉化到切線空間
				o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;

				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
				
				//得到陰影紋理的採樣座標 參見chapter_9
				TRANSFER_SHADOW(o);

				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				fixed3 burn = tex2D(_BurnMap,i.uvBurnMap).rgb;
				
				//burn.r - _BurnAmount < 0 該像素被剔除,不會顯示到屏幕上
				clip(burn.r - _BurnAmount);

				float3 tangentLightDir = normalize(i.lightDir);
				fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap,i.uvBumpMap));

				//採樣紋理,得到漫反射紋理的材質的反射率 = albedo
				fixed3 albedo = tex2D(_MainTex,i.uvMainTex).rgb;
				//得到環境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

				//切線空間下,Lambert計算漫反射
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(tangentNormal,tangentLightDir));

				fixed t = 1 - smoothstep(0.0,_LineWidth,burn.r - _BurnAmount);
				//插值兩種燒焦顏色
				fixed3 burnColor = lerp(_BurnFirstColor,_BurnSecondColor,t);
				//^5 加深顏色?
				burnColor = pow(burnColor,5);

				UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
				//混合正常的光照顏色(環境光+漫反射),和燒焦顏色
				//spec = step(閾值,spec);//    step函數  spec小於閾值返回0,大於等於返回1 保證閾值小於0.0001時,沒有消融效果
				fixed3 finalColor = lerp(ambient + diffuse * atten,burnColor,t * step(0.0001,_BurnAmount));

				return fixed4(finalColor,1);

			}

			ENDCG
		}
	}
	FallBack "Diffuse"
}

有透明度操作的物體,陰影需要特殊處理,不然被剔除掉的區域,仍然會向其他物體投射陰影,造成穿幫。因此爲了讓物體的陰影也配合透明度測試,產生正確的效果。需要自定義投射陰影Pass:

//專門處理陰影
		Pass
		{
			Tags{"LightMode" = "ShadowCaster"}

			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_shadowcaster

			#include "UnityCG.cginc"

			sampler2D _BurnMap;
			float4 _BurnMap_ST;
			fixed _BurnAmount;

			struct v2f
			{
				V2F_SHADOW_CASTER;
				float2 uvBurnMap:TEXCOORD1;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				//填充V2F_SHADOW_CASTER裏邊的變量
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
				//得到噪聲紋理的uv座標
				o.uvBurnMap = TRANSFORM_TEX(v.texcoord,_BurnMap);
				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				fixed3 burn = tex2D(_BurnMap,i.uvBurnMap);
				//根據閾值剔除模型的某些片元
				clip(burn.r - _BurnAmount);
				//把結果輸出到深度圖和陰影映射紋理中
				SHADOW_CASTER_FRAGMENT(i)
			}

			ENDCG
		}

註解:

採樣噪聲紋理,r分量跟閾值進行比較。剔除模型的某些片元,然後根據剔除後的模型,生成陰影。

 https://blog.csdn.net/qq_19269527/article/details/88774065

 

水波效果 

  1. 水波法線紋理由一張噪聲紋理生成,並且隨着時間變化不斷平移,模擬波光粼粼的效果。
  2. 使用菲涅爾係數動態決定,反射和折射混合係數。
Shader "Chan/Chapter15_WaterWave" {
	Properties {
		//控制水面顏色
		_Color("Main Color",Color) = (1,1,1,1)
		//水面波紋紋理
		_MainTex("Main Tex",2D) = "white"{}
		//噪聲紋理生成的法線紋理
		_WaveMap("Wave Map",2D) = "bump"{}
		//模擬反射的立方體紋理
		_Cubemap("Environment Cubemap",Cube) = "_Skybox"{}
		//控制法線紋理的x,y方向的平移速度
		_WaveXSpeed("Wave Horizontal Speed",Range(-0.1,0.1)) = 0.01
		_WaveYSpeed("Wave Vertical Speed",Range(-0.1,0.1)) = 0.01
		_Distortion("Distortion",Range(0,100)) = 10
	}

	SubShader {
		
		//"Queue" = "Transparent" 確保水波紋理渲染時候,所有不透明物體已經被渲染
		Tags{"Queue" = "Transparent" "RenderType" = "Opaque"}
		
		//抓取屏幕的Pass,紋理存到_RefractionTex
		GrabPass{"_RefractionTex"}

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

			CGPROGRAM
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
		
			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _WaveMap;
			float4 _WaveMap_ST;
			samplerCUBE _Cubemap;
			fixed _WaveXSpeed;
			fixed _WaveYSpeed;
			float _Distortion;
			sampler2D _RefractionTex;
			//使用紋素大小,對紋理採樣座標進行偏移時用到
			float4 _RefractionTex_TexelSize;

			struct a2v
			{
				float4 vertex:POSITION;
				float3 normal:NORMAL;
				float4 tangent:TANGENT;
				float4 texcoord:TEXCOORD0;
			};

			struct v2f
			{
				float4 pos:SV_POSITION;
				float4 srcPos:TEXCOORD0;
				float4 uv:TEXCOORD1;
				float4 Ttow0:TEXCOORD2;
				float4 Ttow1:TEXCOORD3;
				float4 Ttow2:TEXCOORD4;
			};

			v2f vert(a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);

				//得到對應被抓取屏幕圖像_RefractionTex的採樣座標
				o.srcPos = ComputeGrabScreenPos(o.pos);
				//得到_MainTex,_WaveMap的紋理座標
				o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uv.zw = TRANSFORM_TEX(v.texcoord,_WaveMap);

				//需要在片元着色器中把法線方向(法線紋理採樣得到)從切線空間變換到世界空間下,以便對Cubemap進行採樣,因此
				//需要計算頂點從切線空間變換到世界空間的變換矩陣,得到切線空間下的三個(切線,副切線,法線)
				//然後按列組成一個變換矩陣即可
				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.z;

				//傳遞給片元着色器
				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;
			}

			float4 frag(v2f i):SV_Target
			{
				float3 worldPos = float3(i.Ttow0.w,i.Ttow1.w,i.Ttow2.w);
				//片元對應的視角方向
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float2 speed = _Time.y * float2(_WaveXSpeed,_WaveYSpeed);

				//對法線紋理進行偏移
				//對法線紋理進行兩次採樣,模擬兩層交叉的水面波動,歸一化後得到切線空間下的法線方向
				fixed3 bump1 = UnpackNormal(tex2D(_WaveMap,i.uv.zw + speed)).rgb;
				fixed3 bump2 = UnpackNormal(tex2D(_WaveMap,i.uv.zw - speed)).rgb;
				fixed3 bump = normalize(bump1 + bump2);

				//利用切線空間下的法線對抓取的屏幕紋理的uv進行偏移,進行uv動畫,得到折射顏色
				//因爲切線空間下的法線方向可以反映頂點局部空間下的法線方向
				float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
				//模擬屏幕深度值z越大,折射程度越大
				i.srcPos.xy = offset * i.srcPos.z + i.srcPos.xy;
				//i.srcPos.xy/i.srcPos.w) =  透視除法
				//以下一句得到模擬的折射顏色
				fixed3 refrCol = tex2D(_RefractionTex,i.srcPos.xy/i.srcPos.w).rgb;

				//將法線從切線空間變化到世界空間
				bump = normalize(half3(dot(i.Ttow0.xyz,bump),dot(i.Ttow1.xyz,bump),dot(i.Ttow2.xyz,bump)));

				//對主紋理也進行uv動畫,模擬水波效果
				fixed4 texColor = tex2D(_MainTex,i.uv.xy + speed);
				fixed3 reflDir = reflect(-viewDir,bump);
				//根據反射角度,採樣Cube 得到反射顏色
				fixed3 reflCol = texCUBE(_Cubemap,reflDir).rgb * texColor.rgb * _Color.rgb;

				//菲涅爾係數
				fixed fresnel = pow(1-saturate(dot(viewDir,bump)),4);
				//根據菲涅爾係數,混合反射顏色和折射顏色 = 最終顏色
				fixed3 finalColor = reflCol * fresnel + refrCol * (1 - fresnel);

				return fixed4(finalColor,1);
			}

			ENDCG
		}
	}
	FallBack "Diffuse"
}

註解:

1.把噪聲紋理當做法線紋理,並對法線進行偏移,然後用偏移後的切線空間下的法線對抓取的屏幕紋理的uv進行偏移,然後用該座標對抓取的屏幕紋理進行採樣,得到折射後的顏色。

2.然後把法線從切線空間變化到世界空間,利用視角方向和法線方向得到反射方向,然後用反射方向對Cubemap進行採樣,得到反射顏色。

3.對主紋理也進行uv動畫,模擬水波效果

效果如下:

第一圖,只有折射,沒有反射。水面成"無色"。第二圖,反射周圍Cubemap顏色。

全局霧效 

基於相同高度的霧,在相同高度霧效是一樣的。要想模擬不均勻的霧效,使霧看起來更縹緲,可以通過噪聲紋理實現。

實現思路:

隨時間變化偏移當前噪聲紋理的uv,拿着偏移後uv採樣噪聲紋理,得到的noise值混入霧效濃度計算中。

Shader "Chan/FogWithNoise" {
	Properties
	{
		_MainTex("Main Tex",2D) = "white"{}
		//霧密度
		_FogDensity("Fog Density",float) = 1.0
		//霧顏色
		_FogColor("Fog Color",Color) = (1,1,1,1)
		//霧開始高度
		_FogStart("Fog Start",float) = 0.0
		//霧結束高度
		_FogEnd("Fog End",float) = 1.0

		//噪聲紋理
		_NoiseTex("Noise Tex",2D) = "white"{}
		//
		_FogXSpeed("Fog Horizontal Speed",float) = 0.1
		_FogYSpeed("Fog Vertical Speed",float) = 0.1
		//控制噪聲程度
		_NoiseAmount("Noise Amount",float) = 1

	}
	SubShader
	{
		CGINCLUDE

		#include "UnityCG.cginc"

			float4x4 _FrustumCornersRay;
			sampler2D _MainTex;
			half4 _MainTex_TexelSize;
			//深度紋理
			sampler2D _CameraDepthTexture;

			half _FogDensity;
			fixed4 _FogColor;
			float _FogStart;
			float _FogEnd;
			sampler2D _NoiseTex;
			half _FogXSpeed;
			half _FogYSpeed;
			half _NoiseAmount;

			struct v2f
			{
				float4 pos:SV_POSITION;
				half2 uv:TEXCOORD0;
				half2 uv_depth:TEXCOORD1;
				//存儲
				float4 interpolateRay:TEXCOORD2;
			};

			v2f vert(appdata_img v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				o.uv_depth = v.texcoord;

				#if UNITY_UV_STARTS_AT_TOP
				if (_MainTex_TexelSize.y < 0)
				{
					o.uv_depth.y = 1 - o.uv_depth.y;
				}
				#endif

				//屏幕後全局所用的模型是一個四邊形網格,只包含四個頂點
				//判斷當前頂點是哪個頂點,此處使用uv紋理座標判斷。左下->右下->右上->左上
				int index = 0;
				if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5)
				{
					index = 0;
				}
				else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5)
				{
					index = 1;
				}
				else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5)
				{
					index = 2;
				}
				else
				{
					index = 3;
				}	

				//平臺,設置差異 可能導致左下->右下->右上->左上錯亂,跟c#腳本對上,差異化處理
				#if UNITY_UV_STARTS_AT_TOP
				if (_MainTex_TexelSize.y < 0)
				{
					index = 3 - index;
				}
				#endif
				o.interpolateRay = _FrustumCornersRay[index];
				return o;
			}

			fixed4 frag(v2f i) :SV_Target
			{
				//不同平臺要通過uv採樣深度紋理得到深度值,可能有差異。使用宏定義 內部處理了。SAMPLE_DEPTH_TEXTURE 
				float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth));
			    //計算得到某個像素點的世界座標
				float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolateRay.xyz;

				//隨時間變化偏移當前噪聲紋理的uv,拿着偏移後uv採樣噪聲紋理,得到的noise值混入霧效濃度計算中
				float2 speed = _Time.y * float2(_FogXSpeed,_FogYSpeed);
				float noise = (tex2D(_NoiseTex,i.uv + speed).r - 0.5) * _NoiseAmount;

				//根據公式,霧效密度
				float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
				//
				fogDensity = saturate(fogDensity * _FogDensity * (1 + noise));
				fixed4 finalColor = tex2D(_MainTex,i.uv);
				//原始紋理顏色混合霧效顏色 = 最終顏色
				finalColor.rgb = lerp(finalColor.rgb,_FogColor.rgb,fogDensity);
				return finalColor;
			}
		ENDCG

		Pass
		{
			ZTest Always Cull Off
			ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			ENDCG
		}
	}

	FallBack Off
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章