目录
实验目的与要求
- 掌握Visual Studio Community 2019集成开发环境的安装;掌握CMake跨平台构建工具的安装;掌握Git版本控制工具的安装;掌握vcpkg库管理工具的安装;掌握系统环境变量的设置;了解和掌握OpenGL的环境配置;掌握OpenGL工程项目的建立和基本设置。
- 理解OpenGL的原理;了解和熟悉OpenGL着色语言;掌握基于OpenGL的C++程序结构;掌握OpenGL中若干基本二维图形的绘制;了解顶点着色器的使用;了解片元着色器的使用。
- 使用现代OpenGL中的着色器,绘制多个简单的二维图形,形状内容不限,自己发挥。
实验过程及内容
VS2019开发环境配置:
打开Visual Studio Installer,点击修改后勾选以下部分,全部勾选完成后点击进行安装即可。
CMake和Git的安装:
在老师发的软件包中找到这两个软件的安装包,默认安装即可,最后在cmd输入cmake和git检验是否安装成功。
vcpkg的安装步骤:
1.找到github上的项目网址,如图所示,这里可以看到最新项目的版本,记下网站地址https://github.com/microsoft/vcpkg/。
2.在准备放置vcpkg文件夹的目录下打开cmd,输入以下命令使用git拉取项目。
3.在vcpkg文件夹下以管理员模式打开powershell,输入以下命令,运行目录下的bootstrap引导脚本,构建vcpkg。
4.执行上一条命令后,输入以下命令,将vcpkg聚合到visual stuido,这样用vcpkg 安装的库就可以直接在visual studio中使用了。
5.添加系统变量,如下图所示,有两个。
6.添加Path路径,如下图所示。
7.在powershell中输入以下命令,安装这三个库文件。
8.安装成功后再次输入相同命令检验是否成功安装,若安装成功可以看到三条绿色的提示,如下图所示。
9.最终的vcpkg文件夹内的文件,如下图所示。
绘制圆和椭圆:
1.在实验留空代码文件夹中打开cmd,输入命令cmake -B . ,回车后cmake会生成项目。
2.命令执行完成后生成的项目文件如下图所示,点击main.sin文件打开项目。
3.右键main将其设置为启动项目,如下图所示。
4.初次运行源代码,观察当前图片样式,如下图所示,有黑白嵌套的三角形、顶点异色的三角形,和蓝白渐变色的对角线。
5.对比目标图片可知,需要在三角形两侧分别添加一个红色椭圆和一个带阴影的红色圆。
6.首先修改vao数组,vao数组用来存储顶点数组对象program 用于存储OpenGL着色器程序的标识符。
7.接下来定义圆和椭圆的点,circle_vertices 和 ellipse_vertices 这两个数组,用于存储圆和椭圆的顶点坐标。
每个数组有 CIRCLE_NUM_POINTS 和 ELLIPSE_NUM_POINTS 个元素,分别表示圆和椭圆上的顶点数目。
circle_colors 和 ellipse_colors 这两个数组,用于存储圆和椭圆上每个顶点的颜色。
这些数组将在之后的代码中填充,以包含实际的顶点坐标。
8.接下来调用generateEllipsePoints函数生成圆的顶点数据和颜色数据。
以圆的为例,circle_center 表示圆的中心点坐标。
circle_vertices:用于存储圆的顶点坐标的数组。
circle_colors:用于存储圆的顶点颜色的数组。
0:表示从数组的第一个元素开始存储顶点数据。
CIRCLE_NUM_POINTS:表示圆上的顶点数量。
circle_center:表示圆的中心点坐标。
0.25:表示圆的水平方向上的缩放因子。
1.0:表示圆的垂直方向上的缩放因子。这里设置为1.0表示圆在垂直方向上没有缩放,保持原始的高度。
9.接下来仿照前三个图形进行圆和椭圆的数据的初始化,如下图所示。
glGenVertexArrays(1, &vao[3]);:生成一个新的顶点数组对象(VAO),并将其标识符存储在 vao[3] 中。这个VAO将用于存储与圆相关的顶点属性和缓冲区对象。
glBindVertexArray(vao[3]);:将刚刚生成的VAO绑定到当前OpenGL上下文,表示之后的OpenGL操作将影响这个VAO。
glGenBuffers(1, &vbo[0]);:生成一个新的缓冲区对象,并将其标识符存储在 vbo[0] 中。这个缓冲区对象将用于存储圆的顶点坐标数据。
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);:将刚刚生成的缓冲区对象绑定到当前OpenGL上下文的GL_ARRAY_BUFFER目标上,表示之后的OpenGL操作将影响这个缓冲区对象。
glBufferData(GL_ARRAY_BUFFER, sizeof(circle_vertices), circle_vertices, GL_STATIC_DRAW);:将圆的顶点坐标数据存储到当前绑定的缓冲区对象中。sizeof(circle_vertices) 表示要拷贝的数据大小,circle_vertices 是包含了圆的顶点坐标的数组。GL_STATIC_DRAW 表示这个缓冲区将被用于静态绘制,即数据不会频繁变化。
location = glGetAttribLocation(program, "vPosition");:获取着色器程序中名为 "vPosition" 的顶点属性的位置,并将其存储在 location 变量中。这个位置将用于指定顶点坐标数据的属性。
glEnableVertexAttribArray(location);:启用指定位置的顶点属性数组。
glVertexAttribPointer(...):用于描述顶点属性数组的函数。指定如何从缓冲区中解析顶点数据。在这里,它用于指定 "vPosition" 属性的属性值。它的参数包括位置,每个顶点的组件数量(2表示二维坐标),数据类型(GL_FLOAT表示浮点数),是否标准化,步长(每个顶点之间的距离),和偏移量(从缓冲区的起始位置开始的偏移量)。这样,OpenGL就知道如何正确解析顶点坐标数据。
10.接下来在display函数中添加代码用于绘制圆和椭圆。
glBindVertexArray(vao[3]);:将第四个(索引为3)顶点数组对象(VAO)绑定到当前OpenGL上下文。这意味着接下来的绘制操作将使用与该VAO相关联的顶点属性和顶点数据。
glDrawArrays(GL_TRIANGLE_FAN, 0, CIRCLE_NUM_POINTS);:用于绘制图形的函数调用。
具体来说:
GL_TRIANGLE_FAN 表示使用三角扇形绘制模式,这种模式常用于绘制圆形和类似的几何形状。
0 表示从顶点数组的第一个顶点开始绘制。
CIRCLE_NUM_POINTS 表示要绘制的顶点数量,这里应该是圆的顶点数量。
11.修改窗口标题,改为“学号_姓名_实验一”格式。
GLFWwindow* window = glfwCreateWindow(1024, 1024, "2021150047_胡云飞_实验一", NULL, NULL);:创建一个大小为 1024x1024 的窗口,并设置窗口标题。如果窗口创建失败,返回一个空指针,否则将返回一个指向窗口的指针。
glfwTerminate();:终止GLFW库的初始化,释放任何由GLFW分配的资源。
glfwMakeContextCurrent(window);:如果窗口创建成功,这行代码将把OpenGL的上下文绑定到刚刚创建的窗口上,这意味着OpenGL的渲染将在这个窗口中进行。
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);:设置一个窗口大小变化回调函数。当窗口的大小发生变化时,将调用 framebuffer_size_callback 函数,这通常用于在窗口大小变化时重新设置OpenGL视口(viewport)等。
绘制二维图形——小火箭:
1.首先确定要绘制的二维图形,这个二维图形是由一个三角形、一个圆、两个长方形、三个椭圆构成,经过搜索和权衡,决定绘制如下图所示的小火箭。
2.因为还需要上颜色,经过挑选决定参考下图给小火箭上颜色。
3.首先添加颜色的选项,如下图所示。
4. 修改计算椭圆上的点的函数,主要是添加一个参数rotate,使得其能够进行旋转,通过更改每一个顶点的坐标以此达到整个图形旋转的目的。
5.更改获得椭圆顶点的函数,主要是添加了颜色和旋转角度的参数,使得其能够将角度换算为弧度后,调用计算椭圆上的点的函数来获取旋转后的所有顶点的坐标。
6.修改获取正方形顶点的函数,添加中心坐标和颜色的参数,使其可以针对不同条件生成正方形。
7.将vao数组的大小设置为7,并在init函数中定义每个几何图形的坐标数组和颜色数组。
8. 调用生成形状顶点位置的函数,为所有几何图形的坐标数组和颜色数组赋值,为后续着色做好准备。
9.接下来是初始化每个几何图形的数据,因为大部分内容均相同,因此只展示正方形1的部分,需要更改的部分为下图红框圈注的地方。
10.在display函数中添加所有几何图形进行上色,如下图所示。
11.运行代码,结果如下图所示,实现了一个小火箭,并成功进行了上色。
实验结论
通过此次实验,了解了OpenGL的基本概念和工作原理,包括顶点缓冲对象(VBO)、顶点数组对象(VAO)以及着色器程序的创建和使用。
学会了如何使用OpenGL绘制不同形状的图形,包括三角形、正方形、椭圆和圆。每个形状都由一系列顶点和颜色定义,然后通过OpenGL的绘制函数进行渲染。
知道了如何在OpenGL中定义和使用颜色。实验代码中使用了glm库来表示颜色,并创建了一些常见颜色的常量,如白色、黑色、红色等。与此同时,我还添加了橘黄色、火红色、淡蓝色等颜色。
明白了如何使用OpenGL的旋转变换函数来旋转图形。通过更改顶点的坐标,可以对图形进行旋转,实现不同角度的展示。
着色器编程:实验中使用了顶点着色器和片段着色器来定义图形的顶点位置和颜色。这些着色器程序负责处理图形的绘制和着色过程。
在实验中也遇到了一些问题,比如其中一个问题就卡了很久,就是小火箭的火焰是有三个椭圆形构成的,需要将椭圆进行不同角度的旋转。一开始,在网上查找资料后,是尝试在display函数中添加glRotatef(-60.0f, 0.0f, 0.0f, 1.0f);进行旋转,结果却报错glad.obj中有很多个重定义,但是glRotatef本身却并没有报错。研究许久后无果,最终还是决定通过改变顶点坐标的形式来实现旋转,好在最后成功实现了旋转。
实验代码
圆和椭圆——main.cpp
#include "Angel.h"
#include <string>
const glm::vec3 WHITE(1.0, 1.0, 1.0);
const glm::vec3 BLACK(0.0, 0.0, 0.0);
const glm::vec3 RED(1.0, 0.0, 0.0);
const glm::vec3 GREEN(0.0, 1.0, 0.0);
const glm::vec3 BLUE(0.0, 0.0, 1.0);
const int CIRCLE_NUM_POINTS = 100;
const int ELLIPSE_NUM_POINTS = 100;
const int TRIANGLE_NUM_POINTS = 3;
const int SQUARE_NUM = 6;
const int SQUARE_NUM_POINTS = 4 * SQUARE_NUM;
const int LINE_NUM_POINTS = 2;
// 每当窗口改变大小,GLFW会调用这个函数并填充相应的参数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// 根据角度生成颜色
float generateAngleColor(double angle)
{
return 1.0 / (2 * M_PI) * angle;
}
// 获得三角形的每个角度
double getTriangleAngle(int point)
{
return 2 * M_PI / 3 * point;
}
// 获得正方形的每个角度
double getSquareAngle(int point)
{
return M_PI / 4 + (M_PI / 2 * point);
}
// 计算椭圆/圆上的点
glm::vec2 getEllipseVertex(glm::vec2 center, double scale, double verticalScale, double angle)
{
glm::vec2 vertex(sin(angle), cos(angle));
vertex *= scale;
vertex.y *= verticalScale;
vertex += center;
return vertex;
}
// 获得椭圆/圆的每个顶点
void generateEllipsePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex, int numPoints,
glm::vec2 center, double scale, double verticalScale)
{
double angleIncrement = (2 * M_PI) / numPoints;
double currentAngle = M_PI / 2;
for (int i = startVertexIndex; i < startVertexIndex + numPoints; ++i) {
vertices[i] = getEllipseVertex(center, scale, verticalScale, currentAngle);
if (verticalScale == 1.0) {
colors[i] = glm::vec3(generateAngleColor(currentAngle), 0.0, 0.0);
}
else {
colors[i] = RED;
}
currentAngle += angleIncrement;
}
}
// 获得三角形的每个顶点
void generateTrianglePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex)
{
glm::vec2 scale(0.25, 0.25);
glm::vec2 center(0.0, 0.70);
for (int i = 0; i < 3; ++i) {
double currentAngle = getTriangleAngle(i);
vertices[startVertexIndex + i] = glm::vec2(sin(currentAngle), cos(currentAngle)) * scale + center;
}
colors[startVertexIndex] = RED;
colors[startVertexIndex + 1] = GREEN;
colors[startVertexIndex + 2] = BLUE;
}
// 获得正方形的每个顶点
void generateSquarePoints(glm::vec2 vertices[], glm::vec3 colors[], int squareNumber, int startVertexIndex)
{
glm::vec2 scale(0.90, 0.90);
double scaleDecrease = 0.15;
glm::vec2 center(0.0, -0.25);
int vertexIndex = startVertexIndex;
for (int i = 0; i < squareNumber; ++i) {
glm::vec3 currentColor;
currentColor = (i % 2) ? BLACK : WHITE;
for (int j = 0; j < 4; ++j) {
double currentAngle = getSquareAngle(j);
vertices[vertexIndex] = glm::vec2(sin(currentAngle), cos(currentAngle)) * scale + center;
colors[vertexIndex] = currentColor;
vertexIndex++;
}
scale -= scaleDecrease;
}
}
void generateLinePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex)
{
vertices[startVertexIndex] = glm::vec2(-1.0, -1.0);
vertices[startVertexIndex + 1] = glm::vec2(1.0, 1.0);
colors[startVertexIndex] = WHITE;
colors[startVertexIndex + 1] = BLUE;
}
//GLuint vao[3], program;
// 添加代码 - begin ****************************
//vao数组用来存储顶点数组对象
//program 用于存储OpenGL着色器程序的标识符。
GLuint vao[5], program;
//添加代码 - end *****************************
void init()
{
// 定义三角形的点
glm::vec2 triangle_vertices[TRIANGLE_NUM_POINTS];
glm::vec3 triangle_colors[TRIANGLE_NUM_POINTS];
// 定义矩形的点
glm::vec2 square_vertices[SQUARE_NUM_POINTS];
glm::vec3 square_colors[SQUARE_NUM_POINTS];
// 定义线的点
glm::vec2 line_vertices[LINE_NUM_POINTS];
glm::vec3 line_colors[LINE_NUM_POINTS];
// @TODO: 生成圆形和椭圆上的点和颜色
// 添加代码 - begin ****************************
// 仿照前面图形定义圆的点
glm::vec2 circle_vertices[CIRCLE_NUM_POINTS];
glm::vec3 circle_colors[CIRCLE_NUM_POINTS];
// 定义椭圆的点
glm::vec2 ellipse_vertices[ELLIPSE_NUM_POINTS];
glm::vec3 ellipse_colors[ELLIPSE_NUM_POINTS];
//circle_vertices 和 ellipse_vertices 这两个数组,用于存储圆和椭圆的顶点坐标。
//每个数组有 CIRCLE_NUM_POINTS 和 ELLIPSE_NUM_POINTS 个元素,分别表示圆和椭圆上的顶点数目。
//circle_colors 和 ellipse_colors 这两个数组,用于存储圆和椭圆上每个顶点的颜色。
//这些数组将在之后的代码中填充,以包含实际的顶点坐标。
//添加代码 - end *****************************
// 调用生成形状顶点位置的函数
generateTrianglePoints(triangle_vertices, triangle_colors, 0);
generateSquarePoints(square_vertices, square_colors, SQUARE_NUM, 0);
generateLinePoints(line_vertices, line_colors, 0);
// 添加代码 - begin ****************************
glm::vec2 circle_center(0.65, 0.70);
generateEllipsePoints(circle_vertices, circle_colors, 0, CIRCLE_NUM_POINTS, circle_center, 0.25, 1.0);
glm::vec2 ellipse_center(-0.65, 0.70);
generateEllipsePoints(ellipse_vertices, ellipse_colors, 0, ELLIPSE_NUM_POINTS, ellipse_center, 0.25, 0.50);
//circle_center 表示圆的中心点坐标
//generateEllipsePoints函数生成圆的顶点数据和颜色数据,以圆为例说明。
//circle_vertices:用于存储圆的顶点坐标的数组。
//circle_colors:用于存储圆的顶点颜色的数组。
//0:表示从数组的第一个元素开始存储顶点数据。
//CIRCLE_NUM_POINTS:表示圆上的顶点数量。
//circle_center:表示圆的中心点坐标。
//0.25:表示圆的水平方向上的缩放因子。
//1.0:表示圆的垂直方向上的缩放因子。这里设置为1.0表示圆在垂直方向上没有缩放,保持原始的高度。
//添加代码 - end *****************************
// 读取着色器并使用
std::string vshader, fshader;
vshader = "shaders/vshader.glsl";
fshader = "shaders/fshader.glsl";
program = InitShader(vshader.c_str(), fshader.c_str());
glUseProgram(program);
// 创建顶点缓存对象,vbo[2]是因为我们将要使用两个缓存对象
GLuint vbo[2];
/*
* 初始化三角形的数据
*/
glGenVertexArrays(1, &vao[0]);
glBindVertexArray(vao[0]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
GLuint location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_colors), triangle_colors, GL_STATIC_DRAW);
GLuint cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化正方形的数据
*/
glGenVertexArrays(1, &vao[1]);
glBindVertexArray(vao[1]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square_vertices), square_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square_colors), square_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化线的数据
*/
glGenVertexArrays(1, &vao[2]);
glBindVertexArray(vao[2]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(line_vertices), line_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(line_colors), line_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
// 添加代码 - begin ****************************
/*
* 初始化圆的数据
*/
//生成一个新的顶点数组对象(VAO),并将其标识符存储在 vao[3] 中。
//这个VAO将用于存储与圆相关的顶点属性和缓冲区对象。
glGenVertexArrays(1, &vao[3]);
//将刚刚生成的VAO绑定到当前OpenGL上下文,表示之后的OpenGL操作将影响这个VAO。
glBindVertexArray(vao[3]);
//生成一个新的缓冲区对象,并将其标识符存储在 vbo[0] 中。
//这个缓冲区对象将用于存储圆的顶点坐标数据。
glGenBuffers(1, &vbo[0]);
//将刚刚生成的缓冲区对象绑定到当前OpenGL上下文的GL_ARRAY_BUFFER目标上,
//表示之后的OpenGL操作将影响这个缓冲区对象。
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
//将圆的顶点坐标数据存储到当前绑定的缓冲区对象中。
//sizeof(circle_vertices) 表示要拷贝的数据大小,circle_vertices 是包含了圆的顶点坐标的数组。
//GL_STATIC_DRAW 表示这个缓冲区将被用于静态绘制,即数据不会频繁变化。
glBufferData(GL_ARRAY_BUFFER, sizeof(circle_vertices), circle_vertices, GL_STATIC_DRAW);
//获取着色器程序中名为 "vPosition" 的顶点属性的位置,并将其存储在 location 变量中。
//这个位置将用于指定顶点坐标数据的属性。
location = glGetAttribLocation(program, "vPosition");
//启用指定位置的顶点属性数组。
glEnableVertexAttribArray(location);
//用于描述顶点属性数组。指定了如何从缓冲区中解析顶点数据。
//在这里,它用于指定 "vPosition" 属性的属性值。
//它的参数包括位置,每个顶点的组件数量(2表示二维坐标),
// 数据类型(GL_FLOAT表示浮点数),是否标准化,步长(每个顶点之间的距离),
// 和偏移量(从缓冲区的起始位置开始的偏移量)。
// 这样,OpenGL就知道如何正确解析顶点坐标数据。
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
//上面部分是对位置的设置,下面这部分是对颜色的设置。
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(circle_colors), circle_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化椭圆的数据
*/
glGenVertexArrays(1, &vao[4]);
glBindVertexArray(vao[4]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse_vertices), ellipse_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse_colors), ellipse_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
//添加代码 - end *****************************
glClearColor(0.0, 0.0, 0.0, 1.0);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(vao[0]);
glDrawArrays(GL_TRIANGLES, 0, TRIANGLE_NUM_POINTS);
glBindVertexArray(vao[1]);
for (int i = 0; i < SQUARE_NUM; ++i) {
glDrawArrays(GL_TRIANGLE_FAN, (i * 4), 4);
}
glBindVertexArray(vao[2]);
glDrawArrays(GL_LINES, 0, LINE_NUM_POINTS);
// 添加代码 - begin ****************************
// @TODO: 绘制圆
//将第四个(索引为3)顶点数组对象(VAO)绑定到当前OpenGL上下文。
//这意味着接下来的绘制操作将使用与该VAO相关联的顶点属性和顶点数据。
glBindVertexArray(vao[3]);
//这是用于绘制图形的函数调用。具体来说:
//GL_TRIANGLE_FAN 表示使用三角扇形绘制模式,这种模式常用于绘制圆形和类似的几何形状。
//0 表示从顶点数组的第一个顶点开始绘制。
//CIRCLE_NUM_POINTS 表示要绘制的顶点数量,这里指的是圆的顶点数量。
glDrawArrays(GL_TRIANGLE_FAN, 0, CIRCLE_NUM_POINTS);
// @TODO: 绘制椭圆
glBindVertexArray(vao[4]);
glDrawArrays(GL_TRIANGLE_FAN, 0, ELLIPSE_NUM_POINTS);
//添加代码 - end *****************************
glFlush();
}
int main(int argc, char** argv)
{
// 初始化GLFW库
glfwInit();
// 配置GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
//修改了窗口标题 **********************
// 配置窗口属性
//创建了一个大小为 1024x1024 的窗口,并设置了窗口的标题为 "2021150047_胡云飞_实验一"。
//如果窗口创建失败,它将返回一个空指针,否则将返回一个指向窗口的指针。
GLFWwindow* window = glfwCreateWindow(1024, 1024, "2021150047_胡云飞_实验一", NULL, NULL);
//如果创建失败则输出提示
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
//终止GLFW库的初始化,释放任何由GLFW分配的资源。
glfwTerminate();
return -1;
}
//如果窗口创建成功,这行代码将把OpenGL的上下文绑定到刚刚创建的窗口上,
//这意味着OpenGL的渲染将在这个窗口中进行。
glfwMakeContextCurrent(window);
//设置了一个窗口大小变化回调函数。
//当窗口的大小发生变化时,将调用 framebuffer_size_callback 函数,
//通常用于在窗口大小变化时重新设置OpenGL视口(viewport)等。
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//***************************
// 调用任何OpenGL的函数之前初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
init();
std::cout << "OpenGL Vendor: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "OpenGL Renderer: " << glGetString(GL_RENDERER) << std::endl;
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "Supported GLSL version is: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
while (!glfwWindowShouldClose(window))
{
display();
// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
小火箭——main.cpp
#include "Angel.h"
#include <string>
const glm::vec3 WHITE(1.0, 1.0, 1.0);
const glm::vec3 BLACK(0.0, 0.0, 0.0);
const glm::vec3 RED(1.0, 0.0, 0.0);
const glm::vec3 GREEN(0.0, 1.0, 0.0);
const glm::vec3 BLUE(0.0, 0.0, 1.0);
const glm::vec3 ORANGE(1.0, 0.5, 0.0); //橘黄色
const glm::vec3 PINK(1.0, 0.75, 0.8); //粉色
const glm::vec3 FIRE_RED(0.89, 0.32, 0.06); // 火红色
const int TRIANGLE_NUM_POINTS = 3;
const int SQUARE_NUM = 1;
const int SQUARE_NUM_POINTS = 4 * SQUARE_NUM;
const int CIRCLE_NUM_POINTS = 100;
const int ELLIPSE_NUM_POINTS = 100;
// 每当窗口改变大小,GLFW会调用这个函数并填充相应的参数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// 根据角度生成颜色
float generateAngleColor(double angle)
{
return 1.0 / (2 * M_PI) * angle;
}
// 获得三角形的每个角度
double getTriangleAngle(int point)
{
return 2 * M_PI / 3 * point;
}
// 获得正方形的每个角度
double getSquareAngle(int point)
{
return M_PI / 4 + (M_PI / 2 * point);
}
// 计算椭圆/圆上的点
glm::vec2 getEllipseVertex(glm::vec2 center, double scale, double verticalScale, double angle, double rotate)
{
// 计算原始的椭圆/圆上的点
glm::vec2 vertex(sin(angle), cos(angle));
// 缩放点的位置
vertex *= scale;
vertex.y *= verticalScale;
// 构建旋转矩阵,用于对点进行旋转 //旋转的关键所在!!!!
glm::mat2x2 rMat(cos(rotate), -sin(rotate), sin(rotate), cos(rotate));
// 应用旋转矩阵,将点旋转到指定角度
vertex = vertex * rMat;
// 将点移动到椭圆/圆的中心
vertex += center;
return vertex;
}
// 获得椭圆/圆的每个顶点
void generateEllipsePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex, int numPoints,
glm::vec2 center, double scale, double verticalScale, const glm::vec3 thisColor,double rotate)
{
double angleIncrement = (2 * M_PI) / numPoints; // 计算角度增量,用于均匀分布顶点
double currentAngle = M_PI / 2; // 初始角度,从90度开始
rotate = rotate / 180 * M_PI; //旋转角度变弧度
for (int i = startVertexIndex; i < startVertexIndex + numPoints; ++i) {
// 使用给定的参数调用 getEllipseVertex 函数,获取椭圆/圆上的顶点坐标
vertices[i] = getEllipseVertex(center, scale, verticalScale, currentAngle,rotate);
// 设置顶点的颜色为指定颜色
colors[i] = thisColor;
currentAngle += angleIncrement; // 增加角度以获取下一个顶点的坐标
}
}
// 获得三角形的每个顶点
void generateTrianglePoints(glm::vec2 vertices[], glm::vec3 colors[], int startVertexIndex)
{
glm::vec2 scale(0.35, 0.35);
glm::vec2 center(0.0, 0.60);
for (int i = 0; i < 3; ++i) {
double currentAngle = getTriangleAngle(i);
vertices[startVertexIndex + i] = glm::vec2(sin(currentAngle), cos(currentAngle)) * scale + center;
}
colors[startVertexIndex] = RED;
colors[startVertexIndex + 1] = RED;
colors[startVertexIndex + 2] = RED;
}
// 获得正方形的每个顶点
void generateSquarePoints(glm::vec2 vertices[], glm::vec3 colors[], int squareNumber, int startVertexIndex,
double xlength, double ylength, glm::vec2 center, const glm::vec3 squareColor)
{
glm::vec2 scale(xlength, ylength); // 正方形的初始缩放比例
double scaleDecrease = 0.15; // 每个正方形的缩放递减量
int vertexIndex = startVertexIndex; // 起始顶点索引
for (int i = 0; i < squareNumber; ++i) {
glm::vec3 currentColor;
currentColor = squareColor; // 交替设置正方形的颜色
for (int j = 0; j < 4; ++j) {
double currentAngle = getSquareAngle(j); // 获取正方形的每个角的角度
// 计算顶点坐标并存储到vertices数组中
vertices[vertexIndex] = glm::vec2(sin(currentAngle), cos(currentAngle)) * scale + center;
// 存储顶点颜色到colors数组中
colors[vertexIndex] = currentColor;
vertexIndex++;
}
scale -= scaleDecrease; // 更新下一个正方形的缩放比例
}
}
GLuint vao[7], program;
void init()
{
// 定义三角形的点
glm::vec2 triangle_vertices[TRIANGLE_NUM_POINTS];
glm::vec3 triangle_colors[TRIANGLE_NUM_POINTS];
// 定义长方形1的点
glm::vec2 square_vertices[SQUARE_NUM_POINTS];
glm::vec3 square_colors[SQUARE_NUM_POINTS];
// 定义长方形2的点
glm::vec2 square2_vertices[SQUARE_NUM_POINTS];
glm::vec3 square2_colors[SQUARE_NUM_POINTS];
// 定义圆的点
glm::vec2 circle_vertices[CIRCLE_NUM_POINTS];
glm::vec3 circle_colors[CIRCLE_NUM_POINTS];
// 定义椭圆的点
glm::vec2 ellipse_vertices[ELLIPSE_NUM_POINTS];
glm::vec3 ellipse_colors[ELLIPSE_NUM_POINTS];
// 定义椭圆2的点
glm::vec2 ellipse2_vertices[ELLIPSE_NUM_POINTS];
glm::vec3 ellipse2_colors[ELLIPSE_NUM_POINTS];
// 定义椭圆的点
glm::vec2 ellipse3_vertices[ELLIPSE_NUM_POINTS];
glm::vec3 ellipse3_colors[ELLIPSE_NUM_POINTS];
// 调用生成形状顶点位置的函数
//三角形
generateTrianglePoints(triangle_vertices, triangle_colors, 0);
//长方形 //横着的长方形
generateSquarePoints(square_vertices, square_colors, SQUARE_NUM, 0, 0.80, 0.15, glm::vec2(0.0, -0.40), PINK);
//长方形2
generateSquarePoints(square2_vertices, square2_colors, SQUARE_NUM, 0, 0.425, 0.66, glm::vec2(0.0, -0.04), ORANGE);
//圆形
glm::vec2 circle_center(0.0, 0.15);
generateEllipsePoints(circle_vertices, circle_colors, 0, CIRCLE_NUM_POINTS, circle_center, 0.15, 1.0, WHITE, 0);
//椭圆形
glm::vec2 ellipse_center(-0.28, -0.73);
generateEllipsePoints(ellipse_vertices, ellipse_colors, 0, ELLIPSE_NUM_POINTS, ellipse_center, 0.25, 0.50, FIRE_RED, 60);
//椭圆形2
glm::vec2 ellipse2_center(0.0, -0.75);
generateEllipsePoints(ellipse2_vertices, ellipse2_colors, 0, ELLIPSE_NUM_POINTS, ellipse2_center, 0.25, 0.50, FIRE_RED, 90);
//椭圆形3
glm::vec2 ellipse3_center(0.28, -0.73);
generateEllipsePoints(ellipse3_vertices, ellipse3_colors, 0, ELLIPSE_NUM_POINTS, ellipse3_center, 0.25, 0.50, FIRE_RED, -60);
// 读取着色器并使用
std::string vshader, fshader;
vshader = "shaders/vshader.glsl";
fshader = "shaders/fshader.glsl";
program = InitShader(vshader.c_str(), fshader.c_str());
glUseProgram(program);
// 创建顶点缓存对象,vbo[2]是因为我们将要使用两个缓存对象
GLuint vbo[2];
/*
* 初始化三角形的数据
*/
glGenVertexArrays(1, &vao[0]);
glBindVertexArray(vao[0]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
GLuint location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_colors), triangle_colors, GL_STATIC_DRAW);
GLuint cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化正方形1的数据
*/
glGenVertexArrays(1, &vao[1]);
glBindVertexArray(vao[1]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square_vertices), square_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square_colors), square_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化正方形2的数据
*/
glGenVertexArrays(1, &vao[2]);
glBindVertexArray(vao[2]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square2_vertices), square2_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(square2_colors), square2_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化圆的数据
*/
glGenVertexArrays(1, &vao[3]);
glBindVertexArray(vao[3]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(circle_vertices), circle_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(circle_colors), circle_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化椭圆的数据
*/
glGenVertexArrays(1, &vao[4]);
glBindVertexArray(vao[4]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse_vertices), ellipse_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse_colors), ellipse_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化椭圆2的数据
*/
glGenVertexArrays(1, &vao[5]);
glBindVertexArray(vao[5]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse2_vertices), ellipse2_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse2_colors), ellipse2_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
/*
* 初始化椭圆3的数据
*/
glGenVertexArrays(1, &vao[6]);
glBindVertexArray(vao[6]);
glGenBuffers(1, &vbo[0]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse3_vertices), ellipse3_vertices, GL_STATIC_DRAW);
location = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location,
2,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec2),
BUFFER_OFFSET(0));
glGenBuffers(1, &vbo[1]);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(ellipse3_colors), ellipse3_colors, GL_STATIC_DRAW);
cLocation = glGetAttribLocation(program, "vColor");
glEnableVertexAttribArray(cLocation);
glVertexAttribPointer(
cLocation,
3,
GL_FLOAT,
GL_FALSE,
sizeof(glm::vec3),
BUFFER_OFFSET(0));
glClearColor(0.0, 0.0, 0.0, 1.0);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
glBindVertexArray(vao[0]);
glDrawArrays(GL_TRIANGLES, 0, TRIANGLE_NUM_POINTS);
glBindVertexArray(vao[1]);
for (int i = 0; i < SQUARE_NUM; ++i) {
glDrawArrays(GL_TRIANGLE_FAN, (i * 4), 4);
}
glBindVertexArray(vao[2]);
for (int i = 0; i < SQUARE_NUM; ++i) {
glDrawArrays(GL_TRIANGLE_FAN, (i * 4), 4);
}
glBindVertexArray(vao[3]);
glDrawArrays(GL_TRIANGLE_FAN, 0, CIRCLE_NUM_POINTS);
// 顺时针旋转60度
/*glPushMatrix();
glRotatef(-60.0f, 0.0f, 0.0f, 1.0f);*/
//椭圆
glBindVertexArray(vao[4]);
glDrawArrays(GL_TRIANGLE_FAN, 0, ELLIPSE_NUM_POINTS);
//椭圆2
glBindVertexArray(vao[5]);
glDrawArrays(GL_TRIANGLE_FAN, 0, ELLIPSE_NUM_POINTS);
//椭圆3
glBindVertexArray(vao[6]);
glDrawArrays(GL_TRIANGLE_FAN, 0, ELLIPSE_NUM_POINTS);
//glPopMatrix(); // 恢复旋转前的状态
glFlush();
}
int main(int argc, char** argv)
{
// 初始化GLFW库
glfwInit();
// 配置GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
GLFWwindow* window = glfwCreateWindow(1024, 1024, "2021150047_胡云飞_实验一", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
//终止GLFW库的初始化,释放任何由GLFW分配的资源。
glfwTerminate();
return -1;
}
//如果窗口创建成功,这行代码将把OpenGL的上下文绑定到刚刚创建的窗口上,
//这意味着OpenGL的渲染将在这个窗口中进行。
glfwMakeContextCurrent(window);
//设置了一个窗口大小变化回调函数。
//当窗口的大小发生变化时,将调用 framebuffer_size_callback 函数,
//通常用于在窗口大小变化时重新设置OpenGL视口(viewport)等。
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 调用任何OpenGL的函数之前初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
init();
std::cout << "OpenGL Vendor: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "OpenGL Renderer: " << glGetString(GL_RENDERER) << std::endl;
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "Supported GLSL version is: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
while (!glfwWindowShouldClose(window))
{
display();
// 交换颜色缓冲 以及 检查有没有触发什么事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
(by 归忆)