链接: https://pan.baidu.com/s/1cBTTbbzRCVBCX_H4jf6qMA 提取码: kj8w
一、实验要求和内容
1.1 实验内容
(1)实验描述
实现一个三维图形交互程序,能够读入三维obj文件、绘制并打上光照,并且实现基本的三维交互—包含平移、旋转和缩放。要求能够成功读取发给大家的obj文件模型(bunny.obj),也可以自行增加另外的三维模型文件。此作业要求每人单独完成。

(2)实验环境
在Clion平台下结合OpenGL开发
操作系统:macOS Monterey 12.0 Beta版(21A5248p)
处理器:Apple M1
内存:16.00GB
系统类型:64位操作系统

1.2 实验要求
(1)读入三维Obj文件
设计一定的数据结构,从而实现从实验资料bunny.obj文件中,将三维模型的必要信息进行读取收集。
(2)绘制三维模型并打光
在上述操作后,结合OpenGL的数据接口,绘制三维模型,给场景添加光照,使得最终效果良好。
(3)实现三维交互
实现基本的三维交互,包括平移,旋转,缩放。
二、实验步骤
2.1 数据结构设计
(1)OpenGL绘制要求
三维模型事实上是由许多的三角面拼接而成的,而OpenGL的接口可以绘制小三角面,通过绘制许多三角面,最终拼接成兔子模型。
要绘制三维模型,OpenGL需要知道三维模型每个三角面的顶点的坐标(每个坐标有x、y、z这3个坐标值),以及每个点或者每个面的法向量;读出obj文件中的顶点坐标v,顶点向量值vn,然后根据每个三角形面的点坐标和法向的索引值,把点和法向按照顺序存储在triangleVerts和normals中,再三个点为一组面进行渲染,得到bunny模型。
(2)设计数据结构
设计存储模型的类Imported

class Imported
{private:int numVertices{};//存放点的个数std::vector<glm::vec3> vertices;//存放顶点集std::vector<glm::vec3> normalVecs;//存放法向集std::vector<float> vertVals;//存放读入的点的值std::vector<float> triangleVerts;//存放按面的索引排序后的顶点集std::vector<float> fnormals;//存放按面的索引排序后的法向集std::vector<float> normVals;//存放法向的值
public:Imported();//无参构造Imported(const char *filePath);//文件名构造,传输本地模型int getNumVertices() const;//返回顶点个数的函数std::vector<glm::vec3> getVertices();//返回顶点集vertices的函数std::vector<glm::vec3> getNormals();//返回法向集normalVecs的函数
};

内置返回顶点个数、顶点集、法向集的函数,Imported利用parseOBJ函数实现获取有顺序意义的点集和法向集,以此来返回正确的顶点个数、顶点集、法向集。

2.2 OBJ文件读取
(1)Bunny.obj文件内容格式
将bunny.obj文件用记事本打开如下(截取部分):

对于该文件内容有如下说明:
v:代表顶点。格式为v、x、y、z,v后面的x、y、z表示三个顶点坐标。
vn:法向量。三角形的三个顶点都要指定法向量。格式为vn,nx、ny、nz。
f: 面。后面的整型值分别是属于这个面的顶点、法向量的索引。
(2)文件读取
在该文件中,顶点数据所在行由“v”字符开头,法向量数据所在行由“vn”字符开头,而面数据所在行由“f”字符开头。由此,根据每行开头字符,可以通过C++文件流将obj文件中不同类型的数据装入特定的数据结构中。
其中对使用的特殊的文件流对象stringstream,通过stringstream对象,我们可以简单的将一行以空格(或其他字符)为分界的字符串进行值分离。在本实验中,以点为例

1.v 0.1102022 0.74011 1.132398

用该对象可以轻松的将三个值读取出来。
然而在读取面时,则先将“//”替换成空格,最后通过子串函数获取相应值。

2.f 6830//6830 10332//10332 15543//15543

