【深圳大学计算机图形学】实验四 带纹理的OBJ文件读取和显示

目录

实验目的与要求

实验过程及内容

实验4.1

实验四

实验结论

实验代码

实验4.1——main.cpp

实验4.1——MeshPainter.cpp

实验4.1——TriMesh.cpp

实验四——main.cpp

实验四——TriMesh.cpp


实验目的与要求

  1. 了解三维曲面和纹理映基本知识
  2. 了解从图片文件载入纹理数据基本步骤
  3. 掌握三维曲面绘制过程中纹理坐标和几何坐标的使用
  4. 在程序中读取带纹理的obj文件,载入相应的纹理图片文件,将带纹理的模型显示在程序窗口中。

实验过程及内容

实验4.1

1.更改标题栏名称。

2.在MeshPainter.cpp更改代码,将纹理坐标传入着色器。

object.tLocation = glGetAttribLocation(object.program, "vTexture");:获取顶点着色器中纹理坐标的属性位置。

glEnableVertexAttribArray(object.tLocation);:启用顶点属性数组,使能用于存储纹理坐标数据的顶点属性数组。

glVertexAttribPointer(object.tLocation, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET((points.size() + colors.size() + normals.size()) * sizeof(glm::vec3)));:指定纹理坐标的属性指针。这一行代码说明了纹理坐标是一个包含两个浮点数的向量,数据类型是GL_FLOAT,步长为0(因为纹理坐标是紧密排列的),偏移量根据之前顶点、颜色和法向量数组的大小计算而来。

3.运行代码,发现圆柱体已经成功贴上了纹理,如下图所示。

4.补全generateDisk函数生成圆盘。

cleanData(): 首先,清空之前的模型数据,确保数据的干净。

计算每个切片的弧度 step,step 等于 2π 除以切片数目,用于后续计算各个顶点的坐标。

通过一个循环生成下表面的顶点坐标、法向量和颜色。循环中,通过极坐标系的转换,计算每个切片上的顶点坐标 (x, y, z),其中 x 和 y 分别为极坐标的变换,而 z 为 0,表示圆盘在 xy 平面上。

将生成的下表面的顶点坐标、法向量和颜色加入到相应的数组中。

添加中心点的顶点坐标、法向量和颜色,用于构成圆盘的中心。

通过另一个循环生成三角面片,每个矩形由两个三角形面片构成。循环中,生成每个三角形面片的顶点索引,并映射 UV 坐标到三角形的每个顶点,这里采用了一个简单的映射方式。

添加纹理坐标到 vertex_textures 数组中,其中 (x, y) 是通过将极坐标映射到 UV 坐标的 0-1 范围内得到的。

存储对应的三角形面片的每个顶点的纹理坐标的下标。

将三角面片的每个顶点的法向量的下标设为与顶点坐标的下标相同,颜色的下标也相同。

最后,通过调用 storeFacesPoints() 存储面片的顶点坐标。

这个函数的目的是创建一个表示圆盘的三维模型,包括顶点坐标、法向量、颜色和纹理坐标,以及构成圆盘的三角形面片的顶点索引。

5.补全generateCone生成圆锥。

准备模型数据: 在函数一开始,通过 cleanData() 清空之前的模型数据,确保从头开始生成新的模型。

计算切片弧度: 根据用户指定的切片数量 num_division,计算每个切片的弧度 step,用于后续生成顶点坐标。

生成底部顶点: 使用循环遍历每个切片,计算底部圆形上的顶点坐标 (x, y, z),并将其添加到模型的顶点数组 vertex_positions 中。同时,计算并添加法向量和颜色,这里法向量使用 (x, y, 0),颜色采用法向量作为颜色。

生成圆锥尖端顶点: 添加圆锥尖端的顶点坐标 (0, 0, height),法向量为 (0, 0, 1),颜色为 (0, 0, 1)。

生成圆锥侧面三角面片: 使用循环遍历每个切片,生成连接底部和尖端的侧面三角面片。每个三角面片包括底部中心点、当前切片点和下一个切片点。将这些面片添加到模型的面片数组 faces 中。

添加纹理坐标: 为每个侧面三角面片的三个顶点添加纹理坐标。底部中心点的纹理坐标为 (0.5, 1),当前切片点和下一个切片点的纹理坐标根据简单映射计算。

存储纹理坐标和法向量索引: 将每个侧面三角面片的纹理坐标的下标存储在 texture_index 中,法向量的下标与顶点坐标的下标相同,存储在 normal_index 中。

存储面片的颜色索引: 面片的颜色索引与顶点坐标的下标相同,存储在 color_index 中。

存储面片的顶点坐标: 调用 storeFacesPoints() 函数将面片的顶点坐标存储在模型中,以便后续渲染。

总体而言,这个函数通过遍历切片,生成底部圆形和圆锥尖端的顶点,然后连接它们形成侧面三角面片,完成了圆锥体的模型生成。

6.创建圆盘、圆锥对象并贴图。

首先,创建了一个 TriMesh 类的实例,代表圆盘对象,命名为 disk,并创建了另一个实例代表圆锥对象,命名为 cone。

然后,通过调用 generateDisk 方法,生成了圆盘的几何形状。该方法接受两个参数,分别是分段数和半径,用于计算顶点坐标、法向量、颜色和纹理坐标等信息。

接下来,通过一系列方法设置了每个对象的变换属性,包括平移、旋转和缩放。对于圆盘,平移、旋转和缩放都设置为零或单位值,而对于圆锥,进行了一些不同的平移。

然后,通过一系列方法设置了每个对象的材质属性。这包括环境光、漫反射、镜面反射和高光系数等。这些属性会影响对象在渲染时的外观。

最后,通过调用 painter->addMesh 方法,将每个对象添加到一个绘制器中,同时指定了纹理和着色器。这个过程中,还将每个对象添加到一个对象列表中(meshList)。

7.运行代码,结果如下图所示。

实验四

1.首先需要理解obj文件的格式。

OBJ 文件(Wavefront .obj)是一种常用的文本格式,用于存储三维模型的几何信息。这种文件格式由 Wavefront Technologies 创建,主要包含了模型的顶点坐标、法线、纹理坐标以及模型的面信息。

以下是 OBJ 文件的主要组成部分:

顶点(Vertices):

以 'v' 开头,后跟三个浮点数,表示模型的顶点坐标。

例如:v 1.0 2.0 3.0

法线(Normals):

以 'vn' 开头,后跟三个浮点数,表示模型的法线方向。

例如:vn 0.0 1.0 0.0

纹理坐标(Texture Coordinates):

以 'vt' 开头,后跟两个浮点数,表示纹理坐标的 U 和 V 分量。

例如:vt 0.5 0.5

面(Faces):

以 'f' 开头,后跟一组顶点、法线和纹理坐标的索引,定义了模型的面。

例如:f 1/1/1 2/2/2 3/3/3

材质库和使用材质(Material Libraries and Usage):

以 'mtllib' 开头,指定了外部材质库文件的路径。

使用 'usemtl' 指令来为模型的一部分指定材质。

对象(Objects):

以 'o' 开头,定义了一个新的对象。

组(Groups):

以 'g' 开头,定义了一个组,可以包含多个面。

注释(Comments):

以 '#' 开头,用于添加注释。

比如以下例子,这表示一个具有一个法线和一个纹理坐标的三角形。在一个实际的 OBJ 文件中,这些元素会按照一定的规则和结构进行组织。 OBJ 文件是一种通用的三维模型文件格式,由于其简单的文本表示,易于阅读和编辑,因此被广泛应用。

2. 补全TriMesh.cpp的storeFacesPoints函数。

这段代码主要用于根据每个三角面片的顶点下标将数据存储到用于传递给 GPU 的容器中。具体解释如下:

faces[i].x, faces[i].y, faces[i].z 表示第 i 个三角面片的顶点下标。

vertex_positions[...] 获取对应顶点下标的坐标。

类似地,vertex_colors[...] 获取对应颜色下标的颜色。

如果模型包含法向量,则从 vertex_normals[...] 获取法向量。

如果模型包含纹理坐标,则从 vertex_textures[...] 获取纹理坐标。

这样,通过遍历每个面片,将顶点坐标、颜色、法向量和纹理坐标按顺序存储在相应的容器(例如 points、colors、normals、textures)中,以便后续传递给 GPU。

3.补全TriMesh.cpp的readObj函数。

这段代码用于解析OBJ文件中的顶点信息(v)、法向量信息(vn)、纹理坐标信息(vt)和面信息(f)。具体解释如下:

当类型为 "v" 时,表示读取到一个顶点,将其坐标存储在 vertex_positions 中。

