【深圳大学计算机图形学】实验三 光照与阴影

目录

实验目的与要求

实验过程及内容

实验3.1

实验3.2

实验3.3

实验3.4

实验三

实验结论

实验代码

实验3.1——main.cpp

实验3.1——Camera.cpp

实验3.2——main.cpp

实验3.2——Camera.cpp

实验3.3——main.cpp

实验3.3——TriMesh.cpp

实验3.3——vshader.glsl

实验3.4——main.cpp

实验3.4——TriMesh.cpp

实验3.4——fshader.glsl

实验三——main.cpp

实验三——fshader.glsl

实验三——vshader.glsl


实验目的与要求

  1. 掌握OpenGL三维场景的读取与绘制方法,理解光照和物体材质对渲染结果的影响,强化场景坐标系转换过程中常见矩阵的计算方法,熟悉阴影的绘制方法。
  2. 创建OpenGL绘制窗口,读入三维场景文件并绘制。
  3. 设置相机并添加交互,实现从不同位置/角度、以正交或透视投影方式观察场景。
  4. 实现Phong光照效果和物体材质效果。
  5. 自定义投影平面(为计算方便,推荐使用y=0平面),计算阴影投影矩阵,为三维物体生成阴影。
  6. 使用鼠标点击(或其他方式)控制光源位置并更新光照效果,并同时更新三维物体的阴影。

实验过程及内容

实验3.1

1.完善lookAt函数,首先计算相机的观察方向(forward),然后基于观察方向和指定的上方向(up)计算相机的右向量(right)和新的上向量(newUp)。

接着,初始化一个单位矩阵,并更新矩阵的各个元素,以便构建观察矩阵。

最后,将相机位置考虑进来,通过更新平移部分来确保相机位置正确。

观察矩阵完成后,可以用于将物体变换到相机的视角。

2.完善ortho函数,正交投影矩阵的计算涉及了缩放和平移因子。

首先,初始化一个单位矩阵作为投影矩阵的起点。

然后,根据输入的 left, right, bottom, top, zNear 和 zFar 参数,更新投影矩阵的各个元素值。这些元素值将控制物体在投影平面上的位置和大小。

最后,返回计算好的正交投影矩阵,用于渲染场景。

3.完善perspective函数,透视投影矩阵的计算与正交投影矩阵类似,同样包括了缩放和平移因子。

首先,初始化一个单位矩阵作为投影矩阵的起点。

然后,根据输入的 fov, aspect, zNear 和 zFar 参数,更新投影矩阵的各个元素值。这些元素值将控制物体在投影平面上的位置和大小,以及视锥体的远近剪裁面。

最后,返回计算好的透视投影矩阵,用于渲染场景。

4. 在 updateCamera() 中根据角度与距离半径更新计算相机eye的位置,用于lookAt函数。

首先将角度 upAngle 和 rotateAngle 转换为弧度,然后使用三角函数根据半径 radius 计算相机位置的 x、y 和 z 坐标。

最后,更新相机的位置 eye、参考点 at 和 VUP 方向 up。

这样的设置可以让相机绕被观察物体的中心旋转,保持 VUP 方向朝上。

5. 首先设置视口,以便在窗口的四分之一中渲染场景。

然后,调用 updateCamera 更新相机的位置和参数。

接下来,创建一个单位矩阵作为物体的变换矩阵。

然后,使用 Camera::lookAt 函数计算视图矩阵。

最后,使用默认的单位正交投影矩阵,传递变换矩阵到着色器,并绘制物体。

6. 定义了透视投影所需的参数,包括视野角度 fov、屏幕宽高比 aspect、近裁剪平面位置 zNear 和远裁剪平面位置 zFar。

然后,我们调用 Camera::perspective 函数来计算透视投影矩阵,并将其存储在 camera_4->projMatrix 中。

最后,我们传递这个投影变换矩阵到渲染流水线中,以实现透视投影效果。

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

实验3.2

1. 首先补全lookAt函数:

glm::vec3 n = glm::normalize(glm::vec3(eye - at));

这一行计算了相机坐标系的z轴向量 n。eye 是相机的位置,at 是相机所看的目标点。通过将 eye 和 at 相减,得到z轴向量。然后使用 glm::normalize 函数将这个向量标准化,以确保其长度为1。

计算相机坐标系的x轴向量(右侧向量):

glm::vec3 u = glm::normalize(glm::cross(glm::vec3(up), n));

这一行计算了相机坐标系的x轴向量 u,它表示相机右侧的方向。up 是定义了相机的上方向的向量。通过使用 glm::cross 函数计算 up 和 n 的叉积,得到一个垂直于这两个向量的向量。然后同样使用 glm::normalize 函数标准化这个向量。

计算相机坐标系的y轴向量(上方向):

glm::vec3 v = glm::cross(n, u);

这一行计算了相机坐标系的y轴向量 v,它表示相机的上方向。通过再次使用 glm::cross 函数,这次将 n 和 u 的向量叉积,得到一个垂直于 n 和 u 的向量。

初始化观察矩阵为单位矩阵:

glm::mat4 c;

这一行创建了一个4x4的矩阵 c,用于构建观察矩阵。初始时,这个矩阵是一个单位矩阵,表示没有任何变换。

填充观察矩阵的前三列(包括平移):

这一部分用于填充观察矩阵的前三列,其中每一列包括 u、v、和 n 向量的分量以及平移信息。

每列的前三行分别是 u、v、和 n 向量的x、y、z分量。

每列的第四行包括了 -glm::dot 函数,用来计算平移信息。这是通过点乘 u、v、和 n 向量分别与 eye 向量(相机位置)来计算的。

填充观察矩阵的第四列:

c[0][3] = 0.0f;

c[1][3] = 0.0f;

c[2][3] = 0.0f;

c[3][3] = 1.0f;

这一部分用于设置观察矩阵的第四列,这个列通常用于执行平移操作。这里所有元素被设置为特定的值,其中最后一个元素 c[3][3] 设置为1.0,表示这是一个点而不是一个方向向量。

返回计算出的观察矩阵:

return c;

最后,函数返回计算出的观察矩阵 c,该矩阵可以用于设置相机视角。

总之,这段代码执行了一系列的向量计算和矩阵填充操作,以构建一个观察矩阵,这个矩阵定义了相机的位置、朝向和方向。这是相机操作中非常重要的一部分,用于确定渲染场景时的视角和观察位置。

2. 然后补全ortho函数:

这是一个用于生成正交投影矩阵的函数,正交投影矩阵用于定义渲染时的视景体积。

计算正交投影矩阵:

glm::mat4 projectionMatrix = glm::mat4(1.0f);

这一行创建一个4x4的矩阵 projectionMatrix,初始时,所有对角线元素都被初始化为1.0,这是一个单位矩阵。

设置正交投影矩阵的各个参数:

projectionMatrix[0][0] = 2.0f / (right - left);

projectionMatrix[1][1] = 2.0f / (top - bottom);

projectionMatrix[2][2] = -2.0f / (zFar - zNear);

这一部分用于填充正交投影矩阵的前三列,这些参数用于控制视景体积的大小和位置。具体解释如下:

projectionMatrix[0][0] 和 projectionMatrix[1][1] 控制X和Y方向上的缩放因子,以确定视景体积的宽度和高度。

projectionMatrix[2][2] 控制Z方向上的缩放因子,负号表示物体离观察者越远,Z坐标变得更小。

设置正交投影矩阵的平移部分:

projectionMatrix[0][3] = -(right + left) / (right - left);

projectionMatrix[1][3] = -(top + bottom) / (top - bottom);

projectionMatrix[2][3] = -(zFar + zNear) / (zFar - zNear);

这一部分设置了正交投影矩阵的平移部分,即第四列。这些参数用于确定视景体积的位置。具体解释如下:

projectionMatrix[0][3] 和 projectionMatrix[1][3] 控制X和Y方向上的平移,以确定视景体积的中心位置。

projectionMatrix[2][3] 控制Z方向上的平移,以确定视景体积沿Z轴的位置。

最后一行通常被设置为(0, 0, 0, 1):

这是一种通常的做法,最后一行通常设置为(0, 0, 0, 1),表示不执行透视变换。正交投影是不会产生透视效果的,因此这一行的值通常保持不变,确保不会引入透视效果。

最后,函数返回计算出的正交投影矩阵 projectionMatrix,这个矩阵可以用于设置渲染时的视景体积,确保渲染结果是正交的,而不是透视的。正交投影通常用于制作平行视图或需要一致比例的渲染,如2D游戏或工程图。这段代码填充了正交投影矩阵的不同部分,以确保正确设置视景体积的大小和位置。

3.接下来补全perspective函数:

这是一个用于生成透视投影矩阵的函数,透视投影用于模拟透视效果。

计算焦距:

float f = 1.0f / tan(fovy / 2.0f);

首先,函数计算焦距 f。fovy 是视野角度,这里将其转换为弧度,并计算其一半的正切的倒数。这个值用于确定透视投影矩阵中的缩放因子。

创建透视投影矩阵:

glm::mat4 c = glm::mat4(1.0f);

首先,创建一个4x4的矩阵 c,初始时,所有对角线元素都被初始化为1.0,这是一个单位矩阵。

设置透视投影矩阵的各个参数:

c[0][0] = f / aspect;

c[1][1] = f;

c[2][2] = (zFar + zNear) / (zNear - zFar);

c[2][3] = (2.0f * zFar * zNear) / (zNear - zFar);

c[3][2] = -1.0f;

这一部分用于填充透视投影矩阵的不同部分,这些参数控制视景体积的大小和透视效果。具体解释如下:

c[0][0] 和 c[1][1] 控制X和Y方向上的缩放因子,以确定视景体积的宽度和高度。

c[2][2] 和 c[2][3] 控制Z方向上的缩放因子和平移,以确定视景体积的深度和位置。

c[3][2] 控制透视投影的效果,通常设置为-1.0,确保透视效果的产生。

透视投影用于模拟视点位置,产生近大远小的透视效果,通常用于3D渲染中,使物体远离观察者时变得较小。这段代码填充了透视投影矩阵的不同部分,以确保正确设置视景体积的大小和位置,并模拟透视效果。

4.最后,在display函数中补全代码生成三角形阴影:

这段代码用于计算阴影坐标和绘制三角形的阴影。

初始化阴影矩阵和相关参数:

glm::mat4 shadowMatrix;: 首先,创建一个4x4的矩阵 shadowMatrix 用于存储阴影矩阵。

glm::vec3 lightPosition = glm::vec3(light_position);: 获取光源的位置,并将其转换为glm的3D向量。

glm::vec3 planeNormal = glm::vec3(0.0f, 1.0f, 0.0f);: 定义投影平面的法向量,投影平面是X-Z平面,法向量指向Y轴正方向。

float D = -glm::dot(planeNormal, glm::vec3(0.0f, 0.0f, 0.0f));: 计算D参数,这是一个平面方程的参数,用于描述投影平面。

填充阴影矩阵:

shadowMatrix[0][0], shadowMatrix[1][0], shadowMatrix[2][0], shadowMatrix[3][0]:这一行代表阴影坐标的X轴分量。

