场景基本渲染的最后一块内容,天空盒,这里能了解到整体的反折射的概念。
cubemaps
通过实现这里cubemap的效果,基本场景里面所有真实的元素都被体现在了物体的光照效果中(更加高级的阴影之类的后面再详细讨论)。就是6张贴图嘛,整合成一个整体-立方体中,如果立方体的中心在坐标原点,这样立方体表面的像素点获取就非常容易,通常向量(原点指向纹理坐标点)跟位置(直接的纹理坐标)相关。
创建cubemap
跟创建普通的没什么区别,绑定的对象变成了GL_TEXTURE_CUBE_MAP,生成是时候还是用glTexImage2D生成,最后fragmentshader中,输入有
1 | in vec3 textureDir; // direction vector representing a 3D texture coordinate |
skybox
- loading a skybox
没啥新的内容,就是加载6张图给返货一个表示GL_TEXTURE_CUBE_MAP的textureID。 - 渲染skybox
这里要注意,我们要实现的效果,不管player怎么移动,这个cube都不会移动。在基本光照里,有这么一个技术处理,我么可以把变换矩阵的平移分量去掉,只取左上方33的矩阵,然后再变换回到44矩阵。这样就消除了平移分量,保留了旋转,你仍然可以查看不同的天空盒视角:
glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
优化天空盒
如果预先渲染天空盒的话,会浪费不少资源,因为很多天空盒的fragment最后要被舍弃掉。所以,一般最后渲染,类似于放在unity里面最底层???
用一个技巧,在深度测试的时候,把天空盒的深度看成最大值1.实际上不是,因为天空盒只是一个1x1x1的立方体,无法通过深度测试。不管物体是什么,天空盒的相应片元都不通过深度测试,这样就不用渲染了。
在坐标系一节,我们说perpective division实在vertex shader以后进行的,用w因子除以gl_position的xyz坐标。而且在深度测试(一节)中,顶点的深度值实际上等于division以后的z值。
利用这个,将输出的z设置成它的w值,导致深度测试中的z始终是1,为什呢?这样进行perpective division的时候,它的z就会编程w/w = 1.
1 | void main(){ |
NDC空间内,z值是1就是最大值,就是永远在背景的东西。其他都在前面。同时,gl_lequal也应该变成gl_less,确保让天空盒通过测试正确渲染。因为它的值是1。
environment mapping
反射
根据观察者和相应物体表面的位置,得到出射的角度,然后根据表面的法向,来确定颜色入射的角度。再根据入射的角度计算最终能得到的天空盒的颜色。全反射,那就肯定是全部颜色了,没有其他颜色。
参考learn4-001
cube顶点shader需要输入:apos 顶点坐标 anormal 法向量(物体的法向量一定要在程序里向vertex输入)
输出:法向量Normal以及世界坐标的位置
1 |
|
cube的fragmentshader需要输入:vs的输出;天空盒,相机坐标
输出最终的颜色
1 |
|
整体代码流程:
1 | // cube VAO |
折射
learn4-002,将cube看成glass制作的。
一般只需处理一次折射。
最后
光照的处理,在shader的基础上,其实就是定义光照模型的过程,结合一定的物理知识将光照模型在场景中计算,最终反馈到输出像素中,所以要想达到逼真的效果,尽可能多地加进光照模型就可以。phong是最简单最常用的一种。其他的模型和方法会慢慢补充进来。