当类型为 "vn" 时,表示读取到一个法向量,将其存储在 vertex_normals 中。你可以选择将其存储在 vertex_colors 中,根据需要取消注释。

当类型为 "vt" 时,表示读取到一个纹理坐标,将其存储在 vertex_textures 中。

当类型为 "f" 时,表示读取到一个面,将其顶点、纹理坐标和法向量的索引信息存储在 faces、texture_index 和 normal_index 中。

在读取完obj内的数据后,用法向量的值赋予vertex_color和color_index并调用storeFacesPoints函数存储数据。

4.补全init函数。

这段代码用于创建并加载桌子和娃娃的三维模型。具体解释如下:

创建桌子对象 TriMesh* table = new TriMesh(); 和娃娃对象 TriMesh* wawa = new TriMesh();。

设置是否进行模型归一化,setNormalize(true); 表示进行模型归一化。

通过 readObj 方法读取桌子和娃娃的OBJ文件,分别为 table.obj 和 wawa.obj。

设置桌子和娃娃的变换属性,包括平移、旋转和缩放。

将桌子和娃娃添加到 Painter 中,使用指定的纹理和着色器。

将桌子和娃娃对象添加到对象列表 meshList 中,以便在程序结束时释放这些数据。

5.运行代码,如下图所示。

实验结论

通过此次实验,了解了obj文件的格式,学会了如何读入obj文件并生成图形。

OBJ 文件(Wavefront .obj)是一种常用的文本格式,用于存储三维模型的几何信息。这种文件格式由 Wavefront Technologies 创建,主要包含了模型的顶点坐标、法线、纹理坐标以及模型的面信息。

值得注意的是在obj文件中索引下标是从1开始的,所以需要在传入的时候减1。

实验代码

实验4.1——main.cpp

#include "Angel.h"
#include "TriMesh.h"
#include "Camera.h"
#include "MeshPainter.h"

#include <vector>
#include <string>

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;

Camera* camera = new Camera();
Light* light = new Light();
MeshPainter *painter = new MeshPainter();

// 这个用来回收和删除我们创建的物体对象
std::vector<TriMesh *> meshList;

void init()
{
	std::string vshader, fshader;
	// 读取着色器并使用
	vshader = "shaders/vshader.glsl";
	fshader = "shaders/fshader.glsl";



	// 设置光源位置
	light->setTranslation(glm::vec3(0.0, 0.0, 2.0));
	light->setAmbient(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 环境光
	light->setDiffuse(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 漫反射
	light->setSpecular(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 镜面反射
	light->setAttenuation(1.0, 0.045, 0.0075); // 衰减系数


	openGLObject mesh_object;
	TriMesh* cylinder = new TriMesh();	
	// 创建圆柱体
	cylinder->generateCylinder(100, 0.1, 0.3);
	// 设置物体的旋转位移
	cylinder->setTranslation(glm::vec3(-0.5, 0.0, 0.0));
	cylinder->setRotation(glm::vec3(90.0, 0.0, 0.0));
	cylinder->setScale(glm::vec3(1.0, 1.0, 1.0));
	// 设置材质(不过本次实验中不会用到光照,感兴趣的图像结合ppt的扩展内容将着色器进行修改)
	cylinder->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 环境光
	cylinder->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0)); // 漫反射
	cylinder->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	cylinder->setShininess(1.0); //高光系数
	// 加到painter中
	painter->addMesh(cylinder, "mesh_a", "./assets/cylinder10.jpg", vshader, fshader); 	// 指定纹理与着色器
	// 我们创建的这个加入一个容器内,为了程序结束时将这些数据释放
	meshList.push_back(cylinder);


	// @TODO: Task2 生成圆盘、圆锥并贴图
	// 创建圆盘对象
	TriMesh* disk = new TriMesh();
	// 生成圆盘并贴图
	disk->generateDisk(100, 0.1);

	// 设置物体的变换属性
	disk->setTranslation(glm::vec3(0.0, 0.0, 0.0));  // 平移
	disk->setRotation(glm::vec3(0.0, 0.0, 0.0));     // 旋转
	disk->setScale(glm::vec3(2.0, 2.0, 1.0));         // 缩放

	// 设置材质属性
	disk->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0));  // 环境光
	disk->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0));  // 漫反射
	disk->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	disk->setShininess(1.0);                           // 高光系数

	// 将圆盘添加到Painter中,指定纹理与着色器
	painter->addMesh(disk, "mesh_b", "./assets/disk.jpg", vshader, fshader);
	// 将圆盘对象添加到对象列表
	meshList.push_back(disk);

	// 创建圆锥对象
	TriMesh* cone = new TriMesh();
	// 生成圆锥并贴图
	cone->generateCone(100, 0.1, 0.5);

	// 设置圆锥的变换属性
	cone->setTranslation(glm::vec3(0.5, -0.2, 0.0));  // 平移
	cone->setRotation(glm::vec3(-90.0, 0.0, 0.0));     // 旋转
	cone->setScale(glm::vec3(1.5, 1.0, 0.7));         // 缩放

	// 设置圆锥的材质属性
	cone->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0));  // 环境光
	cone->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0));  // 漫反射
	cone->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	cone->setShininess(1.0);                           // 高光系数

	// 将圆锥添加到Painter中,指定纹理与着色器
	painter->addMesh(cone, "mesh_c", "./assets/cone.jpg", vshader, fshader);
	// 将圆锥对象添加到对象列表
	meshList.push_back(cone);
	

	glClearColor(1.0, 1.0, 1.0, 1.0);
	// glClearColor(0.0, 0.0, 0.0, 1.0);
}



void display()
{

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	painter->drawMeshes(light, camera);

}


void printHelp()
{
	std::cout << "================================================" << std::endl;
	std::cout << "Use mouse to controll the light position (drag)." << std::endl;
	std::cout << "================================================" << std::endl << std::endl;

	std::cout << "Keyboard Usage" << std::endl;
	std::cout <<
		"[Window]" << std::endl <<
		"ESC:		Exit" << std::endl <<
		"h:		Print help message" << std::endl <<

		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/U:		Increase/Decrease the rotate angle" << std::endl <<
		"i/I:		Increase/Decrease the up angle" << std::endl <<
		"o/O:		Increase/Decrease the camera radius" << std::endl << std::endl;

}

// 键盘响应函数
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_H: printHelp(); break;
		default:
			camera->keyboard(key, action, mode);
			break;
		}
	}
}

// 重新设置窗口
void reshape(GLsizei w, GLsizei h)
{
	glViewport(0, 0, w, h);
}

void cleanData() {
	// 释放内存
	
	delete camera;
	camera = NULL;

	delete light;
	light = NULL;

	painter->cleanMeshes();

	delete painter;
	painter = NULL;
	
	for (int i=0; i<meshList.size(); i++) {
		delete meshList[i];
	}
	meshList.clear();
}

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, "2021150047_hyf_实验4.1", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetKeyCallback(window, key_callback);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// 调用任何OpenGL的函数之前初始化GLAD
	// ---------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// Init mesh, shaders, buffer
	init();
	// 输出帮助信息
	printHelp();
	// 启用深度测试
	glEnable(GL_DEPTH_TEST);
	while (!glfwWindowShouldClose(window))
	{
		display();
		//reshape();

		// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	cleanData();


	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);
}

实验4.1——MeshPainter.cpp

#include "MeshPainter.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

MeshPainter::MeshPainter() {};
MeshPainter::~MeshPainter() {};

std::vector<std::string> MeshPainter::getMeshNames() { return mesh_names; };
std::vector<TriMesh*> MeshPainter::getMeshes() { return meshes; };
std::vector<openGLObject> MeshPainter::getOpenGLObj() { return opengl_objects; };