shadowMatrix[0][0] 和 shadowMatrix[1][0] 会影响阴影坐标的X分量,它们与光源的Y和Z分量以及投影平面的法向量相关。

shadowMatrix[2][0] 影响阴影坐标的X分量,它与光源的X分量和投影平面的法向量相关。

shadowMatrix[3][0] 通常被设置为0.0,不影响坐标变换。

shadowMatrix[0][1], shadowMatrix[1][1], shadowMatrix[2][1], shadowMatrix[3][1]:这一行代表阴影坐标的Y轴分量。

shadowMatrix[0][1] 和 shadowMatrix[1][1] 会影响阴影坐标的Y分量,它们与光源的X和Z分量以及投影平面的法向量相关。

shadowMatrix[2][1] 影响阴影坐标的Y分量,它与光源的Y分量和投影平面的法向量相关。

shadowMatrix[3][1] 通常被设置为0.0,不影响坐标变换。

shadowMatrix[0][2], shadowMatrix[1][2], shadowMatrix[2][2], shadowMatrix[3][2]:这一行代表阴影坐标的Z轴分量。

shadowMatrix[0][2] 和 shadowMatrix[1][2] 会影响阴影坐标的Z分量,它们与光源的Z分量以及投影平面的法向量相关。

shadowMatrix[2][2] 影响阴影坐标的Z分量,它与光源的X和Y分量以及投影平面的法向量相关。

shadowMatrix[3][2] 通常被设置为0.0,不影响坐标变换。

shadowMatrix[0][3], shadowMatrix[1][3], shadowMatrix[2][3], shadowMatrix[3][3]:这一行代表阴影坐标的齐次坐标系的W分量,通常不需要设置为0.0。

shadowMatrix[0][3], shadowMatrix[1][3], shadowMatrix[2][3] 和 shadowMatrix[3][3] 通常根据坐标变换的需求设置,它们会影响坐标的平移。

这些矩阵元素的设置是基于阴影投影矩阵的原理,通过变换原始模型的坐标,以便在投影平面上正确绘制阴影。这是硬阴影的一种实现方式,用于模拟光源投射的阴影效果。

计算阴影坐标:

glm::mat4 shadowModelMatrix = shadowMatrix * modelMatrix;: 将阴影矩阵乘以原始模型的模型矩阵,以获得变换后的阴影坐标。

传递阴影矩阵给着色器:

glUniformMatrix4fv(tri_object.modelLocation, 1, GL_FALSE, &shadowModelMatrix[0][0]);: 这一行代码将阴影矩阵传递给着色器,以便在绘制阴影时使用。

glUniform1i(tri_object.shadowLocation, 0);: 设置 isShadow 变量为0,表示绘制阴影。这是一个uniform变量,影响着色器中的绘制方式。

绘制阴影:

glDrawArrays(GL_TRIANGLES, 0, triangle->getPoints().size());: 最后,使用阴影坐标绘制三角形的阴影。这一步将在屏幕上绘制出阴影的效果。

这段代码计算了阴影矩阵,将阴影矩阵传递给着色器,然后绘制三角形的阴影。这是实现硬阴影的一种方法,它模拟了光源投射在投影平面上的阴影,使三角形看起来像是在这个平面上投下的阴影。

5.运行代码,结果如下图所示,成功实现三角形阴影投影到平面上。

实验3.3

1. 在Trimesh.cpp中完善computeTriangleNormals()函数,用于计算三角网格模型中每个面片的法向量并将其归一化。

初始化法向量数组:通过 resize 函数,为存储面片法向量的数组 face_normals 分配空间,大小与面片数组 faces 相同。

遍历所有面片:使用 for 循环遍历所有的面片,其中 i 表示当前面片的索引。

获取面片的顶点:对于每个面片,通过面片数组 faces 获取三个顶点的索引,分别为 face.x、face.y、face.z。

计算面法向量:获取三个顶点的位置坐标 v0、v1、v2。

计算两个边向量 edge1 和 edge2,分别由 v1 - v0 和 v2 - v0 得到。

使用叉乘计算面法向量 norm,即 edge1 和 edge2 的叉乘结果。

归一化法向量:使用 glm::normalize 函数对计算得到的法向量进行归一化,确保其长度为1。

存储法向量:将归一化后的法向量存储到 face_normals 数组中,以便后续使用。

这段代码的目的是为每个面片计算法向量,这在渲染中常用于实现光照效果。法向量的方向决定了面片的朝向,而归一化确保了在计算光照时的准确性。

2. 在Trimesh.cpp中完善computeVertexNormals()函数,用于计算三角网格模型中每个顶点的法向量。

首先检查面片法向量是否已计算:

首先,通过检查 face_normals 数组是否为空,以及 faces 数组是否非空,判断是否需要计算面片法向量。如果条件满足,则调用 computeTriangleNormals 函数计算面片法向量。

初始化顶点法向量数组:

接下来,代码为存储顶点法向量的数组 vertex_normals 分配空间,并将其初始化为零向量。这个数组的大小与顶点位置数组 vertex_positions 相同。

求法向量均值:

通过遍历所有面片(faces 数组),对每个面片的三个顶点进行累加操作。

对于每个面片,获取面片的三个顶点索引(face.x、face.y、face.z),并将该面片法向量累加到这三个顶点对应的法向量上。

对累加的法向量进行归一化:

最后,遍历顶点法向量数组,并将每个顶点的法向量归一化。

总体而言,该函数通过累加相邻面片的法向量,并将累加的结果归一化,计算了每个顶点的法向量。这对于实现光照等渲染效果非常关键。

3. 从顶点着色器中初始化顶点的法向量。

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.nLocation 是法向量属性的位置。

3 表示法向量是一个由三个浮点数组成的向量。

GL_FLOAT 表示数据类型是浮点数。

GL_FALSE 表示数据是否应该被标准化(这里不标准化)。

0 表示步长,即每个法向量的字节偏移量。

BUFFER_OFFSET((mesh->getPoints().size() + mesh->getColors().size()) * sizeof(glm::vec3)) 表示偏移量,即法向量在顶点数据缓冲中的起始位置。这个偏移量考虑了之前顶点坐标和颜色所占用的字节数。

总体而言,这段代码配置了用于顶点法向量的顶点属性指针,并启用了对应的顶点属性数组,以便在渲染时使用。

4.在vshader.glsl中完善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; 设置最终的裁剪坐标。

传递法向量和位置:norm = vNormal; 将法向量传递给片段着色器。

pos = vPosition; 将顶点位置传递给片段着色器。

计算模视矩阵:mat4 ModelView = view * model; 计算模视矩阵,将模型坐标系变换到相机坐标系。

转换到相机坐标系:将顶点坐标、光源坐标和法向量转换到相机坐标系,计算归一化的向量 N,V,L,R,计算环境光分量 I_a、漫反射分量 I_d、镜面反射分量 I_s,修正透明度并合并分量,光源在背面时去除高光。

这个着色器的目的是为每个顶点计算光照效果,包括环境光、漫反射和镜面反射,并将结果传递给片段着色器进行插值处理。

5.在main.cpp中init()函数里改变材质参数。

此处选取如图所示的淡紫色材质的为参数。

这部分代码是在设置一个三维模型的材质属性。每个材质属性都以glm::vec4类型的颜色值表示,其中包括环境光(Ambient)、漫反射(Diffuse)、镜面反射(Specular)以及高光系数(Shininess)。

环境光(Ambient):glm::vec4(0.105882f, 0.058824f, 0.113725f, 1.0f)。这是模型在环境光照射下的颜色,包括红、绿、蓝三个分量,最后一个分量是透明度,这里设置为完全不透明。

漫反射(Diffuse):glm::vec4(0.427451f, 0.470588f, 0.541176f, 1.0f)。漫反射是模型表面对光源的散射,同样包括红、绿、蓝三个分量和透明度。

镜面反射(Specular):glm::vec4(0.333333f, 0.333333f, 0.521569f, 1.0f)。镜面反射是模型表面对光源的反射,同样包括红、绿、蓝三个分量和透明度。

高光系数(Shininess):9.84615f。高光系数决定了模型表面的光泽程度,这个值越高,高光反射的范围越小,看起来越光滑。

这些属性被用于计算光照效果,进而影响模型在渲染过程中的外观。

5.这是一个键盘事件的回调函数,用于处理用户按键操作,实现了以下功能:

窗口关闭: 如果用户按下ESC键,窗口会关闭。

显示帮助信息: 如果用户按下'H'键,将显示帮助信息。

读取不同的三维模型文件: 如果用户按下'Q'、'A'、'W'、'S'键,分别读取不同的三维模型文件("sphere.off"、"Pikachu.off"、"Squirtle.off"、"sphere_coarse.off")。

调整环境光系数: 如果用户按下数字键'1'并未按下Shift键,增加环境光的红色分量;如果按下Shift键,减小红色分量。同理,数字键'2'增减绿色分量,数字键'3'增减蓝色分量。

调整漫反射系数: 如果用户按下数字键'4'至'6',分别增减漫反射的红、绿、蓝分量。

调整镜面反射系数: 如果用户按下数字键'7'至'9',分别增减镜面反射的红、绿、蓝分量。

调整高光指数: 如果用户按下数字键'0',根据Shift键的状态增加或减少高光指数。

6.运行代码,如下图所示,实现了各项预期功能。

实验3.4

1. 计算面片法向量:给定三角形的三个顶点p_0,p_1和p_2,法向量为:(p_1-p_0)×(p_2-p_0 )

face_normals是存储每个三角面片法向量的容器,通过resize函数确保其大小与faces相同,以便存储每个面片的法向量。

循环遍历每个三角面片,使用faces中的索引获取三个顶点的坐标。

使用叉乘(glm::cross)计算面片的法向量,叉乘的结果是垂直于输入向量的法向量。

对计算得到的法向量进行归一化,确保法向量的长度为1。

将归一化后的法向量存储在face_normals中,最终得到每个三角面片的法向量。

2. b) 计算顶点法向量:给定顶点所在面片的法向量,顶点的平均法向量为法向量的和。

函数首先检查是否已计算面片的法向量,如果没有,则调用computeTriangleNormals来计算。

使用resize函数为vertex_normals分配一个和vertex_positions一样大的空间,并初始化所有法向量为零向量。

循环遍历每个三角面片,累加每个顶点的法向量。

最后,对每个顶点的法向量进行归一化,确保法向量的长度为1。

通过这个过程,每个顶点最终都会有一个与其相邻面法向量相关的法向量,并且这些法向量已经被归一化。

3. 参照顶点坐标vPosition的写法,在bindObjectAndData函数中将传递法向量的代码补全。

创建顶点数组对象和顶点缓存对象。

将顶点坐标、颜色和法向量数据传递给缓存对象。

设置顶点着色器和片段着色器。

从顶点着色器中初始化顶点的坐标、颜色和法向量。

获取矩阵位置和阴影位置。

4.补全fshader.glsl文件。

如果isShadow为1,表示当前渲染是用于阴影,将颜色设置为全黑。

否则,进行光照计算。

将顶点坐标、光源坐标和法向量转换到相机坐标系。

计算归一化的法向量N、观察向量V、光源向量L和反射向量R。

