茂南手机网站建设公司,做一个企业网站花费,郑州专门做网站,wordpress 引用 样式表本人刚学OpenGL不久且自学#xff0c;文中定有代码、术语等错误#xff0c;欢迎指正 我写的项目地址#xff1a;https://github.com/liujianjie/LearnOpenGLProject 文章目录天空盒介绍如何采样OpenGL纹理目标例子0#xff1a;天空盒效果环境映射反射例子1#xff1a;Cube… 本人刚学OpenGL不久且自学文中定有代码、术语等错误欢迎指正 我写的项目地址https://github.com/liujianjie/LearnOpenGLProject 文章目录天空盒介绍如何采样OpenGL纹理目标例子0天空盒效果环境映射反射例子1Cube反射例子2模型反射折射例子1Cube折射例子2模型折射测试-先渲染天空盒再渲染物体默认深度LESS比较方式天空盒
介绍
立方体贴图就是一个包含了6个2D纹理的纹理每个2D纹理都组成了立方体的一个面一个有纹理的立方体。
如何采样 方向向量的大小并不重要只要提供了方向OpenGL就会获取方向向量最终所击中的纹素并返回对应的采样纹理值。只要立方体的中心位于原点我们就能使用立方体的实际位置向量来对立方体贴图进行采样了。我们可以将所有顶点的纹理坐标当做是立方体的顶点位置。最终得到的结果就是可以访问立方体贴图上正确面(Face)纹理的一个纹理坐标。
立方体有36个顶点位置在顶点着色器后每个片段都有自己的顶点位置采样天空盒时用这个顶点位置当做纹理坐标即可。
OpenGL纹理目标 纹理目标方位GL_TEXTURE_CUBE_MAP_POSITIVE_X右GL_TEXTURE_CUBE_MAP_NEGATIVE_X左GL_TEXTURE_CUBE_MAP_POSITIVE_Y上GL_TEXTURE_CUBE_MAP_NEGATIVE_Y下GL_TEXTURE_CUBE_MAP_POSITIVE_Z后GL_TEXTURE_CUBE_MAP_NEGATIVE_Z前
例子0天空盒效果 加载天空盒 // 加载纹理// -------------
unsigned int cubeTexture loadTexture(FileSystem::getPath(assest/textures/container.jpg).c_str());
// 加载天空盒
vectorstd::string faces{FileSystem::getPath(assest/textures/skybox/right.jpg),FileSystem::getPath(assest/textures/skybox/left.jpg),FileSystem::getPath(assest/textures/skybox/top.jpg),FileSystem::getPath(assest/textures/skybox/bottom.jpg),FileSystem::getPath(assest/textures/skybox/front.jpg),FileSystem::getPath(assest/textures/skybox/back.jpg)
};
unsigned int cubemapTexture loadCubemap(faces);
// 加载天空盒
// 加载顺序
// order:
// X (right)
// -X (left)
// Y (top)
// -Y (bottom)
// Z (front)
// -Z (back)
unsigned int loadCubemap(vectorstd::string faces) {unsigned int textureID;glGenTextures(1, textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrChannels;for (unsigned int i 0; i faces.size(); i) {unsigned char* data stbi_load(faces[i].c_str(), width, height, nrChannels, 0);if (data) {glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else {std::cout Cubemap texture failed to load at path: faces[i] std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);;return textureID;
}为天空盒创建立方体的六个面的顶点数据以及VAO VBO // skybox VAO
unsigned int skyboxVAO, skyboxVBO;
glGenVertexArrays(1, skyboxVAO);
glGenBuffers(1, skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glBindVertexArray(0);渲染 glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);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.use();
view camera.GetViewMatrix();
shader.setMat4(model, model);// 不变在中心
shader.setMat4(view, view);
shader.setMat4(projection, projection);
glBindVertexArray(cubeVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);// 渲染天空盒
// 重点代码小于等于。由于深度缓冲区的默认值为1而到顶点着色器里设置了天空盒的深度值为1所以要为小于等于,11,测试才通过才到片段着色器采样颜色
glDepthFunc(GL_LEQUAL);
skyboxShader.use();
//view camera.GetViewMatrix();
// 重点代码取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分再变回4x4矩阵。///
// 防止摄像机移动天空盒会受到视图矩阵的影响而改变位置即摄像机向z后退天空盒和cube向z前进
view glm::mat4(glm::mat3(camera.GetViewMatrix()));
skyboxShader.setMat4(view, view);
skyboxShader.setMat4(projection, projection);
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一个参数从GL_TEXTURE_2D 变为GL_TEXTURE_CUBE_MAP
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS);glsl和采样 #version 330 core
layout (location 0) in vec3 aPos;// 纹理坐标是3维的
out vec3 TexCoords;
// 不用model转换到世界矩阵
uniform mat4 projection;
uniform mat4 view;
void main()
{// 纹理坐标等于位置坐标/TexCoords aPos;vec4 pos projection * view * vec4(aPos, 1.0);// z为w透视除法除后z(zw/w)1深度为最远///gl_Position pos.xyww;
}#version 330 core
out vec4 FragColor;// 纹理坐标是3维的
in vec3 TexCoords;// 纹理坐标// 天空盒纹理采样
uniform samplerCube skybox;void main(){ FragColor texture(skybox, TexCoords);
}关键地方 天空盒不会跟随摄像机移动 // 重点代码取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分再变回4x4矩阵。
// 防止摄像机移动天空盒会受到视图矩阵的影响而改变位置即摄像机向z后退天空盒和cube向z前进
view glm::mat4(glm::mat3(camera.GetViewMatrix()));天空盒后渲染也不会覆盖先前绘制的物体 先绘制其它物体 设置深度测试为小于等于 绘制天空盒 在天空盒的顶点着色器运行后会执行透视除法将gl_Position的xyz坐标除以w分量将gl_Position的xyz坐标除以w分量透视除法所做。 所以我们设置天空盒的z为wz(zw/w)1 gl_Position pos.xyww;// z为w透视除法除后z(zw/w)1深度为最远由于深度测试为小于等于结合下面图示 在其他物体已占据片段的深度缓冲值1 天空盒的深度值1不小于等于这些片段的缓冲值所以不会通过深度测试从而保持原有的物体片段颜色。 其他物体未占据片段深度缓冲的默认值为1 天空盒的深度值1小于等于深度缓冲的值1所以会通过深度测试从而输出天空盒片段。 错误做法将深度测试为默认的小于 其他物体未占据片段深度缓冲的默认值为1 天空盒的深度值1不小于深度缓冲区的默认值1不会通过深度测试从而具有天空盒的颜色的片段不会输出到屏幕上。 效果
环境映射 什么是环境映射 通过使用环境的立方体贴图我们可以给物体反射和折射的属性。 这样使用环境立方体贴图的技术叫做环境映射(Environment Mapping)其中最流行的两个是反射(Reflection)和折射(Refraction)。
反射 原理图
例子1Cube反射 代码 立方体的shader天空盒的shader不变还是和上面例子天空盒效果的一样 #version 330 core
layout (location 0) in vec3 aPos;
layout (location 1) in vec3 aNormal;out vec3 Normal;
out vec3 Position;uniform mat4 projection;
uniform mat4 model;
uniform mat4 view;
void main()
{// 法线矩阵Normal mat3(transpose(inverse(model))) * aNormal;// 到世界空间Position vec3(model * vec4(aPos, 1.0));// 这里不再是gl_Position pos.xyww;因为这是中间立方体的不是天空盒的shadergl_Position projection * view * vec4(aPos, 1.0);
}#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));// 采样天空盒的uv坐标是3维的FragColor vec4(texture(skybox, R).rgb, 1.0);// FragColor texture(skybox, R); 这个效果一样
}cpp Shader shader(assest/shader/4高级OpenGL/6.2.1.cube-反射天空盒.vs, assest/shader/4高级OpenGL/6.2.1.cube-反射天空盒.fs);
Shader skyboxShader(assest/shader/4高级OpenGL/6.1.1.天空盒-普通效果.vs, assest/shader/4高级OpenGL/6.1.1.天空盒-普通效果.fs);
.....
// shader configuration
// --------------------
shader.use();
shader.setInt(skybox, 0);skyboxShader.use();
skyboxShader.setInt(skybox, 0);glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一个参数从GL_TEXTURE_2D 变为GL_TEXTURE_CUBE_MAP// render loop
// -----------
while (!glfwWindowShouldClose(window))
{// 渲染立方体shader.use();view camera.GetViewMatrix();shader.setMat4(model, model);// 不变在中心shader.setMat4(view, view);shader.setMat4(projection, projection);// 为了反射传入shader.setVec3(cameraPos, camera.Position);glBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 其它和天空盒的代码一样效果 箱子上的贴图是后面的天空盒贴图
例子2模型反射 代码 立方体的shader天空盒的shader不变还是和上面例子天空盒效果的一样 #version 330 core
layout (location 0) in vec3 aPos;
layout (location 1) in vec3 aNormal;
layout (location 2) in vec2 aTexCoords;out vec3 Normal;
out vec3 Position; // 片段的坐标-世界空间
out vec2 TexCoords;// 纹理坐标uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main()
{gl_Position projection * view * model * vec4(aPos, 1.0);TexCoords aTexCoords;// 到世界空间Position vec3(model * vec4(aPos, 1.0));// 这里不再是gl_Position pos.xyww;因为这是中间立方体的不是天空盒的shaderNormal mat3(transpose(inverse(model))) * aNormal;
}#version 330 core
out vec4 FragColor;in vec3 Normal;
in vec3 Position; // 片段的坐标-世界空间
in vec2 TexCoords;// 纹理坐标uniform vec3 cameraPos;
uniform sampler2D texture_diffuse1;
uniform sampler2D texture_specular1;
uniform sampler2D texture_height1;// 天空盒纹理采样
uniform samplerCube skybox;void main(){ vec3 I normalize(Position - cameraPos);vec3 R reflect(I, normalize(Normal));// 采样镜面光贴图颜色uv坐标是2维的vec4 specular4 texture(texture_specular1, TexCoords); // 采样出来的颜色是4维的vec3 specular3 specular4.rgb;// 采样天空盒颜色uv坐标是3维的并乘以镜面光贴图颜色FragColor vec4(texture(skybox, R).rgb * specular3, 1.0) ;// FragColor vec4(texture(skybox, R).rgb, 1.0) ;// 未乘以镜面光贴图颜色
}cpp // 加载模型
Model ourModel(FileSystem::getPath(assest/model/nanosuit/nanosuit.obj));// shader configuration
// --------------------
shader.use();
shader.setInt(skybox, 4);skyboxShader.use();
skyboxShader.setInt(skybox, 4);
// 设置的天空盒的纹理单元位置好像不会与普通的纹理冲突但保险起见还是设为4
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一个参数从GL_TEXTURE_2D 变为GL_TEXTURE_CUBE_MAPwhile (!glfwWindowShouldClose(window))
{// 渲染这个模型// 为了反射传入model glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));model glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));shader.use();shader.setVec3(cameraPos, camera.Position);shader.setMat4(model, model);shader.setMat4(view, view);shader.setMat4(projection, projection);ourModel.Draw(shader);
}效果 采样天空盒颜色未乘以镜面光贴图颜色 采样天空盒颜色并乘以镜面光贴图颜色
折射 原理 折射率表 材质折射率空气1.00水1.33冰1.309玻璃1.52钻石2.42例子中光线/视线从空气(折射率1进入玻璃如果我们假设箱子是玻璃制的所以比值为1.00/1.520.658
例子1Cube折射 代码 和反射的代码差不多就是中间立方体的glsl片段着色器代码不一样 void main(){ float ratio 1.00 / 1.52;vec3 I normalize(Position - cameraPos);vec3 R refract(I, normalize(Normal), ratio);// refract第三个参数是折射率// 采样天空盒颜色uv坐标是3维的FragColor vec4(texture(skybox, R).rgb, 1.0);// FragColor texture(skybox, R); 这个效果一样
}效果
例子2模型折射 代码 void main(){ float ratio 1.00 / 1.52;vec3 I normalize(Position - cameraPos);vec3 R refract(I, normalize(Normal), ratio);// refract第三个参数是折射率// 采样天空盒颜色uv坐标是3维的FragColor vec4(texture(skybox, R).rgb, 1.0);// FragColor texture(skybox, R); 这个效果一样
}效果
测试-先渲染天空盒再渲染物体默认深度LESS比较方式 代码 // 将天空盒的盒子长宽为20
float skyboxVertices[] {// positions -10.0f, 10.0f, -10.0f,-10.0f, -10.0f, -10.0f,.....
};
// 渲染天空盒
//glDepthFunc(GL_LEQUAL); // 不用LEQUAL而是默认的LESS
skyboxShader.use();
// 重点代码取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分再变回4x4矩阵。
// 防止摄像机移动天空盒会受到视图矩阵的影响而改变位置即摄像机向z后退天空盒和cube向z前进
view glm::mat4(glm::mat3(camera.GetViewMatrix()));
skyboxShader.setMat4(view, view);
skyboxShader.setMat4(projection, projection);
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一个参数从GL_TEXTURE_2D 变为GL_TEXTURE_CUBE_MAP
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
//glDepthFunc(GL_LESS);// 渲染立方体
shader.use();
view camera.GetViewMatrix();
shader.setMat4(model, model);// 不变在中心
shader.setMat4(view, view);
shader.setMat4(projection, projection);
glBindVertexArray(cubeVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, cubeTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);天空盒的顶点位置z透视除法后不为1 //gl_Position pos.xyww;// zw透视除法除后还是1深度为最远
gl_Position projection * view * vec4(aPos, 1.0);解释代码顺序 天空盒的盒子长宽为20 先绘制天空盒再绘制箱子 这代码天空盒将不会受摄像机的观察矩阵的位移部分影响 所以虽然glsl天空盒的深度值z未设置w但是视觉上依旧是无限远 不过实际上现在代码造成的影响是不论摄像机所在什么位置以摄像机为原点处在一个20*20大小的立方体盒子里在20*20范围内的物体被显示20*20外的物体被天空盒颜色所覆盖。 换句话说注意摄像机在原点所以20*20的盒子半径为10于是原点为出发点距离摄像机长度小于10的物体会显示大于10的物体会被天空盒颜色所覆盖。 进一步解释结合下方图 箱子离摄像机的距离 10第一幅图 箱子的深度值小于天空盒所以天空盒同箱子所占的片段区域会被丢弃显示箱子的片段颜色 箱子离摄像机的距离 10第二幅图 箱子的深度值大于天空盒所以天空盒同箱子所占的片段区域会覆盖箱子显示天空盒的片段颜色