【深圳大学计算机图形学】期末大作业 虚拟场景建模

目录

实验内容

实验要求

实验过程

Bug解决

实验结论

实验代码

期末大作业——main.cpp

期末大作业——Camera.cpp

期末大作业——MeshPainter.cpp

期末大作业——TriMesh.cpp

期末大作业——fshader.glsl

期末大作业——fshader_1.glsl

期末大作业——vshader.glsl


实验内容

在屏幕上显示一个包含多个虚拟物体的虚拟场景,并且响应一定的用户交互操作。如以下例图:

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第1张图片

实验要求

1.     场景设计和显示

学生可以通过层级建模( 实验补充1和2)的方式建立多个虚拟物体,由多个虚拟物体组成一个虚拟场景,要求在程序中显示该虚拟场景,场景可以是室内或者室外场景;场景应包含地面。层级建模的最深层次需要达到至少四层。

2.     添加纹理

参考实验4.1,为场景中至少两个主要物体添加纹理贴图 。

3.     添加光照、材质、阴影效果

参考实验3.2,实验3.3和实验3.4,实现光照效果、材质、阴影等。

4.     用户交互实现视角切换完成对场景的任意角度浏览

参考实验3.1,完成相机变换。

5.     通过交互控制物体

参考实验2.3,实现物体的变换,允许用户通过键盘或者鼠标实现场景中至少两个物体的控制(移动,旋转,缩放等等)。

实验过程

1. 层级建模实现一个机器人,躯干->左上臂->左下臂->剑,达到了四层的层次,如下图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第2张图片    【深圳大学计算机图形学】期末大作业 虚拟场景建模_第3张图片

2. 在层次建模中,利用层级树的方法进行遍历绘制多个长方体,最终拼成一个机器人。

按深度优先顺序,即 “躯干 -> 头 -> 左上臂 -> 左小臂 -> 剑 -> 右上臂 -> 右下臂 -> 左上腿 -> 左下腿 -> 右上腿 -> 右下腿”的顺序完成层级树的遍历,完成display()函数。

先分别实现物体各部位的绘制函数,如head(glm::mat4 modelMatrix)等,再根据上图树的思想,利用堆栈进行绘制。

3.先建立Robot结构体,存储相关参数。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第4张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第5张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第6张图片

4.新建变量,为后面绘制做好准备。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第7张图片

5.实现一系列函数,完成对身体各个部分的绘制。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第8张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第9张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第10张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第11张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第12张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第13张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第14张图片

6.在init函数中设置物体的大小(初始的旋转和位移都为0),并将物体的顶点数据传递。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第15张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第16张图片

7.在display函数中进行机器人的绘制。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第17张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第18张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第19张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第20张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第21张图片

8.设置操控机器人的按键。

                                        [机器人控制选择]

                1:                      躯干

                2:                      头部

                3:                      左大臂

                4:                      左小臂

                5:                      右大臂

                6:                      右小臂

                7:                      左大腿

                8:                      左小腿

                9:                      右大腿

                0:                      右小腿

                        [机器人操作]

                a/A:                    增加旋转角度

                s/S:                    减小旋转角度

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第22张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第23张图片

10.运行代码,操控机器人如图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第24张图片

11.读取桌子模型并给其附加上红木纹理。             

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第25张图片

12.读取girl模型并给其附加上纹理。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第26张图片

13.读取lamp模型并附加上纹理。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第27张图片

14.运行代码,table、girl、lamp如下图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第28张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第29张图片

15.创建长方形平面,并附加上木质纹理,作为木质地板。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第30张图片

16.运行代码,木质地板如下图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第31张图片

17.将背景变为天蓝色,作为天空背景。

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

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第32张图片

19.读取皮卡丘off模型。

20.设置根据按键切换不同的off模型。

                                            [模型切换]

                v/V:                    切换为球体

                b/B:                    切换为牛

                n/N:                    切换为松鼠

                m/M:                    切换为皮卡丘

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第33张图片

21.运行代码,如下图所示,按键可以切换不同的模型。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第34张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第35张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第36张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第37张图片

22.设置光源和阴影。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第38张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第39张图片

23.设置物体材质。

24.调用draw_obj函数绘制图像,将isShadow设置为0表示根据光照绘制,并传递光照和材质数据。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第40张图片

25. 调用draw_sha函数绘制阴影,确定投影矩阵,将isShadow设置为3表示黑色。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第41张图片

26.在display函数中绘制光照模型及阴影。

27.实现相机以及对视图的切换。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第42张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第43张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第44张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第45张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第46张图片

28.设置键盘快捷键进行操控。

                        [相机控制]

                j/J:                    顺时针旋转/逆时针旋转

                k/K:                    上移视角/下移视角

                SPACE:                  重置相机

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第47张图片

29.运行代码,切换不同的视角进行观看。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第48张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第49张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第50张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第51张图片

30.添加操控功能,可以对不同的物体进行放大、缩小、前移、后移、左移、右移、顺时针旋转、逆时针旋转操作。

                        [物体控制选择]

                u/U:                    控制桌子

                i/i:                    控制女孩

                o/O:                    控制灯笼

                p/P:                    控制模型

                        [物体操作]

                left:                   左移

                right:                  右移

                up:                     前移

                down:                   后移

                +/=:                    变大

                -/_:                    变小

                r/R:                    顺时针旋转/逆时针旋转

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第52张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第53张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第54张图片

31.运行代码,进行操控如下图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第55张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第56张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第57张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第58张图片

32.添加光源控制功能,能够控制光源左移、右移、上移、下移、前移、后移。

                        [光源控制]

                x/X:                    光源左移/光源右移

                y/Y:                    光源下移/光源上移

                z/Z:                    光源前移/光源后移

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第59张图片

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

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第60张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第61张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第62张图片

34.添加用户交互界面说明,完善交互性,使得交互更易懂。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第63张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第64张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第65张图片

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

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第66张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第67张图片

Bug解决

(1)中文输出失败,有中文时编译错误。

解决方法:更改文件格式为gbk。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第68张图片

(2)地板附加了木质纹理,在输出的时候,同样会有阴影产生。导致出现平面下半面有纹理,而上半面全黑色,即被阴影覆盖了,如下图所示。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第69张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第70张图片

解决方案:在绘制时添加一个信号变量,来决定是否要绘制阴影,以此使得地板不被绘制阴影。

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第71张图片

【深圳大学计算机图形学】期末大作业 虚拟场景建模_第72张图片

实验结论

在本次实验中,综合了这学期学过的众多知识,包括层级建模,图形绘制,物体绘制,阴影绘制添加纹理,键盘控制,场景交互,相机转动等,所含知识点和实验操作非常多。要想顺利完成整个实验,需要扎实的基础知识,熟悉并实操此前所完成的各种实验并掌握原理,这样才能在自己的场景中灵活运用实现想要的效果。

