【计算机图形学】2 三维模型读取与控制-(1) OFF格式三维模型文件的读取

该程序实现了读取OFF格式的三维模型文件,通过OpenGL进行渲染,并使用鼠标和键盘进行交互控制,包括模型的旋转、缩放和平移。同时,模型支持自定义颜色显示,并能进行360度自动旋转。
摘要由CSDN通过智能技术生成

【计算机图形学】2三维模型读取与控制-C++文档类资源-CSDN文库

目的与要求:

  1. 熟悉OpenGL 三维模型的读取与处理;理解三维模型的基本变换操作;掌握鼠标键盘交互控制逻辑;掌握着色器中uniform关键字的使用以及数据传输方法。
  2. OFF格式三维模型文件的读取:完成对OFF格式三维模型文件的读取与显示,可改变物体的显示颜色。
    /*main.cpp*/
    #include "Angel.h"
    
    #include <vector>
    #include <string>
    #include <fstream>
    #include <string>
    
    // #pragma comment(lib, "glew32.lib")
    
    // 三角面片中的顶点序列
    typedef struct vIndex {
    	unsigned int a, b, c;
    	vIndex(int ia, int ib, int ic) : a(ia), b(ib), c(ic) {}
    } vec3i;
    
    std::string filename;
    std::vector<vec3i> faces;
    
    int nVertices = 0;
    int nFaces = 0;
    int nEdges = 0;
    
    int Automatic_rotation = 0;
    
    const int X_AXIS = 0;
    const int Y_AXIS = 1;
    const int Z_AXIS = 2;
    
    const int ANIMATION_START = 0;
    const int ANIMATION_STOP = 1;
    const int ANIMATION_AXIS = 2; 
    const int TRANSFORM_SCALE = 5;
    const int TRANSFORM_ROTATE = 6;
    const int TRANSFORM_TRANSLATE = 7;
    const int SHOW_STATUS = 8;
    const int RESET = 9;
    
    int rotateAxis = Y_AXIS;	// 旋转轴,默认为y轴
    
    const double DELTA_DELTA = 0.1;		// Delta的变化率
    const double DEFAULT_DELTA = 0.3;	// 默认的Delta值
    
    double scaleDelta = DEFAULT_DELTA;
    double rotateDelta = DEFAULT_DELTA;
    double translateDelta = DEFAULT_DELTA;
    
    glm::vec3 scaleTheta(1.0, 1.0, 1.0);     // 缩放控制变量
    glm::vec3 rotateTheta(0.0, 0.0, 0.0);    // 旋转控制变量
    glm::vec3 translateTheta(0.0, 0.0, 0.0);	// 平移控制变量
    
    GLint matrixLocation;
    int currentTransform = TRANSFORM_TRANSLATE;	// 设置当前变换
    int mainWindow;
    
    // 模型中的各个点
    // const int NUM_VERTICES = 8;
    const int NUM_VERTICES = 2904;
    glm::vec3 vertices[NUM_VERTICES];
    
    void read_off(const std::string filename)
    {
    	if (filename.empty()) {
    		return;
    	}
    	std::ifstream fin;
    	fin.open(filename);
    
    	 TODO:	完成对OFF格式三维模型文件的读取:
    
    	fin.close();
    }
    
    void init()
    {
    	// 创建顶点数组对象
    	GLuint vao[1];
    	#ifdef __APPLE__	// for MacOS
    		glGenVertexArraysAPPLE(1, vao);
    		glBindVertexArrayAPPLE(vao[0]);
    	#else				// for Windows
    		glGenVertexArrays(1, vao);
    		glBindVertexArray(vao[0]);
    	#endif
    
    	// 创建并初始化顶点缓存对象
    	GLuint buffer;
    	glGenBuffers(1, &buffer);
    	glBindBuffer(GL_ARRAY_BUFFER, buffer);
    	glBufferData(GL_ARRAY_BUFFER, nVertices * sizeof(glm::vec3), vertices, GL_STATIC_DRAW);
    
    	// 创建并初始化顶点索引缓存对象
    	GLuint vertexIndexBuffer;
    	glGenBuffers(1, &vertexIndexBuffer);
    	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
    	glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces.size() * sizeof(vec3i), faces.data(), GL_STATIC_DRAW);
    
    	// 读取着色器并使用
    	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
    	GLuint program = InitShader(vshader.c_str(), fshader.c_str());
    	glUseProgram(program);
    
    	// 从顶点着色器中初始化顶点的位置
    	GLuint pLocation = glGetAttribLocation(program, "vPosition");
    	glEnableVertexAttribArray(pLocation);
    	glVertexAttribPointer(pLocation, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
    
    
    	/// /TODO: 获取变换矩阵的存储位置,并保存到matrixLocation:
    
    	// 黑色背景
    	glClearColor(0, 0, 0, 0);
    }
    
    void display()
    {
    	// 清理窗口
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    	// 生成变换矩阵
    	glm::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);
    
    	 TODO: 计算变换矩阵:
    
    	TODO: 从指定位置matrixLocation中传入变换矩阵:
    
    	// 绘制图形中的各个三角形
    	glDrawElements(GL_TRIANGLES, int(faces.size() * 3), GL_UNSIGNED_INT, BUFFER_OFFSET(0));
    
    }
    
    // 根据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 = glm::vec3(1.0, 1.0, 1.0);
    	rotateTheta = glm::vec3(0.0, 0.0, 0.0);
    	translateTheta = glm::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 key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
    {
    	float tmp;
    	glm::vec4 ambient;
    	if (action == GLFW_PRESS) {
    		switch (key)
    		{
    		case GLFW_KEY_ESCAPE: exit(EXIT_SUCCESS); break;
    		// 手动旋转
    		case GLFW_KEY_Q:
    			updateTheta(X_AXIS, 1);
    			break;
    		case GLFW_KEY_A:
    			updateTheta(X_AXIS, -1);
    			break;
    		case GLFW_KEY_W:
    			updateTheta(Y_AXIS, 1);
    			break;
    		case GLFW_KEY_S:
    			updateTheta(Y_AXIS, -1);
    			break;
    		case GLFW_KEY_E:
    			updateTheta(Z_AXIS, 1);
    			break;
    		case GLFW_KEY_D:
    			updateTheta(Z_AXIS, -1);
    			break;
    		// 设置旋转度数
    		case GLFW_KEY_R:
    			updateDelta(1);
    			break;
    		case GLFW_KEY_F:
    			updateDelta(-1);
    			break;
    		case GLFW_KEY_T:
    			resetTheta();
    			break;
    		TODO: 添加代码完成键盘自动旋转动画的旋转轴选取:
    
    			break;
    		}
    	}
    }
    
    //TODO: 修改mouse_button_callback函数,完成鼠标控制旋转的开始与停止:
    
    void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
    {
    	
    }
    
    void prinfStatus()
    {
    	printf("\nCurrent Status:\n");
    	switch (currentTransform) {
    	case TRANSFORM_SCALE:
    		printf("	Current Transform: Scale\n");
    		printf("	scaleTheta: (%.2f, %.2f, %.2f)\n", scaleTheta.x, scaleTheta.y, scaleTheta.z);
    		printf("	scaleDelta: %.2f\n", scaleDelta);
    		break;
    	case TRANSFORM_ROTATE:
    		printf("	Current Transform: Rotate\n");
    		printf("	rotateTheta: (%.2f, %.2f, %.2f)\n", rotateTheta.x, rotateTheta.y, rotateTheta.z);
    		printf("	rotateDelta: %.2f\n", rotateDelta);
    		break;
    	case TRANSFORM_TRANSLATE:
    		printf("	Current Transform: Translate\n");
    		printf("	translateTheta: (%.2f, %.2f, %.2f)\n", translateTheta.x, translateTheta.y, translateTheta.z);
    		printf("	translateDelta: %.2f\n", translateDelta);
    		break;
    	}
    }
    
    void printHelp() {
    	printf("Mouse options:\n");
    	printf("	Left Button: Start Animation\n");
    	printf("	Right Button: Stop Animation\n");
    	printf("\n\n");
    	printf("Keyboard options:\n");
    	printf("	Esc: Exit Program\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");
    	printf(" - While in animation\n");
    	printf("	Z: Set Z Axis as the rotation axis\n");
    	printf("	X: Set X Axis as the rotation axis\n");
    	printf("	C: Set Y Axis as the rotation axis\n");
    	printf(" - While not in animation\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");
    }
    
    void framebuffer_size_callback(GLFWwindow* window, int width, int height);
    
    int main(int argc, char **argv)
    {
    
    	// 初始化GLFW库,必须是应用程序调用的第一个GLFW函数
    	glfwInit();
    
    	// 配置GLFW
    	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
    #ifdef __APPLE__
    	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    #endif
    
    	// 配置窗口属性
    	GLFWwindow* window = glfwCreateWindow(600, 600, "学号_姓名_作业二", NULL, NULL);
    	if (window == NULL)
    	{
    		std::cout << "Failed to create GLFW window" << std::endl;
    		glfwTerminate();
    		return -1;
    	}
    	glfwMakeContextCurrent(window);
    	glfwSetKeyCallback(window, key_callback);
    	glfwSetMouseButtonCallback(window, mouse_button_callback);
    	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    
    	// 调用任何OpenGL的函数之前初始化GLAD
    	// ---------------------------------------
    	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    	{
    		std::cout << "Failed to initialize GLAD" << std::endl;
    		return -1;
    	}
    
    	// 读入OFF
    	// read_off("cube.off");	// 读取立方体
    	read_off("cow.off");		// 读取牛模型
    	// Init mesh, shaders, buffer
    	init();
    	// 输出帮助信息
    	printHelp();
    	// 启用深度测试
    	glEnable(GL_DEPTH_TEST);
    	while (!glfwWindowShouldClose(window))
    	{
    		if (Automatic_rotation == 1)
    			updateTheta(rotateAxis, 1);
    		display();
    		//reshape();
    
    		// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
    		// -------------------------------------------------------------------------------
    		glfwSwapBuffers(window);
    		glfwPollEvents();
    	}
    
    	return 0;
    
    }
    
    // 每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理。
    // ---------------------------------------------------------------------------------------------
    void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    {
    	// make sure the viewport matches the new window dimensions; note that width and 
    	// height will be significantly larger than specified on retina displays.
    	glViewport(0, 0, width, height);
    }

  • 内容

    提供的off格式三维模型,并对其赋色。利用鼠标和键盘的交互,控制动画效果,模型的颜色自己可以自行设置,好看就行。

