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