通过本次实验,更加熟悉了此前不太掌握的纹理绘制和层级建模原理,此外最大的收益在于更深入了解了计算机图形学的原理,熟悉了OpenGL下的基本框架,学会该如何利用计算机进行包含众多物体的场景绘制以及交互控制物体。

实验代码

期末大作业——main.cpp

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

#include 
#include 
#include 
#include 

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;

class MatrixStack {
	int		_index;
	int		_size;
	glm::mat4* _matrices;

public:
	MatrixStack(int numMatrices = 100) :_index(0), _size(numMatrices)
	{
		_matrices = new glm::mat4[numMatrices];
	}

	~MatrixStack()
	{
		delete[]_matrices;
	}

	void push(const glm::mat4& m) {
		assert(_index + 1 < _size);
		_matrices[_index++] = m;
	}

	glm::mat4& pop() {
		assert(_index - 1 >= 0);
		_index--;
		return _matrices[_index];
	}
};


#define White	glm::vec3(1.0, 1.0, 1.0)
#define Yellow	glm::vec3(1.0, 1.0, 0.0)
#define Green	glm::vec3(0.0, 1.0, 0.0)
#define Cyan	glm::vec3(0.0, 1.0, 1.0)
#define Magenta	glm::vec3(1.0, 0.0, 1.0)
#define Red		glm::vec3(1.0, 0.0, 0.0)
#define Black	glm::vec3(0.0, 0.0, 0.0)
#define Blue	glm::vec3(0.0, 0.0, 1.0)
#define Brown	glm::vec3(0.5, 0.5, 0.5)
#define Purple  glm::vec3(0.5, 0.0, 0.5)
#define sky     glm::vec3(0.4, 0.77, 1.0)



struct Robot
{
	// 关节大小
	float TORSO_HEIGHT = 4.0 * 0.1;
	float TORSO_WIDTH = 2.5 * 0.1;
	float UPPER_ARM_HEIGHT = 2.5 * 0.1;
	float LOWER_ARM_HEIGHT = 1.8 * 0.1;
	float UPPER_ARM_WIDTH = 0.8 * 0.1;
	float LOWER_ARM_WIDTH = 0.5 * 0.1;
	float UPPER_LEG_HEIGHT = 2.8 * 0.1;
	float LOWER_LEG_HEIGHT = 2.2 * 0.1;
	float UPPER_LEG_WIDTH = 1.0 * 0.1;
	float LOWER_LEG_WIDTH = 0.5 * 0.1;
	float HEAD_HEIGHT = 1.8 * 0.1;
	float HEAD_WIDTH = 1.5 * 0.1;

	// 关节角和菜单选项值
	enum {
		Torso,			// 躯干
		Head,			// 头部
		RightUpperArm,	// 右大臂
		RightLowerArm,	// 右小臂
		LeftUpperArm,	// 左大臂
		LeftLowerArm,	// 左小臂
		RightUpperLeg,	// 右大腿
		RightLowerLeg,	// 右小腿
		LeftUpperLeg,	// 左大腿
		LeftLowerLeg,	// 左小腿
		left_sword,
	};

	// 关节角大小
	GLfloat theta[10] = {
		0.0,    // Torso
		0.0,    // Head
		0.0,    // RightUpperArm
		0.0,    // RightLowerArm
		0.0,    // LeftUpperArm
		0.0,    // LeftLowerArm
		0.0,    // RightUpperLeg
		0.0,    // RightLowerLeg
		0.0,    // LeftUpperLeg
		0.0     // LeftLowerLeg
	};	
};
Robot robot;
// 被选中的物体
int Selected_mesh = robot.Torso;

TriMesh* Torso = new TriMesh();
TriMesh* Head = new TriMesh();
TriMesh* RightUpperArm = new TriMesh();
TriMesh* RightLowerArm = new TriMesh();
TriMesh* LeftUpperArm = new TriMesh();
TriMesh* LeftLowerArm = new TriMesh();
TriMesh* RightUpperLeg = new TriMesh();
TriMesh* RightLowerLeg = new TriMesh();
TriMesh* LeftUpperLeg = new TriMesh();
TriMesh* LeftLowerLeg = new TriMesh();
TriMesh* left_sword = new TriMesh();

openGLObject TorsoObject;
openGLObject HeadObject;
openGLObject RightUpperArmObject;
openGLObject RightLowerArmObject;
openGLObject LeftUpperArmObject;
openGLObject LeftLowerArmObject;
openGLObject RightUpperLegObject;
openGLObject RightLowerLegObject;
openGLObject LeftUpperLegObject;
openGLObject LeftLowerLegObject;
openGLObject left_sword_Object;

openGLObject mesh_object;
openGLObject plane_object;

TriMesh* table = new TriMesh();
TriMesh* girl = new TriMesh();
TriMesh* lamp = new TriMesh();
TriMesh* plane = new TriMesh();
TriMesh* mesh = new TriMesh();

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

glm::vec3 light_position;

float move_step_size = 0.2;

// 获取生成的所有模型,用于结束程序时释放内存
std::vector meshList;

void draw_sha(TriMesh* m, openGLObject& mesh_object, int sha);
void bindLightAndMaterial(TriMesh* mesh, openGLObject& object, Light* light, Camera* camera);

void drawMesh(glm::mat4 modelMatrix, TriMesh* mesh, openGLObject object) {
	glBindVertexArray(object.vao);
	glUseProgram(object.program);

	// 父节点矩阵 * 本节点局部变换矩阵
	glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(object.viewLocation, 1, GL_TRUE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(object.projectionLocation, 1, GL_TRUE, &camera->projMatrix[0][0]);
	glUniform1i(object.shadowLocation, 0);
	bindLightAndMaterial(mesh, object, light, camera);
	// 绘制
	glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());

	// 根据光源位置,计算阴影投影矩阵
	light_position = light->getTranslation();
	float lx = light_position[0];
	float ly = light_position[1];
	float lz = light_position[2];
	glm::mat4 shadowProjMatrix(
		-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);
	// 计算阴影的模型变换矩阵。
	modelMatrix = shadowProjMatrix * modelMatrix;
	// 传递 isShadow 变量,1表示黑色。
	glUniform1i(object.shadowLocation, 1);
	// 传递 unifrom 关键字的矩阵数据。
	glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	// 绘制
	glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());
}

// 躯体
void torso(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, 0.5 * robot.TORSO_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.TORSO_WIDTH, robot.TORSO_HEIGHT, robot.TORSO_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, Torso, TorsoObject);
}

// 头部
void head(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, 0.5 * robot.HEAD_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.HEAD_WIDTH, robot.HEAD_HEIGHT, robot.HEAD_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, Head, HeadObject);
}