计算环境光分量I_a。

计算漫反射分量I_d。

计算镜面反射分量I_s。

如果光源在背面,去除高光反射。

合并三个分量的颜色,修正透明度。

5.补全键盘响应函数。

首先新建变量diffuse,然后根据键盘输入的不同键位,分别增减漫反射系数的 x、y、z 分量(键位 4、5、6),以及增减高光指数(键位 0)。

注意,漫反射系数是由 mesh->getDiffuse() 获取,高光指数是由 mesh->getShininess() 获取,并通过 mesh->setDiffuse() 和 mesh->setShininess() 进行设置。

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

实验三

1.使用实验3.3的代码,首先更改材质。

2.运行后球体如下图所示。

3.调整光源位置。

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

5.更改标题栏。

6.设置灰色背景。

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

8.默认读取球体。

9.添加一个平面变量。

10.在init函数中载入。

11.更改display函数。

12.更改fshader.glsl和vshader.glsl。

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

实验结论

通过此次实验,加深了对opengl绘制图形的理解和运用,以及对光照和阴影的生成以及变化有了深刻的体会。

实验代码

实验3.1——main.cpp

#include "Angel.h"
#include "TriMesh.h"
#include "Camera.h"
#include <vector>
#include <string>

int WIDTH = 600;
int HEIGHT = 600;
int mainWindow;


struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;

	// 投影变换变量
	GLuint modelLocation;
	GLuint viewLocation;
	GLuint projectionLocation;
};


openGLObject cube_object;
TriMesh *cube = new TriMesh();
Camera* camera_1 = new Camera();
Camera* camera_2 = new Camera();
Camera* camera_3 = new Camera();
Camera *camera_4 = new Camera();


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() * sizeof(glm::vec3) + mesh->getColors().size() * sizeof(glm::vec3),
		NULL, 
		GL_STATIC_DRAW);

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

	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.modelLocation = glGetUniformLocation(object.program, "model");
	object.viewLocation = glGetUniformLocation(object.program, "view");
	object.projectionLocation = glGetUniformLocation(object.program, "projection");
}


void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{	
	HEIGHT = height;
	WIDTH = width;
    glViewport(0, 0, width, height);
}


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

	// 读取立方体
	cube->readOff("./assets/cube.off");
	bindObjectAndData(cube, cube_object, vshader, fshader);

	// 黑色背景
	glClearColor(0.0, 0.0, 0.0, 1.0);
}


void display_1()
{
	glViewport(0, HEIGHT / 2, WIDTH / 2, HEIGHT / 2);

	camera_1->updateCamera();

	// 物体模型的变换矩阵
	glm::mat4 model = glm::mat4(1.0f);
	model = glm::rotate(model, glm::radians(camera_1->upAngle), glm::vec3(1.0, 0.0, 0.0));
	model = glm::rotate(model, glm::radians(-camera_1->rotateAngle), glm::vec3(0.0, 1.0, 0.0));
	model = glm::scale(model, glm::vec3(1.0 / camera_1->scale, 1.0 / camera_1->scale, 1.0 / camera_1->scale));

	glm::mat4 view = glm::mat4(1.0f);
	view[2][2] = -1.0;
	glm::mat4 projection = glm::mat4(1.0f);

	// 传递投影变换矩阵
	glUniformMatrix4fv(cube_object.modelLocation, 1, GL_FALSE, &model[0][0]);
	glUniformMatrix4fv(cube_object.viewLocation, 1, GL_FALSE, &view[0][0]);
	glUniformMatrix4fv(cube_object.projectionLocation, 1, GL_FALSE, &projection[0][0]);

	glUseProgram(cube_object.program);
	glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}


void display_2()
{
	glViewport(0, 0, WIDTH / 2, HEIGHT / 2);

	camera_2->updateCamera();

	glm::mat4 modelMatrix= glm::mat4(1.0); // 物体模型的变换矩阵  

	添加下面部分使其能够通过x和y旋转
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(camera_2->upAngle), glm::vec3(1.0, 0.0, 0.0));
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(-camera_2->rotateAngle), glm::vec3(0.0, 1.0, 0.0));
	//modelMatrix = glm::scale(modelMatrix, glm::vec3(1.0 / camera_2->scale, 1.0 / camera_2->scale, 1.0 / camera_2->scale));
	
	// 调用 Camera::lookAt 函数计算视图变换矩阵
	camera_2->viewMatrix = camera_2->lookAt(camera_2->eye, camera_2->at, camera_2->up);	
	
	// 调用 Camera:frustum 函数计算透视投影矩阵
	float top = tan(camera_2->fov * M_PI / 180 / 2) * camera_2->zNear;
	float right = top * camera_2->aspect;
	camera_2->projMatrix = camera_2->frustum(-right, right, -top, top, camera_2->zNear, camera_2->zFar);
	
	// 传递投影变换矩阵
	glUniformMatrix4fv(cube_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(cube_object.viewLocation, 1, GL_FALSE, &camera_2->viewMatrix[0][0]);
	glUniformMatrix4fv(cube_object.projectionLocation, 1, GL_FALSE, &camera_2->projMatrix[0][0]);

	glUseProgram(cube_object.program);
	glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}


void display_3()
{
	glViewport(WIDTH / 2, HEIGHT / 2, WIDTH / 2, HEIGHT / 2);

	camera_3->updateCamera();

	glm::mat4 modelMatrix = glm::mat4(1.0); // 物体模型的变换矩阵	
	添加下面部分使其能够通过x和y旋转
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(camera_3->upAngle), glm::vec3(1.0, 0.0, 0.0));
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(-camera_3->rotateAngle), glm::vec3(0.0, 1.0, 0.0));
	//modelMatrix = glm::scale(modelMatrix, glm::vec3(1.0 / camera_3->scale, 1.0 / camera_3->scale, 1.0 / camera_3->scale));

	camera_3->viewMatrix = camera_3->lookAt(camera_3->eye, camera_3->at, camera_3->up);	// 调用 Camera::lookAt 函数计算视图变换矩阵
	
	// @TODO: Task2: 调用 Camera::ortho 函数计算正交投影矩阵
	camera_3->projMatrix = camera_3->ortho(
		-camera_3->scale, camera_3->scale, -camera_3->scale, camera_3->scale,
		camera_3->zNear, camera_3->zFar
	);
	
	// 传递投影变换矩阵
	glUniformMatrix4fv(cube_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(cube_object.viewLocation, 1, GL_FALSE, &camera_3->viewMatrix[0][0]);
	glUniformMatrix4fv(cube_object.projectionLocation, 1, GL_FALSE, &camera_3->projMatrix[0][0]);

	glUseProgram(cube_object.program);
	glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}


void display_4()
{
	glViewport(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 2);

	camera_4->updateCamera();

	glm::mat4 modelMatrix = glm::mat4(1.0); // 物体模型的变换矩阵
	//添加下面部分使其能够通过x和y旋转
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(camera_4->upAngle), glm::vec3(1.0, 0.0, 0.0));
	//modelMatrix = glm::rotate(modelMatrix, glm::radians(-camera_4->rotateAngle), glm::vec3(0.0, 1.0, 0.0));
	//modelMatrix = glm::scale(modelMatrix, glm::vec3(1.0 / camera_4->scale, 1.0 / camera_4->scale, 1.0 / camera_4->scale));


	camera_4->viewMatrix = camera_4->lookAt(camera_4->eye, camera_4->at, camera_4->up);	// 调用 Camera::lookAt 函数计算视图变换矩阵

	// @TODO: Task3: 调用 Camera::perspective 函数计算透视投影矩阵
	camera_4->projMatrix = camera_4->perspective(
		camera_4->fov, camera_4->aspect, camera_4->zNear, camera_4->zFar
	);

	// 传递投影变换矩阵
	glUniformMatrix4fv(cube_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(cube_object.viewLocation, 1, GL_FALSE, &camera_4->viewMatrix[0][0]);
	glUniformMatrix4fv(cube_object.projectionLocation, 1, GL_FALSE, &camera_4->projMatrix[0][0]);

	glUseProgram(cube_object.program);
	glDrawArrays(GL_TRIANGLES, 0, cube->getPoints().size());
}


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

	// 调用4个display分别在4个子视图内绘制立方体
	// 每个display使用的相机的的投影类型与计算方法都不一样
	display_1();
	display_2();
	display_3();
	display_4();
}


void reshape(GLsizei w, GLsizei h)
{
	WIDTH = w;
	HEIGHT = h;
	glViewport(0, 0, WIDTH, HEIGHT);
}


void printHelp()
{
	std::cout << "Keyboard Usage" << std::endl;
	std::cout <<
		"Q/ESC: 	Exit" << std::endl <<
		"h: 	Print help message" << std::endl <<
		"SPACE: 	Reset all parameters" << std::endl <<
		"x/(shift+x): 	Increase/Decrease the rotate angle" << std::endl <<
		"y/(shift+y): 	Increase/Decrease the up angle" << std::endl <<
		"r/(shift+r): 	Increase/Decrease the distance between camera and object" << std::endl <<
		"f/(shift+f): 	Increase/Decrease FOV of perspective projection" << std::endl <<
		"a/(shift+a): 	Increase/Decrease WIDTH/HEIGHT aspect of perspective projection" << std::endl <<
		"s/(shift+s):	Increase/Decrease the extent of orthogonal projection" << std::endl;
}


void mainWindow_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	// 'ESC键退出'
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if(key == GLFW_KEY_H && action == GLFW_PRESS) 
	{
		printHelp();
	}
	else
	{
		camera_1->keyboard(key,action,mode);
		camera_2->keyboard(key,action,mode);
		camera_3->keyboard(key,action,mode);
		camera_4->keyboard(key,action,mode);
	}
}


void cleanData() {
	cube->cleanData();

	delete camera_1;
	delete camera_2;
	delete camera_3;
	delete camera_4;

	camera_1 = NULL;
	camera_2 = NULL;
	camera_3 = NULL;
	camera_4 = NULL;

	// 释放内存
	delete cube;
	cube = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &cube_object.vao);
	glDeleteBuffers(1, &cube_object.vbo);
	glDeleteProgram(cube_object.program);
}