/*@author:2019274072许佳妮(代码添加修改均价上【jennie】)

* 本工程基本功能是基于cow.off文件,进行绘制上色并实现360度自动旋转,具体实现功能如下:(以下展示工程里【行号】)

* 1.读取off文件并上色,显示奶牛

*       1.1   定义着色器绘制点和颜色、不同颜色【95-109】

*       1.2   Read_off()函数读文件【122-175】

*/

  • 具体内容
  1. OFF格式三维模型文件的读取

参考上机实验2.2的内容,完成对OFF格式三维模型文件的读取与显示,可改变物体的显示颜色,尽量特别,但不要太难看。

    1. 定义着色器绘制点和颜色、不同颜色
      std::vector<glm::vec3> points;   // 传入着色器的绘制点【jennie(读off 1.1)】
      std::vector<glm::vec3> colors;   // 传入着色器的颜色【jennie(读off 1.1)】
      
      const int COLOR_NUM_VERTICES = 8;	//颜色数【jennie(读off 1.1)】
      
      //色种【jennie(读off 1.1)】
      const glm::vec3 vertex_colors[COLOR_NUM_VERTICES] = {
      	glm::vec3(1.0, 1.0, 1.0),  // White
      	glm::vec3(1.0, 1.0, 0.0),  // Yellow
      	glm::vec3(0.0, 1.0, 0.0),  // Green
      	glm::vec3(0.0, 1.0, 1.0),  // Cyan
      	glm::vec3(1.0, 0.0, 1.0),  // Magenta
      	glm::vec3(1.0, 0.0, 0.0),  // Red
      	glm::vec3(0.0, 0.0, 0.0),  // Black
      	glm::vec3(0.0, 0.0, 1.0)   // Blue
      };
      

  1. Read_off()函数读文件(图表 1

根据off的文件格式,依次读入顶点信息和片元信息存入顶点数组vertices[]和片元数组faces[],在整理二者的数据存入points,同时填色。

void read_off(const std::string filename)
{
	if (filename.empty()) {
		return;
	}
	std::ifstream fin;
	fin.open(filename);

	 TODO:	完成对OFF格式三维模型文件的读取:
	if (!fin)
	{
		printf("文件有误\n");
		return;
	}
	else
	{
		printf("文件打开成功\n");
		for (int i = 0; i < NUM_VERTICES; i++) {
			vertices[i] = glm::vec3(0.0f);	//清空顶点数组
		}
		faces.clear();

		// 读取OFF字符串
		std::string str;
		fin >> str;
		// 读取文件中顶点数、面片数、边数
		fin >> nVertices >> nFaces >> nEdges;

		// 根据顶点数,循环读取每个顶点坐标,将其保存到vertices
		float x, y, z;
		for (int i = 0; i < nVertices; i++) {
			fin >> x >> y >> z;
			vertices[i]=glm::vec3(x, y, z);
		}

		// 根据面片数,循环读取每个面片信息,并用构建的vec3i结构体保存到faces
		unsigned int n, a, b, c;
		for (int i = 0; i < nFaces; i++) {
			fin >> n >> a >> b >> c;
			faces.push_back(vec3i(a, b, c));
		}
		points.clear();
		colors.clear();
// @TODO: Task1:修改此函数在points和colors容器中存储每个三角面片的各个点和颜色信息
		// 在points容器中,依次添加每个面片的顶点,并在colors容器中,添加该点的颜色信息
		// 比如一个正方形由两个三角形构成,那么vertices会由4个顶点的数据构成,faces会记录两个三角形的顶点下标,
		// 而points就是记录这2个三角形的顶点,总共6个顶点的数据。
		// colors容器则是和points的顶点一一对应,保存这个顶点的颜色,这里我们可以使用顶点坐标或者自己设定的颜色赋值。
		for (int i = 0; i < nFaces; i++) {
			points.push_back(vertices[faces[i].a]);
			points.push_back(vertices[faces[i].b]);
			points.push_back(vertices[faces[i].c]);
			/*//单色
			for (int j = 0; j < 3; j++) {
				colors.push_back(vertex_colors[i / 2]);
			}*/
			colors.push_back(vertex_colors[faces[i].a]);
			colors.push_back(vertex_colors[faces[i].b]);
			colors.push_back(vertex_colors[faces[i].c]);
		}
	}
	fin.close();
}
 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jennie佳妮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值