// 左大臂
void left_upper_arm(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.UPPER_ARM_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.UPPER_ARM_WIDTH, robot.UPPER_ARM_HEIGHT, robot.UPPER_ARM_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, LeftUpperArm, LeftUpperArmObject);
}


// @TODO: 左小臂
void left_lower_arm(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.LOWER_ARM_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.LOWER_ARM_WIDTH, robot.LOWER_ARM_HEIGHT, robot.LOWER_ARM_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, LeftLowerArm, LeftLowerArmObject);
}

// 圆柱体
void draw_left_sword(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, 0.1, 0.15));
	instance = glm::scale(instance, glm::vec3(0.03, 0.03, 0.4));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, left_sword, left_sword_Object);
}

// @TODO: 右大臂
void right_upper_arm(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.UPPER_ARM_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.UPPER_ARM_WIDTH, robot.UPPER_ARM_HEIGHT, robot.UPPER_ARM_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, RightUpperArm, RightUpperArmObject);
}

// @TODO: 右小臂
void right_lower_arm(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.LOWER_ARM_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.LOWER_ARM_WIDTH, robot.LOWER_ARM_HEIGHT, robot.LOWER_ARM_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, RightLowerArm, RightLowerArmObject);
}

// @TODO: 左大腿
void left_upper_leg(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.UPPER_LEG_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.UPPER_LEG_WIDTH, robot.UPPER_LEG_HEIGHT, robot.UPPER_LEG_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, LeftUpperLeg, LeftUpperLegObject);
}

// @TODO: 左小腿
void left_lower_leg(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.LOWER_LEG_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.LOWER_LEG_WIDTH, robot.LOWER_LEG_HEIGHT, robot.LOWER_LEG_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, LeftLowerLeg, LeftLowerLegObject);
}

// @TODO: 右大腿
void right_upper_leg(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.UPPER_LEG_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.UPPER_LEG_WIDTH, robot.UPPER_LEG_HEIGHT, robot.UPPER_LEG_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, RightUpperLeg, RightUpperLegObject);
}

// @TODO: 右小腿
void right_lower_leg(glm::mat4 modelMatrix)
{
	// 本节点局部变换矩阵
	glm::mat4 instance = glm::mat4(1.0);
	instance = glm::translate(instance, glm::vec3(0.0, -0.5 * robot.LOWER_LEG_HEIGHT, 0.0));
	instance = glm::scale(instance, glm::vec3(robot.LOWER_LEG_WIDTH, robot.LOWER_LEG_HEIGHT, robot.LOWER_LEG_WIDTH));

	// 乘以来自父物体的模型变换矩阵,绘制当前物体
	drawMesh(modelMatrix * instance, RightLowerLeg, RightLowerLegObject);
}


void bindObjectAndData(TriMesh* mesh, openGLObject& object, const std::string& vshader, const std::string& fshader) {

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

	// 创建并初始化顶点缓存对象
	glGenBuffers(1, &object.vbo);
	glBindBuffer(GL_ARRAY_BUFFER, object.vbo);
	glBufferData(GL_ARRAY_BUFFER,
		(mesh->getPoints().size() + mesh->getColors().size() + mesh->getNormals().size()) * sizeof(glm::vec3),
		NULL,
		GL_STATIC_DRAW);

	// 修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错
	glBufferSubData(GL_ARRAY_BUFFER, 0, mesh->getPoints().size() * sizeof(glm::vec3), &mesh->getPoints()[0]);
	glBufferSubData(GL_ARRAY_BUFFER, mesh->getPoints().size() * sizeof(glm::vec3), mesh->getColors().size() * sizeof(glm::vec3), &mesh->getColors()[0]);
	glBufferSubData(GL_ARRAY_BUFFER, (mesh->getPoints().size() + mesh->getColors().size()) * sizeof(glm::vec3), mesh->getNormals().size() * sizeof(glm::vec3), &mesh->getNormals()[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(glm::vec3)));

	// 从顶点着色器中初始化顶点的法向量
	object.nLocation = glGetAttribLocation(object.program, "vNormal");
	glEnableVertexAttribArray(object.nLocation);
	glVertexAttribPointer(object.nLocation, 3,
		GL_FLOAT, GL_FALSE, 0,
		BUFFER_OFFSET((mesh->getPoints().size() + mesh->getColors().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");
}


void 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]);

}