int main(int argc, char **argv)
{	
	glfwInit();
		
	// 开启超采样,如果绘制图形仍然锯齿感很严重的话,可以尝试调大后面的参数值。
	glfwWindowHint(GLFW_SAMPLES,5);

	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* mainwindow = glfwCreateWindow(WIDTH, HEIGHT, "2021150047_hyf_实验3.1", NULL, NULL);
	if (mainwindow == NULL)
	{
		std::cout << "Failed to create GLFW window!" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(mainwindow);
	glfwSetFramebufferSizeCallback(mainwindow, framebuffer_size_callback);
	glfwSetKeyCallback(mainwindow,mainWindow_key_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
	glEnable(GL_DEPTH_TEST);
	init();
    printHelp();
	while (!glfwWindowShouldClose(mainwindow))
    {
        
        display();
        glfwSwapBuffers(mainwindow);
        glfwPollEvents();
		
    }
    glfwTerminate();
    return 0;
}

实验3.1——Camera.cpp

#include "Camera.h"

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


glm::mat4 Camera::lookAt(const glm::vec4& eye, const glm::vec4& at, const glm::vec4& up)
{	
	 @TODO: Task1:请按照实验课内容补全相机观察矩阵的计算
	//glm::mat4 c = glm::mat4(1.0f);
	//return c;
	
    // 计算相机的观察方向(向量从 eye 指向 at)
    glm::vec3 forward = glm::normalize(glm::vec3(at - eye));
    
    // 计算相机的右向量(与 up 向量和观察方向向量正交)
    glm::vec3 right = -glm::normalize(glm::cross(glm::vec3(up), forward));
    
    // 计算相机的新上向量(与观察方向向量和右向量正交)
    glm::vec3 newUp = -glm::cross(forward, right);

    // 初始化一个单位矩阵作为观察矩阵
    glm::mat4 viewMatrix(1.0f);
    
    // 更新观察矩阵的各个元素
    viewMatrix[0][0] = right.x;
    viewMatrix[1][0] = right.y;
    viewMatrix[2][0] = right.z;
    viewMatrix[0][1] = newUp.x;
    viewMatrix[1][1] = newUp.y;
    viewMatrix[2][1] = newUp.z;
    viewMatrix[0][2] = -forward.x;
    viewMatrix[1][2] = -forward.y;
    viewMatrix[2][2] = -forward.z;
    
    // 更新观察矩阵的平移部分,将相机位置纳入考虑
    viewMatrix[3][0] = -glm::dot(right, glm::vec3(eye));
    viewMatrix[3][1] = -glm::dot(newUp, glm::vec3(eye));
    viewMatrix[3][2] = glm::dot(forward, glm::vec3(eye));

    // 返回计算好的观察矩阵
    return viewMatrix;
}



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);
	//return c;

	// 创建一个单位矩阵作为正交投影矩阵的初始值
	glm::mat4 orthoMatrix(1.0f);

	// 计算投影矩阵的元素值
	orthoMatrix[0][0] = 2.0f / (right - left);  // X 缩放因子
	orthoMatrix[1][1] = 2.0f / (top - bottom);  // Y 缩放因子
	orthoMatrix[2][2] = -2.0f / (zFar - zNear); // Z 缩放因子(注意为负值)
	orthoMatrix[3][0] = -(right + left) / (right - left);     // X 平移因子
	orthoMatrix[3][1] = -(top + bottom) / (top - bottom);     // Y 平移因子
	orthoMatrix[3][2] = -(zFar + zNear) / (zFar - zNear);     // Z 平移因子

	 返回计算好的正交投影矩阵
	return orthoMatrix;
}


glm::mat4 Camera::perspective(const GLfloat fov, const GLfloat aspect,
	const GLfloat zNear, const GLfloat zFar)
{
	// @TODO: Task3:请按照实验课内容补全透视投影矩阵的计算
	//glm::mat4 c = glm::mat4(1.0f);
	//return c;

	// 创建一个单位矩阵作为透视投影矩阵的初始值
	glm::mat4 perspectiveMatrix(1.0f);

	// 计算透视投影矩阵的元素值
	float f = 1.0f / tan(glm::radians(fov / 2.0f)); // 计算焦距
	perspectiveMatrix[0][0] = f / aspect;  // X 缩放因子
	perspectiveMatrix[1][1] = f;           // Y 缩放因子
	perspectiveMatrix[2][2] = (zFar + zNear) / (zNear - zFar); // Z 缩放因子
	perspectiveMatrix[2][3] = -1.0f;       // Z 平移因子(注意为负值)
	perspectiveMatrix[3][2] = (2.0f * zFar * zNear) / (zNear - zFar); // Z 平移因子

	// 返回计算好的透视投影矩阵
	return perspectiveMatrix;
}


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;
	c = glm::transpose(c);
	return c;
}


void Camera::updateCamera()
{
	 @TODO: Task1 设置相机位置和方向
	//float eyex = 0;			// 根据rotateAngle和upAngle设置x
	//float eyey = 0;			// 根据upAngle设置y
	//float eyez = radius;	// 根据rotateAngle和upAngle设置z
	
	/*eye = glm::vec4(eyex, eyey, eyez, 1.0);
	up = glm::vec4(0.0, 1.0, 0.0, 0.0);
	at = glm::vec4(0.0, 0.0, 0.0, 1.0);*/
	
	// 角度转弧度
	float radiansUp = glm::radians(upAngle);
	float radiansRotate = glm::radians(rotateAngle);

	 计算相机位置
	float eyex = radius * cos(radiansUp) * sin(radiansRotate);
	float eyey = radius * sin(radiansUp);
	float eyez = radius * cos(radiansUp) * cos(radiansRotate);

	// 更新相机位置
	eye = glm::vec4(eyex, eyey, eyez, 1.0);

	 设置相机的参考点(at)
	at = glm::vec4(0.0, 0.0, 0.0, 1.0);

	 //设置相机的 VUP 方向,与世界坐标系的 y 方向相同(方向朝上)
	up = glm::vec4(0.0, 1.0, 0.0, 0.0);
	
	// @TODO: Task1:设置相机参数
	// 使用相对于at的角度控制相机的时候,注意在upAngle大于90的时候,相机坐标系的u向量会变成相反的方向,
	// 要将up的y轴改为负方向才不会发生这种问题

	// 也可以考虑直接控制相机自身的俯仰角,
	// 保存up,eye-at 这些向量,并修改这些向量方向来控制
	// 看到这里的有缘人可以试一试
}

//void Camera::updateCamera()
//{
//	// 角度转弧度
//	float radiansUp = glm::radians(upAngle);
//	float radiansRotate = glm::radians(rotateAngle);
//
//	// 计算相机位置
//	float eyex = radius * sin(radiansUp) * cos(radiansRotate);
//	float eyey = radius * cos(radiansUp);
//	float eyez = radius * sin(radiansUp) * sin(radiansRotate);
//
//	// 更新相机位置
//	eye = glm::vec4(eyex, eyey, eyez, 1.0);
//
//	// 设置相机的参考点(at)
//	at = glm::vec4(0.0, 0.0, 0.0, 1.0);
//
//	// 设置相机的 VUP 方向,与世界坐标系的 y 方向相同(方向朝上)
//	up = glm::vec4(0.0, 1.0, 0.0, 0.0);
//
//	// @TODO: Task1:设置相机参数
//	// 使用相对于at的角度控制相机的时候,注意在upAngle大于90的时候,相机坐标系的u向量会变成相反的方向,
//	// 要将up的y轴改为负方向才不会发生这种问题
//
//	// 也可以考虑直接控制相机自身的俯仰角,
//	// 保存up,eye-at 这些向量,并修改这些向量方向来控制
//	// 看到这里的有缘人可以试一试
//}



void Camera::keyboard(int key, int action, int mode)
{

	// 键盘事件处理
	if (key == GLFW_KEY_X && action == GLFW_PRESS && mode == 0x0000)
	{
		rotateAngle += 5.0;
	}
	else if(key == GLFW_KEY_X && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		rotateAngle -= 5.0;
	}
	else if(key == GLFW_KEY_Y && action == GLFW_PRESS && mode == 0x0000)
	{
		upAngle += 5.0;
		if (upAngle > 180)
			upAngle = 180;
	}
	else if(key == GLFW_KEY_Y && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		upAngle -= 5.0;
		if (upAngle < -180)
			upAngle = -180;
	}
	else if(key == GLFW_KEY_R && action == GLFW_PRESS && mode == 0x0000)
	{
		radius += 0.1;
	}
	else if(key == GLFW_KEY_R && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		radius -= 0.1;
	}
	else if(key == GLFW_KEY_F && action == GLFW_PRESS && mode == 0x0000)
	{
		fov += 5.0;
	}
	else if(key == GLFW_KEY_F && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		fov -= 5.0;
	}
	else if(key == GLFW_KEY_A && action == GLFW_PRESS && mode == 0x0000)
	{
		aspect += 0.1;
	}
	else if(key == GLFW_KEY_A && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		aspect -= 0.1;
	}
	else if(key == GLFW_KEY_S && action == GLFW_PRESS && mode == 0x0000)
	{
		scale += 0.1;
	}
	else if(key == GLFW_KEY_S && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		scale -= 0.1;
	}
	else if(key == GLFW_KEY_SPACE && action == GLFW_PRESS && mode == 0x0000)
	{
		radius = 4.0;
		rotateAngle = 0.0;
		upAngle = 0.0;
		fov = 45.0;
		aspect = 1.0;
		scale = 1.5;
	}
}

实验3.2——main.cpp

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

#include <vector>
#include <string>

int WIDTH = 600;
int HEIGHT = 600;
int mainWindow;

struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;

	// 投影变换变量
	GLuint modelLocation;
	GLuint viewLocation;
	GLuint projectionLocation;

	// 阴影变量
	GLboolean shadowLocation;
};

// 全局变量光源位置
glm::vec3 light_position = glm::vec3(0.0, 1.5, 1.0);

// 全局变量键盘控制光源移动的参数
float move_step_size = 0.2;

openGLObject tri_object;
openGLObject plane_object;

TriMesh* triangle = new TriMesh();
TriMesh *plane = new TriMesh();
Camera* camera = new Camera();


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

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

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

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

	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.modelLocation = glGetUniformLocation(object.program, "model");
	object.viewLocation = glGetUniformLocation(object.program, "view");
	object.projectionLocation = glGetUniformLocation(object.program, "projection");

	// 获得阴影标识的位置
	object.shadowLocation = glGetUniformLocation(object.program, "isShadow");
}


