欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL学习笔记之绘制三角形

程序员文章站 2024-02-21 14:23:10
...

参考https://learnopengl.com/

1、创建窗口

glfw是一个针对OpenGL的窗口管理库,这里使用glfw库创建窗口并处理输入,glfw库可以在它的官网上下载,提供了动态库和静态库供选择,也可以下载源码自己编译。

以官方下载的静态库glfw3.lib为例,将下载的静态库复制到自己方便管理的路径,这里放在工程路径下的lib文件夹内,并将该文件路径添加到工程属性的依赖库里

OpenGL学习笔记之绘制三角形

将glfw3.h复制到工程目录下的glfw文件夹内,并在.cpp文件内包含该头文件#include "glfw/glfw3.h"
创建窗口的步骤:
(1)初始化glfw
glfwInit();
(2)配置OpenGL属性项
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint设置相应的属性,GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR设置需要使用的OpenGL版本,这里选择3.3版本,GLFW_OPENGL_CORE_PROFILE表示我们使用core-profile版本,不需要兼容低版本特性
(3)创建窗口
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL);//创建一个800x600大小的窗口
if (window == NULL)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//将window设置为当前窗口
(4)显示窗口并处理消息
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwWindowShouldClose判断窗口是否要关闭,点击关闭按钮的时候返回true,也就是退出循环
glfwSwapBuffers交换缓冲区,将要显示的像素颜色显示到屏幕
glfwPollEvents处理消息
(5)结束
最后调用glfwTerminate()即可。
最后代码合起来

#include "glfw/glfw3.h"
int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "OpenGL", NULL, NULL);
	if (window == NULL)
	{
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	while (!glfwWindowShouldClose(window))
	{
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	glfwTerminate();
	return 0;
}


2、画三角形

窗口创建后就可以在窗口上画出三角形了,这里需要用到OpenGL的api接口,如果自己写代码获取OpenGL动态库中的函数就太麻烦了,而且OpenGL有很多版本,不同版本的api也有区别。
使用glad库可以解决这个问题,glad中包含了OpenGL不同版本的API,使用上面的glfwWindowHint指定版本后,就可以获取到指定版本的所有函数了。
glad文件获取:打开网址http://glad.dav1d.de/,如下图
OpenGL学习笔记之绘制三角形

Language选择C/C++,Specification选择OpenGL,gl选择你需要的版本,这里是Version 3.3,Profile选择Core,勾选Generate a loader,点击GENERATE按钮就会生成glad.zip文件。
将zip文件解压出来有src和include文件夹,分别有glad解压出来有src和include文件夹,分别有glad.h、khrplatform.h和glad.c,将include下面的两个文件夹glad和KHR复制到工程的包含目录,例如VS安装目录下的VC\inlcude目录,glad.c文件添加到工程中。
之后只要在需要使用的地方加上头文件包含#include "glad/glad.h",并在使用OpenGL函数之前加载所有函数指针gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)就可以了,glfwGetProcAddress位于glfw3.h中。
接下来就可以开始准备画三角形了
(1)编写顶点着色器(vertex shader)和片段着色器(fragment shader)
顶点着色器和片断着色器都是使用着色器编程语言GLSL编写
//顶点着色器代码:
#version 330 core	//版本
layout (location = 0) in vec3 vertexPos;	//顶点位置,由程序传入
void main()//着色器程序执行main函数
{
   gl_Position = vec4(vertexPos, 1.0);//gl_Position为内部定义变量,表示顶点的位置
}
//片段着色代码:
#version 330 core
void main()
{
   gl_FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);//gl_FragColor为内部定义变量,表示像素的颜色
}
(2)创建和编译着色器
为了方便,将着色器创建和编译放到CreateShader函数内

GLuint CreateShader(GLuint type,const GLchar* source)
{
	GLuint shader = glCreateShader(type);
	glShaderSource(shader, 1, &source, NULL);
	glCompileShader(shader);
	GLint success;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);//获取编译结果
	if (!success)
	{
		char infoLog[512];
		glGetShaderInfoLog(shader, 512, NULL, infoLog);//获取具体错误描述infoLog
		std::cout << infoLog << std::endl;
	}
	return shader;
}
假设顶点着色器程序字符串为const GLchar* vsSource,片段着色器程序字符串为const GLchar* fsSource,则创建顶点着色器和片断着色器的代码分别为
GLuint vertexShader = CreateShader(GL_VERTEX_SHADER,vsSource);
GLuint fragmentShader = CreateShader(GL_FRAGMENT_SHADER,fsSource);
(3)创建着色器程序
这里将创建和编译着色器以及创建着色器程序一起放到CreateShaderProgram函数内

