目录
各种向量
常用向量:(全要记)
nDir:法线方向,点乘操作时简称n;
IDir:光照方向,点乘操作时简称I;
vDir:观察方向,点乘操作时简称V;
rDir:光反射方向,点乘操作时简称r;
hDir:半角方向(Halfway),IDir和vDir的中间角反向,点乘操作时简称h;
所在空间:(暂时只记WS,其余看热闹)
OS:ObjectSpace 物体空间,本地空间;
WS:WorldSpace世界空间;
VS:ViewSpace观察空间
CS:HomogenousClipSpace齐次裁剪空间;
TS:TangentSpace切线空间;
TXS:TextureSpace纹理空间;
漫反射
满反射-Diffuse
因其向四面八方均匀散射,所以反射亮度和观察者看的方向无关;
实现方式:Lambert(n dot I),显然vDir不参与计算;
镜面反射-Specular:
因其反射具有明显方向性,所以观察者的视角决定了反射光线的有无,明暗;
实现方式:
Phone(r dot v),即光反射方向和视角方向越重合,反射越强;
Bline-Phone(n dot h),即法线方向和半角方向越重合,反射越强;
用连连看实现Phone
思路:先获取正儿八经的光照方向,然后在求出光的反射方向,在与摄像机的观察反向做点积(计算机图形学常用来进行方向性判断,如两矢量点积大于0,则它们的方向朝向相近;如果小于0,则方向相反。)
一步一步实现
在shaderforge中拿到的光方向一般是光的反方向,所以要乘以-1得到真正的光方向,再利用Reflect反射节点和模型表面的法线方向来求得光的发射方向,然后再与摄像机的观察方向来做点积,然后用Max节点来截取掉小于零的部分,然后用Power节点来控制高光的范围大小(Power是啥?一般叫高光次幂; PS里正片叠底就是Power的效果)
但其实前面的大部分可以用光反射方向节点连接得到(上面下面的连接结果得到的效果是等价的)
用连连看实现BlinnPhone
用法线方向和半角方向做点积
正常视角看Phone和BlinnPhone高光区别不大,但是逆光看的话Phone更好看,blinnPhone的存在只是为了节省一点点性能(图形学刚开始都是能省则省)
用代码写一个OldShool
首先声明两个公开的美术可调的参数
Properties {
_MainCol ("颜色",color) = (1.0,1.0,1.0,1.0)
_SpecularPow ("高光次幂",range(1,90)) = 30
}
然后一些形式段,形式段之后就是 VertexInput前的参数声明要和Prooerties段定义的参数一一对应;选取参数类型
// uniform 共享于vert,frag
// attibute 仅用于vert
// varying 用于vert,frag
// VertexInput前的参数声明:要和Prooerties段定义的参数一一对应;适当选取参数类型
uniform float3 _MainCol; // RGB够了 float3
uniform float _SpecularPow;//标量 float
定义输入结构
// 输入结构
struct VertexInput {
float4 vertex : POSITION; //顶点信息 Get
float3 normal : NORMAL; //法线信息 Get
};
定义输出结构 TEXCOORD0 1 2 3这样的可以理解为学号卡,可以跳号但是不能重复
struct VertexOutput {
float4 posCS : SV_POSITION; //裁剪空间(暂时理解为屏幕空间)顶点位置
float4 posWS : TEXCOORD0; //世界空间顶点位置
float3 nDirWS : TEXCOORD1; //世界空间法线方向
};
输入结构 >>> 顶点shader阶段 >>> 输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); //变换顶点位置 OS > CS
o.posWS = mul(unity_ObjectToWorld, v.vertex); //变换顶点位置 OS > WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); //变换法线方向 OS > WS
return o;
}
输出结构 >>> 像素
// 输出结构 >>> 像素
float4 frag(VertexOutput i) : COLOR {
// 准备向量
float3 nDir = i.nDirWS;
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
float3 hDir = normalize(vDir+lDir);
// 准备点积结果
float nDotI = dot(nDir,lDir); //点乘法线方向和光方向
float nDoth = dot(nDir,hDir); //点乘法线方向和半角方向
// 光照模型
float lambert = max(0.0,nDotI); //对点积结果进行截断处理
float blinnPhone = pow(max(0.0,nDoth),_SpecularPow);
float3 finalRGB = _MainCol*lambert + blinnPhone;
// 返回结果
return float4(finalRGB,1.0);
}
用连连看实现lambert+BlinnPhone
很简单直接把lambert和BlinnPhone连出来然后相加就可以
复习一下lambert:法线方向和光方向进行点积,然后使用Remap将点击结果从-1到1映射到0到1,halflambert就是乘0.5再加上0.5,但是我们这里只用Lambert就好
复习一下BlinnPhone:就是法线方向和半角方向进行点积,再使用max节点取相对于0的更大值,再用Power和Slider来控制一个高光点的效果
unity