void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}


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

	// 创建三角形,颜色设置为红色
	triangle->generateTriangle( glm::vec3( 1.0, 0.0, 0.0 ) );
	
	// 设置三角形的位置和旋转
	triangle->setRotation(glm::vec3(90, 0, 0));
	triangle->setTranslation(glm::vec3(0, 0.3, 0));
	triangle->setScale(glm::vec3(0.5, 0.5, 0.5));

	bindObjectAndData(triangle, tri_object, vshader, fshader);

	// 创建正方形平面,设置为黄绿色
	plane->generateSquare(glm::vec3(0.6, 0.8, 0.0));

	// 设置正方形的位置和旋转,注意这里我们将正方形平面下移了一点点距离,
	// 这是为了防止和阴影三角形重叠在同个平面上导致颜色交叉
	plane->setRotation(glm::vec3(90, 0, 0));
	plane->setTranslation(glm::vec3(0, -0.001, 0));
	plane->setScale(glm::vec3(3, 3, 3));

	bindObjectAndData(plane, plane_object, vshader, fshader);

	glClearColor(1.0, 1.0, 1.0, 1.0);
}


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

	camera->updateCamera();

	// 计算视图变换矩阵
	camera->viewMatrix = camera->getViewMatrix();
	// 计算相机投影矩阵
	camera->projMatrix = camera->getProjectionMatrix(true);

	// 绘制三角形
    glBindVertexArray(tri_object.vao);
	glUseProgram(tri_object.program);
	// 三角形物体的变换矩阵,注意初始三角形是与Z轴垂直的
	glm::mat4 modelMatrix = triangle->getModelMatrix();

	// 传递投影变换矩阵
	glUniformMatrix4fv(tri_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(tri_object.viewLocation, 1, GL_FALSE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(tri_object.projectionLocation, 1, GL_FALSE, &camera->projMatrix[0][0]);
	// 将isShadow设置为1,表示正常绘制的颜色,如果是0则表示阴影
	glUniform1i(tri_object.shadowLocation, 1);
	glDrawArrays(GL_TRIANGLES, 0, triangle->getPoints().size());

	
	// @TODO: Task2:根据光源位置,计算阴影投影矩阵
	 绘制三角形阴影
	//glm::mat4 lightProjectionMatrix = glm::ortho(-3.0f, 3.0f, -3.0f, 3.0f, -3.0f, 3.0f);
	//glm::mat4 lightViewMatrix = glm::lookAt(light_position, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));	 
	//
	 计算阴影的模型变换矩阵,使用与三角形相同的模型矩阵
	//glm::mat4 shadowModelMatrix = triangle->getModelMatrix();
	//
	 传递 uniform 关键字的矩阵数据,分别为投影矩阵、视图矩阵和模型矩阵
	//glUniformMatrix4fv(tri_object.projectionLocation, 1, GL_FALSE, &lightProjectionMatrix[0][0]);
	//glUniformMatrix4fv(tri_object.viewLocation, 1, GL_FALSE, &lightViewMatrix[0][0]);
	//glUniformMatrix4fv(tri_object.modelLocation, 1, GL_FALSE, &shadowModelMatrix[0][0]);
	 传递 isShadow 变量,设置为0,表示绘制阴影
	//glUniform1i(tri_object.shadowLocation, 0);
	 绘制三角形
	//glDrawArrays(GL_TRIANGLES, 0, triangle->getPoints().size());
	//
	// 减小远裁剪面的值
	//camera->zFar -= 50.0;

	// 计算阴影坐标
	glm::mat4 shadowMatrix;

	// 光源位置
	glm::vec3 lightPosition = glm::vec3(light_position);

	//  投影平面的法向量
	glm::vec3 planeNormal = glm::vec3(0.0f, 1.0f, 0.0f);

	// 计算D参数
	float D = -glm::dot(planeNormal, glm::vec3(0.0f, 0.0f, 0.0f));

	// 计算阴影矩阵
	shadowMatrix[0][0] = lightPosition.y * planeNormal.y + lightPosition.z * planeNormal.z;
	shadowMatrix[1][0] = lightPosition.x * planeNormal.y;
	shadowMatrix[2][0] = lightPosition.x * planeNormal.z;
	shadowMatrix[3][0] = 0.0f;

	shadowMatrix[0][1] = lightPosition.y * planeNormal.x;
	shadowMatrix[1][1] = lightPosition.x * planeNormal.x + lightPosition.z * planeNormal.z;
	shadowMatrix[2][1] = lightPosition.y * planeNormal.z;
	shadowMatrix[3][1] = 0.0f;

	shadowMatrix[0][2] = lightPosition.z * planeNormal.x;
	shadowMatrix[1][2] = lightPosition.z * planeNormal.y;
	shadowMatrix[2][2] = lightPosition.x * planeNormal.x + lightPosition.y * planeNormal.y;
	shadowMatrix[3][2] = 0.0f;

	shadowMatrix[0][3] = lightPosition.x * D;
	shadowMatrix[1][3] = lightPosition.y * D;
	shadowMatrix[2][3] = lightPosition.z * D;
	shadowMatrix[3][3] = lightPosition.x * planeNormal.x + lightPosition.y * planeNormal.y + lightPosition.z * planeNormal.z;

	// 将阴影矩阵乘以三角形的模型矩阵得到阴影坐标
	glm::mat4 shadowModelMatrix = shadowMatrix * modelMatrix;

	// 传递阴影矩阵给着色器	
	glUniformMatrix4fv(tri_object.modelLocation, 1, GL_FALSE, &shadowModelMatrix[0][0]);
	//glUniformMatrix4fv(tri_object.modelLocation, 1, GL_FALSE, &shadowMatrix[0][0]);	

	// 将isShadow设置为0,表示绘制阴影
	glUniform1i(tri_object.shadowLocation, 0);  
	glDrawArrays(GL_TRIANGLES, 0, triangle->getPoints().size());


	// 绘制平面
    glBindVertexArray(plane_object.vao);
	glUseProgram(plane_object.program);

	modelMatrix = plane->getModelMatrix();

	glUniformMatrix4fv(plane_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(plane_object.viewLocation, 1, GL_FALSE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(plane_object.projectionLocation, 1, GL_FALSE, &camera->projMatrix[0][0]);
	// 将isShadow设置为1,表示正常绘制的颜色,如果是0则表示阴影
	glUniform1i(plane_object.shadowLocation, 1);
	glDrawArrays(GL_TRIANGLES, 0, plane->getPoints().size());
}


void printHelp()
{
	std::cout << "Keyboard Usage" << std::endl;
	std::cout <<
		"[Window]" << std::endl <<
		"ESC:		Exit" << std::endl <<
		"h:		Print help message" << std::endl <<
		std::endl <<
		"[Light]" << std::endl <<
		"r:		Reset light parameters" << std::endl <<
		"x/(shift+x):		move the light along X positive/negative axis" << std::endl <<
		"y/(shift+y):		move the light along Y positive/negative axis" << std::endl <<
		"z/(shift+z):		move the light along Z positive/negative axis" << std::endl <<
		"a/(shift+a):		Increase/Decrease move_step_size" << std::endl <<
		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/(shift+u):		Increase/Decrease the rotate angle" << std::endl <<
		"i/(shift+i):		Increase/Decrease the up angle" << std::endl <<
		"o/(shift+o):		Increase/Decrease the scale" << std::endl;

}

void mainWindow_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	// 'ESC键退出'
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if(key == GLFW_KEY_H && action == GLFW_PRESS) 
	{
		printHelp();
	}
	else if(key == GLFW_KEY_R && action == GLFW_PRESS) 
	{
		light_position = glm::vec3(0.0, 1.5, 1.0);
		move_step_size = 0.2;
	}
	else if(key == GLFW_KEY_X && action == GLFW_PRESS && mode == 0x0000) 
	{
		light_position[0] += move_step_size;
	}
	else if(key == GLFW_KEY_X && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		light_position[0] -= move_step_size;
	}
	else if(key == GLFW_KEY_Y && action == GLFW_PRESS && mode == 0x0000) 
	{
		light_position[1] += move_step_size;
	}
	else if(key == GLFW_KEY_Y && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		light_position[1] -= move_step_size;
		if (light_position[1] <= 1.0 ) {
			light_position[1] += move_step_size;
		}
	}
	else if(key == GLFW_KEY_Z && action == GLFW_PRESS && mode == 0x0000) 
	{
		light_position[2] += move_step_size;
	}
	else if(key == GLFW_KEY_Z && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		light_position[2] -= move_step_size;
	}
	else if(key == GLFW_KEY_A && action == GLFW_PRESS && mode == 0x0000) 
	{
		move_step_size += 0.1;
	}
	else if(key == GLFW_KEY_A && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		move_step_size -= 0.1;
	}
	
	else
	{
		camera->keyboard(key,action,mode);
	}
}


void cleanData() {
	triangle->cleanData();
	plane->cleanData();

	delete camera;
	camera = NULL;

	// 释放内存
	delete triangle;
	triangle = NULL;

	delete plane;
	plane = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &tri_object.vao);
	glDeleteVertexArrays(1, &plane_object.vao);

	glDeleteBuffers(1, &tri_object.vbo);
	glDeleteProgram(tri_object.program);

	glDeleteBuffers(1, &plane_object.vbo);
	glDeleteProgram(plane_object.program);
}


int main(int argc, char **argv)
{
	glfwInit();
	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* mainwindow = glfwCreateWindow(600, 600, "2021150047_hyf_实验3.2", NULL, NULL);
	if (mainwindow == NULL)
	{
		std::cout << "Failed to create GLFW window!" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(mainwindow);
	glfwSetFramebufferSizeCallback(mainwindow, framebuffer_size_callback);
	glfwSetKeyCallback(mainwindow,mainWindow_key_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
	glEnable(GL_DEPTH_TEST);
	init();
    printHelp();
	while (!glfwWindowShouldClose(mainwindow))
    {
        
        display();
        glfwSwapBuffers(mainwindow);
        glfwPollEvents();
		
    }
    glfwTerminate();
    return 0;
}

实验3.2——Camera.cpp

#include "Camera.h"

Camera::Camera() { 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(fov, aspect, this->zNear, this->zFar);
	}
}


glm::mat4 Camera::lookAt(const glm::vec4& eye, const glm::vec4& at, const glm::vec4& up)
{
	// @TODO: Task1:请按照实验课内容补全相机观察矩阵的计算
	// 计算相机坐标系的z轴向量
	glm::vec3 n = glm::normalize(glm::vec3(eye - at));//

	// 计算相机坐标系的x轴向量(右侧向量)
	glm::vec3 u = glm::normalize(glm::vec3(glm::cross(glm::vec3(up), n)));

	// 计算相机坐标系的y轴向量(上方向)
	glm::vec3 v = glm::cross(n, u);

	// 初始化观察矩阵为单位矩阵
	glm::mat4 c;

	// 填充观察矩阵的第一列(右侧向量 + 平移)
	c[0][0] = u.x;
	c[1][0] = u.y;
	c[2][0] = u.z;
	c[3][0] = -glm::dot(u, glm::vec3(eye));

	// 填充观察矩阵的第二列(上方向 + 平移)
	c[0][1] = v.x;
	c[1][1] = v.y;
	c[2][1] = v.z;
	c[3][1] = -glm::dot(v, glm::vec3(eye));

	// 填充观察矩阵的第三列(相机朝向 + 平移)
	c[0][2] = n.x;
	c[1][2] = n.y;
	c[2][2] = n.z;
	c[3][2] = -glm::dot(n, glm::vec3(eye));

	// 填充观察矩阵的第四列
	c[0][3] = 0.0f;
	c[1][3] = 0.0f;
	c[2][3] = 0.0f;
	c[3][3] = 1.0f;

	// 返回计算出的观察矩阵
	return c;
}

glm::mat4 Camera::ortho(const GLfloat left, const GLfloat right,
	const GLfloat bottom, const GLfloat top,
	const GLfloat zNear, const GLfloat zFar)
{	
	// @TODO: Task1:请按照实验课内容补全
	// 计算正交投影矩阵
	glm::mat4 projectionMatrix = glm::mat4(1.0f);	//对角线初始化为1.0

	// 设置正交投影矩阵的各个参数
	projectionMatrix[0][0] = 2.0f / (right - left);
	projectionMatrix[1][1] = 2.0f / (top - bottom);
	projectionMatrix[2][2] = -2.0f / (zFar - zNear);

	projectionMatrix[0][3] = -(right + left) / (right - left);
	projectionMatrix[1][3] = -(top + bottom) / (top - bottom);
	projectionMatrix[2][3] = -(zFar + zNear) / (zFar - zNear);

	// 最后一行通常被设置为(0, 0, 0, 1),表示不执行透视变换

	return projectionMatrix;
}

glm::mat4 Camera::perspective(const GLfloat fovy, const GLfloat aspect,
	const GLfloat zNear, const GLfloat zFar)
{
	// @TODO: Task1:请按照实验课内容补全
	// 计算焦距(fovy是视野角度,将其转换为弧度)
	float f = 1.0f / tan(fovy / 2.0f);//

	// 创建透视投影矩阵
	glm::mat4 c = glm::mat4(1.0f);
	c[0][0] = f / aspect;
	c[1][1] = f;
	c[2][2] = (zFar + zNear) / (zNear - zFar);
	c[2][3] = (2.0f * zFar * zNear) / (zNear - zFar);//
	c[3][2] = -1.0f;//

	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;

	c = glm::transpose(c);
	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);
	at = glm::vec4(0.0, 0.0, 0.0, 1.0);
	// up = vec4(0.0, 1.0, 0.0, 0.0);

}

void Camera::keyboard(int key, int action, int mode)
{
	// 键盘事件处理

	if(key == GLFW_KEY_U && action == GLFW_PRESS && mode == 0x0000) 
	{
		rotateAngle += 5.0;
	}
	else if(key == GLFW_KEY_U && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		rotateAngle -= 5.0;
	}
	else if(key == GLFW_KEY_I && action == GLFW_PRESS && mode == 0x0000) 
	{
		upAngle += 5.0;
		if (upAngle > 180)
			upAngle = 180;
	}
	else if(key == GLFW_KEY_I && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		upAngle -= 5.0;
		if (upAngle < -180)
			upAngle = -180;
	}
	else if(key == GLFW_KEY_O && action == GLFW_PRESS && mode == 0x0000) 
	{
		scale += 0.1;
	}
	else if(key == GLFW_KEY_O && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		scale -= 0.1;
	}
	else if(key == GLFW_KEY_SPACE && action == GLFW_PRESS && mode == 0x0000) 
	{
		radius = 4.0;
		rotateAngle = 0.0;
		upAngle = 30.0;
		fov = 45.0;
		aspect = 1.0;
		scale = 1.5;
	}
}

实验3.3——main.cpp

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

#include <vector>
#include <string>
#include <algorithm>

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;
bool is_click = false;

struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;
	GLuint nLocation;

	// 投影变换变量
	GLuint modelLocation;
	GLuint viewLocation;
	GLuint projectionLocation;

	// 阴影变量
	GLuint shadowLocation;
};