void MeshPainter::bindObjectAndData(TriMesh* mesh, openGLObject& object, const std::string& texture_image, const std::string& vshader, const std::string& fshader) {
	// 初始化各种对象
	std::vector<glm::vec3> points = mesh->getPoints();
	std::vector<glm::vec3> normals = mesh->getNormals();
	std::vector<glm::vec3> colors = mesh->getColors();
	std::vector<glm::vec2> textures = mesh->getTextures();

	// 创建顶点数组对象
	glGenVertexArrays(1, &object.vao);  	// 分配1个顶点数组对象
	glBindVertexArray(object.vao);  	// 绑定顶点数组对象

	// 创建并初始化顶点缓存对象
	glGenBuffers(1, &object.vbo);
	glBindBuffer(GL_ARRAY_BUFFER, object.vbo);
	glBufferData(GL_ARRAY_BUFFER,
		points.size() * sizeof(glm::vec3) +
		colors.size() * sizeof(glm::vec3) +
		normals.size() * sizeof(glm::vec3) +
		textures.size() * sizeof(glm::vec2),
		NULL, GL_STATIC_DRAW);

	// 绑定顶点数据
	glBufferSubData(GL_ARRAY_BUFFER, 0, points.size() * sizeof(glm::vec3), points.data());
	// 绑定颜色数据
	glBufferSubData(GL_ARRAY_BUFFER, points.size() * sizeof(glm::vec3), colors.size() * sizeof(glm::vec3), colors.data());
	// 绑定法向量数据
	glBufferSubData(GL_ARRAY_BUFFER, (points.size() + colors.size()) * sizeof(glm::vec3), normals.size() * sizeof(glm::vec3), normals.data());
	// 绑定纹理数据
	glBufferSubData(GL_ARRAY_BUFFER, (points.size() + normals.size() + colors.size()) * sizeof(glm::vec3), textures.size() * sizeof(glm::vec2), textures.data());


	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(points.size() * sizeof(glm::vec3)));

	// 将法向量传入着色器
	object.nLocation = glGetAttribLocation(object.program, "vNormal");
	glEnableVertexAttribArray(object.nLocation);
	glVertexAttribPointer(object.nLocation, 3,
		GL_FLOAT, GL_FALSE, 0,
		BUFFER_OFFSET((points.size() + colors.size()) * sizeof(glm::vec3)));

	// @TODO: Task1 将纹理坐标传入着色器
	// 获取顶点着色器中纹理坐标的位置
	object.tLocation = glGetAttribLocation(object.program, "vTexture");

	// 启用顶点属性数组,用于存储纹理坐标数据
	glEnableVertexAttribArray(object.tLocation);

	// 指定纹理坐标的属性指针,传递到顶点着色器中
	glVertexAttribPointer(object.tLocation, 2, GL_FLOAT, GL_FALSE, 0,
		BUFFER_OFFSET((points.size() + colors.size() + normals.size()) * sizeof(glm::vec3)));


	// 获得矩阵位置
	object.modelLocation = glGetUniformLocation(object.program, "model");
	object.viewLocation = glGetUniformLocation(object.program, "view");
	object.projectionLocation = glGetUniformLocation(object.program, "projection");

	object.shadowLocation = glGetUniformLocation(object.program, "isShadow");

	// 读取纹理图片数
	object.texture_image = texture_image;
	// 创建纹理的缓存对象
	glGenTextures(1, &object.texture);
	// 调用stb_image生成纹理
	load_texture_STBImage(object.texture_image, object.texture);

	// Clean up
	glUseProgram(0);
	glBindVertexArray(0);
};


void MeshPainter::bindLightAndMaterial(TriMesh* mesh, openGLObject& object, Light* light, Camera* camera) {
	// 传递材质、光源等数据给着色器

	// 传递相机的位置
	glUniform3fv(glGetUniformLocation(object.program, "eye_position"), 1, &camera->eye[0]);

	// 传递物体的材质
	glm::vec4 meshAmbient = mesh->getAmbient();
	glm::vec4 meshDiffuse = mesh->getDiffuse();
	glm::vec4 meshSpecular = mesh->getSpecular();
	float meshShininess = mesh->getShininess();

	glUniform4fv(glGetUniformLocation(object.program, "material.ambient"), 1, &meshAmbient[0]);
	glUniform4fv(glGetUniformLocation(object.program, "material.diffuse"), 1, &meshDiffuse[0]);
	glUniform4fv(glGetUniformLocation(object.program, "material.specular"), 1, &meshSpecular[0]);
	glUniform1f(glGetUniformLocation(object.program, "material.shininess"), meshShininess);


	// 传递光源信息
	glm::vec4 lightAmbient = light->getAmbient();
	glm::vec4 lightDiffuse = light->getDiffuse();
	glm::vec4 lightSpecular = light->getSpecular();
	glm::vec3 lightPosition = light->getTranslation();
	glUniform4fv(glGetUniformLocation(object.program, "light.ambient"), 1, &lightAmbient[0]);
	glUniform4fv(glGetUniformLocation(object.program, "light.diffuse"), 1, &lightDiffuse[0]);
	glUniform4fv(glGetUniformLocation(object.program, "light.specular"), 1, &lightSpecular[0]);
	glUniform3fv(glGetUniformLocation(object.program, "light.position"), 1, &lightPosition[0]);

	glUniform1f(glGetUniformLocation(object.program, "light.constant"), light->getConstant());
	glUniform1f(glGetUniformLocation(object.program, "light.linear"), light->getLinear());
	glUniform1f(glGetUniformLocation(object.program, "light.quadratic"), light->getQuadratic());

}


void MeshPainter::addMesh(TriMesh* mesh, const std::string& name, const std::string& texture_image, const std::string& vshader, const std::string& fshader) {
	mesh_names.push_back(name);
	meshes.push_back(mesh);

	openGLObject object;
	// 绑定openGL对象,并传递顶点属性的数据
	bindObjectAndData(mesh, object, texture_image, vshader, fshader);

	opengl_objects.push_back(object);
};



void MeshPainter::drawMesh(TriMesh* mesh, openGLObject& object, Light* light, Camera* camera) {

	// 相机矩阵计算
	camera->updateCamera();
	camera->viewMatrix = camera->getViewMatrix();
	camera->projMatrix = camera->getProjectionMatrix(false);


	glBindVertexArray(object.vao);
	glUseProgram(object.program);

	// 物体的变换矩阵
	glm::mat4 modelMatrix = mesh->getModelMatrix();

	// 传递矩阵
	glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(object.viewLocation, 1, GL_FALSE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(object.projectionLocation, 1, GL_FALSE, &camera->projMatrix[0][0]);
	// 将着色器 isShadow 设置为0,表示正常绘制的颜色,如果是1着表示阴影
	glUniform1i(object.shadowLocation, 0);


	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, object.texture);// 该语句必须,否则将只使用同一个纹理进行绘制
	// 传递纹理数据 将生成的纹理传给shader
	glUniform1i(glGetUniformLocation(object.program, "texture"), 0);

	// 将材质和光源数据传递给着色器
	bindLightAndMaterial(mesh, object, light, camera);
	// 绘制
	glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());

	glBindVertexArray(0);

	glUseProgram(0);

};


void MeshPainter::drawMeshes(Light* light, Camera* camera) {
	for (int i = 0; i < meshes.size(); i++)
	{
		drawMesh(meshes[i], opengl_objects[i], light, camera);
	}
};

void MeshPainter::cleanMeshes() {
	// 将数据都清空释放
	mesh_names.clear();

	for (int i = 0; i < meshes.size(); i++)
	{
		meshes[i]->cleanData();

		delete meshes[i];
		meshes[i] = NULL;


		glDeleteVertexArrays(1, &opengl_objects[i].vao);

		glDeleteBuffers(1, &opengl_objects[i].vbo);
		glDeleteProgram(opengl_objects[i].program);
	}

	meshes.clear();
	opengl_objects.clear();
};


void MeshPainter::load_texture_STBImage(const std::string& file_name, GLuint& texture) {
	// 读取纹理图片,并将其传递给着色器

	int width, height, channels = 0;
	unsigned char* pixels = NULL;
	// 读取图片的时候先翻转一下图片,如果不设置的话显示出来是反过来的图片
	stbi_set_flip_vertically_on_load(true);
	// 读取图片数据
	pixels = stbi_load(file_name.c_str(), &width, &height, &channels, 0);

	// 调整行对齐格式
	if (width * channels % 4 != 0)
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	GLenum format = GL_RGB;
	// 设置通道格式
	switch (channels)
	{
	case 1:
		format = GL_RED;
		break;
	case 3:
		format = GL_RGB;
		break;
	case 4:
		format = GL_RGBA;
		break;
	default:
		format = GL_RGB;
		break;
	}

	// 绑定纹理对象
	glBindTexture(GL_TEXTURE_2D, texture);

	// 指定纹理的放大,缩小滤波,使用线性方式,即当图片放大的时候插值方式
	// 将图片的rgb数据上传给opengl
	glTexImage2D(
		GL_TEXTURE_2D,    // 指定目标纹理,这个值必须是GL_TEXTURE_2D
		0,                // 执行细节级别,0是最基本的图像级别,n表示第N级贴图细化级别
		format,           // 纹理数据的颜色格式(GPU显存)
		width,            // 宽度。早期的显卡不支持不规则的纹理,则宽度和高度必须是2^n
		height,           // 高度。早期的显卡不支持不规则的纹理,则宽度和高度必须是2^n
		0,                // 指定边框的宽度。必须为0
		format,           // 像素数据的颜色格式(CPU内存)
		GL_UNSIGNED_BYTE, // 指定像素数据的数据类型
		pixels            // 指定内存中指向图像数据的指针
	);

	// 生成多级渐远纹理,多消耗1/3的显存,较小分辨率时获得更好的效果
	// glGenerateMipmap(GL_TEXTURE_2D);

	// 指定插值方法
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	// 恢复初始对齐格式
	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
	// 释放图形内存
	stbi_image_free(pixels);
};

