openGL系列文章目录
前言
在照明和材质章节中,我们考虑了物体的“光泽”。然而,我们从未对非常闪亮的物体
进行建模,例如镜子或铬制品。这些物体在有小范围镜面高光的同时,还能够反射出周围
物体的镜像。当我们看向这些物品时,我们会看到房间里的其他东西,有时甚至会看到我
们自己的倒影。ADS 照明模型并没有提供模拟这种效果的方法。
不过,纹理立方体贴图提供了一种相对简单的方法来模拟(至少部分模拟)反射表面。
其诀窍是使用立方体贴图来构造反射对象本身。①如果想要做得看起来真实,则需要找我们
从物体上看到的周围环境所对应的纹理坐标。
图1 展示了使用视图向量和法向量组合计算反射向量的策略,之后,该反射向量会用
来从立方体贴图中查找纹素。因此,反射向量可用来直接访问纹理立方体贴图。当立方体
贴图用于上述功能时,称其为环境贴图。
我们在之前研究Blinn-Phong 照明时计算过反射向量。除了我们现在使用反射向量从纹
理贴图中查找值,这里的反射向量概念和之前类似。这种技术称为环境贴图或反射贴图。
如果使用我们描述的第二种方法(在9.3.2 小节中,使用OpenGL GL_TEXTURE_CUBE_MAP)
实现立方体贴图,那么OpenGL 可以使用与之前为立方体添加纹理相同的方法来进行环境
贴图查找。我们使用视图向量和曲面法向量计算视图向量对应的离开对象表面的反射向量。
然后可以使用反射向量直接对纹理立方体贴图图像进行采样。查找过程由OpenGL
samplerCube 辅助实现;回忆上一节中,samplerCube 使用视图方向向量索引。因此,反射
向量非常适用于查找所需的纹素。
图1
实现环境贴图需要添加相对少量的代码。程序9.3 展示了display()函数和init()函数以及
相关着色器中的更改,以使用环境贴图渲染“反射”环面。所有更改都已经高亮显示。值
得注意的是,如果使用了Blinn-Phong 照明,那么很多需要添加的代码可能已经存在了。真
正新的代码部分在片段着色器中[在main()函数中]。
乍一看程序中突出显示的代码好像并不是新代码。实际上,在我们研究照明的时候,
已经看到过几乎相同的代码。然而,在当前情况下,法向量和反射向量用于完全不同的目
的。在之前的代码中,它们用于实现ADS 照明模型。而在这里,它们用于计算环境贴图的
纹理坐标。因此,我们将部分代码高亮,以便读者可以更轻松地追踪法向量和反射向量计
算的使用,以实现这一新目的。
渲染的结果会显示使用了环境贴图的“铬制”环面,如图2 所示。
图2
一、代码
1.主程序
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include "Torus.h"
#include "Utils.h"
using namespace std;
float toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
#define numVAOs 1
#define numVBOs 4
Utils util = Utils();
float cameraX, cameraY, cameraZ;
float torLocX, torLocY, torLocZ;
GLuint renderingProgram, renderingProgramCubeMap;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint skyboxTexture;
float rotAmt = 0.0f;
// variable allocation for display
GLuint vLoc, mvLoc, projLoc, nLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat, invTrMat;
Torus myTorus(0.8f, 0.4f, 48);
int numTorusVertices, numTorusIndices;
void setupVertices(void) {
float cubeVertexPositions[108] =
{ -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f
};
numTorusVertices = myTorus.getNumVertices();
numTorusIndices = myTorus.getNumIndices();
std::vector<int> ind = myTorus.getIndices();
std::vector<glm::vec3> vert = myTorus.getVertices();
std::vector<glm::vec2> tex = myTorus.getTexCoords();
std::vector<glm::vec3> norm = myTorus.getNormals();
std::vector<float> pvalues;
std::vector<float> tvalues;
std::vector<float> nvalues;
for (int i = 0; i < numTorusVertices; i++) {
pvalues.push_back(vert[i].x);
pvalues.push_back(vert[i].y);
pvalues.push_back(vert[i].z);
tvalues.push_back(tex[i].s);
tvalues.push_back(tex[i].t);
nvalues.push_back(norm[i].x);
nvalues.push_back(norm[i].y);
nvalues.push_back(norm[i].z);
}
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
glGenBuffers(numVBOs, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertexPositions), cubeVertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW);
}
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
renderingProgramCubeMap = Utils::createShaderProgram("vertCShader.glsl", "fragCShader.glsl");
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f);
setupVertices();
skyboxTexture = Utils::loadCubeMap("cubeMap"); // expects a folder name
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
torLocX = 0.0f; torLocY = 0.0f; torLocZ = 0.0f;
cameraX = 0.0f; cameraY = 0.0f; cameraZ = 5.0f;
}
void display(GLFWwindow* window, double currentTime) {
glClear(GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
// draw cube map
glUseProgram(renderingProgramCubeMap);
vLoc = glGetUniformLocation(renderingProgramCubeMap, "v_matrix");
glUniformMatrix4fv(vLoc, 1, GL_FALSE, glm::value_ptr(vMat));
projLoc = glGetUniformLocation(renderingProgramCubeMap, "p_matrix");
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW); // cube is CW, but we are viewing the inside
glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_DEPTH_TEST);
// draw scene (in this case it is just a torus)
glUseProgram(renderingProgram);
mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
nLoc = glGetUniformLocation(renderingProgram, "normalMat");
rotAmt += 0.01f;
mMat = glm::translate(glm::mat4(1.0f), glm::vec3(torLocX, torLocY, torLocZ));
mMat = glm::rotate(mMat, rotAmt, glm::vec3(1.0f, 0.0f, 0.0f));
mvMat = vMat * mMat;
invTrMat = glm::transpose(glm::inverse(mvMat));
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glDepthFunc(GL_LEQUAL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glDrawElements(GL_TRIANGLES, numTorusIndices, GL_UNSIGNED_INT, 0);
}
void window_size_callback(GLFWwindow* win, int newWidth, int newHeight) {
aspect = (float)newWidth / (float)newHeight;
glViewport(0, 0, newWidth, newHeight);
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f);
}
int main(void) {
if (!glfwInit()) { exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(800, 800, "Chapter9 - program2", NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
glfwSetWindowSizeCallback(window, window_size_callback);
init(window);
while (!glfwWindowShouldClose(window)) {
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
二、着色器程序
1.顶点着色器
#version 430
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
out vec3 vNormal;
out vec3 vVertPos;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 normalMat;
layout (binding = 0) uniform samplerCube t;
void main(void)
{
vVertPos = (mv_matrix * vec4(position,1.0)).xyz;
vNormal = (normalMat * vec4(normal,1.0)).xyz;
gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);
}
#version 430
layout (location = 0) in vec3 position;
out vec3 tc;
uniform mat4 v_matrix;
uniform mat4 p_matrix;
layout (binding = 0) uniform samplerCube samp;
void main(void)
{
tc = position;
mat4 v3_matrix = mat4(mat3(v_matrix));
gl_Position = p_matrix * v3_matrix * vec4(position,1.0);
}
2.片元着色器
#version 430
in vec3 vNormal;
in vec3 vVertPos;
out vec4 fragColor;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 normalMat;
layout (binding = 0) uniform samplerCube t;
void main(void)
{
vec3 r = -reflect(normalize(-vVertPos), normalize(vNormal));
fragColor = texture(t,r);
}
#version 430
in vec3 tc;
out vec4 fragColor;
uniform mat4 v_matrix;
uniform mat4 p_matrix;
layout (binding = 0) uniform samplerCube samp;
void main(void)
{
fragColor = texture(samp,tc);
}
运行效果
总结
虽然该场景需要两组着色器—— 一组用于立方体贴图,另一组用于环面——但是程序9.3
中仅展示了用于绘制环面的着色器。这是因为用于渲染立方体贴图的着色器与程序9.2 中的
相同。通过对程序 的修改得到程序 的过程,总结如下。
在init()函数中:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)