OpenGL学习之绘制三角形

16 篇文章 9 订阅
1 篇文章 0 订阅

在了解了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 <glad/glad.h>
#include <GLFW/glfw3.h>//包含头文件时要确保GLAD头文件是在GLFW的头文件之前。
#include<iostream>

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" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		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" << infolog << std::endl;
	}
	
	//编译片段着色器
	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" << infolog << std::endl;
	}

	//链接着色器
	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" << infolog << std::endl;
	}
	//删除着色器
	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 <glad/glad.h>
#include <GLFW/glfw3.h>//包含头文件时要确保GLAD头文件是在GLFW的头文件之前。
#include<iostream>

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" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
	std::cout << "Failed to initialize GLAD" << std::endl;
	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" << infolog << std::endl;
}

//编译片段着色器
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" << infolog << std::endl;
}

//链接着色器
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" << infolog << std::endl;
}
//删除着色器
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);
}

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Estelle_Z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值