实验4.1——TriMesh.cpp

#include "TriMesh.h"

// 一些基础颜色
const glm::vec3 basic_colors[8] = {
	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
};


// 立方体的各个点
const glm::vec3 cube_vertices[8] = {
	glm::vec3(-0.5, -0.5, -0.5),
	glm::vec3(0.5, -0.5, -0.5),
	glm::vec3(-0.5, 0.5, -0.5),
	glm::vec3(0.5, 0.5, -0.5),
	glm::vec3(-0.5, -0.5, 0.5),
	glm::vec3(0.5, -0.5, 0.5),
	glm::vec3(-0.5, 0.5, 0.5),
	glm::vec3(0.5, 0.5, 0.5)};

// 三角形的点
const glm::vec3 triangle_vertices[3] = {
	glm::vec3(-0.5, -0.5, 0.0),
	glm::vec3(0.5, -0.5, 0.0),
	glm::vec3(0.0, 0.5, 0.0)};

// 正方形平面
const glm::vec3 square_vertices[4] = {
	glm::vec3(-0.5, -0.5, 0.0),
	glm::vec3(0.5, -0.5, 0.0),
	glm::vec3(0.5, 0.5, 0.0),
	glm::vec3(-0.5, 0.5, 0.0),
};

TriMesh::TriMesh()
{
	scale = glm::vec3(1.0);
	rotation = glm::vec3(0.0);
	translation = glm::vec3(0.0);
}


TriMesh::~TriMesh()
{
}

std::vector<glm::vec3> TriMesh::getVertexPositions()
{
	return vertex_positions;
}

std::vector<glm::vec3> TriMesh::getVertexColors()
{
	return vertex_colors;
}

std::vector<glm::vec3> TriMesh::getVertexNormals()
{
	return vertex_normals;
}

std::vector<glm::vec2> TriMesh::getVertexTextures()
{
	return vertex_textures;
}

std::vector<vec3i> TriMesh::getFaces()
{
	return faces;
}

std::vector<glm::vec3> TriMesh::getPoints()
{
	return points;
}

std::vector<glm::vec3> TriMesh::getColors()
{
	return colors;
}

std::vector<glm::vec3> TriMesh::getNormals()
{
	return normals;
}

std::vector<glm::vec2> TriMesh::getTextures()
{
	return textures;
}

void TriMesh::computeTriangleNormals()
{
	face_normals.resize(faces.size());
	for (size_t i = 0; i < faces.size(); i++)
	{
		auto &face = faces[i];
		glm::vec3 v01 = vertex_positions[face.y] - vertex_positions[face.x];
		glm::vec3 v02 = vertex_positions[face.z] - vertex_positions[face.x];
		face_normals[i] = normalize(cross(v01, v02));
	}
}

void TriMesh::computeVertexNormals()
{
	// 计算面片的法向量
	if (face_normals.size() == 0 && faces.size() > 0)
	{
		computeTriangleNormals();
	}
	// 初始化法向量为0
	vertex_normals.resize(vertex_positions.size(), glm::vec3(0, 0, 0));
	for (size_t i = 0; i < faces.size(); i++)
	{
		auto &face = faces[i];
		vertex_normals[face.x] += face_normals[i];
		vertex_normals[face.y] += face_normals[i];
		vertex_normals[face.z] += face_normals[i];
	}
	for (size_t i = 0; i < vertex_normals.size(); i++)
	{
		vertex_normals[i] = normalize(vertex_normals[i]);
	}
	// 球心在原点的球法向量为坐标
	// for (int i = 0; i < vertex_positions.size(); i++)
	// 	vertex_normals.push_back(vertex_positions[i] - vec3(0.0, 0.0, 0.0));
}

glm::vec3 TriMesh::getTranslation()
{
	return translation;
}

glm::vec3 TriMesh::getRotation()
{
	return rotation;
}

glm::vec3 TriMesh::getScale()
{
	return scale;
}

glm::mat4 TriMesh::getModelMatrix()
{
	glm::mat4 model = glm::mat4(1.0f);
	glm::vec3 trans = getTranslation();
	model = glm::translate(model, getTranslation());
	model = glm::rotate(model, glm::radians(getRotation()[2]), glm::vec3(0.0, 0.0, 1.0));
	model = glm::rotate(model, glm::radians(getRotation()[1]), glm::vec3(0.0, 1.0, 0.0));
	model = glm::rotate(model, glm::radians(getRotation()[0]), glm::vec3(1.0, 0.0, 0.0));
	model = glm::scale(model, getScale());
	return model;
}

void TriMesh::setTranslation(glm::vec3 translation)
{
	this->translation = translation;
}

void TriMesh::setRotation(glm::vec3 rotation)
{
	this->rotation = rotation;
}

void TriMesh::setScale(glm::vec3 scale)
{
	this->scale = scale;
}

glm::vec4 TriMesh::getAmbient() { return ambient; };
glm::vec4 TriMesh::getDiffuse() { return diffuse; };
glm::vec4 TriMesh::getSpecular() { return specular; };
float TriMesh::getShininess() { return shininess; };

void TriMesh::setAmbient(glm::vec4 _ambient) { ambient = _ambient; };
void TriMesh::setDiffuse(glm::vec4 _diffuse) { diffuse = _diffuse; };
void TriMesh::setSpecular(glm::vec4 _specular) { specular = _specular; };
void TriMesh::setShininess(float _shininess) { shininess = _shininess; };

void TriMesh::cleanData()
{
	vertex_positions.clear();
	vertex_colors.clear();
	vertex_normals.clear();
	vertex_textures.clear();

	faces.clear();
	normal_index.clear();
	color_index.clear();
	texture_index.clear();

	face_normals.clear();


	points.clear();
	colors.clear();
	normals.clear();
	textures.clear();
}

void TriMesh::storeFacesPoints()
{
	// 计算法向量
	if (vertex_normals.size() == 0)
		computeVertexNormals();

	// 根据每个三角面片的顶点下标存储要传入GPU的数据
	for (int i = 0; i < faces.size(); 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(vertex_colors[color_index[i].x]);
		colors.push_back(vertex_colors[color_index[i].y]);
		colors.push_back(vertex_colors[color_index[i].z]);
		// 法向量
		if (vertex_normals.size() != 0)
		{
			normals.push_back(vertex_normals[normal_index[i].x]);
			normals.push_back(vertex_normals[normal_index[i].y]);
			normals.push_back(vertex_normals[normal_index[i].z]);
		}
		// 纹理
		if (vertex_textures.size() != 0)
		{
			textures.push_back(vertex_textures[texture_index[i].x]);
			textures.push_back(vertex_textures[texture_index[i].y]);
			textures.push_back(vertex_textures[texture_index[i].z]);
		}
	}
}

// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube()
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 8; i++)
	{
		vertex_positions.push_back(cube_vertices[i]);
		vertex_colors.push_back(basic_colors[i]);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 3, 1));
	faces.push_back(vec3i(0, 2, 3));
	faces.push_back(vec3i(1, 5, 4));
	faces.push_back(vec3i(1, 4, 0));
	faces.push_back(vec3i(4, 2, 0));
	faces.push_back(vec3i(4, 6, 2));
	faces.push_back(vec3i(5, 6, 4));
	faces.push_back(vec3i(5, 7, 6));
	faces.push_back(vec3i(2, 6, 7));
	faces.push_back(vec3i(2, 7, 3));
	faces.push_back(vec3i(1, 7, 5));
	faces.push_back(vec3i(1, 3, 7));
	
	// faces.push_back(vec3i(0, 1, 3));
	// faces.push_back(vec3i(0, 3, 2));

	// faces.push_back(vec3i(1, 4, 5));
	// faces.push_back(vec3i(1, 0, 4));

	// faces.push_back(vec3i(4, 0, 2));
	// faces.push_back(vec3i(4, 2, 6));

	// faces.push_back(vec3i(5, 6, 4));
	// faces.push_back(vec3i(5, 7, 6));

	// faces.push_back(vec3i(2, 6, 7));
	// faces.push_back(vec3i(2, 7, 3));
	
	// faces.push_back(vec3i(1, 7, 5));
	// faces.push_back(vec3i(1, 3, 7));

	// 顶点纹理坐标,这里是每个面都用一个正方形图片的情况
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0, 1));
	vertex_textures.push_back(glm::vec2(1, 1));
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0, 1));
	vertex_textures.push_back(glm::vec2(1, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();

	normals.clear();
	// 正方形的法向量不能靠之前顶点法向量的方法直接计算,因为每个四边形平面是正交的,不是连续曲面
	for (int i = 0; i < faces.size(); i++)
	{
		normals.push_back( face_normals[i] );
		normals.push_back( face_normals[i] );
		normals.push_back( face_normals[i] );
	}
}

void TriMesh::generateTriangle(glm::vec3 color)
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 3; i++)
	{
		vertex_positions.push_back(triangle_vertices[i]);
		vertex_colors.push_back(color);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 1, 2));

	// 顶点纹理坐标
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0.5, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
}

