光照实现
LightedCube
1 | let VSHADER_SOURCE = ` |
1 | function main() { |
1 | function initVertexBuffers(gl) { |
1 | function initArrayBuffer(gl,attribute,data,num,type) { |
入射光方向设置在世界坐标系下是什么意思?
答: 表示光照效果是在世界坐标系下计算的。
这样做可以使程序更简单,代码比较直观。
为什么在入射光方向归一化处理在Js中,法向量归一化处理在着色器内?
答:
- 入射光方向 u_LightDirection 是 uniform 类型,适用于所有顶点,
没有必要在着色器中逐顶点重复归一化操作,因此归一化后传入即可。 - 法向量 a_Normal 是 attribute 类型,放在缓冲区传入,
每个顶点都有对应的法向量值,需要在着色器内逐顶点的处理。
法向量和入射光向量点积值小于0,说明出现了什么情况?
答: cosθ<0,说明θ>90°,即 光线照在了表面的背面上。
Shader和Js中的normalize有什么区别?
1 | vec3 normal = normalize(vec3(a_Normal)); |
normalize为GLSL ES的内置函数,接受并返回 vec3 类型参数。
1 | let lightDirection = new Vector3([0.5, 3.0, 4.0]); |
Vector3对象的normalize函数返回 Float32Array 类型参数,
并存储在Vector对象的 elements 属性中。
注意在Cube顶点Position旋转变换时,也需要对顶点的法向量进行旋转变换。
1 | let VSHADER_SOURCE = ` |
1 | let u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor'); |
环境光下的漫反射
LightedCube中使用的是 平行光源 ,没有被直射光照射到的面按理来说应该被 环境光 照亮。
由于环境光是由其他物体反射产生的,因此环境光的强度通常比较弱。
1 | let VSHADER_SOURCE = ` |
1 | let u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight'); |
运动物体的光照效果
当对物体进行变换时,需要考虑其法向量的变换情况。
- 平移变换 不会改变法向量
- 旋转变换 会改变法向量
- 缩放变换 有可能会改变法向量,存在一些特殊情况,如:
- 所有轴等比缩放
- 向两个轴的方向等比缩放
如何计算变换之后的法向量呢?
答: 将变换之前的法向量乘以 ModelMatrix的逆转置矩阵(inverse transpose matrix) 即可。
如果矩阵M的逆矩阵是R,则 R*M=M*R=单位矩阵
1 | let modelMatrix = new Matrix4(); //模型矩阵 |
1 | let VSHADER_SOURCE = ` |
1 | let u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix'); |
点光源光
点光源光的方向不是 恒定不变 的,而是 根据每个顶点的位置 逐一计算的。
回顾平行光源方向的获取
1 | let VSHADER_SOURCE = ` |
需要两个值:
- 点光源位置 u_LightPosition
- 顶点位置 a_Position
- 计算顶点的世界坐标的模型矩阵 u_ModelMatrix
- 顶点对应法向量的模型矩阵 u_NormalMatrix
为什么要转换到世界坐标系下?
答: 你可能已经知道 u_MvpMatrix 是做什么用的了,
它会设置一个视点,并计算出传入的坐标 a_Position 相对视点的绘制坐标,
点光源光线是和 点光源和物体顶点的相对位置 有关的,即两个物体的相对位置关系,
这个位置关系是 绝对的,与视点坐标系无关,
因此就需要一个 绝对的空间 来为它们做基准,这就是 世界坐标系。
就像冬天你从寒冷的外面进入正常气温的屋子一样,你可能认为屋内气温比实际的要高一些,世界坐标系就像温度计一样,告诉你绝对现实是什么。视点坐标:我相比之前感觉有了很大的进步,我正在逐渐理解一切!世界坐标:哦?你是这样认为的吗?
Step1
计算顶点在 世界坐标系 中的坐标,
计算 世界顶点 相对应的 法向量。
Step2
计算顶点处 点光源方向 :
设世界坐标系下,
顶点向量为 OA,点光源向量为 OB
则顶点处点光源光线向量为 BA = OA - OB
即 顶点世界矢量 - 点光源世界矢量
漫反射光的方向与入射光方向相反,
因此最终反射光线 = 点光源世界矢量 - 顶点世界矢量
1 | let VSHADER_SOURCE = ` |
1 | function main() { |
逐片元光照
当物体运动时,会发现 立方体表面上有不自然的线条。
这是片元的颜色是 由顶点颜色内插得出的 ,因此不够逼真。
逐片片元操作实际上就是把反射光颜色计算的步骤提取到片元着色器中。
可以看到逐片元处理的物体表面颜色过渡更加自然。
将计算过程提取到片元着色器中,需要准备哪些数据?
答:
- 片元世界坐标
- 片元处法向量
把顶点坐标在顶点着色器中转换为世界坐标系下的坐标,将法向量进行相应转换,
这样通过varying传入的同名变量就已经是内插后的逐片元值了。
法向量传入片元着色器后为什么又要进行归一化处理?
答: 因为内插之后的法向量可能不再是1.0了。
1 | let VSHADER_SOURCE = ` |
1 | let FSHADER_SOURCE = ` |