openGL系列文章目录
前言
假设我们想要对不规则表面的物体进行建模,例如橘子凹凸的表皮、葡萄干褶皱的表面
或月球的陨石坑表面。我们该怎么做?到目前为止,我们已经学会了两种可能的方法:(a)
我们可以对整个不规则表面进行建模,但这么做通常不切实际(一个有许多坑的表面需要
大量的顶点);(b)我们可以将不规则表面的纹理图图像应用于平滑的对象。第二种选择通
常比较高效。但是,如果场景中有光源,当光源(或摄像机角度)移动时,我们很快就会
发现物体使用了静态纹理渲染(以及物体表面是平滑的),因为纹理上的亮区和暗区不会像
真正凹凸不平的表面那样,随着光源或摄像机移动而改变。
在本章中,我们将探讨几种与实现凹凸表面相关的方法,通过使用光照效果,即使在实
际对象模型表面平滑的情况下,也能使对象看起来具有逼真的表面纹理。我们将首先观察
凹凸贴图和法线贴图,当直接为对象添加微小表面细节会使得计算代价过高时,它们可以
为场景中的对象增加相当程度的真实感。我们还将研究通过高度贴图实际扰乱光滑表面中
顶点的方法,这对于生成地形(和其他一些用途)非常有用。
一、凹凸贴图
,我们了解了表面法向量在创建令人信服的光照效果中是至关重要的。像素处
的光强度主要由反射角确定,即需要考虑到光源位置、相机位置和像素处的法向量。因此,如
果我们能找到生成相应法向量的方法,就可以避免生成与凹凸不平或褶皱表面相对应的顶点。
图1 展示了对于单个“凸起”修改法向量的概念。图1 用于凹凸贴图的扰动法向量异
图1
因此,如果我们想让一个物体看起来好像有凹凸(或皱纹,陨石坑等),一种方法是计
算当表面确实凹凸不平时其上的法向量。当场景点亮时,光照会让人产生我们所期望的幻
觉。这是Blinn 在1978 年首次提出的[BL78],随着在片段着色器拥有了可以对每个像素进行
光照计算的能力,这种方法就变得切实可行了。
程序10.1 中展示了顶点着色器和片段着色器的一个示例,这段程序会生成一个带有“高
尔夫球”表面的环面,如图2 所示。其代码几乎与我们之前在程序中看到的相同。片
段着色器中唯一显著的变化是——输入的已插值法向量(在原程序中名为“varyingNormal”)
在这里变得凹凸不平了,其方法是对环面模型的原始(未变形)顶点的X、Y 和Z 轴应用正弦
函数。请注意,这里需要顶点着色器将未经变换的顶点沿管线传递给片段着色器。以这种方式对法向量进行改变,即在运行时使用数学函数进行计算,称为过程式凹凸贴图。
图2
二、代码
1.主程序c++
#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
float cameraX, cameraY, cameraZ;
float torLocX, torLocY, torLocZ;
float lightLocX, lightLocY, lightLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
// variable allocation for display
GLuint mvLoc, projLoc, nLoc;
int width, height;
float aspect;
glm::mat4 pMat, vMat, mMat, mvMat, invTrMat;
GLuint globalAmbLoc, ambLoc, diffLoc, specLoc, posLoc, mambLoc, mdiffLoc, mspecLoc, mshiLoc;
glm::vec3 currentLightPos;
float lightPos[3];
Torus myTorus(0.5f, 0.2f, 48);
int numTorusVertices, numTorusIndices;
// white light
float globalAmbient[4] = { 0.7f, 0.7f, 0.7f, 1.0f };
float lightAmbient[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
float lightDiffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
float lightSpecular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
// gold material
float* matAmb = Utils::goldAmbient();
float* matDif = Utils::goldDiffuse();
float* matSpe = Utils::goldSpecular();
float matShi = Utils::goldShininess();
void setupVertices(void) {
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 < myTorus.getNumVertices(); 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, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[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 installLights(glm::mat4 vMatrix) {
glm::vec3 transformed = glm::vec3(vMatrix * glm::vec4(currentLightPos, 1.0));
lightPos[0] = transformed.x;
lightPos[1] = transformed.y;
lightPos[2] = transformed.z;
// get the locations of the light and material fields in the shader
globalAmbLoc = glGetUniformLocation(renderingProgram, "globalAmbient");
ambLoc = glGetUniformLocation(renderingProgram, "light.ambient");
diffLoc = glGetUniformLocation(renderingProgram, "light.diffuse");
specLoc = glGetUniformLocation(renderingProgram, "light.specular");
posLoc = glGetUniformLocation(renderingProgram, "light.position");
mambLoc = glGetUniformLocation(renderingProgram, "material.ambient");
mdiffLoc = glGetUniformLocation(renderingProgram, "material.diffuse");
mspecLoc = glGetUniformLocation(renderingProgram, "material.specular");
mshiLoc = glGetUniformLocation(renderingProgram, "material.shininess");
// set the uniform light and material values in the shader
glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
glProgramUniform4fv(renderingProgram, specLoc, 1, lightSpecular);
glProgramUniform3fv(renderingProgram, posLoc, 1, lightPos);
glProgramUniform4fv(renderingProgram, mambLoc, 1, matAmb);
glProgramUniform4fv(renderingProgram, mdiffLoc, 1, matDif);
glProgramUniform4fv(renderingProgram, mspecLoc, 1, matSpe);
glProgramUniform1f(renderingProgram, mshiLoc, matShi);
}
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");
cameraX = 0.0f; cameraY = 0.0f; cameraZ = 1.0f;
torLocX = 0.0f; torLocY = 0.0f; torLocZ = -1.0f;
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f);
lightLocX = 5.0f; lightLocY = 2.0f; lightLocZ = 2.0f;
setupVertices();
}
void display(GLFWwindow* window, double currentTime) {
glClear(GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(renderingProgram);
mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
nLoc = glGetUniformLocation(renderingProgram, "norm_matrix");
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
mMat = glm::translate(glm::mat4(1.0f), glm::vec3(torLocX, torLocY, torLocZ));
mMat = glm::rotate(mMat, toRadians(35.0f), glm::vec3(1.0f, 0.0f, 0.0f));
mvMat = vMat * mMat;
invTrMat = glm::transpose(glm::inverse(mvMat));
currentLightPos = glm::vec3(lightLocX, lightLocY, lightLocZ);
installLights(vMat);
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[0]);
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);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glDrawElements(GL_TRIANGLES, myTorus.getIndices().size(), 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, "Chapter6 - 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);
}
## 2.着色器程序
1.顶点着色器
```cpp
#version 430
layout (location = 0) in vec3 vertPos;
layout (location = 1) in vec3 vertNormal;
out vec3 varyingNormal;
out vec3 varyingLightDir;
out vec3 varyingVertPos;
out vec3 originalVertex;
struct PositionalLight
{ vec4 ambient;
vec4 diffuse;
vec4 specular;
vec3 position;
};
struct Material
{ vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
};
uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;
void main(void)
{ varyingVertPos = (mv_matrix * vec4(vertPos,1.0)).xyz;
varyingLightDir = light.position - varyingVertPos;
varyingNormal = (norm_matrix * vec4(vertNormal,1.0)).xyz;
originalVertex = vertPos;
gl_Position = proj_matrix * mv_matrix * vec4(vertPos,1.0);
}
2.片元着色器
#version 430
in vec3 varyingNormal;
in vec3 varyingLightDir;
in vec3 varyingVertPos;
in vec3 originalVertex;
out vec4 fragColor;
struct PositionalLight
{ vec4 ambient;
vec4 diffuse;
vec4 specular;
vec3 position;
};
struct Material
{ vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
};
uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;
void main(void)
{ // normalize the light, normal, and view vectors:
vec3 L = normalize(varyingLightDir);
vec3 N = normalize(varyingNormal);
vec3 V = normalize(-varyingVertPos);
float a = 0.25; // controls depth of bumps
float b = 100.0; // controls width of bumps
float x = originalVertex.x;
float y = originalVertex.y;
float z = originalVertex.z;
N.x = varyingNormal.x + a*sin(b*x);
N.y = varyingNormal.y + a*sin(b*y);
N.z = varyingNormal.z + a*sin(b*z);
N = normalize(N);
// compute light reflection vector, with respect N:
vec3 R = normalize(reflect(-L, N));
// get the angle between the light and surface normal:
float cosTheta = dot(L,N);
// angle between the view vector and reflected light:
float cosPhi = dot(V,R);
// compute ADS contributions (per pixel):
fragColor = globalAmbient * material.ambient
+ light.ambient * material.ambient
+ light.diffuse * material.diffuse * max(cosTheta,0.0)
+ light.specular * material.specular *
pow(max(cosPhi,0.0), material.shininess);
}