openGLObject mesh_object;

Light* light = new Light();

TriMesh* mesh = new TriMesh();

Camera* camera = new Camera();


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


	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]);
	// @TODO: Task1 修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错
	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)));

	// @TODO: Task1 从顶点着色器中初始化顶点的法向量
	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 framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

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

	// @TODO: Task3 请自己调整光源参数和物体材质参数来达到不同视觉效果
	// 设置光源位置
	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)); // 镜面反射

	// 设置物体的旋转位移
	mesh->setTranslation(glm::vec3(0.0, 0.0, 0.0));
	mesh->setRotation(glm::vec3(0, 0.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); //高光系数
	//淡紫色
	mesh->setAmbient(glm::vec4(0.105882f, 0.058824f, 0.113725f, 1.0f)); // 环境光
	mesh->setDiffuse(glm::vec4(0.427451f, 0.470588f, 0.541176f, 1.0f)); // 漫反射
	mesh->setSpecular(glm::vec4(0.333333f, 0.333333f, 0.521569f, 1.0f)); // 镜面反射
	mesh->setShininess(9.84615f); //高光系数

	// 将物体的顶点数据传递
	bindObjectAndData(mesh, mesh_object, vshader, fshader);

	glClearColor(0.0, 0.0, 0.0, 0.0);
}


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

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

	glBindVertexArray(mesh_object.vao);

	glUseProgram(mesh_object.program);

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

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

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

}


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 <<
		"[Model]" << std::endl <<
		"-:		Reset material parameters" << std::endl <<
		"(shift) + 1/2/3:	Change ambient parameters" << std::endl <<
		"(shift) + 4/5/6:	Change diffuse parameters" << std::endl <<
		"(shift) + 7/8/9:	Change specular parameters" << std::endl <<
		"(shift) + 0:		Change shininess parameters" << std::endl <<
		std::endl <<
		"q:		Load sphere model" << std::endl <<
		"a:		Load Pikachu model" << std::endl <<
		"w:		Load Squirtle model" << std::endl <<
		"s:		Load sphere_coarse model" << std::endl <<
		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/(shift+u):		Increase/Decrease the rotate angle" << std::endl <<
		"i/(shift+i):		Increase/Decrease the up angle" << std::endl <<
		"o/(shift+o):		Increase/Decrease the camera radius" << std::endl << std::endl;

}


void mainWindow_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	float tmp;
	glm::vec4 ambient;
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if (key == GLFW_KEY_H && action == GLFW_PRESS)
	{
		printHelp();
	}
	else if (key == GLFW_KEY_Q && action == GLFW_PRESS)
	{
		std::cout << "read sphere.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere.off");
		init();
	}
	else if (key == GLFW_KEY_A && action == GLFW_PRESS)
	{
		std::cout << "read Pikachu.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Pikachu.off");
		init();
	}
	else if (key == GLFW_KEY_W && action == GLFW_PRESS)
	{
		std::cout << "read Squirtle.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Squirtle.off");
		init();
	}
	else if (key == GLFW_KEY_S && action == GLFW_PRESS)
	{
		std::cout << "read sphere_coarse.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere_coarse.off");
		init();
	}
	else if (key == GLFW_KEY_1 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_1 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_2 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_2 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_3 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_3 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_MINUS && action == GLFW_PRESS && mode == 0x0000)
	{
		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);
	}
	// @TODO: Task4 添加更多交互 1~9增减反射系数(1~3已写好),0增减高光指数
	//1-3分别是环境光(ambient)的xyz,4-6是漫反射(diffuse)的xyz、7-9是镜面反射(specular)的xyz
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}

	else if (key == GLFW_KEY_7 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.x;
		specular.x = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_7 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.x;
		specular.x = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_8 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.y;
		specular.y = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_8 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.y;
		specular.y = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_9 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.z;
		specular.z = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_9 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.z;
		specular.z = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_0 && action == GLFW_PRESS)
	{
		float shininess = mesh->getShininess();
		float delta = 1.0;  // 根据需要调整增减的步长

		// 根据用户的操作增加或减少高光指数
		if (mode == GLFW_MOD_SHIFT){  // 如果按下了 Shift 键
			shininess -= delta;  // 减少高光指数
		}else{
			shininess += delta;  // 增加高光指数
		}

		// 确保高光指数在合理范围内
		shininess = glm::clamp(shininess, 0.1f, 100.0f);

		// 将新的高光指数设置回物体
		mesh->setShininess(shininess);
	}
	else
	{
		camera->keyboard(key, action, mode);
	}

}


void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
	if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
	{
		double x, y;
		glfwGetCursorPos(window, &x, &y);

		float half_winx = WIDTH / 2.0;
		float half_winy = HEIGHT / 2.0;
		float lx = float(x - half_winx) / half_winx;
		float ly = float(HEIGHT - y - half_winy) / half_winy;

		glm::vec3 pos = light->getTranslation();

		pos.x = lx;
		pos.y = ly;

		light->setTranslation(pos);
	}
}


void cleanData() {
	mesh->cleanData();

	delete camera;
	camera = NULL;

	// 释放内存
	delete mesh;
	mesh = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &mesh_object.vao);
	glDeleteBuffers(1, &mesh_object.vbo);
	glDeleteProgram(mesh_object.program);

}


int main(int argc, char** argv)
{
	glfwInit();
	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* mainwindow = glfwCreateWindow(600, 600, "2021150047_hyf_实验3.3", NULL, NULL);
	if (mainwindow == NULL)
	{
		std::cout << "Failed to create GLFW window!" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(mainwindow);
	glfwSetFramebufferSizeCallback(mainwindow, framebuffer_size_callback);
	glfwSetKeyCallback(mainwindow, mainWindow_key_callback);
	glfwSetMouseButtonCallback(mainwindow, mouse_button_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}
	glEnable(GL_DEPTH_TEST);
	mesh->readOff("./assets/sphere.off");
	mesh->generateCube();
	// Init mesh, shaders, buffer
	init();
	printHelp();
	// bind callbacks
	while (!glfwWindowShouldClose(mainwindow))
	{

		display();
		glfwSwapBuffers(mainwindow);
		glfwPollEvents();

	}
	glfwTerminate();
	return 0;
}

实验3.3——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<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;
}

void TriMesh::computeTriangleNormals()
{
	// 这里的resize函数会给face_normals分配一个和faces一样大的空间
	face_normals.resize(faces.size());
	for (size_t i = 0; i < faces.size(); i++) {
		
		// @TODO: Task1 计算每个面片的法向量并归一化
		const vec3i& face = faces[i];

		// 获取面片的三个顶点
		glm::vec3 v0 = vertex_positions[face.x];
		glm::vec3 v1 = vertex_positions[face.y];
		glm::vec3 v2 = vertex_positions[face.z];

		// 计算面法向量
		glm::vec3 edge1 = v1 - v0;
		glm::vec3 edge2 = v2 - v0;
		glm::vec3 norm = glm::cross(edge1, edge2);

		// 归一化法向量
		norm = glm::normalize(norm);

		// 将法向量存储到face_normals中
		face_normals[i] = norm;
	}
}