void TriMesh::generateSquare(glm::vec3 color)
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 4; i++)
	{
		vertex_positions.push_back(square_vertices[i]);
		vertex_colors.push_back(color);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 1, 2));
	faces.push_back(vec3i(0, 2, 3));

	// 顶点纹理坐标
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(1, 1));
	vertex_textures.push_back(glm::vec2(0, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
}

void TriMesh::generateCylinder(int num_division, float radius, float height)
{

	cleanData();

	int num_samples = num_division;
	float step = 2 * M_PI / num_samples; // 每个切片的弧度

	// 按cos和sin生成x,y坐标,z为负,即得到下表面顶点坐标
	// 顶点, 纹理
	float z = -height;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);
		// 添加顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		vertex_normals.push_back( normalize(glm::vec3(x, y, 0)));
		// 这里颜色和法向量一样
		vertex_colors.push_back( normalize(glm::vec3(x, y, 0)));
	}

	// 按cos和sin生成x,y坐标,z为正,即得到上表面顶点坐标
	z = height;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);
		vertex_positions.push_back(glm::vec3(x, y, z));
		vertex_normals.push_back( normalize(glm::vec3(x, y, 0)));
		vertex_colors.push_back( normalize(glm::vec3(x, y, 0)));
	}

	// 面片生成三角面片,每个矩形由两个三角形面片构成
	for (int i = 0; i < num_samples; i++)
	{
		// 面片1
		faces.push_back(vec3i(i, (i + 1) % num_samples, (i) + num_samples));
		// 面片2
		faces.push_back(vec3i((i) + num_samples, (i + 1) % num_samples, (i + num_samples + 1) % (num_samples) + num_samples));

		// 面片1对应的顶点的纹理坐标
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i+1) / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 1.0));
		// 对应的三角面片的纹理坐标的下标
		texture_index.push_back( vec3i( 6*i, 6*i+1, 6*i+2 ) );

		// 面片2对应的顶点的纹理坐标
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 1.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i+1) / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i+1) / num_samples, 1.0));
		// 对应的三角面片的纹理坐标的下标
		texture_index.push_back( vec3i( 6*i+3, 6*i+4, 6*i+5 ) );
	}

	// 三角面片的每个顶点的法向量的下标,这里和顶点坐标的下标 faces是一致的,所以我们用faces就行
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标
	color_index = faces;

	storeFacesPoints();
}

void TriMesh::generateDisk(int num_division, float radius)
{
	cleanData(); // 清空之前的数据

	// @TODO: Task2 在此添加代码生成圆盘
	int num_samples = num_division;
	float step = 2 * M_PI / num_samples; // 计算每个切片的弧度

	// 生成下表面的顶点坐标,法向量和颜色
	float z = 0;
	for (int i = 0; i < num_samples; i++)
	{
		float theta = i * step;
		float x = radius * cos(theta);
		float y = radius * sin(theta);

		// 添加顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		// 添加法向量
		vertex_normals.push_back(glm::vec3(0, 0, 1));
		// 使用法向量生成颜色,你也可以根据需要自定义颜色生成方式
		vertex_colors.push_back(glm::vec3(0, 0, 1));
	}

	// 中心点
	vertex_positions.push_back(glm::vec3(0, 0, 0));
	vertex_normals.push_back(glm::vec3(0, 0, 1));
	vertex_colors.push_back(glm::vec3(0, 0, 1));

	// 生成三角面片,每个矩形由两个三角形面片构成
	for (int i = 0; i < num_samples; i++)
	{
		// 面片1
		faces.push_back(vec3i(i, (i + 1) % num_samples, num_samples));

		// 将0-360度映射到UV坐标的0-1
		for (int j = 0; j < 2; j++)
		{
			float theta = (i + j) * step;
			float x = cos(theta) / 2.0 + 0.5;
			float y = sin(theta) / 2.0 + 0.5;

			// 添加纹理坐标
			vertex_textures.push_back(glm::vec2(x, y));
		}

		// 中心点的纹理坐标
		vertex_textures.push_back(glm::vec2(0.5, 0.5));

		// 对应的三角面片的每个顶点的纹理坐标的下标
		texture_index.push_back(vec3i(3 * i, 3 * i + 1, 3 * i + 2));
	}

	// 三角面片的每个顶点的法向量的下标,这里和顶点坐标的下标 faces 是一致的,所以我们用 faces 就行
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标
	color_index = faces;

	// 存储面片顶点坐标
	storeFacesPoints();
}


void TriMesh::generateCone(int num_division, float radius, float height)
{
	// 清空之前的模型数据
	cleanData();

	// 计算每个切片的弧度 step
	int num_samples = num_division;
	float step = 2 * M_PI / num_samples;

	// 生成圆锥底部的顶点坐标、法向量和颜色
	float z = 0;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);

		// 添加底部顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		// 计算法向量并添加
		vertex_normals.push_back(normalize(glm::vec3(x, y, 0)));
		// 添加颜色,这里采用法向量作为颜色
		vertex_colors.push_back(normalize(glm::vec3(x, y, 0)));
	}

	// 添加圆锥尖端的顶点坐标、法向量和颜色
	vertex_positions.push_back(glm::vec3(0, 0, height));
	vertex_normals.push_back(glm::vec3(0, 0, 1));
	vertex_colors.push_back(glm::vec3(0, 0, 1));

	// 生成圆锥侧面的三角面片
	for (int i = 0; i < num_samples; i++)
	{
		// 三角面片
		faces.push_back(vec3i(num_samples, (i) % (num_samples), (i + 1) % (num_samples)));

		// 添加纹理坐标,这里采用简单的映射方式
		vertex_textures.push_back(glm::vec2(0.5, 1 - 0));
		vertex_textures.push_back(glm::vec2(1.0 * (i) / num_samples, 1 - 1));
		vertex_textures.push_back(glm::vec2(1.0 * (i + 1) / num_samples, 1 - 1));

		// 存储三角面片的每个顶点的纹理坐标的下标
		texture_index.push_back(vec3i(3 * i, 3 * i + 1, 3 * i + 2));
	}

	// 三角面片的每个顶点的法向量的下标,与顶点坐标的下标 faces 一致
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标,与顶点坐标的下标 faces 一致
	color_index = faces;

	// 存储面片的顶点坐标
	storeFacesPoints();
}


void TriMesh::readOff(const std::string &filename)
{
	// fin打开文件读取文件信息
	if (filename.empty())
	{
		return;
	}
	std::ifstream fin;
	fin.open(filename);
	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++)
		{
			glm::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();

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
};

// Light
glm::mat4 Light::getShadowProjectionMatrix()
{
	// 这里只实现了Y=0平面上的阴影投影矩阵,其他情况自己补充
	float lx, ly, lz;

	glm::mat4 modelMatrix = this->getModelMatrix();
	glm::vec4 light_position = modelMatrix * glm::vec4(this->translation, 1.0);

	lx = light_position[0];
	ly = light_position[1];
	lz = light_position[2];

	return glm::mat4(
		-ly, 0.0, 0.0, 0.0,
		lx, 0.0, lz, 1.0,
		0.0, 0.0, -ly, 0.0,
		0.0, 0.0, 0.0, -ly);
}

// 设置衰减系数的参数
void Light::setAttenuation(float _constant, float _linear, float _quadratic)
{
	constant = _constant;
	linear = _linear;
	quadratic = _quadratic;
}

float Light::getConstant() { return constant; };
float Light::getLinear() { return linear; };
float Light::getQuadratic() { return quadratic; };

实验四——main.cpp

#include "Angel.h"
#include "TriMesh.h"
#include "Camera.h"
#include "MeshPainter.h"

#include <vector>
#include <string>

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;

Camera* camera = new Camera();
Light* light = new Light();
MeshPainter* painter = new MeshPainter();