2.3 程序模块介绍
(1)读取文件模块
通过Imported读取文件模块,可以将Obj文件的点、法向量、面的数据转移到内存中,通过一定数据结构进行存储。上述内容已经给出了文件模块的具体代码,其主要思想是:先读取模型中的点和点的法向,存储起来,然后将模型的面部分逐行读取,通过面的索引把顶点数据和法向数据按照面的顺序重新储存一遍。

#include <fstream>
#include <sstream>
#include "glm/glm.hpp"
#include "Model.h"
using namespace std;Imported::Imported() = default;Imported::Imported(const char *filePath) {float x, y, z;ifstream fileStream(filePath, ios::in);string line;while (!fileStream.eof()) {getline(fileStream, line);if (line.compare(0, 2, "v ") == 0) {stringstream ss(line.erase(0, 1));ss >> x; ss >> y; ss >> z;vertVals.push_back(x);vertVals.push_back(y);vertVals.push_back(z);}if (line.compare(0, 2, "vn") == 0) {stringstream ss(line.erase(0, 2));ss >> x; ss >> y; ss >> z;normVals.push_back(x);normVals.push_back(y);normVals.push_back(z);}if (line.compare(0, 2, "f ") == 0) {string oneCorner, v, t, n;stringstream ss(line.erase(0, 2));for (int i = 0; i < 3; i++) {getline(ss, oneCorner, ' ');stringstream oneCornerSS(oneCorner);getline(oneCornerSS, v, '/');//顶点索引值getline(oneCornerSS, t, '/');//纹理坐标索引值getline(oneCornerSS, n, '/');//顶点法线索引值int vertRef = (stoi(v) - 1) * 3;    //第v个顶点int normRef = (stoi(n) - 1) * 3;triangleVerts.push_back(vertVals[vertRef]);//xtriangleVerts.push_back(vertVals[vertRef + 1]);//ytriangleVerts.push_back(vertVals[vertRef + 2]);//zfnormals.push_back(normVals[normRef]);//nxfnormals.push_back(normVals[normRef + 1]);//nyfnormals.push_back(normVals[normRef + 2]);//nz}}}numVertices = (int)triangleVerts.size()/3;//获取顶点的个数for (int i = 0; i < numVertices; i++) {//将点和法向加入点集和法向集vertices.emplace_back(triangleVerts[i*3], triangleVerts[i*3+1], triangleVerts[i*3+2]);normalVecs.emplace_back(fnormals[i*3], fnormals[i*3+1], fnormals[i*3+2]);}
}//返回类中参数的各个函数
int Imported::getNumVertices() const { return numVertices; }
std::vector<glm::vec3> Imported::getVertices() { return vertices; }
std::vector<glm::vec3> Imported::getNormals() { return normalVecs; }

(2)绘制模型模块
通过绘制模型模块,可以将vector中存储的数据,调用OpenGL的接口,绘制一个个小的三角形面片,最后绘制成三维模型。

(3)函数、参数定义模块
这里再主函数外声明了需要用到的参数和函数等等,比如鼠标和键盘的监听函数等等,以及模型几何变换的平移、缩放、旋转等参数。

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;//缩放参数
float size= 0.3f;//大小//平移参数
float front_back = 0;
float front_back2 = 0;
float left_right = 0;
float left_right2 = 2.0;
float up_down = 0;
float up_down2 = 0;//线框图转换标志
int line_flag = 0;// camera视角变化类的引入
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;float deltaTime = 0.0f;
float lastFrame = 0.0f;

(4)主函数初始化模块
主函数开始先进行glfw的初始化。然后建立着色器、调用输入设备监听函数等等,为后面的模型渲染做铺垫。

glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//OpenGL主版本号 3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//OpenGL副版本号 .3
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//OpenGL模式 OpenGL核心模式#ifdef __APPLE__//MacOS 下必须的调用glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", nullptr, nullptr);//窗口宽、高、标题
if (window == nullptr)
{std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;
}//各类主进程函数的调用,比如鼠标和键盘监听等等。
glfwMakeContextCurrent(window);//让当前窗口的环境在当前线程上成为当前环境
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//监听窗口大小变化
glfwSetCursorPosCallback(window, mouse_callback);//鼠标监听
glfwSetScrollCallback(window, scroll_callback);//滚轮
glfwSetKeyCallback(window, key_callback);//键盘监听// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{std::cout << "Failed to initialize GLAD" << std::endl;return -1;
}// build and compile our shader zprogram
// ------------------------------------
Shader lightingShader("/Users/bloodsvery/Desktop/学习/大二下/计算机图形学/Curriculumdesign1/1.colors.vert", "/Users/bloodsvery/Desktop/学习/大二下/计算机图形学/Curriculumdesign1/1.colors.frag");

(5)几何变换以及光源设置模块
该模块设定好缩放平移旋转、视角移动的变换矩阵,以及光照模型需要的参数向量,利用定义好的set方法传入着色器lightingShader中。

glClearColor(0.5f, 0.5f, 0.5f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);auto timeControl = (float)glfwGetTime();//定义timeControl系统时间函数float radius = 10.0f;float camX = sin(timeControl) * radius;float camZ = cos(timeControl) * radius;//设置X、Z轴随时间的三角函数,实现光源的旋转glm::vec3 lightPos(camX, 0.0f, camZ);//设置光源三维向量的旋转glm::vec3 lightPos_pro(-camX, 0.0f, -camZ);//设置光源三维向量的旋转lightingShader.setVec3("objectColor", 0.788f, 0.459f, 0.525f);lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);lightingShader.setVec3("lightPos", lightPos);lightingShader.setVec3("lightPos_pro", lightPos_pro);lightingShader.setVec3("viewPos", camera.Position);// view/projection transformationsglm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();lightingShader.setMat4("projection", projection);lightingShader.setMat4("view", view);// world transformationglm::mat4 model = glm::mat4(1.0f);//缩放model = glm::scale(model, glm::vec3(size,size,size));//平移model = glm::translate(model, glm::vec3(left_right, up_down, front_back));//旋转lightingShader.setMat4("model", model);glBindVertexArray(vao[0]);glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices());glm::mat4 projection2 = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.3f, 100.0f);glm::mat4 view2 = camera.GetViewMatrix();lightingShader.setMat4("projection2", projection2);lightingShader.setMat4("view2", view2);// world transformationglm::mat4 model2 = glm::mat4(3.0f);//缩放model2 = glm::scale(model2, glm::vec3(size,size,size));//平移model2 = glm::translate(model2, glm::vec3(left_right2, up_down2, front_back2));//旋转lightingShader.setMat4("model", model2);

(6)视角变化监听模块
鼠标移动可以视点不变,操控视角变化。

void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if (firstMouse){lastX = (float)xpos;lastY = (float)ypos;firstMouse = false;}float xoffset = (float)xpos - lastX;float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to toplastX = (float)xpos;lastY = (float)ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}

键盘的WSADQE可以控制视点的左、右、前、后、上、下六个方位变化,封装在processInput函数中,这样就可以控制视角和视点的变化了。

void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)camera.ProcessKeyboard(FORWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)camera.ProcessKeyboard(BACKWARD, deltaTime);if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)camera.ProcessKeyboard(LEFT, deltaTime);if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)camera.ProcessKeyboard(RIGHT, deltaTime);if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)camera.ProcessKeyboard(UP, deltaTime);if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)camera.ProcessKeyboard(DOWN, deltaTime);
}