void mesh_init() {
	std::string vshader, fshader;
	// 读取着色器并使用
	vshader = "shaders/vshader.glsl";
	fshader = "shaders/fshader.glsl";
	// 设置物体的旋转位移
	mesh->setTranslation(glm::vec3(0.9, 0.34, 0.0));
	mesh->setRotation(glm::vec3(0.0, -90.0, 0.0));
	mesh->setScale(glm::vec3(1.0, 1.0, 1.0));
	// 设置材质
	mesh->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 环境光
	mesh->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0)); // 漫反射
	mesh->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	mesh->setShininess(1.0); //高光系数
	// 将物体的顶点数据传递
	bindObjectAndData(mesh, mesh_object, vshader, fshader);
}

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

	// 设置光源位置
	light->setTranslation(glm::vec3(-5.0, 15.0, 10.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 读取桌子模型
	table->setNormalize(true);
	table->readObj("./assets/table.obj");
	// 设置物体的旋转位移
	table->setTranslation(glm::vec3(0.0, 0.23, 0.0));
	table->setRotation(glm::vec3(-90.0, 0.0, 0.0));
	table->setScale(glm::vec3(1.0, 1.0, 1.0));
	// 加到painter中
	//painter->addMesh(table, "mesh_a", "./assets/table.png", vshader, fshader); 	// 指定纹理与着色器
	painter->addMesh(table, "mesh_a", "./assets/cat/table.png", vshader, fshader); 	// 指定纹理与着色器
	// 我们创建的这个加入一个容器内,为了程序结束时将这些数据释放
	meshList.push_back(table);

	// 读取girl模型
	girl->setNormalize(true);
	//sword->readObj("./assets/chr_sword/chr_sword.obj");
	//sword->readObj("./assets/cat/cat.obj");
	girl->readObj("./assets/cat/cat2.obj");
	// 设置物体的旋转位移
	girl->setTranslation(glm::vec3(0.0, 0.45, -0.85));
	girl->setRotation(glm::vec3(0.0, 0.0, 0.0));
	girl->setScale(glm::vec3(1.0, 1.0, 1.0));

	// 设置材质
	girl->setAmbient(glm::vec4(0.60f, 0.60f, 0.60f, 1.0f)); // 环境光
	girl->setDiffuse(glm::vec4(1.700f, 1.700f, 1.700f, 1.0f)); // 漫反射
	girl->setSpecular(glm::vec4(0.000f, 0.000f, 0.000f, 1.0f)); // 镜面反射
	girl->setShininess(1.0f); //高光系数

	// 使用默认颜色填充模型
	//glm::vec3 defaultColor = glm::vec3(0.7, 0.7, 0.7); // 设置默认颜色
	//sword->generateCube(defaultColor);

	// 加到painter中
	//painter->addMesh(sword, "mesh_b", "./assets/chr_sword/chr_sword.png", vshader, fshader);
	/*painter->addMesh(sword, "mesh_b1", "./assets/cat/1.png", vshader, fshader);
	painter->addMesh(sword, "mesh_b2", "./assets/cat/2.png", vshader, fshader);
	painter->addMesh(sword, "mesh_b3", "./assets/cat/3.png", vshader, fshader);
	painter->addMesh(sword, "mesh_b4", "./assets/cat/4.png", vshader, fshader);
	painter->addMesh(sword, "mesh_b5", "./assets/cat/5.png", vshader, fshader);
	painter->addMesh(sword, "mesh_b6", "./assets/cat/6.png", vshader, fshader);*/
	painter->addMesh(girl, "mesh_b7", "./assets/cat/cat2.png", vshader, fshader);
	//painter->addMesh(sword, "mesh_b7", "./assets/disk.jpg", vshader, fshader);

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

	// 读取lamp模型
	lamp->setNormalize(true);
	//wawa->readObj("./assets/wawa.obj");
	lamp->readObj("./assets/cat/lamp.obj");
	// 设置物体的旋转位移
	lamp->setTranslation(glm::vec3(-0.85, 0.42, 0.0));
	lamp->setRotation(glm::vec3(180.0, 0.0, 0.0));
	lamp->setScale(glm::vec3(1.0, 1.0, 1.0));
	// 设置材质
	lamp->setAmbient(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 环境光
	lamp->setDiffuse(glm::vec4(0.7, 0.7, 0.7, 1.0)); // 漫反射
	lamp->setSpecular(glm::vec4(0.2, 0.2, 0.2, 1.0)); // 镜面反射
	lamp->setShininess(100.0); //高光系数
	// 加到painter中
	//painter->addMesh(wawa, "mesh_c", "./assets/wawa.png", vshader, fshader);
	painter->addMesh(lamp, "mesh_c", "./assets/cat/lamp.png", vshader, fshader);
	// 我们创建的这个加入一个容器内,为了程序结束时将这些数据释放
	meshList.push_back(lamp);

	mesh_init();

	// 创建长方形平面,给它一个其他颜色
	//plane->generateSquare(glm::vec3(0.6, 0.8, 0.0));
	plane->generateSquare(Purple);
	// 设置正方形的位置和旋转,注意这里我们将正方形平面下移了一点点距离,
	// 这是为了防止和阴影三角形重叠在同个平面上导致颜色交叉
	plane->setRotation(glm::vec3(90, 0, 0));
	plane->setTranslation(glm::vec3(0, -0.01, 0));
	plane->setScale(glm::vec3(4, 4, 4));
	//plane->setScale(glm::vec3(1,1,1));
	// 设置材质
	//plane->setAmbient(glm::vec4(0.329412f, 0.223529f, 0.027451f, 1.0f)); // 环境光
	//plane->setDiffuse(glm::vec4(0.780392f, 0.568627f, 0.113725f, 1.0f)); // 漫反射
	//plane->setSpecular(glm::vec4(0.992157f, 0.941176f, 0.807843f, 1.0f)); // 镜面反射
	//plane->setShininess(27.8974f); //高光系数
	// 加到plane_painter中
	plane_painter->addMesh(plane, "mesh_plane", "./assets/cat/egg.png", vshader, fshader);
	//bindObjectAndData(plane, plane_object, vshader, fshader);

	fshader = "shaders/fshader_1.glsl";
	// 设置物体的大小(初始的旋转和位移都为0)
	Torso->setNormalize(true);
	Head->setNormalize(true);
	RightUpperArm->setNormalize(true);
	LeftUpperArm->setNormalize(true);
	RightUpperLeg->setNormalize(true);
	LeftUpperLeg->setNormalize(true);
	RightLowerArm->setNormalize(true);
	LeftLowerArm->setNormalize(true);
	RightLowerLeg->setNormalize(true);
	LeftLowerLeg->setNormalize(true);
	left_sword->setNormalize(true);

	Torso->generateCube(Blue);
	Head->generateCube(Green);
	RightUpperArm->generateCube(Yellow);
	LeftUpperArm->generateCube(Yellow);
	RightUpperLeg->generateCube(Brown);
	LeftUpperLeg->generateCube(Brown);
	RightLowerArm->generateCube(Red);
	LeftLowerArm->generateCube(Red);
	RightLowerLeg->generateCube(Cyan);
	LeftLowerLeg->generateCube(Cyan);
	left_sword->generateCube(Purple);


	// 将物体的顶点数据传递
	bindObjectAndData(Torso, TorsoObject, vshader, fshader);
	bindObjectAndData(Head, HeadObject, vshader, fshader);
	bindObjectAndData(RightUpperArm, RightUpperArmObject, vshader, fshader);
	bindObjectAndData(LeftUpperArm, LeftUpperArmObject, vshader, fshader);
	bindObjectAndData(RightUpperLeg, RightUpperLegObject, vshader, fshader);
	bindObjectAndData(LeftUpperLeg, LeftUpperLegObject, vshader, fshader);
	bindObjectAndData(RightLowerArm, RightLowerArmObject, vshader, fshader);
	bindObjectAndData(LeftLowerArm, LeftLowerArmObject, vshader, fshader);
	bindObjectAndData(RightLowerLeg, RightLowerLegObject, vshader, fshader);
	bindObjectAndData(LeftLowerLeg, LeftLowerLegObject, vshader, fshader);
	bindObjectAndData(left_sword, left_sword_Object, vshader, fshader);

	//glClearColor(135.0f / 255.0f, 206.0f / 255.0f, 250.0f / 255.0f, 1.0);
	glClearColor(0.4,0.77,1.0, 1.0);
}

void draw_obj(TriMesh* mesh, openGLObject& mesh_object, int a) {
	glm::mat4 modelMatrix;
	// 绘制光照物体
	glBindVertexArray(mesh_object.vao);
	glUseProgram(mesh_object.program);
	modelMatrix = mesh->getModelMatrix();
	glUniformMatrix4fv(mesh_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(mesh_object.viewLocation, 1, GL_TRUE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(mesh_object.projectionLocation, 1, GL_TRUE, &camera->projMatrix[0][0]);
	// 将isShadow设置为a,根据传入的参数绘制
	glUniform1i(mesh_object.shadowLocation, a);
	bindLightAndMaterial(mesh, mesh_object, light, camera);
	glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());
}

void draw_sha(TriMesh* m, openGLObject& mesh_object, int sha) {
	// 根据光源位置,计算阴影投影矩阵
	light_position = light->getTranslation();
	float lx = light_position[0];
	float ly = light_position[1];
	float lz = light_position[2];
	glm::mat4 shadowProjMatrix(
		-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);
	// 计算阴影的模型变换矩阵。
	glm::mat4 modelMatrix = m->getModelMatrix();
	modelMatrix = shadowProjMatrix * modelMatrix;
	// 传递 isShadow 变量,3表示黑色。
	glUniform1i(mesh_object.shadowLocation, sha);
	// 传递 unifrom 关键字的矩阵数据。
	glUniformMatrix4fv(mesh_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	// 绘制
	glDrawArrays(GL_TRIANGLES, 0, m->getPoints().size());
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

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

	// 绘制一系列带纹理物体
	painter->drawMeshes(light, camera,1);
	plane_painter->drawMeshes(light, camera,-1);

	// 绘制平面
	//draw_obj(plane, plane_object, 1);

	// 绘制光照模型及阴影
	draw_obj(mesh, mesh_object, 0);
	draw_sha(mesh, mesh_object, 3);

	// 物体的变换矩阵
	glm::mat4 modelMatrix;
	modelMatrix = glm::mat4(1.0);
	// 保持变换矩阵的栈
	MatrixStack mstack;

	// 躯干(这里我们希望机器人的躯干只绕Y轴旋转,所以只计算了RotateY)
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, 0.5, 0.7));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.Torso]), glm::vec3(0.0, 1.0, 0.0));
	torso(modelMatrix);

	mstack.push(modelMatrix); // 保存躯干变换矩阵
	// 头部(这里我们希望机器人的头部只绕Y轴旋转,所以只计算了RotateY)
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, robot.TORSO_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.Head]), glm::vec3(0.0, 1.0, 0.0));
	head(modelMatrix);
	modelMatrix = mstack.pop(); // 恢复躯干变换矩阵

	// =========== 左臂 ===========
	mstack.push(modelMatrix);   // 保存躯干变换矩阵
	// 左大臂(这里我们希望机器人的左大臂只绕X轴旋转,所以只计算了RotateX,后面同理)
	modelMatrix = glm::translate(modelMatrix, glm::vec3(-0.5 * robot.TORSO_WIDTH - 0.5 * robot.UPPER_ARM_WIDTH, robot.TORSO_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.LeftUpperArm]), glm::vec3(1.0, 0.0, 0.0));
	left_upper_arm(modelMatrix);
	// @TODO: 左小臂
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, -robot.UPPER_ARM_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.LeftLowerArm]), glm::vec3(1.0, 0.0, 0.0));
	left_lower_arm(modelMatrix);
	// 长方体
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, -robot.UPPER_ARM_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.left_sword]), glm::vec3(0.0, 1.0, 0.0));
	draw_left_sword(modelMatrix);
	modelMatrix = mstack.pop(); // 恢复躯干变换矩阵

	// =========== 右臂 ===========
	mstack.push(modelMatrix);   // 保存躯干变换矩阵
	// @TODO: 右大臂
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.5 * robot.TORSO_WIDTH + 0.5 * robot.UPPER_ARM_WIDTH, robot.TORSO_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.RightUpperArm]), glm::vec3(1.0, .0, 0.0));
	right_upper_arm(modelMatrix);
	// @TODO: 右小臂
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, -robot.UPPER_ARM_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.RightLowerArm]), glm::vec3(1.0, 0.0, 0.0));
	right_lower_arm(modelMatrix);
	modelMatrix = mstack.pop(); // 恢复躯干变换矩阵

	// =========== 左腿 ===========
	mstack.push(modelMatrix);   // 保存躯干变换矩阵
	// @TODO: 左大腿
	modelMatrix = glm::translate(modelMatrix, glm::vec3(-0.35 * robot.TORSO_WIDTH, 0.0, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.LeftUpperLeg]), glm::vec3(1.0, 0.0, 0.0));
	left_upper_leg(modelMatrix);
	// @TODO: 左小腿
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, -robot.UPPER_LEG_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.LeftLowerLeg]), glm::vec3(1.0, 0.0, 0.0));
	left_lower_leg(modelMatrix);
	modelMatrix = mstack.pop(); // 恢复躯干变换矩阵

	// =========== 右腿 ===========
	mstack.push(modelMatrix);   // 保存躯干变换矩阵
	// @TODO: 右大腿
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.35 * robot.TORSO_WIDTH, 0.0, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.RightUpperLeg]), glm::vec3(1.0, 0.0, 0.0));
	right_upper_leg(modelMatrix);
	// @TODO: 右小腿
	modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0, -robot.UPPER_LEG_HEIGHT, 0.0));
	modelMatrix = glm::rotate(modelMatrix, glm::radians(robot.theta[robot.RightLowerLeg]), glm::vec3(1.0, 0.0, 0.0));
	right_lower_leg(modelMatrix);
	modelMatrix = mstack.pop(); // 恢复躯干变换矩阵
}

