OpenGL-Learning-4

场景基本渲染的最后一块内容,天空盒,这里能了解到整体的反折射的概念。

cubemaps

通过实现这里cubemap的效果,基本场景里面所有真实的元素都被体现在了物体的光照效果中(更加高级的阴影之类的后面再详细讨论)。就是6张贴图嘛,整合成一个整体-立方体中,如果立方体的中心在坐标原点,这样立方体表面的像素点获取就非常容易,通常向量(原点指向纹理坐标点)跟位置(直接的纹理坐标)相关。

创建cubemap

跟创建普通的没什么区别,绑定的对象变成了GL_TEXTURE_CUBE_MAP,生成是时候还是用glTexImage2D生成,最后fragmentshader中,输入有

1
2
3
4
in vec3 textureDir; // direction vector representing a 3D texture coordinate
uniform samplerCube cubemap;// cubemap texture sampler
void main(){
FragColor = texture(cubemap, textureDir);}

skybox

  1. loading a skybox
    没啥新的内容,就是加载6张图给返货一个表示GL_TEXTURE_CUBE_MAP的textureID。
  2. 渲染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
2
3
4
void main(){
TexCoords = aPos;
vec4 pos = projection * view * vec4(aPos, 1.0);
gl_Position = pos.xyww;}

NDC空间内,z值是1就是最大值,就是永远在背景的东西。其他都在前面。同时,gl_lequal也应该变成gl_less,确保让天空盒通过测试正确渲染。因为它的值是1。

environment mapping

反射

根据观察者和相应物体表面的位置,得到出射的角度,然后根据表面的法向,来确定颜色入射的角度。再根据入射的角度计算最终能得到的天空盒的颜色。全反射,那就肯定是全部颜色了,没有其他颜色。
参考learn4-001
cube顶点shader需要输入:apos 顶点坐标 anormal 法向量(物体的法向量一定要在程序里向vertex输入)
输出:法向量Normal以及世界坐标的位置

1
2
3
4
5
6
7
8
9
10
11
12
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 Normal;
out vec3 Position;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
Normal = mat3(transpose(inverse(model))) * aNormal;
Position = vec3(model * vec4(aPos, 1.0));
gl_Position = projection * view * model * vec4(aPos, 1.0);}

cube的fragmentshader需要输入:vs的输出;天空盒,相机坐标
输出最终的颜色

1
2
3
4
5
6
7
8
9
10
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 Position;
uniform vec3 cameraPos;
uniform samplerCube skybox;
void main(){
vec3 I = normalize(Position - cameraPos);
vec3 R = reflect(I, normalize(Normal));
FragColor = vec4(texture(skybox, R).rgb, 1.0);}

整体代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// cube VAO
unsigned int cubeVAO, cubeVBO;
// skybox VAO
unsigned int skyboxVAO, skyboxVBO;

// load textures
vector<std::string> faces
{
FileSystem::getPath("resources/textures/skybox/right.jpg")...
};
unsigned int cubemapTexture = loadCubemap(faces);
// shader configuration
shader.use();
shader.setInt("skybox", 0);
skyboxShader.use();
skyboxShader.setInt("skybox", 0);

// render loop
while (!glfwWindowShouldClose(window))
{
// render
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw scene as normal
shader.use();
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
shader.setMat4("model", model);
shader.setMat4("view", view);
shader.setMat4("projection", projection);
shader.setVec3("cameraPos", camera.Position);
// cubes
glBindVertexArray(cubeVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);

// draw skybox as last
glDepthFunc(GL_LEQUAL); // change depth function so depth test passes when values are equal to depth buffer's content
skyboxShader.use();
view = glm::mat4(glm::mat3(camera.GetViewMatrix())); // remove translation from the view matrix
skyboxShader.setMat4("view", view);
skyboxShader.setMat4("projection", projection);
// skybox cube
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // set depth function back to default
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
glfwSwapBuffers(window);
glfwPollEvents();
}

折射

learn4-002,将cube看成glass制作的。
一般只需处理一次折射。

最后

光照的处理,在shader的基础上,其实就是定义光照模型的过程,结合一定的物理知识将光照模型在场景中计算,最终反馈到输出像素中,所以要想达到逼真的效果,尽可能多地加进光照模型就可以。phong是最简单最常用的一种。其他的模型和方法会慢慢补充进来。