(7)几何变换监听模块
该模块主要负责键盘事件控制几何变换,以及线框图与面图转换的监听。

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{if (key == GLFW_KEY_Z) size += 0.03;//模型放大if (key == GLFW_KEY_X) size -= 0.03;//模型缩小if (key == GLFW_KEY_F) front_back += 0.3;//模型前移if (key == GLFW_KEY_G) front_back -= 0.3;//模型后移if (key == GLFW_KEY_J) left_right += 0.3;//模型右移if (key == GLFW_KEY_H) left_right -= 0.3;//模型左移if (key == GLFW_KEY_K) up_down += 0.3;//模型上移if (key == GLFW_KEY_L) up_down -= 0.3;//模型下移if (key == GLFW_KEY_C) line_flag = 0;//转换为面图if (key == GLFW_KEY_V) line_flag = 1;//转换为线框图
}

(8)着色器模块

其中lightPos和lightPos_pro是主函数中定义的可以随系统时间旋转的两个光源的位置向量。

1.colors.vert#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec3 aNormal;layout (location = 2) in vec2 aTexCoord;out vec3 FragPos;out vec3 Normal;out vec2 TexCoord;uniform mat4 model;uniform mat4 view;uniform mat4 projection;void main(){FragPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;gl_Position = projection * view * vec4(FragPos, 1.0);TexCoord = vec2(aTexCoord.x, aTexCoord.y);}1.colors.frag#version 330 core
out vec4 FragColor;in vec3 Normal;
in vec3 FragPos;uniform vec3 lightPos;
uniform vec3 lightPos_pro;
uniform vec3 lightColor;
uniform vec3 viewPos;
uniform vec3 objectColor;void main()
{// ambientfloat ambientStrength = 0.12;vec3 ambient = ambientStrength * lightColor;// diffuse vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);//入射方向vec3 lightDir_pro = normalize(lightPos_pro - FragPos);//入射方向float diff = max(dot(norm, lightDir), 0.0);float diff_pro = max(dot(norm, lightDir_pro), 0.0);vec3 diffuse = diff * lightColor;vec3 diffuse_pro = diff_pro * lightColor;// specularfloat specularStrength = 0.5;vec3 viewDir = normalize(viewPos - FragPos);vec3 reflectDir = reflect(-lightDir, norm);vec3 reflectDir_pro = reflect(-lightDir_pro, norm);float spec = pow(max(dot(viewDir, reflectDir), 0.0), 128);float spec_pro = pow(max(dot(viewDir, reflectDir_pro), 0.0), 128);vec3 specular = specularStrength * lightColor * spec;vec3 specular_pro = specularStrength * lightColor * spec_pro;vec3 result = (ambient + diffuse + specular + diffuse_pro + specular_pro) * objectColor;FragColor = vec4(result, 1.0);
}