void TriMesh::computeVertexNormals()
{
	// 计算面片的法向量
	if (face_normals.size() == 0 && faces.size() > 0) {
		computeTriangleNormals();
	}

	// 为vertex_normals分配与vertex_positions相同大小的空间,并初始化法向量为0
	vertex_normals.resize(vertex_positions.size(), glm::vec3(0, 0, 0));

	// @TODO: Task1 求法向量均值
	// 求法向量均值
	for (size_t i = 0; i < faces.size(); i++)
	{
		const vec3i& 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] = glm::normalize(vertex_normals[i]);
	}
	
	//for (size_t i = 0; i < faces.size(); i++) {
	//	auto& face = faces[i];
	//	// @TODO: 先累加面的法向量
	//	// vertex_normals[face.x] += face_normals[i];
	//	// ...
	//}
	 @TODO 对累加的法向量归一化
	//for (size_t i = 0; i < vertex_normals.size(); i++) {

	//}
}

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

	faces.clear();
	face_normals.clear();

	points.clear();
	colors.clear();
	normals.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[faces[i].x]);
		colors.push_back(vertex_colors[faces[i].y]);
		colors.push_back(vertex_colors[faces[i].z]);
		// 法向量
		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() {
	// 创建顶点前要先把那些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));

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

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

void TriMesh::readOff(const std::string& filename)
{
	// fin打开文件读取文件信息
	if (filename.empty())
	{
		return;
	}
	std::ifstream fin;
	fin.open(filename);
	// 此函数读取OFF文件中三维模型的信息
	if (!fin)
	{
		printf("File on error\n");
		return;
	}
	else
	{
		printf("File open success\n");

		cleanData();

		int nVertices, nFaces, nEdges;

		// 读取OFF字符串
		std::string str;
		fin >> str;
		// 读取文件中顶点数、面片数、边数
		fin >> nVertices >> nFaces >> nEdges;
		// 根据顶点数,循环读取每个顶点坐标
		for (int i = 0; i < nVertices; i++)
		{
			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();

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

实验3.3——vshader.glsl

#version 330 core

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

	vec3 position;
};

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

	float shininess;
};

in vec3 vPosition;
in vec3 vColor;
// 顶点法向量
in vec3 vNormal;

// 传给片元着色器的变量
out vec4 color;
out vec3 norm;
out vec3 pos;

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

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



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;

    norm = vNormal;
    pos = vPosition;

    // 计算模视矩阵
    mat4 ModelView = view * model;

    // 将顶点坐标、光源坐标和法向量转换到相机坐标系
    vec3 pos = (ModelView * vec4(vPosition, 1.0) ).xyz;
    vec3 l_pos = (view * vec4(light.position, 1.0) ).xyz;
    vec3 norm = (ModelView * vec4(vNormal, 0.0)).xyz;

    // @TODO: Task2 计算四个归一化的向量 N,V,L,R(或半角向量H)
    vec3 N = normalize(norm);
    vec3 V = normalize(eye_position - pos);
    vec3 L = normalize(l_pos - pos);
    vec3 R = reflect(-L, N);
    
    // 环境光分量I_a
    vec4 I_a = light.ambient * material.ambient;

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

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

    // 注意如果光源在背面则去除高光
    // if( dot(L, N) < 0.0 ) {
	//     I_s = vec4(0.0, 0.0, 0.0, 1.0);
    // } 
    if (diffuse_dot < 0.0) {
        I_s = vec4(0.0, 0.0, 0.0, 1.0);
    }

    // 合并三个分量的颜色,修正透明度
    color = I_a + I_d + I_s;
    color.a = 1.0;
}

实验3.4——main.cpp

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

#include <vector>
#include <string>
#include <algorithm>

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;
bool is_click = false;

struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;
	GLuint nLocation;

	// 投影变换变量
	GLuint modelLocation;
	GLuint viewLocation;
	GLuint projectionLocation;

	// 阴影变量
	GLuint shadowLocation;
};


openGLObject mesh_object;

Light* light = new Light();

TriMesh* mesh = new TriMesh();

Camera* camera = new Camera();


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

	
	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]);
	// @TODO: Task1 修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错
	 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)));

	// @TODO: Task1 从顶点着色器中初始化顶点的法向量
	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 framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

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

	// @TODO: Task3 请自己调整光源参数和物体材质参数来达到不同视觉效果
	// 设置光源位置
	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)); // 镜面反射

	// 设置物体的旋转位移
	mesh->setTranslation(glm::vec3(0.0, 0.0, 0.0));
	mesh->setRotation(glm::vec3(0, 0.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);

	glClearColor(0.0, 0.0, 0.0, 0.0);
}


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

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

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

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

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

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

}


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 <<
		"[Model]" << std::endl <<
		"-:		Reset material parameters" << std::endl <<
		"(shift) + 1/2/3:	Change ambient parameters" << std::endl <<
		"(shift) + 4/5/6:	(TODO) Change diffuse parameters" << std::endl <<
		"(shift) + 7/8/9:	(TODO) Change specular parameters" << std::endl <<
		"(shift) + 0:		(TODO) Change shininess parameters" << std::endl <<
		std::endl <<
		"q:		Load sphere model" << std::endl << 
		"a:		Load Pikachu model" << std::endl << 
		"w:		Load Squirtle model" << std::endl << 
		"s:		Load sphere_coarse model" << std::endl << 
		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/(shift+u):		Increase/Decrease the rotate angle" << std::endl <<
		"i/(shift+i):		Increase/Decrease the up angle" << std::endl <<
		"o/(shift+o):		Increase/Decrease the camera radius" << std::endl << std::endl;

}


void mainWindow_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	float tmp;
	glm::vec4 ambient;
	glm::vec4 diffuse;
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if(key == GLFW_KEY_H && action == GLFW_PRESS) 
	{
		printHelp();
	}
	else if(key == GLFW_KEY_Q && action == GLFW_PRESS) 
	{
		std::cout << "read sphere.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere.off");
		init();
	}
	else if(key == GLFW_KEY_A && action == GLFW_PRESS) 
	{
		std::cout << "read Pikachu.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Pikachu.off");
		init();
	}
	else if(key == GLFW_KEY_W && action == GLFW_PRESS) 
	{
		std::cout << "read Squirtle.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Squirtle.off");
		init();
	}
	else if(key == GLFW_KEY_S && action == GLFW_PRESS) 
	{
		std::cout << "read sphere_coarse.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere_coarse.off");
		init();
	}
	else if(key == GLFW_KEY_1 && action == GLFW_PRESS && mode == 0x0000) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::min(tmp + 0.1, 1.0); 
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_1 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::max(tmp - 0.1, 0.0); 
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_2 && action == GLFW_PRESS && mode == 0x0000) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_2 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_3 && action == GLFW_PRESS && mode == 0x0000) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_3 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT) 
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if(key == GLFW_KEY_MINUS && action == GLFW_PRESS && mode == 0x0000)
	{
		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);
	}
	// @TODO: Task4 添加更多交互 1~9增减反射系数(1~3已写好),0增减高光指数(实现一半)
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == 0x0000)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == 0x0000)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == 0x0000)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_0 && action == GLFW_PRESS && mode == 0x0000)
	{
		float shininess = mesh->getShininess();
		shininess = std::min(shininess + 1.0, 128.0);
		mesh->setShininess(shininess);
	}
	else if (key == GLFW_KEY_0 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		float shininess = mesh->getShininess();
		shininess = std::max(shininess - 1.0, 1.0);
		mesh->setShininess(shininess);
	}
	else
	{
		camera->keyboard(key, action, mode);
	}

}


void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
	if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
	{
		double x, y;
		glfwGetCursorPos(window, &x, &y);

		float half_winx = WIDTH / 2.0;
		float half_winy = HEIGHT / 2.0;
		float lx = float(x - half_winx) / half_winx;
		float ly = float(HEIGHT - y - half_winy) / half_winy;

		glm::vec3 pos = light->getTranslation();
		
		pos.x = lx;
		pos.y = ly;

		light->setTranslation(pos);
	}
}


void cleanData() {
	mesh->cleanData();
	
	delete camera;
	camera = NULL;

	// 释放内存
	delete mesh;
	mesh = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &mesh_object.vao);
	glDeleteBuffers(1, &mesh_object.vbo);
	glDeleteProgram(mesh_object.program);

}


int main(int argc, char **argv)
{
	glfwInit();
	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* mainwindow = glfwCreateWindow(600, 600, "2021150047_hyf_实验3.4", NULL, NULL);
	if (mainwindow == NULL)
	{
		std::cout << "Failed to create GLFW window!" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(mainwindow);
	glfwSetFramebufferSizeCallback(mainwindow, framebuffer_size_callback);
	glfwSetKeyCallback(mainwindow,mainWindow_key_callback);
	glfwSetMouseButtonCallback(mainwindow, mouse_button_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
	glEnable(GL_DEPTH_TEST);
	mesh->readOff("./assets/sphere.off");
	mesh->generateCube();
	// Init mesh, shaders, buffer
	init();
    printHelp();
	// bind callbacks
	while (!glfwWindowShouldClose(mainwindow))
    {
        
        display();
        glfwSwapBuffers(mainwindow);
        glfwPollEvents();
		
    }
    glfwTerminate();
    return 0;
}

实验3.4——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<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;
}

void TriMesh::computeTriangleNormals()
{
	// 这里的resize函数会给face_normals分配一个和faces一样大的空间
	face_normals.resize(faces.size());
	for (size_t i = 0; i < faces.size(); i++) {
		auto& face = faces[i];
		// @TODO: Task1 计算每个面片的法向量并归一化
		// 获取三个顶点坐标
		glm::vec3 p0 = vertex_positions[face.x];
		glm::vec3 p1 = vertex_positions[face.y];
		glm::vec3 p2 = vertex_positions[face.z];

		// 计算面片法向量
		glm::vec3 norm = glm::cross(p1 - p0, p2 - p0);

		// 归一化法向量
		face_normals[i] = glm::normalize(norm);

		//glm::vec3 norm;
		// face_normals[i] = norm;
	}
}

void TriMesh::computeVertexNormals()
{
	// 计算面片的法向量
	if (face_normals.size() == 0 && faces.size() > 0) {
		computeTriangleNormals();
	}
	// 这里的resize函数会给vertex_normals分配一个和vertex_positions一样大的空间
	// 并初始化法向量为0
	vertex_normals.resize(vertex_positions.size(), glm::vec3(0, 0, 0));
	// @TODO: Task1 求法向量均值
	for (size_t i = 0; i < faces.size(); i++) {
		auto& face = faces[i];
		// @TODO: 先累加面的法向量
		// vertex_normals[face.x] += face_normals[i];
		// ...
		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] = glm::normalize(vertex_normals[i]);
	}
}

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();
	
	faces.clear();
	face_normals.clear();

	points.clear();
	colors.clear();
	normals.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[faces[i].x]);
		colors.push_back(vertex_colors[faces[i].y]);
		colors.push_back(vertex_colors[faces[i].z]);
		// 法向量
		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() {
	// 创建顶点前要先把那些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));

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

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

void TriMesh::readOff(const std::string& filename)
{
    // fin打开文件读取文件信息
    if (filename.empty())
    {
        return;
    }
    std::ifstream fin;
    fin.open(filename);
    // 此函数读取OFF文件中三维模型的信息
    if (!fin)
    {
        printf("File on error\n");
        return;
    }
    else
    {
        printf("File open success\n");

		cleanData();

		int nVertices, nFaces, nEdges;

        // 读取OFF字符串
        std::string str;
        fin >> str;
        // 读取文件中顶点数、面片数、边数
        fin >> nVertices >> nFaces >> nEdges;
        // 根据顶点数,循环读取每个顶点坐标
        for (int i = 0; i < nVertices; i++)
        {
            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();

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

实验3.4——fshader.glsl

#version 330 core

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

	vec3 position;
};

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

	float shininess;
};

in vec3 position;
in vec3 normal;

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

uniform int isShadow;

out vec4 fColor;

void main()
{
	if (isShadow == 1) {
		fColor = vec4(0.0, 0.0, 0.0, 1.0);
	}
	else {

		// 将顶点坐标、光源坐标和法向量转换到相机坐标系
		vec3 norm = (vec4(normal, 0.0)).xyz;

		// @TODO: 计算四个归一化的向量 N,V,L,R(或半角向量H)
		vec3 V = normalize(eye_position - position);
        vec3 L = normalize(light.position - position);
        vec3 R = reflect(-L, norm);
        vec3 N = norm;



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

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

		// @TODO: Task2 计算系数和镜面反射分量I_s
		float specular_dot_pow = pow(max(dot(R, V), 0.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;
		
	}
}

实验三——main.cpp

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

#include <vector>
#include <string>
#include <algorithm>

int WIDTH = 600;
int HEIGHT = 600;

int mainWindow;

struct openGLObject
{
	// 顶点数组对象
	GLuint vao;
	// 顶点缓存对象
	GLuint vbo;

	// 着色器程序
	GLuint program;
	// 着色器文件
	std::string vshader;
	std::string fshader;
	// 着色器变量
	GLuint pLocation;
	GLuint cLocation;
	GLuint nLocation;

	// 投影变换变量
	GLuint modelLocation;
	GLuint viewLocation;
	GLuint projectionLocation;

	// 阴影变量
	GLuint shadowLocation;
};


openGLObject mesh_object;
openGLObject plane_object;

Light* light = new Light();

TriMesh* mesh = new TriMesh();

Camera* camera = new Camera();

TriMesh* plane = new TriMesh();


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


	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]);
	// @TODO: Task1 修改完TriMesh.cpp的代码成后再打开下面注释,否则程序会报错
	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)));

	// @TODO: Task1 从顶点着色器中初始化顶点的法向量
	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 framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

