热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenGL学习之绘制三角形

在了解了opengl渲染流水线并且配置好环境以后就可以绘制第一个三角形了。

在了解了opengl渲染流水线并且配置好环境以后就可以绘制第一个三角形了。

一、基本概念

1.顶点数组对象(Vertex Array Object,VAO)

VAO记录数据的存储和如何使用的细节信息。使用VAO的优势是,如果有多个物体需要绘制,那么我们设置一次绘制物体需要的顶点数据、数据解析方式等信息,然后通过VAO保存起来后,后续的绘制操作不再需要重复这一过程,只需要将VAO设定为当前VAO,那么opengl则会使用这些状态信息。

2.顶点缓冲对象(Vertex Buffer Object,VBO)

顶点缓冲对象VBO是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高。

所以可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。

3.顶点索引对象(Element Buffer Object,EBO或者Index Buffer Object,IBO)

索引缓冲对象EBO相当于OpenGL中的顶点数组的概念,是为了解决同一个顶点多次重复调用的问题,可以减少内存空间浪费,提高执行效率。当需要使用重复的顶点时,通过顶点的位置索引来调用顶点,而不是对重复的顶点信息重复记录,重复调用。

EBO中存储的内容就是顶点位置的索引indices,EBO跟VBO类似,也是在显存中的一块内存缓冲器,只不过EBO保存的是顶点的索引。

3.渲染流水线

这一部分解释可以参考opengl渲染管线
在这里插入图片描述
二、三角形绘制
此部分参考 https://blog.csdn.net/fatfish_/article/details/82453765
1、顶点输入

OpenGL处理的是-1.0到1.0范围之间的3D坐标,并且绘制图形的顺序是逆时针方向,我们定义一个float类型的三角形顶点数组,其中Z坐标为0,使得最终的三角形是2D的。