void printHelp()
{

	std::cout << "=========================按键介绍==============================" << std::endl << std::endl;

	//std::cout << "按键介绍" << std::endl;
	std::cout <<
		"			[窗口控制]" << std::endl <<
		"		ESC:			退出" << std::endl <<
		std::endl <<

		//"			[机器人控制]" << std::endl <<
		"			[机器人控制选择]" << std::endl <<
		"		1:			躯干" << std::endl <<
		"		2:			头部" << std::endl <<
		"		3:			左大臂" << std::endl <<
		"		4:			左小臂" << std::endl <<
		"		5:			右大臂" << std::endl <<
		"		6:			右小臂" << std::endl <<
		"		7:			左大腿" << std::endl <<
		"		8:			左小腿" << std::endl <<
		"		9:			右大腿" << std::endl <<
		"		0:			右小腿" << std::endl < 360.0)
				robot.theta[Selected_mesh] -= 360.0;
			break;
		case GLFW_KEY_S:
			robot.theta[Selected_mesh] -= 5.0;
			if (robot.theta[Selected_mesh] < 0.0)
				robot.theta[Selected_mesh] += 360.0;
			break;
			// 选择展示光照效果的物体
		case GLFW_KEY_V:
			mesh = new TriMesh();
			mesh->readOff("./assets/sphere.off");
			mesh_init();
			break;
		case GLFW_KEY_B:
			mesh = new TriMesh();
			mesh->readOff("./assets/cow.off");
			mesh_init();
			break;
		case GLFW_KEY_N:
			mesh = new TriMesh();
			mesh->readOff("./assets/Squirtle.off");
			mesh_init();
			break;
		case GLFW_KEY_M:
			mesh = new TriMesh();
			mesh->readOff("./assets/Pikachu.off");
			mesh_init();
			break;
			// 选择需要控制的物体
		case GLFW_KEY_U:
			obj = table;
			break;
		case GLFW_KEY_I:
			obj = girl;
			break;
		case GLFW_KEY_O:
			obj = lamp;
			break;
		case GLFW_KEY_P:
			obj = mesh;
			break;
		case GLFW_KEY_MINUS:
			temp = obj->getScale();
			temp.x -= 0.2;
			temp.y -= 0.2;
			temp.z -= 0.2;
			obj->setScale(temp);
			break;
		case GLFW_KEY_EQUAL:
			temp = obj->getScale();
			temp.x += 0.2;
			temp.y += 0.2;
			temp.z += 0.2;
			obj->setScale(temp);
			break;
		case GLFW_KEY_R:
			temp = obj->getRotation();
			if (mode == GLFW_MOD_SHIFT)
				temp.y += 90.0;
			else
				temp.y -= 90.0;
			obj->setRotation(temp);
			break;
		case GLFW_KEY_LEFT:
			temp = obj->getTranslation();
			temp.x -= 0.2;
			obj->setTranslation(temp);
			break;
		case GLFW_KEY_RIGHT:
			temp = obj->getTranslation();
			temp.x += 0.2;
			obj->setTranslation(temp);
			break;
		case GLFW_KEY_UP:
			temp = obj->getTranslation();
			temp.z -= 0.2;
			obj->setTranslation(temp);
			break;
		case GLFW_KEY_DOWN:
			temp = obj->getTranslation();
			temp.z += 0.2;
			obj->setTranslation(temp);
			break;
		case GLFW_KEY_X:
			light_position = light->getTranslation();
			if (mode == GLFW_MOD_SHIFT)
				light_position[0] += move_step_size;
			else
				light_position[0] -= move_step_size;
			light->setTranslation(light_position);
			break;
		case GLFW_KEY_Y:
			light_position = light->getTranslation();
			if (mode == GLFW_MOD_SHIFT)
				light_position[1] += move_step_size;
			else {
				light_position[1] -= move_step_size;
				if (light_position[1] <= 1.0) {
					light_position[1] += move_step_size;
				}
			}
			light->setTranslation(light_position);
			break;
		case GLFW_KEY_Z:
			light_position = light->getTranslation();
			if (mode == GLFW_MOD_SHIFT)
				light_position[2] += move_step_size;
			else
				light_position[2] -= move_step_size;
			light->setTranslation(light_position);
			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++) {
		meshList[i]->cleanData();
		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_胡云飞_期末大作业", 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;
	}
	mesh->setNormalize(true);
	mesh->readOff("./assets/Pikachu.off");
	// Init mesh, shaders, buffer
	init();
	// 输出帮助信息
	printHelp();
	// 启用深度测试
	glEnable(GL_DEPTH_TEST);
	while (!glfwWindowShouldClose(window))
	{
		display();
		// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
		// -------------------------------------------------------------------------------
		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);
}

