光照模型是计算机图形学中最基础的知识,是用数学方法和物理方法来描述现实生活中的光照。
现实环境中真实的光照可以抽象为三种独立的光照效果叠加:
1.环境光(Ambient Lighting)
2.漫反射(Diffuse Lighting)
3.镜面光(Specular Lighting)
环境光:在现实生活中太阳光经过无数物体的反射能够充斥到任何一个它能进入的角落。我们把这种经过无数次发射而弥漫开来的光称为环境光。环境光只有一个亮度值,并且不会衰减。
漫反射:漫反射,是投射在粗糙表面上的光向各个
方向反射的现象。当一束平行的
入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然
互相平行,由于各点的法线方向不一致,造成
反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“
漫射”。这种反射的光称为漫射光。很多物体,如植物、墙壁、衣服等,其表面粗看起来似乎是平滑,但用放大镜仔细观察,就会看到其表面是凹凸不平的,所以本来是平行的太阳光被这些表面反射后,弥漫地射向不同方向。(百度百科)。自然界中绝大部分物体都为理想的漫反射体,例如无光泽表面的物体。
镜面光:顾名思义,镜面会将光线以入射光相同角度的反方向反射出去。
环境反射光 Ambient
环境反射光是由环境光在临近物体上经过多次反射所产生的,没有位置和方向,在仅有环境光的情况上,物体的明暗亮度是一样的。
环境反射光亮度公式:
Ia为物体的环境光反射亮度,
Ipa为环境光亮度
Ka为物体表面的环境光反射系数
漫反射 Diffuse
Lambert光照模型
兰伯特余弦定理(Lambert’s cosine law)是模拟漫反射最贱的光照模型,模型表面的光亮度直接取决于光线向量(light vector)和表面发现两个向量夹角的余弦值。
兰伯特余弦定理给出的函数定义为:
f(θ) = max(cosθ,0) = max(L•n,0)
其中,L和n是单位向量。下图是f(θ)的曲线图。我们可以看到,随着θ的变化,强度在0.0到1.0(即,0%到100%)之间变化。
hlsl:
#include "HrLightingCommon.fx"
cbuffer cbPerFrame
{
float4 ambientLightColor;
DirectionLight directLight;
};
cbuffer cbPerObject
{
float4x4 world_matrix;
float4x4 world_view_proj_matrix;
float4x4 inverse_transpose_world_matrix;
Material mat;
};
//纹理
Texture2D g_tex;
//设置过滤
SamplerState samTex
{
Filter = MIN_MAG_MIP_LINEAR;
};
struct VertexIn
{
float3 vertexPosition : POSITION;
float3 vertexNormal : NORMAL;
float2 textureCoord : TEXCOORD;
};
struct VertexOut
{
float4 posWorldViewProj : SV_POSITION;
float4 pixelColor : COLOR;
};
VertexOut VS_Main(VertexIn vIn)
{
VertexOut vOut;
//法线变换
float3 normalDirection = normalize(mul(float4(vIn.vertexNormal, 0.0f), inverse_transpose_world_matrix).xyz);
float3 reflectLightDir = normalize(directLight.light_direction[0]);
float fDiffuseFactor = dot(reflectLightDir, normalDirection);
float4 fAmbient = mat.ambient_material_color * ambientLightColor;
float4 fDiffuse = fDiffuseFactor * mat.diffuse_material_color * directLight.diffuse_light_color[0];
vOut.posWorldViewProj = mul(float4(vIn.vertexPosition, 1.f), world_view_proj_matrix);
fDiffuse.w = 1;
vOut.pixelColor = fDiffuse + fAmbient;
return vOut;
}
float4 PS_Main(VertexOut pixIn) : SV_TARGET
{
return pixIn.pixelColor;
}
镜面反射 Specular
Phone光照模型 Lambert光照模型并没有考虑到表面光泽的镜面反射效果。
百度百科:Phong光照模型是真实图形学中提出的第一个有影响的光照明模型,该模型只考虑物体对直接光照的反射作用,认为环境光是常量,没有考虑物体之间相互的
反射光,物体间的反射光只用环境光表示。Phong光照模型属于简单光照模型。
对于理想的镜面反射,入射角等于反射角。非理想的镜面反射表面实际上是有许多朝向不同的微笑平面组成,其镜面反射光分布于表面镜面反射方向的周围,常采用余弦函数的幂来模拟一般光滑表面的反射光的空间分布。所以,对于镜面反射的函数表达为:
Ips为入射光的光亮度。
θ为镜面反射方向和视线方向的夹角,介于0到90之间
n为镜面反射光的汇聚指数(光滑度)
Ks为镜面反射系数
对于镜面高光指数
较光滑的物体表面比如金属,玻璃,光强的空间分布较集中,高光范围较小,益取较大的值(大于100或者更大)
粗糙的物体表面比如纸张,木材,粉笔等,光强的空间分布比较分散,高光范围较大,益取较小的值(小于或者接近1)
从图中可以看到,镜面反射光不仅决定于入射光,而且和观察者的观察方向有关。当观察者靠近反射方向的时候,接收到的反射光较强,但是如果偏离这一方向,会迅速减弱。加入环境光,漫反射和高光部分,Phone光照模型:
Ka为环境反射系数
Kd漫反射系数
Ks镜面反射系数
hlsl:
#include "HrLightingCommon.fx" cbuffer cbPerFrame { uint3 lightsNum; uint shininess; float4 ambientLightColor; DirectionLight directLight; }; cbuffer cbPerObject { float4x4 world_matrix; float4x4 world_view_proj_matrix; float4x4 inverse_transpose_world_matrix; Material mat; float3 camera_position; }; struct VertexIn { float3 vertexPosition : POSITION; float3 vertexNormal : NORMAL; float2 textureCoord : TEXCOORD; }; struct VertexOut { float4 posWorldViewProj : SV_POSITION; float4 pixelColor : COLOR; }; /// // /// VertexOut Pass0_VS_Main(VertexIn vIn) { VertexOut vOut; vOut.posWorldViewProj = mul(float4(vIn.vertexPosition, 1.f), world_view_proj_matrix); //法线变换 float3 normalDirection = normalize(mul(float4(vIn.vertexNormal, 0.0f), inverse_transpose_world_matrix).xyz); float3 worldCameraPos = camera_position; float3 posWorldTranslate = mul(float4(vIn.vertexPosition, 1.0f), world_matrix).xyz; float3 viewDir = normalize(worldCameraPos - posWorldTranslate); float4 rtAmbient = { 0.0f, 0.0f, 0.0f, 1.0f }; float4 rtDiffuse = { 0.0f, 0.0f, 0.0f, 1.0f }; float4 rtSpecular = { 0.0f, 0.0f, 0.0f, 1.0f }; rtAmbient = mat.ambient_material_color * ambientLightColor; for (uint i = 0; i < lightsNum[0]; ++i) { float3 lightDir = normalize(-directLight.light_direction[i]); float fDiffuseFactor = max(0, dot(lightDir, normalDirection)); rtDiffuse += fDiffuseFactor * mat.diffuse_material_color * directLight.diffuse_light_color[i]; float3 reflectSpecularDir = 2 * lightDir * normalDirection * normalDirection - lightDir; rtSpecular += mat.specular_material_color * directLight.specular_light_color[i] * pow(max(dot(viewDir, reflectSpecularDir), 0), 30); } vOut.pixelColor = rtAmbient + rtDiffuse + rtSpecular; return vOut; } float4 Pass0_PS_Main(VertexOut pixIn) : SV_TARGET { return pixIn.pixelColor; }
Blinn-Phong光照模型
Blinn-Phong是Blinn提出的一个简单修改Phong光照模型而达到更好效果的光照模型。它的基本思想是用N
•H替换了V•R。
H是对观察向量和光源向量取平均再归一化得到的。
所以Blinn-Phong光照模型的函数仅仅把Phong公式用N
•H替换了V•R。在一些情况下,Blinn效果更好些。
hlsl Phong + Blinn-Phong:
VertexOut Pass1_VS_Main(VertexIn vIn) { VertexOut vOut; vOut.posWorldViewProj = mul(float4(vIn.vertexPosition, 1.f), world_view_proj_matrix); //法线变换 float3 normalDirection = normalize(mul(float4(vIn.vertexNormal, 0.0f), inverse_transpose_world_matrix).xyz); float3 worldCameraPos = camera_position; float3 posWorldTranslate = mul(float4(vIn.vertexPosition, 1.0f), world_matrix).xyz; float3 viewDir = normalize(worldCameraPos - posWorldTranslate); float4 rtAmbient = { 0.0f, 0.0f, 0.0f, 1.0f }; float4 rtDiffuse = { 0.0f, 0.0f, 0.0f, 1.0f }; float4 rtSpecular = { 0.0f, 0.0f, 0.0f, 1.0f }; rtAmbient = mat.ambient_material_color * ambientLightColor; for (uint i = 0; i < lightsNum[0]; ++i) { float3 lightDir = normalize(-directLight.light_direction[i]); float fDiffuseFactor = max(0, dot(lightDir, normalDirection)); rtDiffuse += fDiffuseFactor * mat.diffuse_material_color * directLight.diffuse_light_color[i]; //float3 reflectSpecularDir = 2 * lightDir * normalDirection * normalDirection - lightDir; float3 halfViewLight = normalize(lightDir + viewDir); rtSpecular += mat.specular_material_color * directLight.specular_light_color[i] * pow(max(dot(viewDir, halfViewLight), 0), 30); } vOut.pixelColor = rtAmbient + rtDiffuse + rtSpecular; return vOut; } float4 Pass1_PS_Main(VertexOut pixIn) : SV_TARGET { return pixIn.pixelColor; }