这一小节,我们要实现模型的平移、缩放、旋转基础变换。
实现一个模型类:
TriMesh.h:
#ifndef _TRI_MESH_H_
#define _TRI_MESH_H_
#include "Angel.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
// 三角面片中的顶点序列
typedef struct vIndex {
unsigned int x, y, z;
vIndex(int ix, int iy, int iz) : x(ix), y(iy), z(iz) {}
} vec3i;
class TriMesh
{
public:
TriMesh();
~TriMesh();
std::vector<vec3> getVertexPositions();
std::vector<vec3> getVertexColors();
std::vector<vec3i> getFaces();
std::vector<vec3> getPoints();
std::vector<vec3> getColors();
// 这里添加各种形状的、模型的读取顶点的函数
void generateCube();
void readOff(const std::string& filename);
// 将读取的顶点根据三角面片上的顶点下标逐个加入
// 要传递给GPU的points等容器内
void storeFacesPoints();
// 清除数据
void cleanData();
private:
std::vector<vec3> vertex_positions; // 顶点坐标
std::vector<vec3> vertex_colors; // 顶点颜色
std::vector<vec3i> faces; // 三角面片上每个顶点对应的下标
std::vector<vec3> points; // 传入着色器的绘制点
std::vector<vec3> colors; // 传入着色器的颜色
};
#endif
TriMesh.cpp:
#include "TriMesh.h"
// 一些基础颜色
const vec3 basic_colors[8] = {
vec3(1.0, 1.0, 1.0), // White
vec3(1.0, 1.0, 0.0), // Yellow
vec3(0.0, 1.0, 0.0), // Green
vec3(0.0, 1.0, 1.0), // Cyan
vec3(1.0, 0.0, 1.0), // Magenta
vec3(1.0, 0.0, 0.0), // Red
vec3(0.0, 0.0, 0.0), // Black
vec3(0.0, 0.0, 1.0) // Blue
};
// 立方体的各个点
const vec3 cube_vertices[8] = {
vec3(-0.5, -0.5, -0.5),
vec3(0.5, -0.5, -0.5),
vec3(-0.5, 0.5, -0.5),
vec3(0.5, 0.5, -0.5),
vec3(-0.5, -0.5, 0.5),
vec3(0.5, -0.5, 0.5),
vec3(-0.5, 0.5, 0.5),
vec3(0.5, 0.5, 0.5)
};
TriMesh::TriMesh()
{
}
TriMesh::~TriMesh()
{
}
std::vector<vec3> TriMesh::getVertexPositions()
{
return vertex_positions;
}
std::vector<vec3> TriMesh::getVertexColors()
{
return vertex_colors;
}
std::vector<vec3i> TriMesh::getFaces()
{
return faces;
}
std::vector<vec3> TriMesh::getPoints()
{
return points;
}
std::vector<vec3> TriMesh::getColors()
{
return colors;
}
void TriMesh::cleanData() {
vertex_positions.clear();
vertex_colors.clear();
faces.clear();
points.clear();
colors.clear();
}
void TriMesh::storeFacesPoints() {
int u = 0;
// 根据每个三角面片的顶点下标存储要传入GPU的数据
for (int i = 0; i < 12; ++i) {
points.push_back(vertex_positions[faces[i].x]);
points.push_back(vertex_positions[faces[i].y]);
points.push_back(vertex_positions[faces[i].z]);
colors.push_back(basic_colors[u]);
colors.push_back(basic_colors[u]);
colors.push_back(basic_colors[u]);
if ((i+1) % 2 == 0) ++u;
}
}
// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube() {
// 创建顶点前要先把那些vector清空
cleanData();
// 存储立方体的各个面信息
for (int i = 0; i < 8; ++i) {
vertex_positions.push_back(cube_vertices[i]);
}
faces.push_back(vec3i(6, 4, 7));
faces.push_back(vec3i(4, 5, 7));
faces.push_back(vec3i(0, 2, 3));
faces.push_back(vec3i(0, 3, 1));
faces.push_back(vec3i(7, 5, 3));
faces.push_back(vec3i(5, 1, 3));
faces.push_back(vec3i(4, 6, 2));
faces.push_back(vec3i(4, 2, 0));
faces.push_back(vec3i(6, 7, 2));
faces.push_back(vec3i(7, 3, 2));
faces.push_back(vec3i(4, 0, 5));
faces.push_back(vec3i(5, 0, 1));
int u = 0;
for (int i = 0; i < 12; ++i) {
for (int j = 0; j < 3; ++j) {
vertex_colors.push_back(basic_colors[u]);
}
if (i % 2 == 0) {
++u;
}
}
storeFacesPoints();
}
void TriMesh::readOff(const std::string& filename)
{
// fin打开文件读取文件信息
if (filename.empty())
{
return;
}
std::ifstream fin;
fin.open(filename);
// 读取OFF文件中三维模型的信息
if (!fin)
{
printf("File on error\n");
return;
}
else
{
printf("File open success\n");
cleanData();
int nVertices, nFaces, nEdges;
// 读取OFF字符串
std::string str;
fin >> str;
// 读取文件中顶点数、面片数、边数
fin >> nVertices >> nFaces >> nEdges;
// 根据顶点数,循环读取每个顶点坐标
for (int i = 0; i < nVertices; i++)
{
vec3 tmp_node;
fin >> tmp_node.x >> tmp_node.y >> tmp_node.z;
vertex_positions.push_back(tmp_node);
// vertex_colors.push_back(tmp_node);
}
// 根据面片数,循环读取每个面片信息,并用构建的vec3i结构体保存
for (int i = 0; i < nFaces; i++)
{
int num, a, b, c;
// num记录此面片由几个顶点构成,a、b、c为构成该面片顶点序号
fin >> num >> a >> b >> c;
faces.push_back(vec3i(a, b, c));
}
}
fin.close();
storeFacesPoints();
};
main.cpp:
#include "Angel.h"
#include "mat.h"
#include "vec.h"
#include "TriMesh.h"
#include <vector>
#include <string>
const int X_AXIS = 0;
const int Y_AXIS = 1;
const int Z_AXIS = 2;
const int TRANSFORM_SCALE = 0;
const int TRANSFORM_ROTATE = 1;
const int TRANSFORM_TRANSLATE = 2;
const double DELTA_DELTA = 0.3; // Delta的变化率
const double DEFAULT_DELTA = 0.5; // 默认的Delta值
double scaleDelta = DEFAULT_DELTA;
double rotateDelta = DEFAULT_DELTA;
double translateDelta = DEFAULT_DELTA;
vec3 scaleTheta(1.0, 1.0, 1.0); // 缩放控制变量
vec3 rotateTheta(0.0, 0.0, 0.0); // 旋转控制变量
vec3 translateTheta(0.0, 0.0, 0.0); // 平移控制变量
int currentTransform = TRANSFORM_ROTATE; // 设置当前变换
int mainWindow;
struct openGLObject
{
// 顶点数组对象
GLuint vao;
// 顶点缓存对象
GLuint vbo;
// 着色器程序
GLuint program;
// 着色器文件
std::string vshader;
std::string fshader;
// 着色器变量
GLuint pLocation;
GLuint cLocation;
GLuint matrixLocation;
};
openGLObject cube_object;
TriMesh *cube = new TriMesh();
void bindObjectAndData(TriMesh* mesh, openGLObject& object, const std::string &vshader, const std::string &fshader) {
// 创建顶点数组对象
#ifdef __APPLE__ // for MacOS
glGenVertexArraysAPPLE(1, &object.vao); // 分配1个顶点数组对象
glBindVertexArrayAPPLE(object.vao); // 绑定顶点数组对象
#else // for Windows
glGenVertexArrays(1, &object.vao); // 分配1个顶点数组对象
glBindVertexArray(object.vao); // 绑定顶点数组对象
#endif
// 创建并初始化顶点缓存对象
glGenBuffers(1, &object.vbo);
glBindBuffer(GL_ARRAY_BUFFER, object.vbo);
glBufferData(GL_ARRAY_BUFFER,
mesh->getPoints().size() * sizeof(vec3) + mesh->getColors().size() * sizeof(vec3),
NULL,
GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh->getPoints().size() * sizeof(vec3), &mesh->getPoints()[0]);
glBufferSubData(GL_ARRAY_BUFFER, mesh->getPoints().size() * sizeof(vec3), mesh->getColors().size() * sizeof(vec3), &mesh->getColors()[0]);
object.vshader = vshader;
object.fshader = fshader;
object.program = InitShader(object.vshader.c_str(), object.fshader.c_str());
// 从顶点着色器中初始化顶点的位置
object.pLocation = glGetAttribLocation(object.program, "vPosition");
glEnableVertexAttribArray(object.pLocation);
glVertexAttribPointer(object.pLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// 从顶点着色器中初始化顶点的颜色
object.cLocation = glGetAttribLocation(object.program, "vColor");
glEnableVertexAttribArray(object.cLocation);
glVertexAttribPointer(object.cLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(mesh->getPoints().size() * sizeof(vec3)));
// 获得矩阵存储位置
object.matrixLocation = glGetUniformLocation(object.program, "matrix");
}
void init()
{
std::string vshader, fshader;
// 读取着色器并使用
#ifdef __APPLE__ // for MacOS
vshader = "shaders/vshader_mac.glsl";
fshader = "shaders/fshader_mac.glsl";
#else // for Windows
vshader = "shaders/vshader_win.glsl";
fshader = "shaders/fshader_win.glsl";
#endif
cube->generateCube();
bindObjectAndData(cube, cube_object, vshader, fshader);
// 黑色背景
glClearColor(0.0, 0.0, 0.0, 1.0);
}
void display()
{
// 清理窗口
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(cube_object.program);
#ifdef __APPLE__
glBindVertexArrayAPPLE(cube_object.vao);
#else
glBindVertexArray(cube_object.vao);
#endif
// 初始化变换矩阵
mat4 m(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
// 计算最终的变换矩阵
// 调用函数传入三种变化的变化量,累加得到变化矩阵
// 注意三种变化累加的顺序
m = m * Translate(translateTheta.x, translateTheta.y, translateTheta.z);
m = m * RotateX(rotateTheta.x);
m = m * RotateY(rotateTheta.y);
m = m * RotateZ(rotateTheta.z);
m = m * Scale(scaleTheta.x, scaleTheta.y, scaleTheta.z);
// 从指定位置matrixLocation中传入变换矩阵m
glUniformMatrix4fv(cube_object.matrixLocation, 1, GL_TRUE, m);
// 绘制立方体中的各个三角形
glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
glutSwapBuffers();
}
// 通过Delta值更新Theta
void updateTheta(int axis, int sign) {
switch (currentTransform) {
// 根据变换类型,增加或减少某种变换的变化量
case TRANSFORM_SCALE:
scaleTheta[axis] += sign * scaleDelta;
break;
case TRANSFORM_ROTATE:
rotateTheta[axis] += sign * rotateDelta;
break;
case TRANSFORM_TRANSLATE:
translateTheta[axis] += sign * translateDelta;
break;
}
}
// 复原Theta和Delta
void resetTheta()
{
scaleTheta = vec3(1.0, 1.0, 1.0);
rotateTheta = vec3(0.0, 0.0, 0.0);
translateTheta = vec3(0.0, 0.0, 0.0);
scaleDelta = DEFAULT_DELTA;
rotateDelta = DEFAULT_DELTA;
translateDelta = DEFAULT_DELTA;
}
// 更新变化Delta值
void updateDelta(int sign)
{
switch (currentTransform) {
// 根据变化类型增加或减少每一次变化的单位变化量
case TRANSFORM_SCALE:
scaleDelta += sign * DELTA_DELTA;
break;
case TRANSFORM_ROTATE:
rotateDelta += sign * DELTA_DELTA;
break;
case TRANSFORM_TRANSLATE:
translateDelta += sign * DELTA_DELTA;
break;
}
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'q':
updateTheta(X_AXIS, 1);
break;
case 'a':
updateTheta(X_AXIS, -1);
break;
case 'w':
updateTheta(Y_AXIS, 1);
break;
case 's':
updateTheta(Y_AXIS, -1);
break;
case 'e':
updateTheta(Z_AXIS, 1);
break;
case 'd':
updateTheta(Z_AXIS, -1);
break;
case 'r':
updateDelta(1);
break;
case 'f':
updateDelta(-1);
break;
case 't':
resetTheta();
break;
case 'l':
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 'L':
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 033:
// Esc按键
exit(EXIT_SUCCESS);
break;
}
// 标记主窗口mianWindow重绘
glutPostWindowRedisplay(mainWindow);
}
void menuEvents(int menuChoice)
{
currentTransform = menuChoice;
}
void setupMenu()
{
glutCreateMenu(menuEvents);
glutAddMenuEntry("Scale", TRANSFORM_SCALE);
glutAddMenuEntry("Rotate", TRANSFORM_ROTATE);
glutAddMenuEntry("Translate", TRANSFORM_TRANSLATE);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
void printHelp() {
printf("%s\n\n", "3D Transfomations");
printf("Keyboard options:\n");
printf("q: Increase x\n");
printf("a: Decrease x\n");
printf("w: Increase y\n");
printf("s: Decrease y\n");
printf("e: Increase z\n");
printf("d: Decrease z\n");
printf("r: Increase delta of currently selected transform\n");
printf("f: Decrease delta of currently selected transform\n");
printf("t: Reset all transformations and deltas\n");
}
void cleanData() {
cube->cleanData();
// 释放内存
delete cube;
cube = NULL;
// 删除绑定的对象
#ifdef __APPLE__
glDeleteVertexArraysAPPLE(1, &cube_object.vao);
#else
glDeleteVertexArrays(1, &cube_object.vao);
#endif
glDeleteBuffers(1, &cube_object.vbo);
glDeleteProgram(cube_object.program);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
// 窗口支持双重缓冲、深度测试、超采样
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(600, 600);
mainWindow = glutCreateWindow("3D Transfomations");
#ifdef __APPLE__
#else
glewExperimental = GL_TRUE;
glewInit();
#endif
init();
setupMenu();
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
// 输出帮助信息
printHelp();
// 启用深度测试
glEnable(GL_DEPTH_TEST);
glutMainLoop();
cleanData();
return 0;
}
顶点着色器vshader.glsl:
#version 330 core
in vec3 vPosition;
in vec3 vColor;
out vec3 color;
uniform mat4 matrix;
void main()
{
// 将变换矩阵作用在各个顶点上
gl_Position = matrix * vec4(vPosition, 1.0);
color = vColor;
}
片元着色器fshader.glsl:
#version 330 core
in vec3 color;
out vec4 fColor;
void main()
{
fColor = vec4(color, 1.0);
}