期末大作业——Camera.cpp

#include "Camera.h"

Camera::Camera() {
	initCamera();
	updateCamera();
};
Camera::~Camera() {}

glm::mat4 Camera::getViewMatrix()
{
	return this->lookAt(eye, at, up);
}

glm::mat4 Camera::getProjectionMatrix(bool isOrtho)
{
	if (isOrtho) {
		return this->ortho(-scale, scale, -scale, scale, this->zNear, this->zFar);
	}
	else {
		return this->perspective(this->fovy, this->aspect, this->zNear, this->zFar);
	}
}

glm::mat4 Camera::lookAt(const glm::vec4& eye, const glm::vec4& at, const glm::vec4& up)
{
	// @TODO: Task1:请按照实验课内容补全相机观察矩阵的计算
	// 获得相机方向。
	glm::vec4 n = glm::normalize(eye - at);
	// 获得右(x)轴方向。
	glm::vec3 up_3 = up;
	glm::vec3 n_3 = n;
	glm::vec4 u = glm::normalize(glm::vec4(glm::cross(up_3, n_3), 0.0));
	// 获得上(y)轴方向。
	glm::vec3 u_3 = u;
	glm::vec4 v = glm::normalize(glm::vec4(glm::cross(n_3, u_3), 0.0));

	glm::vec4 t = glm::vec4(0.0, 0.0, 0.0, 1.0);
	glm::mat4 c = glm::mat4(u, v, n, t);

	// 处理相机位置向量。
	glm::mat4 p = glm::mat4(1.0f);
	p[0].w = -(eye.x);
	p[1].w = -(eye.y);
	p[2].w = -(eye.z);

	glm::mat4 view = p * c;
	return view;					// 计算最后需要沿-eye方向平移
}

glm::mat4 Camera::ortho(const GLfloat left, const GLfloat right,
	const GLfloat bottom, const GLfloat top,
	const GLfloat zNear, const GLfloat zFar)
{
	// @TODO: Task2:请按照实验课内容补全正交投影矩阵的计算
	glm::mat4 c = glm::mat4(1.0f);
	c[0][0] = 2.0 / (right - left);
	c[1][1] = 2.0 / (top - bottom);
	c[2][2] = -2.0 / (zFar - zNear);
	c[3][3] = 1.0;
	c[0][3] = -(right + left) / (right - left);
	c[1][3] = -(top + bottom) / (top - bottom);
	c[2][3] = -(zFar + zNear) / (zFar - zNear);
	return c;
}

glm::mat4 Camera::perspective(const GLfloat fovy, const GLfloat aspect,
	const GLfloat zNear, const GLfloat zFar)
{
	// @TODO: Task2:请按照实验课内容补全透视投影矩阵的计算
	GLfloat top = tan(fovy * M_PI / 180 / 2) * zNear;
	GLfloat right = top * aspect;

	glm::mat4 c = glm::mat4(1.0f);
	c[0][0] = zNear / right;
	c[1][1] = zNear / top;
	c[2][2] = -(zFar + zNear) / (zFar - zNear);
	c[2][3] = -(2.0 * zFar * zNear) / (zFar - zNear);
	c[3][2] = -1.0;
	c[3][3] = 0.0;
	return c;
}

glm::mat4 Camera::frustum(const GLfloat left, const GLfloat right,
	const GLfloat bottom, const GLfloat top,
	const GLfloat zNear, const GLfloat zFar)
{
	// 任意视锥体矩阵
	glm::mat4 c = glm::mat4(1.0f);
	c[0][0] = 2.0 * zNear / (right - left);
	c[0][2] = (right + left) / (right - left);
	c[1][1] = 2.0 * zNear / (top - bottom);
	c[1][2] = (top + bottom) / (top - bottom);
	c[2][2] = -(zFar + zNear) / (zFar - zNear);
	c[2][3] = -2.0 * zFar * zNear / (zFar - zNear);
	c[3][2] = -1.0;
	c[3][3] = 0.0;
	return c;
}