// 这个用来回收和删除我们创建的物体对象
std::vector<TriMesh*> meshList;

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

	// 设置光源位置
	light->setTranslation(glm::vec3(0.0, 0.0, 2.0));
	light->setAmbient(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 环境光
	light->setDiffuse(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 漫反射
	light->setSpecular(glm::vec4(1.0, 1.0, 1.0, 1.0)); // 镜面反射
	light->setAttenuation(1.0, 0.045, 0.0075); // 衰减系数


	// @TODO: Task2 读取桌子模型
	TriMesh* table = new TriMesh();
	table->setNormalize(true);
	table->readObj("./assets/table.obj");

	// 设置物体的旋转位移
	table->setTranslation(glm::vec3(-0.7, 0.0, 0.0));
	table->setRotation(glm::vec3(-90.0, 0.0, 0.0));
	table->setScale(glm::vec3(2.0, 2.0, 2.0));

	// 加到painter中
	painter->addMesh(table, "mesh_a", "./assets/table.png", vshader, fshader); 	// 指定纹理与着色器

	// 加入一个容器内,为了程序结束时将这些数据释放
	meshList.push_back(table);

	// @TODO: Task2 读取娃娃模型
	TriMesh* wawa = new TriMesh();
	wawa->setNormalize(true);
	wawa->readObj("./assets/wawa.obj");

	// 设置物体的旋转位移
	wawa->setTranslation(glm::vec3(0.7, 0.0, 0.0));
	wawa->setRotation(glm::vec3(-90.0, 0.0, 0.0));
	wawa->setScale(glm::vec3(2.0, 2.0, 2.0));

	// 加到painter中
	painter->addMesh(wawa, "mesh_b", "./assets/wawa.png", vshader, fshader); 	// 指定纹理与着色器

	// 加入一个容器内,为了程序结束时将这些数据释放
	meshList.push_back(wawa);


	glClearColor(1.0, 1.0, 1.0, 1.0);
	// glClearColor(0.0, 0.0, 0.0, 1.0);
}



void display()
{
	// #ifdef __APPLE__ // 解决 macOS 10.15 显示画面缩小问题
	// 	glViewport(0, 0, WIDTH * 2, HEIGHT * 2);
	// #endif
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	painter->drawMeshes(light, camera);

	//glutSwapBuffers();
}


void printHelp()
{
	std::cout << "================================================" << std::endl;
	std::cout << "Use mouse to controll the light position (drag)." << std::endl;
	std::cout << "================================================" << std::endl << std::endl;

	std::cout << "Keyboard Usage" << std::endl;
	std::cout <<
		"[Window]" << std::endl <<
		"ESC:		Exit" << std::endl <<
		"h:		Print help message" << std::endl <<

		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/U:		Increase/Decrease the rotate angle" << std::endl <<
		"i/I:		Increase/Decrease the up angle" << std::endl <<
		"o/O:		Increase/Decrease the camera radius" << std::endl << std::endl;

}

// 键盘响应函数
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_H: printHelp(); break;
		default:
			camera->keyboard(key, action, mode);
			break;
		}
	}
}

void cleanData() {
	// 释放内存

	delete camera;
	camera = NULL;

	delete light;
	light = NULL;

	painter->cleanMeshes();

	delete painter;
	painter = NULL;

	for (int i = 0; i < meshList.size(); i++) {
		delete meshList[i];
	}
	meshList.clear();
}

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
	//设置字符格式
#pragma execution_character_set("utf-8");
	// 配置窗口属性
	GLFWwindow* window = glfwCreateWindow(600, 600, "2021150047_hyf_实验四", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetKeyCallback(window, key_callback);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// 调用任何OpenGL的函数之前初始化GLAD
	// ---------------------------------------
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	// Init mesh, shaders, buffer
	init();
	// 输出帮助信息
	printHelp();
	// 启用深度测试
	glEnable(GL_DEPTH_TEST);
	while (!glfwWindowShouldClose(window))
	{
		display();
		//reshape();

		// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
		// -------------------------------------------------------------------------------
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	cleanData();


	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);
}

实验四——TriMesh.cpp

#include "TriMesh.h"

// 一些基础颜色
const glm::vec3 basic_colors[8] = {
	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
};

// 立方体的各个点
const glm::vec3 cube_vertices[8] = {
	glm::vec3(-0.5, -0.5, -0.5),
	glm::vec3(0.5, -0.5, -0.5),
	glm::vec3(-0.5, 0.5, -0.5),
	glm::vec3(0.5, 0.5, -0.5),
	glm::vec3(-0.5, -0.5, 0.5),
	glm::vec3(0.5, -0.5, 0.5),
	glm::vec3(-0.5, 0.5, 0.5),
	glm::vec3(0.5, 0.5, 0.5) };

// 三角形的点
const glm::vec3 triangle_vertices[3] = {
	glm::vec3(-0.5, -0.5, 0.0),
	glm::vec3(0.5, -0.5, 0.0),
	glm::vec3(0.0, 0.5, 0.0) };

// 正方形平面
const glm::vec3 square_vertices[4] = {
	glm::vec3(-0.5, -0.5, 0.0),
	glm::vec3(0.5, -0.5, 0.0),
	glm::vec3(0.5, 0.5, 0.0),
	glm::vec3(-0.5, 0.5, 0.0),
};

TriMesh::TriMesh()
{
	do_normalize_size = true;
	diagonal_length = 1.0;
}

TriMesh::~TriMesh()
{
}

std::vector<glm::vec3> TriMesh::getVertexPositions()
{
	return vertex_positions;
}

std::vector<glm::vec3> TriMesh::getVertexColors()
{
	return vertex_colors;
}

std::vector<glm::vec3> TriMesh::getVertexNormals()
{
	return vertex_normals;
}

std::vector<glm::vec2> TriMesh::getVertexTextures()
{
	return vertex_textures;
}

std::vector<vec3i> TriMesh::getFaces()
{
	return faces;
}

std::vector<glm::vec3> TriMesh::getPoints()
{
	return points;
}

std::vector<glm::vec3> TriMesh::getColors()
{
	return colors;
}

std::vector<glm::vec3> TriMesh::getNormals()
{
	return normals;
}

std::vector<glm::vec2> TriMesh::getTextures()
{
	return textures;
}

void TriMesh::computeTriangleNormals()
{
	face_normals.resize(faces.size());
	for (size_t i = 0; i < faces.size(); i++)
	{
		auto& face = faces[i];
		glm::vec3 v01 = vertex_positions[face.y] - vertex_positions[face.x];
		glm::vec3 v02 = vertex_positions[face.z] - vertex_positions[face.x];
		face_normals[i] = normalize(cross(v01, v02));
	}
}

void TriMesh::computeVertexNormals()
{
	// 计算面片的法向量
	if (face_normals.size() == 0 && faces.size() > 0)
	{
		computeTriangleNormals();
	}
	// 初始化法向量为0
	vertex_normals.resize(vertex_positions.size(), glm::vec3(0, 0, 0));
	for (size_t i = 0; i < faces.size(); i++)
	{
		auto& face = faces[i];
		vertex_normals[face.x] += face_normals[i];
		vertex_normals[face.y] += face_normals[i];
		vertex_normals[face.z] += face_normals[i];
	}
	for (size_t i = 0; i < vertex_normals.size(); i++)
	{
		vertex_normals[i] = normalize(vertex_normals[i]);
	}
	// 球心在原点的球法向量为坐标
	// for (int i = 0; i < vertex_positions.size(); i++)
	// 	vertex_normals.push_back(vertex_positions[i] - vec3(0.0, 0.0, 0.0));
}

void TriMesh::setNormalize(bool do_norm) { do_normalize_size = do_norm; }
bool TriMesh::getNormalize() { return do_normalize_size; }
float TriMesh::getDiagonalLength() { return diagonal_length; }

glm::vec3 TriMesh::getTranslation()
{
	return translation;
}

glm::vec3 TriMesh::getRotation()
{
	return rotation;
}

glm::vec3 TriMesh::getScale()
{
	return scale;
}

glm::mat4 TriMesh::getModelMatrix()
{
	glm::mat4 model = glm::mat4(1.0f);
	glm::vec3 trans = getTranslation();
	model = glm::translate(model, getTranslation());
	model = glm::rotate(model, glm::radians(getRotation()[2]), glm::vec3(0.0, 0.0, 1.0));
	model = glm::rotate(model, glm::radians(getRotation()[1]), glm::vec3(0.0, 1.0, 0.0));
	model = glm::rotate(model, glm::radians(getRotation()[0]), glm::vec3(1.0, 0.0, 0.0));
	model = glm::scale(model, getScale());
	return model;
}

void TriMesh::setTranslation(glm::vec3 translation)
{
	this->translation = translation;
}

void TriMesh::setRotation(glm::vec3 rotation)
{
	this->rotation = rotation;
}

