OpenGL学习笔记之绘制三角形
参考https://learnopengl.com/
1、创建窗口
glfw是一个针对OpenGL的窗口管理库,这里使用glfw库创建窗口并处理输入,glfw库可以在它的官网上下载,提供了动态库和静态库供选择,也可以下载源码自己编译。以官方下载的静态库glfw3.lib为例,将下载的静态库复制到自己方便管理的路径,这里放在工程路径下的lib文件夹内,并将该文件路径添加到工程属性的依赖库里
将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/,如下图
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到1glVertexAttribPointer参数说明:
第一个参数表示变量的位置,也就是着色器中的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);
运行结果:
#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;
}
推荐阅读
-
OpenGL 绘制三角形
-
OpenGL学习笔记之绘制三角形
-
Django学习笔记之Class-Based-View
-
OpenGL ES for Android 绘制三角形
-
【JS013】ES6的学习笔记之数据结构——Map
-
Hibernate查询之list/iterator 博客分类: hibernate学习笔记 HibernateSQLJVM
-
PHP学习笔记之php文件操作
-
Android学习笔记之AndroidManifest.xml文件解析 博客分类: android androidAndroidManifest
-
Android学习笔记之AndroidManifest.xml文件解析(转) 博客分类: Android androidAndroidManifest
-
opengl编程学习笔记(三)(2D绘图)