消融(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
水波效果
- 水波法線紋理由一張噪聲紋理生成,並且隨着時間變化不斷平移,模擬波光粼粼的效果。
- 使用菲涅爾係數動態決定,反射和折射混合係數。
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
}