void Camera::updateCamera()
{
	// 使用相对于at的角度控制相机的时候,注意在upAngle大于90的时候,相机坐标系的u向量会变成相反的方向,
	// 要将up的y轴改为负方向才不会发生这种问题

	// 也可以考虑直接控制相机自身的俯仰角,
	// 保存up,eye-at 这些向量,并修改这些向量方向来控制
	// 看到这里的有缘人可以试一试
	up = glm::vec4(0.0, 1.0, 0.0, 0.0);
	if (upAngle > 90) {
		up.y = -1;
	}
	else if (upAngle < -90) {
		up.y = -1;
	}

	float eyex = radius * cos(upAngle * M_PI / 180.0) * sin(rotateAngle * M_PI / 180.0);
	float eyey = radius * sin(upAngle * M_PI / 180.0);
	float eyez = radius * cos(upAngle * M_PI / 180.0) * cos(rotateAngle * M_PI / 180.0);

	eye = glm::vec4(eyex, eyey, eyez, 1.0);

}

void Camera::initCamera() {
	at = glm::vec4(0.0, 0.0, 0.0, 1.0);

	radius = 4.0;		//前后远近变动视角角度
	rotateAngle = 20.0;	//环绕中心左右转动视角角度
	upAngle = 20.0;		//环绕中心上下转动视角角度
	fovy = 45.0;
	aspect = 1.0;
	scale = 1.5;
	zNear = 0.01;
	zFar = 100.0;

}

void Camera::keyboard(int key, int action, int mode)
{
	// 键盘事件处理
	// 通过按键改变相机和投影的参数
	if (key == GLFW_KEY_J && mode == 0x0000) {
		rotateAngle += 5.0;
		if (rotateAngle > 180)
			rotateAngle = rotateAngle - 360;
	}
	else if (key == GLFW_KEY_J && mode == GLFW_MOD_SHIFT) {
		rotateAngle -= 5.0;
		if (rotateAngle < -180)
			rotateAngle = rotateAngle + 360;
	}
	else if (key == GLFW_KEY_K && mode == 0x0000) {
		upAngle += 5.0;
		if (upAngle >= 180)
			upAngle = upAngle - 360;
	}
	else if (key == GLFW_KEY_K && mode == GLFW_MOD_SHIFT) {
		upAngle -= 5.0;
		if (upAngle <= -180)
			upAngle = upAngle + 360;
	}
	else if (key == GLFW_KEY_L && mode == 0x0000) {
		radius += 0.1;
	}
	else if (key == GLFW_KEY_L && mode == GLFW_MOD_SHIFT) {
		radius -= 0.1;
	}
	// 空格键初始化所有参数
	else if (key == GLFW_KEY_SPACE && mode == 0x0000) {
		initCamera();
	}

}

期末大作业——MeshPainter.cpp

#include "MeshPainter.h"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

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

std::vector MeshPainter::getMeshNames() { return mesh_names; };
std::vector MeshPainter::getMeshes() { return meshes; };
std::vector 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 points = mesh->getPoints();
    std::vector normals = mesh->getNormals();
    std::vector colors = mesh->getColors();
    std::vector textures = mesh->getTextures();

    // 创建顶点数组对象
#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,
        points.size() * sizeof(glm::vec3) +
        normals.size() * sizeof(glm::vec3) +
        colors.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)));

    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);
#ifdef __APPLE__
    glBindVertexArrayAPPLE(0);
#else
    glBindVertexArray(0);
#endif

};


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,int flag) {

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

#ifdef __APPLE__	// for MacOS
    glBindVertexArrayAPPLE(object.vao);
#else
    glBindVertexArray(object.vao);
#endif
    glUseProgram(object.program);

    // 物体的变换矩阵
    glm::mat4 modelMatrix = mesh->getModelMatrix();
    // 传递矩阵
    glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
    glUniformMatrix4fv(object.viewLocation, 1, GL_TRUE, &camera->viewMatrix[0][0]);
    glUniformMatrix4fv(object.projectionLocation, 1, GL_TRUE, &camera->projMatrix[0][0]);
    // 将着色器 isShadow 设置为2,表示根据纹理绘制
    glUniform1i(object.shadowLocation, 2);
    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());

    // @TODO: 根据光源位置,计算阴影投影矩阵
    glm::vec3 light_position = light->getTranslation();
    float lx = light_position[0];
    float ly = light_position[1];
    float lz = light_position[2];
    glm::mat4 shadowProjMatrix(
        -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);
    // 计算阴影的模型变换矩阵。
    modelMatrix = shadowProjMatrix * modelMatrix;
    if (flag != -1)//若flag为-1则不绘制阴影
    {
        // 传递 isShadow 变量,3表示黑色。
        glUniform1i(object.shadowLocation, 3);
        // 传递 unifrom 关键字的矩阵数据。
        glUniformMatrix4fv(object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
        // 绘制
        glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());
    }

#ifdef __APPLE__	// for MacOS
    glBindVertexArrayAPPLE(0);
#else
    glBindVertexArray(0);
#endif
    glUseProgram(0);

};


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

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

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

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

#ifdef __APPLE__
        glDeleteVertexArraysAPPLE(1, &opengl_objects[i].vao);
#else
        glDeleteVertexArrays(1, &opengl_objects[i].vao);
#endif
        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);
};

期末大作业——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()
{
}

TriMesh::~TriMesh()
{
}

std::vector TriMesh::getVertexPositions()
{
	return vertex_positions;
}

std::vector TriMesh::getVertexColors()
{
	return vertex_colors;
}

std::vector TriMesh::getVertexNormals()
{
	return vertex_normals;
}

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


std::vector TriMesh::getPoints()
{
	return points;
}

std::vector TriMesh::getColors()
{
	return colors;
}

std::vector TriMesh::getNormals()
{
	return normals;
}

std::vector 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];
		// @TODO: Task2 计算每个面片的法向量并归一化
		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));
	// @TODO: Task2 求法向量均值
	for (size_t i = 0; i < faces.size(); i++) {
		auto& face = faces[i];
		// @TODO: 先累加面的法向量
		vertex_normals[face.x] += face_normals[i];
		vertex_normals[face.y] += face_normals[i];
		vertex_normals[face.z] += face_normals[i];

	}
	 @TODO 对累加的法向量并归一化
	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::norm() {
	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;
		}
	}
}

void TriMesh::storeFacesPoints() {
	norm();
	// 计算法向量
	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]);
		}
	}
}

void TriMesh::storeFacesPoints_1() {
	// 计算法向量
	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[faces[i].x]);
		colors.push_back(vertex_colors[faces[i].y]);
		colors.push_back(vertex_colors[faces[i].z]);
		// 颜色
		/*colors.push_back(glm::vec3(0.5, 0.0, 0.5));
		colors.push_back(glm::vec3(0.5, 0.0, 0.5));
		colors.push_back(glm::vec3(0.5, 0.0, 0.5));*/
		// 法向量
		if (vertex_normals.size() != 0) {
			normals.push_back(vertex_normals[faces[i].x]);
			normals.push_back(vertex_normals[faces[i].y]);
			normals.push_back(vertex_normals[faces[i].z]);
		}
	}
}



