【计算机图形学-10】OpenGL程序实例分析2——模型自变换

这一小节,我们要实现模型的平移、缩放、旋转基础变换。

实现一个模型类:
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);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用OpenGL绘制国旗可以通过以下步骤实现: 1. 创建OpenGL上下文和窗口。 2. 设置绘制区域和视口。 3. 设置绘制颜色和深度缓冲区。 4. 绘制国旗的红色背景。 5. 绘制五颗黄色的五角星。可以使用OpenGL的绘制多边形功能来绘制五角星,也可以使用纹理映射来贴上五角星的图案。 6. 交换前后缓冲区,显示绘制结果。 以下是一个简单的OpenGL绘制国旗的示例代码: ``` #include <GL/glut.h> void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(1.0, 0.0, 0.0); // 设置绘制颜色为红色 glBegin(GL_QUADS); glVertex2f(-1.0, 1.0); glVertex2f(-1.0, -1.0); glVertex2f(1.0, -1.0); glVertex2f(1.0, 1.0); // 绘制国旗的红色背景 glEnd(); glColor3f(1.0, 1.0, 0.0); // 设置绘制颜色为黄色 glBegin(GL_POLYGON); glVertex2f(-0.5, 0.5); glVertex2f(-0.25, 0.25); glVertex2f(0.0, 0.5); glVertex2f(0.25, 0.25); glVertex2f(0.5, 0.5); glVertex2f(0.375, 0.25); glVertex2f(0.5, 0.0); glVertex2f(0.375, -0.25); glVertex2f(0.5, -0.5); glVertex2f(0.25, -0.375); glVertex2f(0.0, -0.5); glVertex2f(-0.25, -0.375); glVertex2f(-0.5, -0.5); glVertex2f(-0.375, -0.25); glVertex2f(-0.5, 0.0); glVertex2f(-0.375, 0.25); // 绘制五角星 glEnd(); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(800, 600); glutInitWindowPosition(100, 100); glutCreateWindow("OpenGL Flag"); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 该示例代码使用了OpenGL的基本绘图功能,通过绘制红色背景和黄色五角星实现了绘制国旗的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值