一、噪声纹理
听名字挺蒙的,其实它的作用就是展现出一个事物“混沌”或“杂乱无章”的效果,就像雾气和雪地,它们绝对不会是均匀单一的,不然给人的感觉就像是屏幕变灰了,以及往地上涂了一层白色油漆一样,很笨
噪声纹理本质上是一种“固定”的随机,本章第二节使用的噪声纹理如左下:
何为“固定”的随机呢?在 C++ 中我们会使用 Random() 函数来解决一类问题,图形领域也一样,只不过有时只使用简单的随机生成器反而会带来更不自然的效果,右上图就是一张完全随机的噪声纹理,可以看出来它有点太过于杂乱无章了,而生活中的木头纹路、天空中的云彩、山脉地形等等,它们都是趋于分形(fractal)的,即在体现“随机”的基础上却又包含了不同程度的细节以及规律
因此,后面的学者们根据效率、用途、自然程度(即效果好坏)等方面的衡量,提出了许多希望用程序模拟自然噪声的方法,根据不同的算法,可以生成一些看上去“规律”的随机,例如:Perlin 噪声被大量用于云朵、火焰和地形等自然环境的模拟;Simplex 噪声在其基础上进行了改进,提到了效率和效果;而 Worley 噪声被提出用于模拟一些多孔结构,例如纸张、木纹等
其所谓“固定”的随机就是按照特定算法生成的随机序列或噪声纹理中特定的一种,也因此,噪声纹理也被认为是一种程序纹理
二、消融效果
过程中使用了 Smoothstep:
- smoothstep 平滑插值公式,计算方法为 ,其中
其它没有什么难点,shader 中有注释
关于 ShadowCast 的部分可以参考 UnityShader17:光照属性与阴影
Shader "Jaihk662/Burn"
{
Properties
{
_BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0 //物体消融程度(0:正常;1:完全消融)
_LineWidth ("Burn Line Width", Range(0.0, 0.2)) = 0.1 //模拟烧焦效果时边缘的线宽
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BurnMap ("Burn Map", 2D) = "white" {} //消融噪声纹理
_BurnFirstColor("Burn First Color", Color) = (1, 0, 0, 1) //消融边界颜色
_BurnSecondColor("Burn Second Color", Color) = (1, 0, 0, 1)
}
SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}
LOD 200
PASS
{
Tags { "LightMode" = "ForwardBase" } //开启前向渲染,设置 multi_compile_fwdbase 编译指令,可以得到正确的平行光光照及其它光源顶点光照
Cull Off //发生消融效果时可能会看到模拟内部构造,因此不要开启面剔除
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed _BurnAmount;
fixed _LineWidth;
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _BurnFirstColor;
fixed4 _BurnSecondColor;
sampler2D _BurnMap;
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
struct _2vert
{
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 tangent: TANGENT;
float2 texcoord: TEXCOORD0;
};
struct vert2frag
{
float4 pos: SV_POSITION;
float2 uvMainTex: TEXCOORD0;
float2 uvBumpMap: TEXCOORD1;
float2 uvBurnMap: TEXCOORD2;
float3 lightDir: TEXCOORD3;
float3 worldPos: TEXCOORD4;
SHADOW_COORDS(5)
};
vert2frag vert(_2vert v)
{
vert2frag 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; //拿到切线空间变换矩阵rotation
o.lightDir = normalize(mul(rotation, ObjSpaceLightDir(v.vertex)).xyz);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o); //为了得到阴影信息,需要计算世界空间下的顶点位置和阴影纹理采样坐标 _ShadowCoord
return o;
}
fixed4 frag(vert2frag i): SV_Target
{
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount); //消融效果的关键①:采样结果小于消融阈值_BurnAmount时,剔除该片段
//单纯计算漫反射光照
fixed3 normal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(normal, i.lightDir));
//消融效果的关键②:使用smoothstep差值拿到消融混合系数t,当t为1表示在消融→完全消融的边界处,当t为0表示在正常物体颜色→开始消融的边界处
fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
//拿到当前烧焦颜色,使用次方计算是为了使得更接近烧焦的颜色
fixed3 burnColor = pow(lerp(_BurnFirstColor, _BurnSecondColor, t), 5);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 finalColor = lerp(diffuse * atten, burnColor, t * step(0.0001, _BurnAmount)); //step(a, x):如果 x ≥ a 返回1,否则返回0
//step(0.0001, _BurnAmount)是为了保证如果_BurnAmount为0时,永远显示物体颜色
return fixed4(finalColor, 1);
}
ENDCG
}
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 vert2frag
{
V2F_SHADOW_CASTER;
float2 uvBurnMap: TEXCOORD1;
};
vert2frag vert(appdata_base v)
{
vert2frag o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
return o;
}
fixed4 frag(vert2frag i): SV_Target
{
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}
三、水波效果
也可以在这一章着色器上稍做修改,利用噪声纹理生成其对应的法线纹理,实现一个简单的水面效果
过程中用到了菲涅尔反射:
Shader "Jaihk662/Water"
{
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
{
Tags { "Queue" = "Transparent" "RenderType" = "Opaque" }
GrabPass { "_RefractionTex" }
LOD 200
PASS
{
Tags { "LightMode" = "ForwardBase" } //开启前向渲染,设置 multi_compile_fwdbase 编译指令,可以得到正确的平行光光照及其它光源顶点光照
Cull Off //发生消融效果时可能会看到模拟内部构造,因此不要开启面剔除
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
sampler2D _WaveMap;
samplerCUBE _Cubemap;
fixed _WaveXSpeed;
fixed _WaveYSpeed;
float _Distortion;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
float4 _WaveMap_ST;
float4 _MainTex_ST;
struct _2vert
{
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 tangent: TANGENT;
float2 texcoord: TEXCOORD0;
};
struct vert2frag
{
float4 pos: SV_POSITION;
float4 scrPos: TEXCOORD0;
float4 uv: TEXCOORD1;
float4 TtoW1: TEXCOORD2;
float4 TtoW2: TEXCOORD3;
float4 TtoW3: TEXCOORD4;
};
vert2frag vert(_2vert v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos); //获得屏幕图像的采样坐标,考虑过平台差异,输入裁剪空间坐标,输出齐次坐标系下的屏幕坐标值(就是屏幕坐标乘上w,只计算xy,zw不变)
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _WaveMap);
float3 wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 wNormal = UnityObjectToWorldNormal(v.normal);
float3 wTangent = UnityObjectToWorldDir(v.tangent);
float3 wBinormal = cross(wNormal, wTangent) * v.tangent.w;
o.TtoW1 = float4(wTangent.x, wBinormal.x, wNormal.x, wPos.x);
o.TtoW2 = float4(wTangent.y, wBinormal.y, wNormal.y, wPos.y);
o.TtoW3 = float4(wTangent.z, wBinormal.z, wNormal.z, wPos.z);
return o;
}
fixed4 frag(vert2frag i): SV_Target
{
float3 worldPos = float3(i.TtoW1.w, i.TtoW2.w, i.TtoW3.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 normal = normalize(bump1 + bump2); //得到法向量均值
//计算折射,考虑玻璃材质
float2 offest = normal.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = i.scrPos.xy + offest;
fixed3 refractCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb; //根据偏移后的坐标进行采样,得到折射颜色
//正常计算反射
normal = normalize(half3(dot(i.TtoW1.xyz, normal), dot(i.TtoW2.xyz, normal), dot(i.TtoW3.xyz, normal)));
fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
fixed3 reflectionDir = reflect(-viewDir, normal);
fixed3 reflectionCol = texCUBE(_Cubemap, reflectionDir).rgb * texColor.rgb * _Color.rgb;
//菲涅尔系数
fixed fresnel = pow(1 - saturate(dot(viewDir, normal)), 4);
fixed3 finalColor = reflectionCol * fresnel + refractCol * (1 - fresnel);
return fixed4(finalColor, 1);
}
ENDCG
}
}
FallBack Off
}
参考文章:
- https://blog.csdn.net/candycat1992/article/details/50346469
- 《UnityShader入门精要》