标签:
按照书中的光照模型,光的类型分为3种:漫反射光,环境光,镜面光。
光源类型也是三种:平行光,点光,聚光灯。
其它需要的数据:材质,法线方向(光照角度)。
现在,先以平行光源为例,因为它最简单,不需要去计算距离,角度对于光线的影响:
先定义光源:
//这里所有的定义只与光的颜色有关,就是定义光的颜色 DirLight.Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);//环境光 DirLight.Diffuse = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f);//漫反射光 DirLight.Specular = XMFLOAT4(1.0f, 1.0f, 1.0f, 0.0f);//镜面光
1.环境光:
所谓环境光,可以认为有一种光充满了整个照射区域,无论在哪个位置,都可以被覆盖到。他的作用是用于模拟现实世界中的漫反射效果。
实现公式.. A=la?ma ,解释,获得的环境光等于输入的环境光(Input Ambient 三维向量)逐项相乘材质的环境光系数(Material Ambient三维向量)
HLSL代码: ambient = mat.Ambient * L.Ambient;
例子:光线(0.2,0.2,0.2)*材质(0.5,0.5,0.5) = (0.1,0.1,0.1)
也就是说,环境光只与材质,光本身有关,与角度无关。
(这里好郁闷。。为什么?表示规是逐项相乘。。。。我记得我们的教材中这个符号表示叉乘。。。而且向量有这种定义吗。。。以下所有的?都表示逐项相乘)
2.漫反射光:
很简单,光打在粗糙的表面上会有各个方向的均匀反射,因此无论在那个方向,我们看到的光的强度是一样的。
实现公式:cd = kd•ld ? md 解释:后一半和前面的环境光一样,材质与光线逐项相乘,前一个kd表示的是一个"角度",在书中7.1的法线定理与7.2中的兰伯特余弦定义有详细的解释,照射点的法线与朝向光线角度的余弦值。
代码与后面的镜面光一起。
3.镜面光(也有叫做高光的):
对于一些光滑的材质,光会形成镜面反射,这些光只有在一些特别的角度才可以看见。
实现公式:cs = ks•ls ? ms 这里的ks指的是观察方向与与反射的光线方向的夹角(角度为0时最强)
完全代码:
变量的定义:
//------------------------------------------------------------------------------------- //定义光照结构 //---------------------------------------------------------------------------------------- struct DirectionalLight ////平行光 { float4 Ambient; float4 Diffuse; float4 Specular; float3 Direction; float pad; }; //-------------------------------------------------------------------------------------- // 常量缓冲定义 //-------------------------------------------------------------------------------------- cbuffer ConstantBuffer : register(b0) //世界,投影坐标转换 { matrix World; matrix View; matrix Projection; } cbuffer cbPerFrameLight //光线结构打包,暂时就一个平行光 { DirectionalLight gDirLight; //平行光 //PointLight gPointLight; //点光 //SpotLight gSpotLight; //聚光灯 float4 gEyePosW; //眼睛坐标 }; //-------------------------------------------------------------------------------------- //其它结构定义 //-------------------------------------------------------------------------------------- struct Material { float4 Ambient; float4 Diffuse; float4 Specular; // w = SpecPower float4 Reflect; };
函数的实现:
//-------------------------------------------------------------------------------------- // 功能函数实现 //-------------------------------------------------------------------------------------- //实现平行光ComputeDirectionalLight(材质,平行光结构,反射点法线,指向眼睛的单位向量,返回1,返回2,返回3) void ComputeDirectionalLight( Material mat, DirectionalLight L, float3 normal, float3 toEye, out float4 ambient, out float4 diffuse, out float4 spec) { // 初始化输出的变量 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f); diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f); spec = float4(0.0f, 0.0f, 0.0f, 0.0f); // 光照矢量与光线的传播方向相反 float3 lightVec = -L.Direction; // 添加环境光 ambient = mat.Ambient * L.Ambient; // 添加漫反射和镜面光,注意,保证normal,lightVec是单位向量(归一化) float diffuseFactor = dot(lightVec, normal);//计算cosA // Flatten避免动态分支 [flatten] if (diffuseFactor > 0.0f) //如果角度的余弦值大于0, { float3 v = reflect(-lightVec, normal);//reflect通过入射光与法线计算反射向量 float specFactor = pow(max(dot(v, toEye), 0.0f), mat.Specular.w);//pow用于计算材质光的线反射度 diffuse = diffuseFactor * mat.Diffuse * L.Diffuse; spec = specFactor * mat.Specular * L.Specular; } }
相比于原书的代码,我做了更多的注释,但还是要解释一遍:
已经叙述过的环境光计算:ambient = mat.Ambient * L.Ambient;
这里面光线的矢量方向是光线入射方向的反向,也就是反射点指向光源的方向,平行光直接反向就可以了:float3 lightVec = -L.Direction;
计算反射的法线与光线矢量的夹角的余弦值:float diffuseFactor = dot(lightVec, normal),这里面的normal是法线,lightVec上面计算了,他们都是单位向量,只有这样的点乘为cos值,还有一点,关于法线的计算方式可以看书中的7.2节。
然后是一个if语句,关于HLSL中的支持控制语句看msdn的介绍:
https://msdn.microsoft.com/en-us/library/bb509600.aspx
这里面的Flatten与branch的解释我是完全没明白。。。。。再说吧。。。。。。
使用入射光与法线向量计算反射光:float3 v = reflect(-lightVec, normal);
计算反射光与指向眼睛的向量(观察向量,指向观察点)的余弦值dot(v, toEye),判断它是否大于0:max(dot(v, toEye))
最后使用pow对材质进行分析,这是为了用于区分不同材质的反射能力不同。
这一节比较简单,不给代码了。其它的2种光源差不多,不再详细分析了。。。。。。
好麻烦啊。
追加:关于flatten与branch
https://msdn.microsoft.com/en-us/library/windows/desktop/bb509610
http://www.gamedev.net/topic/650462-branch-and-flatten-attributes-in-sm-5/
我的理解:GPU是尽量避免分支的,因此程序编译的时候会采用一种启发式的智能优化方法去摘除分支,这是为了同个程序在不同分支的情况下都可以有相同的运行时间,避免时间的浪费?但是我们知道有时候一些分支是必须的,因此他提供了branch与flatten来让我们判断是否使用动态分支。书中例子使用了flatten避免了动态分支。
------以上为我看文档与评论得出的结论,自己没有实际的方法去验证。
标签:
原文地址:http://www.cnblogs.com/Windogs/p/4985811.html