void init()
{
	mesh->readOff("./assets/sphere.off");

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

	// @TODO: Task3 请自己调整光源参数和物体材质参数来达到不同视觉效果
	// 设置光源位置
	//light->setTranslation(glm::vec3(0.0, 0.0, 2.0));
	light->setTranslation(glm::vec3(0.0, 3.0, 0.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)); // 镜面反射

	// 设置物体的旋转位移
	mesh->setTranslation(glm::vec3(0.0, 0.5, 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); //高光系数
	//淡紫色
	//mesh->setAmbient(glm::vec4(0.105882f, 0.058824f, 0.113725f, 1.0f)); // 环境光
	//mesh->setDiffuse(glm::vec4(0.427451f, 0.470588f, 0.541176f, 1.0f)); // 漫反射
	//mesh->setSpecular(glm::vec4(0.333333f, 0.333333f, 0.521569f, 1.0f)); // 镜面反射
	//mesh->setShininess(9.84615f); //高光系数

	// 将物体的顶点数据传递
	bindObjectAndData(mesh, mesh_object, vshader, fshader);

	plane->generateSquare(glm::vec3(0.7, 0.7, 0.7));
	plane->setRotation(glm::vec3(0, 90, 90));
	plane->setTranslation(glm::vec3(0, -0.001, 0));
	plane->setScale(glm::vec3(3., 3., 3.));

	bindObjectAndData(plane, plane_object, vshader, fshader);

	glClearColor(0.4, 0.4, 0.4, 1.0);
}


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

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

	glBindVertexArray(mesh_object.vao);

	glUseProgram(mesh_object.program);

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

	// 传递矩阵
	/*glUniformMatrix4fv(mesh_object.modelLocation, 1, GL_FALSE, &modelMatrix[0][0]);
	glUniformMatrix4fv(mesh_object.viewLocation, 1, GL_FALSE, &camera->viewMatrix[0][0]);
	glUniformMatrix4fv(mesh_object.projectionLocation, 1, GL_FALSE, &camera->projMatrix[0][0]);*/
	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 设置为0,表示正常绘制的颜色,如果是1着表示阴影
	glUniform1i(mesh_object.shadowLocation, 0);

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

	//-------------------------------------
		// 绘制阴影
	glm::vec3 light_pos = light->getTranslation();
	float lx = light_pos[0];
	float ly = light_pos[1];
	float lz = light_pos[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;
	glUniform1i(mesh_object.shadowLocation, 1);
	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]);
	glDrawArrays(GL_TRIANGLES, 0, mesh->getPoints().size());
}


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 <<
		"[Model]" << std::endl <<
		"-:		Reset material parameters" << std::endl <<
		"(shift) + 1/2/3:	Change ambient parameters" << std::endl <<
		"(shift) + 4/5/6:	Change diffuse parameters" << std::endl <<
		"(shift) + 7/8/9:	Change specular parameters" << std::endl <<
		"(shift) + 0:		Change shininess parameters" << std::endl <<
		std::endl <<
		"q:		Load sphere model" << std::endl <<
		"a:		Load Pikachu model" << std::endl <<
		"w:		Load Squirtle model" << std::endl <<
		"s:		Load sphere_coarse model" << std::endl <<
		std::endl <<
		"[Camera]" << std::endl <<
		"SPACE:		Reset camera parameters" << std::endl <<
		"u/(shift+u):		Increase/Decrease the rotate angle" << std::endl <<
		"i/(shift+i):		Increase/Decrease the up angle" << std::endl <<
		"o/(shift+o):		Increase/Decrease the camera radius" << std::endl << std::endl;

}


void mainWindow_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
	float tmp;
	glm::vec4 ambient;
	if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
	{
		glfwSetWindowShouldClose(window, GL_TRUE);
	}
	else if (key == GLFW_KEY_H && action == GLFW_PRESS)
	{
		printHelp();
	}
	else if (key == GLFW_KEY_Q && action == GLFW_PRESS)
	{
		std::cout << "read sphere.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere.off");
		init();
	}
	else if (key == GLFW_KEY_A && action == GLFW_PRESS)
	{
		std::cout << "read Pikachu.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Pikachu.off");
		init();
	}
	else if (key == GLFW_KEY_W && action == GLFW_PRESS)
	{
		std::cout << "read Squirtle.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/Squirtle.off");
		init();
	}
	else if (key == GLFW_KEY_S && action == GLFW_PRESS)
	{
		std::cout << "read sphere_coarse.off" << std::endl;
		mesh = new TriMesh();
		mesh->readOff("./assets/sphere_coarse.off");
		init();
	}
	else if (key == GLFW_KEY_1 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_1 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.x;
		ambient.x = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_2 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_2 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.y;
		ambient.y = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_3 && action == GLFW_PRESS && mode == 0x0000)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::min(tmp + 0.1, 1.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_3 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		ambient = mesh->getAmbient();
		tmp = ambient.z;
		ambient.z = std::max(tmp - 0.1, 0.0);
		mesh->setAmbient(ambient);
	}
	else if (key == GLFW_KEY_MINUS && action == GLFW_PRESS && mode == 0x0000)
	{
		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);
	}
	// @TODO: Task4 添加更多交互 1~9增减反射系数(1~3已写好),0增减高光指数
	//1-3分别是环境光(ambient)的xyz,4-6是漫反射(diffuse)的xyz、7-9是镜面反射(specular)的xyz
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_4 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.x;
		diffuse.x = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_5 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.y;
		diffuse.y = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::min(tmp + 0.1, 1.0);
		mesh->setDiffuse(diffuse);
	}
	else if (key == GLFW_KEY_6 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 diffuse = mesh->getDiffuse();
		tmp = diffuse.z;
		diffuse.z = std::max(tmp - 0.1, 0.0);
		mesh->setDiffuse(diffuse);
	}

	else if (key == GLFW_KEY_7 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.x;
		specular.x = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_7 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.x;
		specular.x = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_8 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.y;
		specular.y = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_8 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.y;
		specular.y = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_9 && action == GLFW_PRESS && mode == 0x0000)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.z;
		specular.z = std::min(tmp + 0.1, 1.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_9 && action == GLFW_PRESS && mode == GLFW_MOD_SHIFT)
	{
		glm::vec4 specular = mesh->getSpecular();
		tmp = specular.z;
		specular.z = std::max(tmp - 0.1, 0.0);
		mesh->setSpecular(specular);
	}
	else if (key == GLFW_KEY_0 && action == GLFW_PRESS)
	{
		float shininess = mesh->getShininess();
		float delta = 1.0;  // 根据需要调整增减的步长

		// 根据用户的操作增加或减少高光指数
		if (mode == GLFW_MOD_SHIFT){  // 如果按下了 Shift 键
			shininess -= delta;  // 减少高光指数
		}else{
			shininess += delta;  // 增加高光指数
		}

		// 确保高光指数在合理范围内
		shininess = glm::clamp(shininess, 0.1f, 100.0f);

		// 将新的高光指数设置回物体
		mesh->setShininess(shininess);
	}
	else
	{
		camera->keyboard(key, action, mode);
	}

}


void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
	if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
	{
		double x, y;
		glfwGetCursorPos(window, &x, &y);

		float half_winx = WIDTH / 2.0;
		float half_winy = HEIGHT / 2.0;
		float lx = float(x - half_winx) / half_winx;
		float ly = float(HEIGHT - y - half_winy) / half_winy;

		glm::vec3 pos = light->getTranslation();

		pos.x = lx;
		pos.y = ly;

		light->setTranslation(pos);
	}
}


void cleanData() {
	mesh->cleanData();

	delete camera;
	camera = NULL;

	// 释放内存
	delete mesh;
	mesh = NULL;

	// 删除绑定的对象
	glDeleteVertexArrays(1, &mesh_object.vao);
	glDeleteBuffers(1, &mesh_object.vbo);
	glDeleteProgram(mesh_object.program);

}


int main(int argc, char** argv)
{
	glfwInit();
	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* mainwindow = glfwCreateWindow(600, 600, "2021150047_hyf_实验3", NULL, NULL);
	if (mainwindow == NULL)
	{
		std::cout << "Failed to create GLFW window!" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(mainwindow);
	glfwSetFramebufferSizeCallback(mainwindow, framebuffer_size_callback);
	glfwSetKeyCallback(mainwindow, mainWindow_key_callback);
	glfwSetMouseButtonCallback(mainwindow, mouse_button_callback);
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}
	glEnable(GL_DEPTH_TEST);
	mesh->readOff("./assets/sphere.off");
	mesh->generateCube();
	// Init mesh, shaders, buffer
	init();
	printHelp();
	// bind callbacks
	while (!glfwWindowShouldClose(mainwindow))
	{

		display();
		glfwSwapBuffers(mainwindow);
		glfwPollEvents();

	}
	glfwTerminate();
	return 0;
}

实验三——fshader.glsl

#version 330 core

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

	vec3 position;
};

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

	float shininess;
};

in vec3 position;
in vec3 normal;

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

uniform int isShadow;

out vec4 fColor;

void main()
{
	if (isShadow == 1) {
		fColor = vec4(0.0, 0.0, 0.0, 1.0);
	}
	else {

		// 将顶点坐标、光源坐标和法向量转换到相机坐标系
		vec3 norm = (vec4(normal, 0.0)).xyz;

		// @TODO: 计算四个归一化的向量 N,V,L,R(或半角向量H)
		// vec3 N=
		// vec3 V=
		// vec3 L=
		// vec3 R=

		vec3 N = normalize( norm );
		vec3 L = normalize( light.position - position );
		vec3 V = normalize( eye_position - position );
		vec3 R = reflect(-L, N);
		vec3 H = normalize( L + V );

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

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

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


		// 注意如果光源在背面则去除高光
		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;
		
	}
}

实验三——vshader.glsl

#version 330 core

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

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

// 模型变换矩阵、相机观察矩阵、投影矩阵
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 = vPosition;
    // normal = vNormal;
    position = vec3(v2.xyz);
    normal = vec3( model * vec4(vNormal, 0.0) );

}

(by 归忆)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

归忆_AC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值