关于光照模型的总结,我就不一一细谈了,一是自己记不住那么多,二呢,我觉得知识还是要实践为主。所以以下的东西主要针对编程而言的,不会涉及很综合深刻的讲解。照例最后可以猛戳下载代码。
1.漫反射
漫反射光大概就是从光源照射到物体表面,不考虑人的观察视角与反射的角度形成的光源强弱差别,而只考虑光源入射角和顶点法线的夹角,在各个方向上均等的反射光线的一种光。
假设Ld表示光源强度,Kd表示某个物体的反射系数,那么漫反射公式就是
Ia = Ld * Kd * cos(b)
如下图
漫反射
那么s和n的点乘就表示夹角的cos了(有疑惑的翻翻几何课本),于是就有公式如下
漫反射公式
有了这个公式,我们愉快的开始写代码吧!
attribute vec3 vertPosition; // Vertex position
attribute vec3 vertNorm; // Vertex normal
varying vec3 lightIntensity; // Light intensity
uniform vec3 lightPosition; // Light position in eye - coordinate
uniform vec3 Kd; // Reflect of diffuse light
uniform vec3 Ld; // Intensity of light
uniform mat3 normMat; // matrix of normal
uniform mat4 modelview; // matrix of modelview
uniform mat4 projection; // matrix of projection
void main() {
// Convert normal and position to eye coord
vec3 tnorm = normalize(normMat * vertNorm);
vec3 eyecoord = vec3(modelview * vec4(vertPosition, 1.0));
vec3 s = normalize(lightPosition - eyecoord);
// The diffuse shading equation
lightIntensity = Ld * Kd * max(dot(s, tnorm), 0.0);
gl_Position = projection * modelview * vec4(vertPosition, 1.0);
}
接下来,说代码吧。
前两行的attribute变量用于定义顶点坐标和顶点法线,也就是我们届时要输入的值。这里一定要注意,这个shader文件里的处理流程是逐顶点的,就是每次处理一个顶点变换。
然后定义一个varying输出变量,一会儿在fragment shader中要用到的。
之后是光源坐标Position,这里已经转换为观察坐标了,你也可以在shader里面转换。然后就是光照强度La,反射系数Kd。
接着定义了法线变换矩阵。这个东西简单讲一下。
我扣的图
类似于图中的法线,在图被拉伸(还有旋转什么的,法线要跟着顶点动才对)了之后,法线的方向就错了,因此需要法线矩阵来变换一下。法线矩阵的推导公式就不列出来了,线性代数我还在复习,也讲不太好,网上有不少资料,能看懂就好了。刚体变换就用观察矩阵modelview的左上角3x3就可以了。
然后定义了观察变换矩阵和投影变换矩阵。
然后,鸡冻人心的时刻就到了。
首先我们用法线矩阵变换顶点法向量,观察矩阵变换顶点坐标
vec3 tnorm = normalize(normMat * vertNorm);
vec3 eyecoord = vec3(modelview * vec4(vertPosition, 1.0));
然后计算指向光源坐标的向量,并且归一化
vec3 s = normalize(lightPosition - eyecoord);
接着,就要用到我们上面的公式了
lightIntensity = Ld * Kd * max(dot(s, tnorm), 0.0);
这个lightIntensity一会儿就是我们计算出来的顶点颜色。
接下来,使用gl_Position输出最终经过观察->投影变换后的顶点坐标
gl_Position = projection * modelview * vec4(vertPosition, 1.0);
至此,顶点shader的工作就完成了。
然后开始处理片段shader了。
上代码
precision mediump float;
varying vec3 lightIntensity;
void main() {
gl_FragColor = vec4(lightIntensity, 1.0);
}
其中定义的varying vec3 lightIntensity就是我们在vertex shader中计算出来的光照后的顶点颜色值。最终赋值给gl_FragColor,就是顶点的最后颜色了。
然后让程序跑起来,最终结果如下
渲染结果
另外代码中的ToyMater/ToyTest目录下是测试用的,可以很方便的修修改改的来测试。