庄懂的TA笔记(九)<菲涅尔 + MatCap + CubeMap>
课程内容:
1、菲涅尔
0、效果展示、公式:
菲涅尔现象:
Fresnel(菲涅尔) 用到的两个向量 NdirWS(世界空间 法线向量) VdirWS(世界空间 视角向量)
注意:VdirWS: 的默认为视角的反方向。如需取视角正方向,需 1 - ldotv。兰伯特点积视方向-1.
菲涅尔: 算法公式 :
1-(N dot V)= Fresnel
Fresnel = Pow(1-ndotv , powVal);
ldotv : 从眼睛发出的Lambert,中间亮,边缘暗 。
1- ldotv : 黑白相反,中间暗,边缘亮 。
powVal : 控制反射 高光次幂。
菲涅尔:反射中间弱,外部反射强。(以圆为例)
金属 与 菲涅尔 相反 :反射中间强,外部反射弱。(以圆为例)
1、Fresnel在SF中的使用:
2、Fresnel在SL中的使用:
Fresnel = 1- dot (ndir , vdir )
注意:vidr = vdir.xyz - posWS .xyz
菲涅尔 SL 写法:
Shader "Unlit/Sc09_SL_Fresnel01"
{
Properties
{
_FresnelInt("菲涅尔强度",Range(0.1,5))=1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
UNITY_INSTANCING_BUFFER_START( Props )
// UNITY_DEFINE_INSTANCED_PROP( float4, _Color)
UNITY_INSTANCING_BUFFER_END( Props )
uniform float _FresnelInt;
//输入结构
struct VertexInput
{
float4 vertex : POSITION;
float4 normal : NORMAL;
};
//顶点输出结构
struct VertexOutput
{
float4 posCS : SV_POSITION;
float3 posWS : TEXCOORD0;
float3 nDirWS : TEXCOORD1;
};
//输出结构>>>顶点shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
o.posCS = UnityObjectToClipPos(v.vertex);
o.posWS = mul(unity_ObjectToWorld,v.vertex);
o.nDirWS = UnityObjectToWorldNormal(v.normal);
return o;
}
//色彩输出结构
float4 frag(VertexOutput i) : COLOR
{
//准备向量
float3 ndir = i.nDirWS;
float3 vdir = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
float3 ldir = _WorldSpaceLightPos0.xyz;
//点积结果
float ndotl = dot(ndir,ldir);
float ndotv = 1-dot(ndir,vdir); //菲涅尔 1 - n dot v
//光照模型公式
float Lambert = max(0,ndotl *0.5+0.5);
float3 fresnel = pow(ndotv,_FresnelInt);
//返回结果
//return float4(Lambert,Lambert,Lambert,1.0f);//输出最终颜色
return float4 (fresnel,1.0f);
}
ENDCG
}
}
}
2、MatCap
0、效果展示:
PS:1、无视BRDF(双向反射分布函数) ,直接 取 参考 原型的图的 XY的-1到1 的 光照信息。
用View空间法线朝向,直接映射模型表面的流氓算法。
2、常用模拟环境反射。
PS:知识点:
1、未解码下 法线 的 状态: == Tangent 切线空间法线 的状态。>> nDirTS
2、 世界空间下法线 的 状态 : >> nDirWS
3、 本地空间下法线 的 状态: >> nDirTS or >> nDirLS
4、视 空间下 法线 的状态: >> nDirVS
Remap(0 -1)后,可看到,熟悉的,常用的那种法线色彩效果。
PS弹幕知识:顶点变化过程:模型空间=》世界空间=》观察空间=》裁剪空间=》屏幕空间
公式:
1、nDirWS 变换到(transforem) = nDirVS(观察空间法线Vive space)
2、取 nDirVS(观察空间法线) 的R,G通道,Remap到(0-1),作为uv对MatCap图采样。
3、叠加菲涅尔,(通过滑条)以模拟金属和非金属不同质感。
1、MatCap在SF中的使用:
MatCap图的状态如此,仅仅是一个预览。 关键词搜索:MatCap texture
2、MatCap在SL中的使用:
重点:
nDirVS = mul(UNITY_MATRIX_V,float4(nDirWS,0.0)); (矩阵:从WS世界空间,转到VS视空间)
matCapUV = nDirVS.rg * 0.5 +0.5;
(这里的 *0.5 +0.5 属于 remap 从-1 到 1 ,截断为 0 到 1)。
单纯的MatCap代码段示例:
Shader "Unlit/Sc09_MatCap02"
{
Properties
{
_MatCapMap("MacCapMap",2D) = "gray"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
UNITY_INSTANCING_BUFFER_START( Props )
// UNITY_DEFINE_INSTANCED_PROP( float4, _Color)
UNITY_INSTANCING_BUFFER_END( Props )
uniform sampler2D _MatCapMap;
//输入结构
struct VertexInput
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float2 uv0 : TEXCOORD0;
};
//顶点输出结构
struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv :TEXCOORD0;
float3 nDirWS : TEXCOORD1;
};
//输出结构>>>顶点shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.nDirWS = UnityObjectToWorldNormal(v.normal);
o.uv = v.uv0;
return o;
}
//色彩输出结构
float4 frag(VertexOutput i) : COLOR
{
//准备向量
float3 ndirWS = i.nDirWS;
float3 ldir = _WorldSpaceLightPos0.xyz;
//声明 得到视空间法线
float3 ndirVS = mul(UNITY_MATRIX_V,ndirWS);
//点积结果 / 中间变量
//声明 matCap的UV ,并 赋值截断.
float2 matCapUV = ndirVS.rg * 0.5 + 0.5;
float ndotl = dot(ndirWS,ldir);
//光照模型公式
float Lambert = max(0,ndotl *0.5+0.5);
//最终光照模型 图片类型 输出
float3 matCap = tex2D(_MatCapMap,matCapUV);
float3 finalRGB = Lambert * matCap;
//返回结果
return float4(finalRGB,1.0f);//输出最终颜色
}
ENDCG
}
}
}
复合的MatCap代码段示例:MatCap + NormalMap
Shader "Unlit/Sc09_MatCap01"
{
Properties
{
_MainCol("MainCol",Color)=(1,1,1,1)
_NormalMap("NormalMap",2D)="bump"{}
_MatCap("MatCapMap",2D)="gray"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
UNITY_INSTANCING_BUFFER_START( Props )
// UNITY_DEFINE_INSTANCED_PROP( float4, _Color)
UNITY_INSTANCING_BUFFER_END( Props )
uniform float3 _MainCol;
uniform sampler2D _NormalMap;
uniform sampler2D _MatCap;
//输入结构
struct VertexInput
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float2 uv0 : TEXCOORD0;
float4 tangent : TANGENT;
};
//顶点输出结构
struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 posWS : TEXCOORD1;
float3 nDirWS : TEXCOORD2;
float3 tDirWS : TEXCOORD3;
float3 bDirWS : TEXCOORD4;
LIGHTING_COORDS(5,6)
};
//输出结构>>>顶点shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
o.uv = v.uv0;
o.pos = UnityObjectToClipPos(v.vertex);
o.posWS = mul(unity_ObjectToWorld,v.vertex);
o.nDirWS = UnityObjectToWorldNormal(v.normal);
o.tDirWS = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz);
o.bDirWS = normalize(cross(o.nDirWS,o.tDirWS) * v.tangent.w);
TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
}
//色彩输出结构
float4 frag(VertexOutput i) : COLOR
{
//准备向量
float3 var_NormalMap = UnpackNormal(tex2D(_NormalMap,i.uv)).rgb; //法线解码 (法线,uv),取rgb值
float3x3 TBN = float3x3(i.tDirWS,i.bDirWS,i.nDirWS); //矩阵
float3 ndirWS = normalize(mul(var_NormalMap,TBN)); //取 世界空间下法线
float3 ndirVS = mul(UNITY_MATRIX_V,ndirWS); //取 视 空间下法线
float3 ldir = _WorldSpaceLightPos0.xyz; //取 世界空间下灯光位置
float2 MatCapUV = ndirVS.rg * 0.5 + 0.5 ; //取 到 视 空间下法线rg通道,对应xy轴 >> 赋值截断.
float4 var_MatCap = tex2D(_MatCap,MatCapUV); //声明 赋值,并取到类型为贴图的 matCap 图的变量名。
//点积结果
float ndotl = dot(ndirWS,ldir);
//光照模型公式
float Lambert = max(0,ndotl *0.5+0.5);
float attenuation = LIGHT_ATTENUATION(i);
float3 finalRGB = _MainCol * Lambert * var_MatCap * attenuation ;//混入 MatCap图(前提是模型分了xy的UV才能显示)。
//返回结果
return float4(finalRGB,1.0f);//输出最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
3、CubeMap
0、效果展示 / 公式:
注意: 注意区分,理解,视方向的反方向 和 反射方向 的 不同。
先拿到 观察方向 的反方向 (真正的,从眼睛射出 的 光线),因为,shader中,观察方向vDir的默认方向,是从物体射到 观察(眼睛)方向的。
在用vDir观察方向,打到 对象 上,在从对象 反弹,反射出去。vrDir
顺序为:视方向,射到 对象,对象在反射到 CubeMap环境球 。
所以,需要取到,观察方向的反方向。
1、CubeMap在SF中的使用:
环境球贴图的设置:
因为HDRI高动态范围的环境球,在手机端,和多设备中,不支持
所以需要讲高动态范围的环境球贴图,转为 LDRI 低动态范围 环境贴图。
尽管转为LDRI但是还是保留了高动态范围的效果。(原理需要继续深入暂时不讲)
先将贴图 在PS 中,转为 8位 通道:
Mapping : >> Latitude - Longitude Layout (Cylindrical)==经纬度展开
Mipmap(生成8张低级别递减清晰度图LOD)
Convolution Type :>>Specular (Glossy Refiection) (变为金属平滑,否则会有格子像素)
Fixup Edge Seams (修正接缝)
2、CubeMap在SL中的使用:
理解公式:
vrDir =(1-vDirWS)* nDirWS. >翻译> 视反射 = (1 - 视角反方向) * 世界空间法线向量.
重要 代码段 实际写法:
vrDir = reflect(-vDirWS , nDirWS);
floa3 cubemap = texCUBElod (_Cubemap,float4(vrDirWS , _CubemapMip ));
1、示例代码段:CubeMap + NormalMap + fresnel
...
2、示例代码拗断: CubeMap
Shader "Unlit/Sc09_SL_CubeMap01"
{
Properties
{
_CubeMap("CubeMap",Cube)="Skybox"{}
_CubeMapMip("CubeMap",Range(0,7))=0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
UNITY_INSTANCING_BUFFER_START( Props )
// UNITY_DEFINE_INSTANCED_PROP( float4, _Color)
UNITY_INSTANCING_BUFFER_END( Props )
uniform samplerCUBE _CubeMap;
uniform float _CubeMapMip;
//输入结构
struct VertexInput
{
float4 vertex : POSITION;
float4 normal : NORMAL;
float2 uv0 : TEXCOORD0;
};
//顶点输出结构
struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 posWS : TEXCOORD1;
float3 nDirWS : TEXCOORD2;
};
//输出结构>>>顶点shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
o.uv = v.uv0;
o.pos = UnityObjectToClipPos(v.vertex);
o.posWS = mul(unity_ObjectToWorld,v.vertex);
o.nDirWS = UnityObjectToWorldNormal(v.normal);
return o;
}
//色彩输出结构
float4 frag(VertexOutput i) : COLOR
{
//准备向量
float3 ldir = _WorldSpaceLightPos0.xyz;
float3 ndirWS = i.nDirWS;
float3 vDirWS = normalize(_WorldSpaceCameraPos.xyz - i.posWS);
float3 vrDirWS = reflect(-vDirWS,ndirWS); //重点:采样CubeMap
//点积结果
float ndotl = dot(ndirWS,ldir);
//光照模型公式
float Lambert = max(0,ndotl *0.5+0.5);
//加载lod Cube类型的图像 并 通过mip控制参数,并取 rgb 变量数值
float3 var_CubeMap = texCUBElod(_CubeMap,float4(vrDirWS,_CubeMapMip)).rgb;
float3 finalRGB = Lambert * var_CubeMap;
//返回结果
return float4(finalRGB,1.0f);//输出最终颜色
}
ENDCG
}
}
}