重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
1,
漫射光和环境光的主要不同是漫射光的特性依赖光线的方向,而环境光完全忽略光的方向。当只有环境光时整个场景是被均匀照亮的,而漫射光使物体朝向它的那一面比其他背向光的面要更亮。漫射光还增加了一个光强度的变化现象,光的强度大小还取决于光线和物体表面的角度。
漫射光模型是建立在兰伯特余弦定律上的,光线的强度和观察者视线与物体表面法线夹角的余弦值成正比(夹角越大光强度越小)。注意这里略有变化,我么使用的是光线的方向而不是观察者视线(在镜面发射中用到)的方向。
为了计算光的强度我们要引入光线和物体表面法线(兰伯特定律中更加通用的概念叫做'directionaly proportional')夹角的余弦值作为一个参数变量。看下面这幅图:
图中四条光线以不同的角度照到表面上(光线仅仅是方向不同),绿色的箭头是表面法向量,从表面垂直往外发出。
光线A的强度是大的,因为光线A和法线的夹角为0,余弦值为大的1,
也就是这个光线的强度(三个通道的三个0-1的值)和表面颜色相乘每个颜色通道都是乘以1,这是漫射光强度大的情况了。
光线B以一定角度(0-90之间)照射到表面,这个角度就是光线和法线的夹角,那么夹角的余弦值应该在0-1之间,表面颜色值最后要和这个角度的余弦值相乘,那么得到的光的强度一定是比光线A要弱的。
对于光线C和D情况又不同了。C从表面的一侧入射,光线和表面的夹角为0,和法线垂直,对应的余弦值为0,这会导致光线C对表面照亮没有任何效果。光线D从表面的背面入射和法线成钝角,余弦值为负比0还小甚至小到-1。所以光线D和C一样都对物体表面没有照亮作用。
2,
可以看到表面法线对漫射光的计算很重要。上面的例子是很简化的:表面是平坦的直线只需要考虑一条法线。而真实世界中的物体有无数的多边形组成,每个多边形的法线和附近的多边形基本都不一样。例如:
因为一个多边形面上分布的任意法向量都是一样的,足以用其中一个代表来计算顶点着色器中的漫射光。
一个三角形上的三个顶点会有相同的颜色而且整个三角形面的颜色都相同,但这样看上去效果并不好,每个多边形之间的颜色值都不一样这样我们会看到多边形之间边界的颜色变化不平滑。
因此这个明显是需要进行优化的:顶点法线。
顶点法线是共用一个顶点的所有三角形法线的平均值。事实上我们并没有在顶点着色器中计算漫射光颜色,而只是将顶点法线作为一个成员属性传给片段着色器。
光栅器会得到三个不同的法向量并对其之间进行插值运算。
片段着色器将会对每个像素计算其特定的插值法向量对应的颜色值。
这样使用那个插值后得到的每个像素特定法向量,我们对漫射光的计算可以达到像素级别。效果是光照效果在每个相邻三角形面之间会平滑的变化。这种技术叫做Phong着色(Phong Shading)。下面是顶点法线插值后的样子:
3,
顶点和他们的法线都定义在本地坐标系空间,并且都在顶点着色器中被我们提供给shader的WVP矩阵进行了变换,然后到裁剪空间。
然而,在世界坐标系中来定义光线的方向才是最合理的,毕竟光线的方向决定于世界空间中某个地方的光源将光线投射到某个方向(甚至太阳都是在世界空间中,只是距离极远)。所以,在计算之前,我们首先要将法线向量变换到世界坐标系空间。
4,
5,
输入定点、纹理、法线。
6,
初始化光
_gl_shardes->gluniform_light(_directionallight);
gDirectionalLight.Direction光源向量不是单位化的。如果对所有像素的同一个向量都进行反复单位化会很浪费GPU资源。
因此我们只要保证应用程序传递的向量在draw call之前被单位化即可。
direction.Normalize();
glUniform3f(_dirlight_directionlocation, direction.x, direction.y, direction.z);