float vertice[3]={
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

2.VAO与VBO

(1)产生并绑定VAO

产生VAO

void glGenVertexArrays(GLsizei n,GLuint *array);

其中 n:VAO对象数量,arrays:VAO对象指针

绑定VAO

void glBindVertexArray(GLuint array);

array:要绑定数组的名称

(2)产生VBOs并绑定VBO

void glGenBuffers(GLsizei n,GLuint *buffers);

n:VBO对象数量,buffers:VBO对象指针

void glBindBuffer(GLenum target,GLuint buffer);

target:GL_ARRAY_BUFFER,GL_ELEMENT_ARRAY_BUFFER,GL_PIXEL_PACK_BUFFER,or GL_PIXEL_UNPACK_BUFFER

(3)给VBO分配数据

void glBufferData(GLenum target,GLsizeiptr size,const GLvoid *data,GLenum usage);

target:GL_ARRAY_BUFFER(表示顶点数据),GL_ELEMENT_ARRAY_BUFFER(表示索引数据),GL_PIXEL_PACK_BUFFER(表示从opengl获取的像素数据),or GL_PIXEL_UNPACK_BUFFER(表示传递给OpenGL的像素数据。
size:缓存对象字节数
data:指向数据对象的指针,或者是NULL,表示暂时不分配数据。

(4)指定需要启用的顶点属性数组的索引并给对应的VAO指定数据

void glEnableVertexAttaibArray(GLuint index);
void glVertexAttribPointer(Gluint index,GLint size,GLenum type,GLboolean normalized,const GLvoid *pointer);

index: 数据存储的VBO下标
size: 数据量
type: 数据类型
normalized: 是否需要归一化
pointer: 指向数组中的第一个顶点属性的第一个数据

(5)渲染时绑定对应的VAO

glBindVertexArray(vAOHandle);

(6)使用完毕清除绑定

glBindVertexArray(0);

3.顶点着色器

(1)创建顶点着色器

顶点着色器(Vertex Shader)是可编程着色器之一,它是使用GLSL(OpenGL Shading Language)编写的。

#version 330 core
layout(location=0) in vec3 aPos;
void main()
{
gl_Position=vec(aPos.x,aPos.y,aPos.z,1.0);
}

#version 330 core:GLSL版本号和OpenGL的版本号是匹配的,GLSL330版本对应于OpenGL 3.3,并且同样使用核心模式。
layout(location=0) in vec3 aPos; 这一句使用in关键字,在顶点着色器中声明所有的输入顶点属性(此处为3D位置属性),创建一个vec3输入变量aPos,layout(location=0)设定输入变量的位置值。
gl_Position=vec(aPos.x,aPos.y,aPos.z,1.0); 为了设置顶点着色器的输出,把位置数据赋给预定义的vec4变量gl_Position,所以把vec3类型的aPosition数据作为vec4构造器的参数,并把w分量设置为1.0f.

(2)编译着色器

a.创建着色器对象

把需要创建的着色器类型以参数形式提供给glCreateShader,则将顶点着色器存储为unsigned int类型

unsigned int vertexShader;
vertexShader=glCreateShader(GL_VERTEX_SHADER);

b.编译着色器对象

glShaderSource(glShaderSource(GLuint shader,GLsizei count,const GLchar * const *string,const GLint *length););

shader:要被替换源代码的着色器对象的句柄(ID)
count:指定字符串和长度数组中的元素数
string:指定指向包含要加载到着色器的源代码的字符串的指针数组
length:指定字符串长度

glCompileShader(vertexShader);
检测在调用glCompileShader后编译是否成功

4.片段着色器

a.创建片段着色器对象

片段着色器的功能是计算像素的最后的颜色输出。

#version 330 core
out vec FragColor;
void main()
{
FragColor=vec4(1.0f,0.5f,0.2f,1.0f);
}

out vec FragColor;使用out关键字声明命名为FragColor的输出变量。并将一个alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。

b.编译着色器对象
编译片段着色器的过程与顶点着色器类似,区别是使用GL_FRAGMENT_SHADER常来那个作为着色器类型。

unsigned int fragmentShader;
fragmentShader=glCreatShader(GL_fragment_shader);
glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
glCompileShader(fragmentShader);

5.着色器程序

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本,如果要使用前面编译的着色器,必须把它们链接作为一个着色器对象,然后再渲染对象的时候激活这个着色器程序。已激活的着色器程序的着色器在我们发送渲染调用时被使用。
当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下一个着色器的输入。当输出和输入不匹配是,发生连接错误。

a.创建程序对象

unsigned int shaderProgram;
shaderProgram=glCreateProgram();

glCreateProgram():创建一个程序,并返回新创建程序对象的ID引用。

b.链接

glAttachShader(shaderProgram,vertexShader);
glAttachShader(shaderProgram,fragmentShader);
glLinkProgram(shaderProgram);

c.激活程序对象

glUseProgram(shaderProgram);

d.删除着色器对象

glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

6.三角形绘制

#include
#include //包含头文件时要确保GLAD头文件是在GLFW的头文件之前。
#include
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
//顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
//顶点着色器
const GLchar* vertexShaderSource="#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器
const GLchar* fragmentShaderSource="#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
glfwInit(); //初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //主版本号为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); //次版本号为3
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //使用核心模式
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Mac OS X系统
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout <<"Failed to create GLFW window" < glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout <<"Failed to initialize GLAD" < return -1;
}
unsigned int VAO, VBO;
//创建并绑定VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//创建并绑定VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//给VBO分配数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//指定顶点属性数组索引
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

//解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//创建着色器
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

//编译着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

//检测编译时错误
int success;
char infolog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" < }

//编译片段着色器
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// 检测编译时错误
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" < }
//链接着色器
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::PROGRAM::LINKING_FAILED\n" < }
//删除着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

//渲染循环
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画三角形
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{

glViewport(0, 0, width, height);
}

在这里插入图片描述
7.EBO(索引缓冲对象)

当绘制一个矩形时时,需要两个三角形(六组顶点)来组成一个矩形,热一个矩形只需要四个顶点就可以了,这个时候会有浪费。使用索引缓冲可以解决这一问题。

(1)输入数据

float vertices[]={
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f // 左上角
};
unsigned int indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};

(2)创建索引缓冲对象

unsigned int EBO;
glGenBuffer(1,&EBO);

(3)绑定EBO

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

(4)使用当前索引绘制

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
#include
#include //包含头文件时要确保GLAD头文件是在GLFW的头文件之前。
#include
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
//顶点数据
float vertices[] = {
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f // 左上角
};
//顶点着色器
const GLchar* vertexShaderSource="#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器
const GLchar* fragmentShaderSource="#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
unsigned int indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
int main()
{
glfwInit(); //初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //主版本号为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); //次版本号为3
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //使用核心模式
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //Mac OS X系统
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout <<"Failed to create GLFW window" < glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout <<"Failed to initialize GLAD" < return -1;
}
unsigned int VAO, VBO,EBO;
//创建并绑定VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//创建并绑定VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
//EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//给VBO分配数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//指定顶点属性数组索引
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
//解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//创建着色器
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
//编译着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//检测编译时错误
int success;
char infolog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" <}
//编译片段着色器
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// 检测编译时错误
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" <}
//链接着色器
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infolog);
std::cout <<"ERROR::SHADER::PROGRAM::LINKING_FAILED\n" <}
//删除着色器
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
//渲染循环
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画三角形
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{

glViewport(0, 0, width, height);
}

结果如下:
在这里插入图片描述


版权声明:本文为qq_41631051原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_41631051/article/details/90047508
推荐阅读
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
author-avatar
缕足迹_124
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有