// 立方体生成12个三角形的顶点索引
void TriMesh::generateCube(glm::vec3 _color)
{
	// 创建顶点前要先把那些vector清空
	cleanData();
	for (int i = 0; i < 8; i++)
	{
		vertex_positions.push_back(cube_vertices[i]);
		if (_color[0] == -1) {
			vertex_colors.push_back(basic_colors[i]);
		}
		else {
			vertex_colors.push_back(_color);
		}
	}

	// 每个三角面片的顶点下标

	// 每个三角面片的顶点下标
	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));

	storeFacesPoints_1();

	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::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);
		//vertex_colors.push_back(glm::vec3(0.5, 0.0, 0.5));
	}

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

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::readOff(const std::string& filename)
{
	// fin打开文件读取文件信息
	if (filename.empty())
	{
		return;
	}
	std::ifstream fin;
	fin.open(filename);
	// @TODO: Task1:修改此函数读取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;
		// @TODO
		// 根据顶点数,循环读取每个顶点坐标
		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();
	norm();
	storeFacesPoints_1();
};

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;
		int a3, b3, c3;
		char slash;
		sin >> type;
		// @TODO: Task2 读取obj文件,记录里面的这些数据,可以参考readOff的写法
		// vertex_positions
		// vertex_normals
		// vertex_textures
		if (type == "v")
		{
			sin >> _x >> _y >> _z;
			vertex_positions.push_back(glm::vec3(_x, _y, _z));
		}
		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));
		}
		if (type == "vt")
		{
			sin >> _x >> _y >> _z;
			vertex_textures.push_back(glm::vec2(_x, _y));
		}
		// faces
		// normal_index
		// texture_index
		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_color和color_index可以用法向量的数值赋值
	}
	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
	);
}

期末大作业——fshader.glsl

#version 330 core

// 给光源数据一个结构体
struct Light{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;

	vec3 position;

    // 光源衰减系数的三个参数
    float constant; // 常数项
    float linear;	// 一次项
    float quadratic;// 二次项

};

// 给物体材质数据一个结构体
struct Material{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;

	float shininess;
};

in vec3 position;
in vec3 normal;
in vec2 texCoord;


// 相机坐标
uniform vec3 eye_position;
// 光源
uniform Light light;
// 物体材质
uniform Material material;

uniform int isShadow;
// 纹理数据
uniform sampler2D texture;

in vec3 color;
out vec4 fColor;

void main()
{
	if (isShadow == 0) {
		// 将顶点坐标、光源坐标和法向量转换到相机坐标系
		vec3 norm = (vec4(normal, 0.0)).xyz;
		
		// @TODO: 计算四个归一化的向量 N,V,L,R(或半角向量H)
		vec3 N = normalize(norm);
		vec3 V = normalize(eye_position - position);
		vec3 L = normalize(light.position - position);
		vec3 R = reflect(-L, N);


		// 环境光分量I_a
		vec4 I_a = light.ambient * material.ambient;

		// @TODO: Task2 计算系数和漫反射分量I_d
		float diffuse_dot = max(dot(L, N), 0);
		vec4 I_d = diffuse_dot *  light.diffuse * material.diffuse;

		// @TODO: Task2 计算系数和镜面反射分量I_s
		float specular_dot_pow = pow(max(dot(R, V), 0), material.shininess);
		vec4 I_s = specular_dot_pow * light.specular * material.specular;


		// @TODO: Task2 计算高光系数beta和镜面反射分量I_s
		// 注意如果光源在背面则去除高光
		if( dot(L, N) < 0.0 ) {
		    I_s = vec4(0.0, 0.0, 0.0, 1.0);
		}
		
		// 合并三个分量的颜色,修正透明度
		fColor = I_a + I_d + I_s;
		fColor.a = 1.0;
	}
	else if (isShadow == 2) {
		fColor = texture2D( texture, texCoord );
	}
	else if (isShadow == 1) {
		fColor = vec4(0.0, 0.5, 0.5, 1.0);	//黑色
	}
	else if (isShadow == 3) {
		fColor = vec4(0.0, 0.0, 0.0, 1.0);	//黑色
	}
	// else if (isShadow == 1) {
	// 	fColor = vec4(0.0, 1.0, 0.0, 1.0);
	// }
	
}

期末大作业——fshader_1.glsl

// #version 330 core

// in vec3 position;
// in vec3 normal;
// in vec3 color;

// uniform int isShadow;

// out vec4 fColor;

// void main()
// {
// 	if (isShadow == 1) {
// 		fColor = vec4(0.0, 0.0, 0.0, 1.0);
// 	}
// 	else {
// 		fColor = vec4(color, 1.0);
// 	}
// }
#version 330 core
in vec3 color;

// 给光源数据一个结构体
struct Light{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;

	vec3 position;

    // 光源衰减系数的三个参数
    float constant; // 常数项
    float linear;	// 一次项
    float quadratic;// 二次项

};

// 给物体材质数据一个结构体
struct Material{
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;

	float shininess;
};

in vec3 position;
in vec3 normal;
in vec2 texCoord;


// 相机坐标
uniform vec3 eye_position;
// 光源
uniform Light light;
// 物体材质
uniform Material material;

uniform int isShadow;
// 纹理数据
uniform sampler2D texture;


out vec4 fColor;

void main()
{
	if (isShadow == 1) {
		fColor = vec4(0.0, 0.0, 0.0, 1.0);
	}
	else {
		//fColor = texture2D( texture, texCoord );
		fColor = vec4(color, 1.0);
	}
}

期末大作业——vshader.glsl

#version 330 core

// 顶点着色器
in vec3 vPosition;
in vec3 vColor;
in vec3 vNormal;
in vec2 vTexture;

// 传给片元着色器的变量
out vec3 position;
out vec3 normal;
out vec3 color;
out vec2 texCoord;

// 模型变换矩阵、相机观察矩阵、投影矩阵
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main() 
{
	vec4 v1 = model * vec4(vPosition, 1.0);  
	// 由于model矩阵有可能为阴影矩阵,为了得到正确位置,我们需要做一次透视除法
	vec4 v2 = vec4(v1.xyz / v1.w, 1.0);
	// 考虑相机和投影
	vec4 v3 = projection* view * v2;
	
	gl_Position = v3;

    position = vec3(v2.xyz);
    normal = vec3( model * vec4(vNormal, 0.0) );
    color = vColor;
    normal = vec3( (model *  vec4(vNormal, 0.0)).xyz );
	texCoord = vTexture;

}

(by 归忆)

你可能感兴趣的:(计算机图形学,深圳大学,计算机图形学,opengl,期末大作业,虚拟场景建模)