前言💬
在计算机图形学中,光照模型是非常重要的一部分。
为了模拟现实中的光照效果,我们需要理解一些基本的光学原理。
本文将介绍反射向量的概念以及如何在Unity引擎中使用Cg reflect函数实现反射效果
一、什么是反射向量
在图形学中,反射向量是一个描述光线如何反射的重要概念。当光线照射到一个表面上,它会被反射到不同的方向。反射向量的计算是基于光线的入射角和法线向量的。通过计算反射向量,我们可以模拟出光线在表面上的反射效果,从而制作出逼真的光照效果。
二、Cg reflect函数
Cg中的reflect函数可以帮助我们快速计算反射向量
在Unity中改函数被定义在
函数原型如下:
float3 reflect(float3 incidentVector, float3 normalVector);
其中,incidentVector 是入射光线的方向,normalVector 是表面的法线向量。
函数返回一个反射光线的方向向量。
原理
CG当中reflect函数的实现
// i为入射光线,n为法线
float3 reflect( float3 i, float3 n )
{
return i - 2.0 \* n \* dot(n,i);
}
推导过程
求反射向量OB
$$ \vec{OB}=\vec{AB}-\vec{AO} $$
$$ \vec{OB}=2\vec{AP}-\vec{AO}=2(\vec{AO}+\vec{OP})-\vec{AO}=\vec{AO}+2\vec{OP} $$
所以此时重点在于求向量OP
那么如何求向量OP
我们据下图举例模型进行推导
根据点积的性质可知:
$$
\vec{OA}\cdot\cosθ=\vec{OA^{’}}
$$
$$
\vec{OA}\cdot\vec{n}=\vec{OA}\cdot\vec{n}\cdot\cosθ
$$
所以就有
$$
\vec{OA^{’}}=\vec{OA}\cdot\cosθ\cdot\vec{n}
$$
$$ \vec{OA^{’}}=\vec{OA}\cdot\frac{\vec{OA}\cdot\vec{n}}{\vec{OA}*\vec{n}}\cdot\vec{n} $$
$$ 设\vec{n}=1,就有\vec{OA^{’}}=(\vec{OA}\cdot\vec{n})\cdot\vec{n} $$
所以根据此推导的原理可知
$$ \vec{OP}=-(\vec{AO}\cdot\vec{n})\cdot\vec{n} $$
即:
$$
\vec{OB}=\vec{AO}-2\vec{n}\cdot(\vec{AO}\cdot\vec{n})
$$
所以Cg当中的reflect函数中的实现
i - 2.0 * n * dot(n,i)
就是这样推导来的
更详细的推导过程可以看下图
三、实现Specular高光
首先我们需要计算出反射光的向量,利用reflect函数
float3 N=normalize(mul(unity_WorldToObject,v.normal));
float3 L=normalize(_WorldSpaceLightPos0);
// L也可以替换为WorldSpaceLightDir(v.vertex)
float3 R= reflect(-L,N);//由于L是朝向光源方向的向量取反-L就示入射光的向量了
计算出反射光向量后,用反射光向量和朝向摄像机视角向量进行点积,并且将值映射在0~1之间
float3 V= normalize(WorldSpaceViewDir(v.vertex));
float specularL= saturate(dot(V,R));
为了实现指数级的衰减效果,可以使用pow函数,根据_Shininess调整衰减效果,_Shininess可以在Properties中定义暴露的参数
specularL=pow(specularL,_Shininess);
最后将灯光颜色乘上Specular的亮度,附加到color上,即可
o.color+=_LightColor0*specularL;
四、完整代码
Shader "ShaderLearning/Diffuse/SpecularDiffuse"
{
Properties
{
\_Shininess("Shininess",Range(1,64))=1
}
SubShader
{
Tags { "LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct v2f
{
float4 vertex: POSITION;
fixed4 color: COLOR;
};
float \_Shininess;
v2f vert (appdata\_full v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
float3 N=normalize(mul(unity\_WorldToObject,v.normal));
float3 L=normalize(\_WorldSpaceLightPos0);
float dotL=saturate(dot(N,L));
o.color=\_LightColor0\*dotL;
// L也可以替换为WorldSpaceLightDir(v.vertex)
float3 R= reflect(-L,N);
float3 V= normalize(WorldSpaceViewDir(v.vertex));
float specularL= saturate(dot(V,R));
specularL=pow(specularL,\_Shininess);
o.color+=\_LightColor0\*specularL;
return o;
}
fixed4 frag (v2f i) : COLOR
{
return i.color+UNITY\_LIGHTMODEL\_AMBIENT;
}
ENDCG
}
}
}
效果如下图所示
实现了一个简单的Specular的效果
要注意的是,在使用reflect的函数的要注意,入射光和法线要在同一个坐标系空间


...