为了 使用 实例 化 来 重复 我们 以前 的 翻滚 立方体 示例, 我们 需要 将 构建 不同 模型 矩阵 的 计算[ 先前 在 display() 中的 循环 内 实现] 移动 到顶 点 着色 器 中。 由于 GLSL 不提 供 平移 或 旋转 函数, 并且 我们 无法 从 着色 器 内部 调用 GLM, 因此 我们 需要 使用 工具 函数。 我们 还需 要将 C++/ OpenGL 应用 程序 中的“ 时间 因子” 通过 统一 变量 传递 给 顶点 着色 器。 我们 还需 要将 模型 和 视图 矩阵 传递 到 单独 的 统一 变量 中, 因为 对 每个 立方体 的 模型 矩阵 都 需要 进行 旋转 计算。
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include "Utils.h"
using namespace std;
#define numVAOs 1
#define numVBOs 2
Utils util = Utils();
float cameraX, cameraY, cameraZ;
float cubeLocX, cubeLocY, cubeLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
// 分配在display()函数中使用的变量空间,这样它们就不必在渲染过程中分配
GLuint mLoc,vLoc, projLoc,tfLoc;
int width, height;
float aspect, timeFactor;
glm::mat4 pMat, vMat, mMat, mvMat;
void setupVertices(void) { //36个顶点,12个三角形,组成了放置在原点处的2×2×2立方体
float vertexPositions[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
};
glGenVertexArrays(1, vao);//创建顶点数组对象VAO, //数据集发送给管线时,是以缓冲区形式发送的
glBindVertexArray(vao[0]);//将指定的VAO标记为活跃,生成的缓冲区和这个VAO关联
glGenBuffers(numVBOs, vbo);//创建VBO.//参数1,创建多少个ID,参数2,保存返回的ID的数组
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//将缓冲区【0】标记为活跃
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);//将包含顶点数据的数组复制进活跃缓冲区
}
//init()函数负责执行只需要一次的任务
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");//获取GL程序的ID,该程序创建着色器
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;//屏幕纵横比.透视矩阵需要的参数
//构建透视矩阵//根据所需的视锥提供3D效果,这里放在了init里,因为只需要构建一次
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f);//构建透视矩阵//参数 1. 指定视场角(这里是60°,对应弧度是1.0472)
//,2. 屏幕纵横比 ,3.近裁剪平面,4.远裁剪平面
cameraX = 0.0f; cameraY = 0.0f; cameraZ = 420.0f;//相机放在z轴的下方,z=32对应24个实例,420对应100000个实例
setupVertices();//将顶点数据复制进缓冲区
}
//调用display函数的速率被称为帧率
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);//将含有两个已编译着色器的程序载入OpenGL管线阶段(在GPU上!)
//glUseProgram没有运行着色器,它只是将着色器加载进硬件
//着色器需要视图矩阵的统一变量
//获取V矩阵和投影矩阵的统一变量// m/模型矩阵在世界坐标空间中表示对象的位置和朝向
vLoc = glGetUniformLocation(renderingProgram, "v_matrix");//获取着色器程序中V矩阵统一变量的位置
//mLoc = glGetUniformLocation(renderingProgram, "m_matrix");
projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");//获取着色器程序中投影矩阵统一变量的位置
//构建MV矩阵 其中,mMat的计算被移动到顶点着色器中去了
//在C++应用程序中不再需要构建MV矩阵
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));//v,视图,相机,变换矩阵
//将矩阵和时间因子复制给相应的统一变量
glUniformMatrix4fv(vLoc, 1, GL_FALSE, glm::value_ptr(vMat));//将矩阵数据发送到统一变量中,value_ptr返回的是对矩阵数据的引用
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
timeFactor = ((float)currentTime);//为了获得时间因子信息
tfLoc = glGetUniformLocation(renderingProgram, "tf");//获取着色器中时间因子的同一变量
glUniform1f(tfLoc, (float)timeFactor);
//将VBO关联给顶点着色器中相应的顶点属性
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
//调整OpenGL设置,绘制模型
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawArraysInstanced(GL_TRIANGLES, 0, 36,100000);//0,36,24/100000
}
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(600, 600, "Chapter 4 - program 2", 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);
}
``#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <glm\gtc\matrix_transform.hpp>
class Utils
{
private:
static std::string readShaderFile(const char *filePath);
static void printShaderLog(GLuint shader);
static void printProgramLog(int prog);
static GLuint prepareShader(int shaderTYPE, const char *shaderPath);
static int finalizeShaderProgram(GLuint sprogram);
public:
Utils();
static bool checkOpenGLError();
static GLuint createShaderProgram(const char *vp, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *gp, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp);
static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp);
static GLuint loadTexture(const char *texImagePath);
static GLuint loadCubeMap(const char *mapDir);
static float* goldAmbient();
static float* goldDiffuse();
static float* goldSpecular();
static float goldShininess();
static float* silverAmbient();
static float* silverDiffuse();
static float* silverSpecular();
static float silverShininess();
static float* bronzeAmbient();
static float* bronzeDiffuse();
static float* bronzeSpecular();
static float bronzeShininess();
};
```cpp
#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp> // glm::value_ptr
#include <glm\gtc\matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include "Utils.h"
using namespace std;
Utils::Utils() {}
string Utils::readShaderFile(const char *filePath) {
string content;
ifstream fileStream(filePath, ios::in);
string line = " ";
while (!fileStream.eof()) {
getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
bool Utils::checkOpenGLError() {
bool foundError = false;
int glErr = glGetError();
while (glErr != GL_NO_ERROR) {
cout << "glError: " << glErr << endl;
foundError = true;
glErr = glGetError();
}
return foundError;
}
void Utils::printShaderLog(GLuint shader) {
int len = 0;
int chWrittn = 0;
char *log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char *)malloc(len);
glGetShaderInfoLog(shader, len, &chWrittn, log);
cout << "Shader Info Log: " << log << endl;
free(log);
}
}
GLuint Utils::prepareShader(int shaderTYPE, const char *shaderPath)
{ GLint shaderCompiled;
string shaderStr = readShaderFile(shaderPath);
const char *shaderSrc = shaderStr.c_str();//获取着色器信息的字符串
GLuint shaderRef = glCreateShader(shaderTYPE);//创建着色器
glShaderSource(shaderRef, 1, &shaderSrc, NULL);//将字符串复制进着色器中
glCompileShader(shaderRef);//编译着色器
checkOpenGLError();
glGetShaderiv(shaderRef, GL_COMPILE_STATUS, &shaderCompiled);
if (shaderCompiled != 1)
{ if (shaderTYPE == 35633) cout << "Vertex ";
if (shaderTYPE == 36488) cout << "Tess Control ";
if (shaderTYPE == 36487) cout << "Tess Eval ";
if (shaderTYPE == 36313) cout << "Geometry ";
if (shaderTYPE == 35632) cout << "Fragment ";
cout << "shader compilation error." << endl;
printShaderLog(shaderRef);
}
return shaderRef;//返回程序的ID
}
void Utils::printProgramLog(int prog) {
int len = 0;
int chWrittn = 0;
char *log;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char *)malloc(len);
glGetProgramInfoLog(prog, len, &chWrittn, log);
cout << "Program Info Log: " << log << endl;
free(log);
}
}
int Utils::finalizeShaderProgram(GLuint sprogram)
{ GLint linked;
glLinkProgram(sprogram); //请求GLSL编译器确保他们的兼容性
checkOpenGLError();
glGetProgramiv(sprogram, GL_LINK_STATUS, &linked);
if (linked != 1)
{ cout << "linking failed" << endl;
printProgramLog(sprogram);
}
return sprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);//获取编译着色器后得到的着色器对象
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vfprogram = glCreateProgram();//创建程序对象
glAttachShader(vfprogram, vShader);//将着色器对象载入程序对象中
glAttachShader(vfprogram, fShader);
finalizeShaderProgram(vfprogram); //请求GLSL编译器确保他们的兼容性
return vfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *gp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vgfprogram = glCreateProgram();
glAttachShader(vgfprogram, vShader);
glAttachShader(vgfprogram, gShader);
glAttachShader(vgfprogram, fShader);
finalizeShaderProgram(vgfprogram);
return vgfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vtfprogram = glCreateProgram();
glAttachShader(vtfprogram, vShader);
glAttachShader(vtfprogram, tcShader);
glAttachShader(vtfprogram, teShader);
glAttachShader(vtfprogram, fShader);
finalizeShaderProgram(vtfprogram);
return vtfprogram;
}
GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vtgfprogram = glCreateProgram();
glAttachShader(vtgfprogram, vShader);
glAttachShader(vtgfprogram, tcShader);
glAttachShader(vtgfprogram, teShader);
glAttachShader(vtgfprogram, gShader);
glAttachShader(vtgfprogram, fShader);
finalizeShaderProgram(vtgfprogram);
return vtgfprogram;
}
GLuint Utils::loadCubeMap(const char *mapDir) {
GLuint textureRef;
string xp = mapDir; xp = xp + "/xp.jpg";
string xn = mapDir; xn = xn + "/xn.jpg";
string yp = mapDir; yp = yp + "/yp.jpg";
string yn = mapDir; yn = yn + "/yn.jpg";
string zp = mapDir; zp = zp + "/zp.jpg";
string zn = mapDir; zn = zn + "/zn.jpg";
textureRef = SOIL_load_OGL_cubemap(xp.c_str(), xn.c_str(), yp.c_str(), yn.c_str(), zp.c_str(), zn.c_str(),
SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);
if (textureRef == 0) cout << "didnt find cube map image file" << endl;
// glBindTexture(GL_TEXTURE_CUBE_MAP, textureRef);
// reduce seams
// 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 textureRef;
}
GLuint Utils::loadTexture(const char *texImagePath)
{ GLuint textureRef;
textureRef = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureRef == 0) cout << "didnt find texture file " << texImagePath << endl;
// ----- mipmap/anisotropic section
glBindTexture(GL_TEXTURE_2D, textureRef);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
GLfloat anisoset = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
}
// ----- end of mipmap/anisotropic section
return textureRef;
}
// GOLD material - ambient, diffuse, specular, and shininess
float* Utils::goldAmbient() { static float a[4] = { 0.2473f, 0.1995f, 0.0745f, 1 }; return (float*)a; }
float* Utils::goldDiffuse() { static float a[4] = { 0.7516f, 0.6065f, 0.2265f, 1 }; return (float*)a; }
float* Utils::goldSpecular() { static float a[4] = { 0.6283f, 0.5559f, 0.3661f, 1 }; return (float*)a; }
float Utils::goldShininess() { return 51.2f; }
// SILVER material - ambient, diffuse, specular, and shininess
float* Utils::silverAmbient() { static float a[4] = { 0.1923f, 0.1923f, 0.1923f, 1 }; return (float*)a; }
float* Utils::silverDiffuse() { static float a[4] = { 0.5075f, 0.5075f, 0.5075f, 1 }; return (float*)a; }
float* Utils::silverSpecular() { static float a[4] = { 0.5083f, 0.5083f, 0.5083f, 1 }; return (float*)a; }
float Utils::silverShininess() { return 51.2f; }
// BRONZE material - ambient, diffuse, specular, and shininess
float* Utils::bronzeAmbient() { static float a[4] = { 0.2125f, 0.1275f, 0.0540f, 1 }; return (float*)a; }
float* Utils::bronzeDiffuse() { static float a[4] = { 0.7140f, 0.4284f, 0.1814f, 1 }; return (float*)a; }
float* Utils::bronzeSpecular() { static float a[4] = { 0.3936f, 0.2719f, 0.1667f, 1 }; return (float*)a; }
float Utils::bronzeShininess() { return 25.6f; }
vertShader.glsl
#version 430
layout (location=0) in vec3 position;
uniform mat4 v_matrix;
uniform mat4 proj_matrix;
uniform float tf;//用于动画和放置立方体的时间因子
out vec4 varyingColor;
mat4 buildRotateX(float rad);//矩阵变换工具函数的声明
mat4 buildRotateY(float rad);//glsl要求函数先声明后调用
mat4 buildRotateZ(float rad);
mat4 buildTranslate(float x,float y,float z);
void main(void)
{
float i=gl_InstanceID+tf; //取值基于时间因子,但是对每个立方体示例也都不同的。
//float a=sin(0.35*i)*8.0; //这些是用来平移的x,y,z分量,24个的时候
//float b=sin(0.52*i)*8.0;
//float c=sin(0.70*i)*8.0;
float a=sin(203.0*i/8000.0)*403.0; //这些是用来平移的x,y,z分量
float b=sin(301.0*i/4001.0)*401.0;
float c=sin(400.0*i/6003.0)*405.0;
//构建旋转和平移矩阵,将会应用于当前立方体的模型矩阵
mat4 localRotX=buildRotateX(0.1*i);
mat4 localRotY=buildRotateY(0.1*i);
mat4 localRotZ=buildRotateZ(0.1*i);
mat4 localTrans=buildTranslate(a,b,c);
//构建模型矩阵,然后是模型-视图矩阵
mat4 newM_matrix=localTrans*localRotX*localRotY*localRotZ;
mat4 mv_matrix=v_matrix*newM_matrix;
gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);
varyingColor = vec4(position,1.0)*0.5+vec4(0.5,0.5,0.5,0.5);
}
//构建平移矩阵的工具函数(来自第三章)
mat4 buildTranslate(float x,float y,float z)
{
mat4 trans=mat4(1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
x,y,z,1.0);
return trans;
}
//用来绕x,y,z轴旋转的类似函数
mat4 buildRotateX(float rad)
{
mat4 trans=mat4(
1.0,0.0,0.0,0.0,
0.0,cos(rad),-sin(rad),0.0,
0.0,sin(rad),cos(rad),0.0,
0.0,0.0,0.0,1.0);
return trans;
}
mat4 buildRotateY(float rad)
{
mat4 trans=mat4(
cos(rad),0.0,sin(rad),0.0,
0.0,1.0,0.0,0.0,
-sin(rad),0.0,cos(rad),0.0,
0.0,0.0,0.0,1.0);
return trans;
}
mat4 buildRotateZ(float rad)
{
mat4 trans=mat4(
cos(rad),sin(rad),0.0,0.0,
-sin(rad),cos(rad),0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0);
return trans;
}
fragShader.glsl
#version 430
in vec4 varyingColor;
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
void main(void)
{ color = varyingColor;
}