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
}

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