消融效果
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 15/Dissolve" {
Properties {
_BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0 //控制消融程度,值越大,消融程度越明显
_LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1 //控制模拟消融程度时的线宽,值越大,火焰边缘蔓延的范围越广
_MainTex ("Base (RGB)", 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;
fixed4 _BurnFirstColor;
fixed4 _BurnSecondColor;
sampler2D _BurnMap;
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
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);
//计算三张纹理的纹理坐标
o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
//变换到切线空间
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
//对噪声采样,和阈值相减后小于0时就会剔除该像素
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
//剩下的像素进行光照计算
float3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
//计算烧焦颜色,在宽度_LineWidth内模拟颜色变化,t为1时,说明该像素位于消融的边界处,t为0时说明该像素为正常颜色,中间插值模拟烧焦效果
fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
burnColor = pow(burnColor, 5); //pow函数使效果更像烧焦颜色
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
//_BurnAmount为0时不显示任何消融效果
fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
return fixed4(finalColor, 1);
}
ENDCG
}
// 因为消融的地方不需要投射阴影,所以自定义一个阴影Pass
Pass {
Tags {
"LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
fixed _BurnAmount;
sampler2D _BurnMap;
float4 _BurnMap_ST;
struct v2f {
V2F_SHADOW_CASTER; //定义投射阴影需要的变量
float2 uvBurnMap : TEXCOORD1;
};
v2f vert(appdata_base v) {
v2f o;
//填充变量(过程都是unity自动完成)
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}
水波效果
使用噪声纹理作为高度图,不断修改水面的法线方向,使用和时间相关的变量对噪声纹理采样,得到水流动的效果;
反射:Cubemap模拟
折射:Grabpass获取当前屏幕的渲染纹理,并使用切线空间下的法线方向对像素的屏幕坐标进行偏移,再使用该坐标对渲染纹理进行屏幕采样;
水波的法线纹理是由一张噪声纹理生成而得,并且会随着时间不断偏移,模拟波光粼粼的效果。这里使用菲涅耳系数混合反射和折射颜色:
f r e s n e l = p o w ( 1 − m a x ( 0 , ν ⋅ n ) ) fresnel=pow(1-max(0,\nu \cdot n)) fresnel=pow(1−max(0,ν⋅n))
v和n分别对应视角方向和法线方向,夹角越小,fresnel值越小,反射越弱,折射越强;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 15/Water Wave" {
Properties {
_Color ("Main Color", Color) = (0, 0.15, 0.115, 1)
_MainTex ("Base (RGB)", 2D) = "white" {
}
_WaveMap ("Wave Map", 2D) = "bump" {
} //噪声纹理生成的法线纹理
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {
}
_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 {
// We must be transparent, so other objects are drawn before this one.
Tags {
"Queue"="Transparent" "RenderType"="Opaque" }
// This pass grabs the screen behind the object into a texture.
// 将抓取到的屏幕图像存到_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 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, _WaveMap);
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;
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 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);
// Compute the offset in tangent space
//折射,使用切线空间的法线方向偏移是因为该空间下的法线可以反应顶点局部空间下的法线方向;
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
//i.scrPos.z是为了模拟深度越大,折射程度越大的效果
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//使用透视除法,再使用该坐标采样得到模拟的折射颜色
fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
// Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
//得到视角方向相当于法线方向的反射方向
fixed3 reflDir = reflect(-viewDir, bump);
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
}
}
// Do not cast shadow
FallBack Off
}
在这里需要一张法线纹理,可以从该噪声纹理的灰度值生成需要的法线信息,步骤:在噪声纹理的纹理面板中将纹理类型设置为normal map,并选中create from grayscale完成,再把生成的法线纹理赋给_WaveMap属性;
缥缈雾效
脚本:
using UnityEngine;
using System.Collections;
public class FogWithNoise : PostEffectsBase {
public Shader fogShader;
private Material fogMaterial = null;
public Material material {
get {
fogMaterial = CheckShaderAndCreateMaterial(fogShader, fogMaterial);
return fogMaterial;
}
}
private Camera myCamera;
public Camera camera {
get {
if (myCamera == null) {
myCamera = GetComponent<Camera>();
}
return myCamera;
}
}
private Transform myCameraTransform;
public Transform cameraTransform {
get {
if (myCameraTransform == null) {
myCameraTransform = camera.transform;
}
return myCameraTransform;
}
}
[Range(0.1f, 3.0f)]
public float fogDensity = 1.0f;
public Color fogColor = Color.white;
public float fogStart = 0.0f;
public float fogEnd = 2.0f;
public Texture noiseTexture;
[Range(-0.5f, 0.5f)]
public float fogXSpeed = 0.1f;
[Range(-0.5f, 0.5f)]
public float fogYSpeed = 0.1f;
[Range(0.0f, 3.0f)]
public float noiseAmount = 1.0f; //控制噪声程度,值为0时不应用噪声,得到均匀的高度雾
void OnEnable() {
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
}
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
Matrix4x4 frustumCorners = Matrix4x4.identity;
float fov = camera.fieldOfView;
float near = camera.nearClipPlane;
float aspect = camera.aspect;
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
Vector3 toRight = cameraTransform.right * halfHeight * aspect;
Vector3 toTop = cameraTransform.up * halfHeight;
Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
float scale = topLeft.magnitude / near;
topLeft.Normalize();
topLeft *= scale;
Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
topRight.Normalize();
topRight *= scale;
Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
bottomLeft.Normalize();
bottomLeft *= scale;
Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
bottomRight.Normalize();
bottomRight *= scale;
frustumCorners.SetRow(0, bottomLeft);
frustumCorners.SetRow(1, bottomRight);
frustumCorners.SetRow(2, topRight);
frustumCorners.SetRow(3, topLeft);
material.SetMatrix("_FrustumCornersRay", frustumCorners);
material.SetFloat("_FogDensity", fogDensity);
material.SetColor("_FogColor", fogColor);
material.SetFloat("_FogStart", fogStart);
material.SetFloat("_FogEnd", fogEnd);
material.SetTexture("_NoiseTex", noiseTexture);
material.SetFloat("_FogXSpeed", fogXSpeed);
material.SetFloat("_FogYSpeed", fogYSpeed);
material.SetFloat("_NoiseAmount", noiseAmount);
Graphics.Blit (src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
shader
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 15/Fog With Noise" {
Properties {
_MainTex ("Base (RGB)", 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 Texture", 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;
float2 uv : TEXCOORD0;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : 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
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;
}
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
index = 3 - index;
#endif
o.interpolatedRay = _FrustumCornersRay[index];
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
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 {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}