void TriMesh::setScale(glm::vec3 scale)
{
	this->scale = scale;
}

glm::vec4 TriMesh::getAmbient() { return ambient; };
glm::vec4 TriMesh::getDiffuse() { return diffuse; };
glm::vec4 TriMesh::getSpecular() { return specular; };
float TriMesh::getShininess() { return shininess; };

void TriMesh::setAmbient(glm::vec4 _ambient) { ambient = _ambient; };
void TriMesh::setDiffuse(glm::vec4 _diffuse) { diffuse = _diffuse; };
void TriMesh::setSpecular(glm::vec4 _specular) { specular = _specular; };
void TriMesh::setShininess(float _shininess) { shininess = _shininess; };

void TriMesh::cleanData()
{
	vertex_positions.clear();
	vertex_colors.clear();
	vertex_normals.clear();
	vertex_textures.clear();

	faces.clear();
	normal_index.clear();
	color_index.clear();
	texture_index.clear();

	face_normals.clear();


	points.clear();
	colors.clear();
	normals.clear();
	textures.clear();
}

void TriMesh::storeFacesPoints()
{
	// 将读取的顶点根据三角面片上的顶点下标逐个加入
	// 要传递给GPU的points等容器内

	// 看是否归一化物体大小,是的话,这里将物体顶点缩放到对角线长度为1的包围盒内
	if (do_normalize_size) {
		// 记录物体包围盒大小,可以用于大小的归一化
		// 先获得包围盒的对角顶点
		float max_x = -FLT_MAX;
		float max_y = -FLT_MAX;
		float max_z = -FLT_MAX;
		float min_x = FLT_MAX;
		float min_y = FLT_MAX;
		float min_z = FLT_MAX;
		for (int i = 0; i < vertex_positions.size(); i++) {
			auto& position = vertex_positions[i];
			if (position.x > max_x) max_x = position.x;
			if (position.y > max_y) max_y = position.y;
			if (position.z > max_z) max_z = position.z;
			if (position.x < min_x) min_x = position.x;
			if (position.y < min_y) min_y = position.y;
			if (position.z < min_z) min_z = position.z;
		}
		up_corner = glm::vec3(max_x, max_y, max_z);
		down_corner = glm::vec3(min_x, min_y, min_z);
		center = glm::vec3((min_x + max_x) / 2.0, (min_y + max_y) / 2.0, (min_z + max_z) / 2.0);

		diagonal_length = length(up_corner - down_corner);

		for (int i = 0; i < vertex_positions.size(); i++) {
			vertex_positions[i] = (vertex_positions[i] - center) / diagonal_length;
		}
	}

	// 计算法向量
	if (vertex_normals.size() == 0)
		computeVertexNormals();

	// @TODO Task1 根据每个三角面片的顶点下标存储要传入GPU的数据
	for (int i = 0; i < faces.size(); 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(vertex_colors[color_index[i].x]);
		colors.push_back(vertex_colors[color_index[i].y]);
		colors.push_back(vertex_colors[color_index[i].z]);
		// 法向量
		if (vertex_normals.size() != 0)
		{
			normals.push_back(vertex_normals[normal_index[i].x]);
			normals.push_back(vertex_normals[normal_index[i].y]);
			normals.push_back(vertex_normals[normal_index[i].z]);
		}
		// 纹理
		if (vertex_textures.size() != 0)
		{
			textures.push_back(vertex_textures[texture_index[i].x]);
			textures.push_back(vertex_textures[texture_index[i].y]);
			textures.push_back(vertex_textures[texture_index[i].z]);
		}
	}
}

// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube()
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 8; i++)
	{
		vertex_positions.push_back(cube_vertices[i]);
		vertex_colors.push_back(basic_colors[i]);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 1, 3));
	faces.push_back(vec3i(0, 3, 2));
	faces.push_back(vec3i(1, 4, 5));
	faces.push_back(vec3i(1, 0, 4));
	faces.push_back(vec3i(4, 0, 2));
	faces.push_back(vec3i(4, 2, 6));
	faces.push_back(vec3i(5, 6, 4));
	faces.push_back(vec3i(5, 7, 6));
	faces.push_back(vec3i(2, 6, 7));
	faces.push_back(vec3i(2, 7, 3));
	faces.push_back(vec3i(1, 5, 7));
	faces.push_back(vec3i(1, 7, 3));

	// 顶点纹理坐标,这里是每个面都用一个正方形图片的情况
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0, 1));
	vertex_textures.push_back(glm::vec2(1, 1));
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0, 1));
	vertex_textures.push_back(glm::vec2(1, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
}

void TriMesh::generateTriangle(glm::vec3 color)
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 3; i++)
	{
		vertex_positions.push_back(triangle_vertices[i]);
		vertex_colors.push_back(color);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 1, 2));

	// 顶点纹理坐标
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(0.5, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
}

void TriMesh::generateSquare(glm::vec3 color)
{
	// 创建顶点前要先把那些vector清空
	cleanData();

	for (int i = 0; i < 4; i++)
	{
		vertex_positions.push_back(square_vertices[i]);
		vertex_colors.push_back(color);
	}

	// 每个三角面片的顶点下标
	faces.push_back(vec3i(0, 1, 2));
	faces.push_back(vec3i(0, 2, 3));

	// 顶点纹理坐标
	vertex_textures.push_back(glm::vec2(0, 0));
	vertex_textures.push_back(glm::vec2(1, 0));
	vertex_textures.push_back(glm::vec2(1, 1));
	vertex_textures.push_back(glm::vec2(0, 1));

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
}

void TriMesh::generateCylinder(int num_division, float radius, float height)
{

	cleanData();

	int num_samples = num_division;
	float step = 2 * M_PI / num_samples; // 每个切片的弧度

	// 按cos和sin生成x,y坐标,z为负,即得到下表面顶点坐标
	// 顶点, 纹理
	float z = -height;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);
		// 添加顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		vertex_normals.push_back(normalize(glm::vec3(x, y, 0)));
		// 这里颜色和法向量一样
		vertex_colors.push_back(normalize(glm::vec3(x, y, 0)));
	}

	// 按cos和sin生成x,y坐标,z为正,即得到上表面顶点坐标
	z = height;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);
		vertex_positions.push_back(glm::vec3(x, y, z));
		vertex_normals.push_back(normalize(glm::vec3(x, y, 0)));
		vertex_colors.push_back(normalize(glm::vec3(x, y, 0)));
	}

	// 面片生成三角面片,每个矩形由两个三角形面片构成
	for (int i = 0; i < num_samples; i++)
	{
		// 面片1
		faces.push_back(vec3i(i, (i + 1) % num_samples, (i)+num_samples));
		// 面片2
		faces.push_back(vec3i((i)+num_samples, (i + 1) % num_samples, (i + num_samples + 1) % (num_samples)+num_samples));

		// 面片1对应的顶点的纹理坐标
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i + 1) / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 1.0));
		// 对应的三角面片的纹理坐标的下标
		texture_index.push_back(vec3i(6 * i, 6 * i + 1, 6 * i + 2));

		// 面片2对应的顶点的纹理坐标
		vertex_textures.push_back(glm::vec2(1.0 * i / num_samples, 1.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i + 1) / num_samples, 0.0));
		vertex_textures.push_back(glm::vec2(1.0 * (i + 1) / num_samples, 1.0));
		// 对应的三角面片的纹理坐标的下标
		texture_index.push_back(vec3i(6 * i + 3, 6 * i + 4, 6 * i + 5));
	}

	// 三角面片的每个顶点的法向量的下标,这里和顶点坐标的下标 faces是一致的,所以我们用faces就行
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标
	color_index = faces;

	storeFacesPoints();
}