GLuint CreateShaderProgram(const GLchar* vsSource,const GLchar* fsSource)
{
	GLuint vertexShader = CreateShader(GL_VERTEX_SHADER,vsSource);
	GLuint fragmentShader = CreateShader(GL_FRAGMENT_SHADER,fsSource);
	GLuint program = glCreateProgram();
	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);
	glLinkProgram(program);
	GLint success;
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success) 
	{
		char infoLog[512];
		glGetProgramInfoLog(program, 512, NULL, infoLog);
		std::cout << infoLog << std::endl;
	}
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);
	return program;
}
(4)将三角形顶点数据传入着色器
GLfloat vertices[] = 
{
	-0.5f,  0.5f, 0.0f,  // 1
	-0.5f, -0.5f, 0.0f,  // 2
	0.5f, -0.5f, 0.0f,  // 3
};
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);//创建顶点数组对象
glGenBuffers(1, &VBO);//创建顶点缓冲
glBindVertexArray(VAO);//设置当前顶点数组对象为VAO
{
	glBindBuffer(GL_ARRAY_BUFFER, VBO);//绑定VBO到GL_ARRAY_BUFFER
	{
		//将定点数据赋值给GL_ARRAY_BUFFER,也就是VBO
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
		//将缓冲中的顶点数据和vertexPos关联起来,这里是通过第一个参数0和着色器中的location=0关联的
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
		glEnableVertexAttribArray(0);
	}
	glBindBuffer(GL_ARRAY_BUFFER, 0);//取消VBO绑定
}
glBindVertexArray(0);//取消VAO绑定
为了看起来清晰,将部分代码用{}括起来,后面只要使用VAO就可以获取到顶点数据,OpenGL的坐标范围为-1到1
glVertexAttribPointer参数说明:
第一个参数表示变量的位置,也就是着色器中的location=n中的n;
第二个参数表示一个顶点的数据个数,这里是3个;
第三个参数为数据类型,这里为GL_FLOAT;
第四个参数表示数据是否要做标准化处理,如果为true,数据将被映射到-1到1之间,false则不做处理;
第五个参数为顶点数据间隔,就是相邻两个数据的起始地址之间的间隔,这里一个顶点3个GLfloat数据,所以间隔也是3*sizeof(GLfloat);
第六个参数为顶点数据起始地址偏移,这里从0位置开始所以就是(void*)0;
(5)渲染三角形
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置清除颜色
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);//shaderProgram为CreateShaderProgram返回的着色器程序
glBindVertexArray(VAO);		//绑定VAO
glDrawArrays(GL_TRIANGLES, 0, 3);//画三角形
(6)结束
程序退出前执行相应的清理工作,这里只要删除VAO、VBO和shaderProgram
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);

运行结果:

OpenGL学习笔记之绘制三角形

附上完整代码:
#include "glad/glad.h"
#include "glfw/glfw3.h"
#include<iostream>

const GLchar *vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 vertexPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(vertexPos, 1.0);\n"
"}\0";
const GLchar *fragmentShaderSource =
"#version 330 core\n"
"void main()\n"
"{\n"
"   gl_FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n\0";

GLuint CreateShader(GLuint type, const GLchar* source)
{
	GLuint shader = glCreateShader(type);
	glShaderSource(shader, 1, &source, NULL);
	glCompileShader(shader);
	GLint success;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		char infoLog[512];
		glGetShaderInfoLog(shader, 512, NULL, infoLog);
		std::cout << infoLog << std::endl;
	}
	return shader;
}

GLuint CreateShaderProgram(const GLchar* vsSource, const GLchar* fsSource)
{
	GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, vsSource);
	GLuint fragmentShader = CreateShader(GL_FRAGMENT_SHADER, fsSource);
	GLuint program = glCreateProgram();
	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);
	glLinkProgram(program);
	GLint success;
	glGetProgramiv(program, GL_LINK_STATUS, &success);
	if (!success)
	{
		char infoLog[512];
		glGetProgramInfoLog(program, 512, NULL, infoLog);
		std::cout << infoLog << std::endl;
	}
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);
	return program;
}

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", NULL, NULL);
	if (!window)
	{
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		return -1;
	}
	GLuint shaderProgram = CreateShaderProgram(vertexShaderSource,fragmentShaderSource);
	GLfloat vertices[] = {
		-0.5f,  0.5f, 0.0f,  // 1
		-0.5f, -0.5f, 0.0f,  // 2
		0.5f, -0.5f, 0.0f,  // 3
	};
	GLuint VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glBindVertexArray(VAO);
	{
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		{
			glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
			glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*)0);
			glEnableVertexAttribArray(0);
		}
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}
	glBindVertexArray(0);

	while (!glfwWindowShouldClose(window))
	{
		glClearColor(0.0f, 0.0f, 0.0f, 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);
	glDeleteProgram(shaderProgram);
	glfwTerminate();
	return 0;
}