(9)纹理模块

三、实验结果展示与分析
(1)功能整体概述
本次实验实现了双光照模型,通过两个光源旋转对bunny和shuttle进行打光,程序也可以实现对模型的线框转换、旋转、平移、缩放操作,占用22个键盘快捷键,实现对模型几何状态的完全操控。

(2)截图展示

初始模型

视角变换

前后平移

上下平移

左右平移

模型缩放

线框转换

四、实验体会

这次3D图形的课程设计,我成功地用OpenGL的GLFW绘制出了三维的兔子模型以及航天飞机模型,这也加深了我对三角形面片构成三维模型的理解和认识。同时,这也让我复习了C++的文件流输入输出和字符串处理,也对obj文件也有了一定的认识。
实验过程中,我遇到了很多各种各样的问题,比如如何同时存放两个模型,依照之前的太阳系实验代码,成功做出来了。以及模型读取那里卡了很久,后来完成了还是很有成就感的。
唯一可惜的就是我使用的是现成的vn,并没有通过计算来实现。
这次实验使我对图形学的兴趣更加浓厚,做出完整的模型也使我充满了自豪感,今后的学习中我会更加努力。

OpenGL课程设计 三维图形交互程序 bunny兔+飞机模型相关推荐

  1. 计算机网络课程设计之网络聊天程序的设计与实现

    前言 计算机网络课程设计开始,要求用C/C++,而且有些题目还要有图形界面,因此决定用Qt写图形界面,同时决定用一个程序完成所有的实验设计. 白嫖容易,创作不易,本文原创,转载请注明!!! 源码和可运 ...

  2. java模拟时钟课程设计_java课程设计-时钟图形模拟

    java课程设计-时钟图形模拟 计算机与信息工程系 <高级语言程序设计>课程设计报告课 程 设 计 任 务 书专 业 通信工程 班 级 13 级四班 姓 名 张凯铭设 计 起 止 日 期设 ...

  3. Springboot企业工资管理rycxe计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot企业工资管理rycxe计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot企业工资管理rycxe计算机毕业设计-课程设计-期末作业-毕设程序代做 ...

  4. java时钟课程设计,Java课程设计-时钟图形模拟

    <Java课程设计-时钟图形模拟>由会员分享,可在线阅读,更多相关<Java课程设计-时钟图形模拟(19页珍藏版)>请在人人文库网上搜索. 1.课 程 设 计 任 务 书专 业 ...

  5. Springboot门诊电子处方管理系统3kqta计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot门诊电子处方管理系统3kqta计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot门诊电子处方管理系统3kqta计算机毕业设计-课程设计-期末作业 ...

  6. Springboot旅游管理系统的设计与实现4eqkg计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot旅游管理系统的设计与实现4eqkg计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot旅游管理系统的设计与实现4eqkg计算机毕业设计-课程设计- ...

  7. Springboot老来福平台682f5计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot老来福平台682f5计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot老来福平台682f5计算机毕业设计-课程设计-期末作业-毕设程序代做 本源 ...

  8. c语言课程设计作业计算器图形界面,C语言课程设计--计算器(图形界面).doc

    C语言课程设计--计算器(图形界面) 扬 州 大 学 ------------------1 程序设计内容:------------------1 课程设计所补充的内容:补充的函数或算法----3,4 ...

  9. Springboot列车调度信息系统的设计与实现4guf9计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot列车调度信息系统的设计与实现4guf9计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot列车调度信息系统的设计与实现4guf9计算机毕业设计-课 ...

  10. Springboot就业推荐系统qwy6c计算机毕业设计-课程设计-期末作业-毕设程序代做

    Springboot就业推荐系统qwy6c计算机毕业设计-课程设计-期末作业-毕设程序代做 [免费赠送源码]Springboot就业推荐系统qwy6c计算机毕业设计-课程设计-期末作业-毕设程序代做 ...

