Matcat(Material Capture)和CubeMap都常用来模拟环境(注意是环境,不是环境光)的镜面反射,通常情况下还会加一个菲涅尔效果,以模拟金属和非金属不同质感。
Matcat原理算法:
- 将nDir从切线空间转到观察空间;
- 取RG通道Remap到(0~1),作为UV对Matcap图采样;
- 叠加菲涅尔效果,以模拟金属和非金属不同质感;
简单说就是用View空间法线朝向,直接映射到模型表面的算法。
代码实现过程
原图:
5.png
- 1.第一步采样Matcap
Shader "Unlit/Matcap01"
{
Properties
{
_MainTex ("_NormalMap", 2D) = "white" {}
_Matcap("Matcap", 2D) = "gray" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv0 : TEXCOORD0; // uv信息
float3 normal : NORMAL; // 法线信息
float4 tangent : TANGENT; // 切线信息
};
struct v2f
{
float4 pos : SV_POSITION; // 屏幕顶点位置
float2 uv0 : TEXCOORD0; // uv信息
float4 posWS : TEXCOORD1; // 世界顶点位置
float3 nDirWS : TEXCOORD2; // 世界法线方向
float3 tDirWS : TEXCOORD3; // 世界切线方向
float3 bDirWS : TEXCOORD4; // 世界副切线方向
};
sampler2D _NormalMap;
uniform sampler2D _Matcap;
v2f vert (appdata v)
{
v2f o = (v2f)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos(v.vertex);
o.uv0 = v.uv0; // 传递uv信息
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 法线方向 OS>WS
o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); // 切线方向 OS>WS
o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w); // 根据nDir tDir求bDir
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 准备向量
float3 nDirTS = UnpackNormal(tex2D(_NormalMap, i.uv0)).rgb;//通过法线给更多的细节
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS);
float3 nDirWS = normalize(mul(nDirTS, TBN)); // 计算nDirVS 计算Fresnel
float3 nDirVS = mul(UNITY_MATRIX_V, nDirWS); // 拿到观察空间的法线,用来计算MatcapUV
// 准备中间变量
float2 matcapUV = nDirVS.rg * 0.5 + 0.5;
// 光照模型
float3 matcap = tex2D(_Matcap, matcapUV);
// 返回值
return float4(matcap, 1);
}
ENDCG
}
}
}
得到一个看起来比较带金属感的效果
效果图:
6.png
-
2.第一步采样环境
用视空间下用法线直接采样一个多色渐变的环境
结果:
7.png -
3.做一些细节处理,最后效果:
8.png
Shader "Unlit/Matcap01"
{
Properties
{
_MainTex ("_MainTex", 2D) = "white" {}
_NormalMap("_NormalMap", 2D) = "white" {}
_Matcap("Matcap", 2D) = "gray" {}
_Matcap_add("Matcap Add", 2D) = "gray" {}
_RampTex("Ramp Tex",2D) = "white"{}
_FresnelPow("菲涅尔次幂", Range(0, 10)) = 1
_MatcapIntensity("Matcap Intensity",Float) = 1.0
_MatcapIntensity_add("Matcap Intensity Add",Float) = 1.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv0 : TEXCOORD0; // uv信息
float3 normal : NORMAL; // 法线信息
float4 tangent : TANGENT; // 切线信息
};
struct v2f
{
float4 pos : SV_POSITION; // 屏幕顶点位置
float2 uv : TEXCOORD5; // uv信息
float2 uv0 : TEXCOORD0; // uv信息
float4 posWS : TEXCOORD1; // 世界顶点位置
float3 nDirWS : TEXCOORD2; // 世界法线方向
float3 tDirWS : TEXCOORD3; // 世界切线方向
float3 bDirWS : TEXCOORD4; // 世界副切线方向
};
sampler2D _NormalMap;
uniform sampler2D _Matcap;
uniform sampler2D _Matcap_add;
sampler2D _RampTex;
sampler2D _MainTex;
float4 _MainTex_ST;
uniform float _FresnelPow;
float _MatcapIntensity;
float _MatcapIntensity_add;
v2f vert (appdata v)
{
v2f o = (v2f)0; // 新建一个输出结构
o.pos = UnityObjectToClipPos(v.vertex);
o.uv0 = v.uv0;
o.uv = TRANSFORM_TEX(v.uv0, _MainTex);// 传递uv信息
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 法线方向 OS>WS
o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); // 切线方向 OS>WS
o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w); // 根据nDir tDir求bDir
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 准备向量
float3 nDirTS = UnpackNormal(tex2D(_NormalMap, i.uv0)).rgb;//通过法线给更多的细节
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS);
float3 nDirWS = normalize(mul(nDirTS, TBN)); // 用来计算nDirVS 计算Fresnel
float3 nDirVS = mul(UNITY_MATRIX_V, nDirWS); // 拿到观察空间的法线,用来计算MatcapUV
// 准备中间变量
half4 diffuse_color = tex2D(_MainTex, i.uv);
//Mapcap1
float2 matcapUV = nDirVS.rg * 0.5 + 0.5;
//Ramp
half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
half NdotV = saturate(dot(i.nDirWS, view_dir));
half fresnel = 1.0 - NdotV;
half2 uv_ramp = half2(fresnel, 0.5);
half4 ramp_color = tex2D(_RampTex, uv_ramp);
// 光照模型
float3 matcap = tex2D(_Matcap, matcapUV) * _MatcapIntensity;
float3 matcap_add = tex2D(_Matcap_add, matcapUV) * _MatcapIntensity_add;
// 返回值
return float4(diffuse_color*matcap* ramp_color + matcap_add, 1);
}
ENDCG
}
}
}
CubeMap原理算法:
将视角方向和法线向量点积,然后再进行发射,根据这个反射点对Cubemap采样。
网友评论