void TriMesh::generateDisk(int num_division, float radius)
{
	cleanData(); // 清空之前的数据

	// @TODO: Task2 在此添加代码生成圆盘
	int num_samples = num_division;
	float step = 2 * M_PI / num_samples; // 计算每个切片的弧度

	// 生成下表面的顶点坐标,法向量和颜色
	float z = 0;
	for (int i = 0; i < num_samples; i++)
	{
		float theta = i * step;
		float x = radius * cos(theta);
		float y = radius * sin(theta);

		// 添加顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		// 添加法向量
		vertex_normals.push_back(glm::vec3(0, 0, 1));
		// 使用法向量生成颜色,你也可以根据需要自定义颜色生成方式
		vertex_colors.push_back(glm::vec3(0, 0, 1));
	}

	// 中心点
	vertex_positions.push_back(glm::vec3(0, 0, 0));
	vertex_normals.push_back(glm::vec3(0, 0, 1));
	vertex_colors.push_back(glm::vec3(0, 0, 1));

	// 生成三角面片,每个矩形由两个三角形面片构成
	for (int i = 0; i < num_samples; i++)
	{
		// 面片1
		faces.push_back(vec3i(i, (i + 1) % num_samples, num_samples));

		// 将0-360度映射到UV坐标的0-1
		for (int j = 0; j < 2; j++)
		{
			float theta = (i + j) * step;
			float x = cos(theta) / 2.0 + 0.5;
			float y = sin(theta) / 2.0 + 0.5;

			// 添加纹理坐标
			vertex_textures.push_back(glm::vec2(x, y));
		}

		// 中心点的纹理坐标
		vertex_textures.push_back(glm::vec2(0.5, 0.5));

		// 对应的三角面片的每个顶点的纹理坐标的下标
		texture_index.push_back(vec3i(3 * i, 3 * i + 1, 3 * i + 2));
	}

	// 三角面片的每个顶点的法向量的下标,这里和顶点坐标的下标 faces 是一致的,所以我们用 faces 就行
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标
	color_index = faces;

	// 存储面片顶点坐标
	storeFacesPoints();
}


void TriMesh::generateCone(int num_division, float radius, float height)
{
	// 清空之前的模型数据
	cleanData();

	// 计算每个切片的弧度 step
	int num_samples = num_division;
	float step = 2 * M_PI / num_samples;

	// 生成圆锥底部的顶点坐标、法向量和颜色
	float z = 0;
	for (int i = 0; i < num_samples; i++)
	{
		float r_r_r = i * step;
		float x = radius * cos(r_r_r);
		float y = radius * sin(r_r_r);

		// 添加底部顶点坐标
		vertex_positions.push_back(glm::vec3(x, y, z));
		// 计算法向量并添加
		vertex_normals.push_back(normalize(glm::vec3(x, y, 0)));
		// 添加颜色,这里采用法向量作为颜色
		vertex_colors.push_back(normalize(glm::vec3(x, y, 0)));
	}

	// 添加圆锥尖端的顶点坐标、法向量和颜色
	vertex_positions.push_back(glm::vec3(0, 0, height));
	vertex_normals.push_back(glm::vec3(0, 0, 1));
	vertex_colors.push_back(glm::vec3(0, 0, 1));

	// 生成圆锥侧面的三角面片
	for (int i = 0; i < num_samples; i++)
	{
		// 三角面片
		faces.push_back(vec3i(num_samples, (i) % (num_samples), (i + 1) % (num_samples)));

		// 添加纹理坐标,这里采用简单的映射方式
		vertex_textures.push_back(glm::vec2(0.5, 1 - 0));
		vertex_textures.push_back(glm::vec2(1.0 * (i) / num_samples, 1 - 1));
		vertex_textures.push_back(glm::vec2(1.0 * (i + 1) / num_samples, 1 - 1));

		// 存储三角面片的每个顶点的纹理坐标的下标
		texture_index.push_back(vec3i(3 * i, 3 * i + 1, 3 * i + 2));
	}

	// 三角面片的每个顶点的法向量的下标,与顶点坐标的下标 faces 一致
	normal_index = faces;
	// 三角面片的每个顶点的颜色的下标,与顶点坐标的下标 faces 一致
	color_index = faces;

	// 存储面片的顶点坐标
	storeFacesPoints();
}

void TriMesh::readOff(const std::string& filename)
{
	// fin打开文件读取文件信息
	if (filename.empty())
	{
		return;
	}
	std::ifstream fin;
	fin.open(filename);
	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++)
		{
			glm::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();

	normal_index = faces;
	color_index = faces;
	texture_index = faces;

	storeFacesPoints();
};


void TriMesh::readObj(const std::string& filename)
{
	std::ifstream fin(filename);
	std::string line;

	if (!fin)
	{
		std::cout << "ERROR: cannot open the file: " << filename << std::endl;
		exit(0);	// 退出程序
	}

	cleanData();

	while (std::getline(fin, line))
	{
		std::istringstream sin(line);
		std::string type;
		GLfloat _x, _y, _z;
		int a0, b0, c0;
		int a1, b1, c1;
		int a2, b2, c2;
		char slash;
		sin >> type;

		// @TODO: Task2 读取obj文件,记录里面的这些数据,可以参考readOff的写法
		// vertex_positions
		// vertex_normals
		// vertex_textures		

		// faces
		// normal_index
		// texture_index

		// 其中vertex_color和color_index可以用法向量的数值赋值

		// 解析OBJ文件中的顶点信息
		if (type == "v")
		{
			sin >> _x >> _y >> _z;
			// 存储顶点坐标
			vertex_positions.push_back(glm::vec3(_x, _y, _z));
		}

		// 解析OBJ文件中的法向量信息
		if (type == "vn")
		{
			sin >> _x >> _y >> _z;
			// 存储法向量
			vertex_normals.push_back(glm::vec3(_x, _y, _z));
			// 可选择存储颜色信息
			// vertex_colors.push_back(glm::vec3(_x, _y, _z));
		}

		// 解析OBJ文件中的纹理坐标信息
		if (type == "vt")
		{
			sin >> _x >> _y >> _z;
			// 存储纹理坐标
			vertex_textures.push_back(glm::vec2(_x, _y));
		}

		// 解析OBJ文件中的面信息
		if (type == "f")
		{
			sin >> a0 >> slash >> b0 >> slash >> c0;
			sin >> a1 >> slash >> b1 >> slash >> c1;
			sin >> a2 >> slash >> b2 >> slash >> c2;
			// 可选择解析四边形面信息
			// sin >> a3 >> slash >> b3 >> slash >> c3;

			// 存储面的顶点、纹理坐标和法向量的索引信息
			faces.push_back(vec3i(a0 - 1, a1 - 1, a2 - 1));
			texture_index.push_back(vec3i(b0 - 1, b1 - 1, b2 - 1));
			normal_index.push_back(vec3i(c0 - 1, c1 - 1, c2 - 1));
		}


	}

	vertex_colors = vertex_normals;
	color_index = normal_index;
	storeFacesPoints();
}



// Light
glm::mat4 Light::getShadowProjectionMatrix()
{
	// 这里只实现了Y=0平面上的阴影投影矩阵,其他情况自己补充
	float lx, ly, lz;

	glm::mat4 modelMatrix = this->getModelMatrix();
	glm::vec4 light_position = modelMatrix * glm::vec4(this->translation, 1.0);

	lx = light_position[0];
	ly = light_position[1];
	lz = light_position[2];

	return glm::mat4(
		-ly, 0.0, 0.0, 0.0,
		lx, 0.0, lz, 1.0,
		0.0, 0.0, -ly, 0.0,
		0.0, 0.0, 0.0, -ly);
}

// 设置衰减系数的参数
void Light::setAttenuation(float _constant, float _linear, float _quadratic)
{
	constant = _constant;
	linear = _linear;
	quadratic = _quadratic;
}

float Light::getConstant() { return constant; };
float Light::getLinear() { return linear; };
float Light::getQuadratic() { return quadratic; };

(by 归忆)

  • 28
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用OpenMesh读取.obj文件纹理时,我们可以使用以下步骤: 第一步是使用OpenMesh的函数打开.obj文件,可以使用类似于下面的代码: ```cpp #include <iostream> #include <OpenMesh/Core/IO/MeshIO.hh> #include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh> typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh; int main() { // 创建一个网格对象 MyMesh mesh; // 从.obj文件读取网格数据 if (!OpenMesh::IO::read_mesh(mesh, "filename.obj")) { std::cerr << "无法读取文件!" << std::endl; return 1; } // 打印顶点和面的数量 std::cout << "顶点数量: " << mesh.n_vertices() << std::endl; std::cout << "面数量: " << mesh.n_faces() << std::endl; return 0; } ``` 这样就可以使用OpenMesh读取.obj文件并打印出顶点和面的数量。 第二步是读取纹理信息。对于纹理信息,我们可以使用OpenMesh库的属性(Property)特性。可以使用以下代码读取.obj文件中的纹理坐标: ```cpp MyMesh::TexCoord2D texCoord; // 获取属性句柄 MyMesh::VertexHandle vh; // 检查是否有纹理坐标 if (mesh.has_vertex_texcoords2D()) { // 获取纹理坐标 texCoord = mesh.texcoord2D(vh); std::cout << "纹理坐标: " << texCoord[0] << ", " << texCoord[1] << std::endl; } ``` 当然,你也可以根据自己的需求进一步扩展代码,例如读取法线、颜色等其他属性信息。 总之,OpenMesh提供了方便的函数来读取.obj文件纹理信息,你可以根据你的需求来使用它们。希望这个简短的回答对你有帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

归忆_AC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值