最新文章

  1. 用PS制作APP的界面图片
  2. 写了一个puppet web 管理界面,打算开源
  3. C++线性序列容器vector简单总结
  4. 阴差阳错2019-12-13
  5. python unit test_python 中unittest单元测试为什么addTest没用。
  6. 东野圭吾梦幻花读后感_《梦幻花》精选读后感
  7. “在格力干到退休,这套房就给你了!”董明珠称将投放3700套人才房…
  8. Linux系统fb驱动信息,Linux下利用fb驱动截屏
  9. appium python api
  10. linux-什么是Linux系统?linux详解Linux与Windows的区别Linux发行版本及特点介绍
  11. 中首清算:“股神”很闹心,巴菲特曾割肉的航空股竟连续领涨?
  12. linux audit原理,Wauzh原理简析及audit规则风险评估
  13. Python手撸机器学习系列(十六):循环神经网络RNN的实现
  14. GoogLeNet网络结构学习
  15. html转换为pdf php,js实现html转成pdf
  16. 常见的端口号以及协议
  17. ESP8266--SDK开发(TCP服务端)
  18. Latex中TikZ初步使用
  19. 百微秒时延,腾讯云云硬盘CBS架构深度解密
  20. 为什么“时间管理四象限”没有用处?

热门文章

  1. 头条小程序可以使用uniapp的地图选择(uni.chooseLocation)
  2. lpad函数和rpad函数的用法
  3. 不重装系统的情况下对C盘扩容
  4. centos7开启网卡命令_Centos7启动网卡并查看IP地址的方法
  5. 就叫2021年度总结吧
  6. 计算机桌面移动如何解决,【电脑小知识】桌面布局被锁定,图标不能随意移动了怎么办?...
  7. 【华为云·云筑2020】DevCloud考卷答案
  8. Android 判断邮箱格式是否正确
  9. 多媒体一体机计算机打不开,多媒体教学一体机突然打不开PPT是怎么回事?
  10. 【OI好题推荐 #1】洛谷-P1183 多边形的面积