开始学习OpenGL,参考的是著名的LearnOpenGL这个网站,在这里做一些总结性的记录,只是方便自己日后查找或者记录自己的一些拓展思考,关于OpenGL的具体内容请移步:

https://learnopengl-cn.github.io/

或英文原版:https://learnopengl.com/

配置环境

LearnOpenGL中使用了GLFW和GLAD两个库来配置环境,原文已经很详细地列出了所有步骤,就不再多说了,获取两个库之后在Visual Studio的项目属性中的VC++目录中,将两个库的include文件夹加入包含目录,将GLFW的lib文件夹加入库目录,然后在链接器的输入中把glfw3.lib这个文件加入附加依赖项,最后别忘记把glad.c这个文件加入到工程中。

初始化OpenGL

首先包含之前引入的两个库的头文件,注意一定要先引入glad.h

#include <glad/glad.h>
#include <GLFW/glfw3.h>

开始使用OpenGL绘图之前,要先初始化OpenGL环境。

	glfwInit();//初始化GLFW

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//告诉glfw使用OpenGL3.3版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//告诉glfw使用OpenGL核心(core)模式

创建窗口

使用glfwCreateWindow创建一个窗口对象,传入窗口的宽度、高度和窗口名字。

创建窗口成功之后就将窗口设置为当前线程主上下文。

	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);//创建窗口对象

	if (window == NULL)//若窗口生成失败则退出程序
{
std::cout << "Create Window failed" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文

初始化GLAD

调用OpenGL的函数之前需初始化GLAD用于管理函数指针。

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}

视口设置

最后还需要设置OpenGL的视口大小,并编写用户改变窗口大小时的回调函数。

glViewport(0, 0, 800, 600);//设置视口,前两个参数为视口左下角位置,后两个为视口宽高
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//设置回调函数,用户改变窗口大小时调用

回调函数:

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}

渲染循环

做好这些准备工作之后就可以开始通过循环来持续渲染我们要显示的内容了,在循环体中我们需要响应用户的按键,渲染并交换缓冲和检查事件,此处使用glClearColor来设置背景色并清空屏幕缓冲,这样屏幕就会一直渲染为我们设置的背景色。

	while (!glfwWindowShouldClose(window))//render loop
{
processInput(window);//按键响应 glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//设置颜色
glClear(GL_COLOR_BUFFER_BIT);//清空屏幕的颜色缓冲 glfwSwapBuffers(window);//交换前缓冲和后缓冲
glfwPollEvents();//检查有没有触发什么事件
}
glfwTerminate();//清理所有的资源并正确地退出应用程序
return 0;

按键响应函数,当用户按下ESC键时关闭窗口:

void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window,true);
}
}

绘制三角形

准备工作就绪,终于可以开始绘制了,绘制第一个三角形被LearnOpenGL的作者称之为入门现代OpenGL的最难部分。旧版本的OpenGL使用立即渲染模式,只需几行代码就能绘制出一个简单图形,但现代OpenGL使用核心渲染模式,绘图要基于着色器和缓冲区来进行,导致要画出一个三角形之前,需要掌握基本的着色器编写和控制OpenGL中各缓冲区的知识,对我这个没有图形编程基础的人来说第一次看完这部分内容的时候说实话是很蒙圈的,但理解了之后也会发现这样的设计在渲染大量复杂物体的时候相比于旧的立即渲染模式是非常优越的。

绘制一个三角形(或者说任何图形)大概分为这几个步骤:

准备顶点数据

首先当然要知道这个图形的所有顶点,OpenGL的坐标系取值在-1.0到1.0之间,所以首先要准备好包含顶点数据的数组:

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

编写顶点着色器和片元着色器

OpenGL使用GLSL语言编写着色器,语法类似于C语言,关于这两个着色器的具体原理需要了解计算机图形渲染管线的知识,LearnOpenGL里说的也不是很详细,目前只需要知道顶点着色器用于处理每个顶点,片元着色器用于处理光栅化之后的每个像素即可,这些代码的具体含义LearnOpenGL里有详细解释,不再详述。

const char* 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 char* 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";

创建并编译Shader

	unsigned int VertexShader;
VertexShader = glCreateShader(GL_VERTEX_SHADER);//创建Shader
glShaderSource(VertexShader, 1, &vertexShaderSource, NULL);//把着色器源码附加到着色器对象上
glCompileShader(VertexShader);//编译Shader

检查Shader是否编译成功,若失败则打印日志:

	int  success;
char infoLog[512];
glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &success);//检测编译时错误 if (!success)
{
glGetShaderInfoLog(VertexShader, 512, NULL, infoLog);//如果编译失败,用glGetShaderInfoLog获取错误消息
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}

对片元着色器也做相同操作:

	unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader); // check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}

链接着色器程序

创建一个总着色器程序,并将顶点和片元着色器链接上去,后续就可以使用这个ShaderProgram来直接渲染物体。

	//创建着色器程序
unsigned int ShaderProgram;
ShaderProgram = glCreateProgram(); glAttachShader(ShaderProgram, VertexShader);
glAttachShader(ShaderProgram, fragmentShader);
glLinkProgram(ShaderProgram);//把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们 //检测链接着色器程序是否失败,并获取相应的日志
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(ShaderProgram, 512, NULL, infoLog);
//cout...
}

链接完毕后就可以删除掉之前创建的Shader对象了:

	glDeleteShader(VertexShader);
glDeleteShader(fragmentShader);

创建缓冲对象

OpenGL中有多种缓冲对象用于存放各种数据,在绘制之前要将数据先放入缓冲区。

对每个缓冲对象都有如下步骤:

  1. 创建缓冲对象
  2. 绑定缓冲区
  3. 将数据放入缓冲区

我们先来创建VAO和VBO对象:

	unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); unsigned int VBO;
glGenBuffers(1, &VBO);//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象
glBindBuffer(GL_ARRAY_BUFFER, VBO);//使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把之前定义的顶点数据复制到缓冲的内存

然后告诉OpenGL如何解析顶点数据并启用顶点属性:

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
glEnableVertexAttribArray(0);//以顶点属性位置值作为参数,启用顶点属性

VAO用于存储随后的顶点属性调用。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了 。VBO用于存放顶点数据并发送到GPU上。

绘制物体

接下来只需要在渲染循环中调用绘制函数即可。

	while (!glfwWindowShouldClose(window))//render loop
{
processInput(window); glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//设置颜色
glClear(GL_COLOR_BUFFER_BIT);//清空屏幕的颜色缓冲 glUseProgram(ShaderProgram);//激活程序对象
glBindVertexArray(VAO);//这里由于只有一个对象所以其实不需要每次都绑定VAO,但若有多个需要绘制的对象则需要切换绑定不同的VAO
glDrawArrays(GL_TRIANGLES, 0, 3);//绘制 glfwSwapBuffers(window);//交换前缓冲和后缓冲
glfwPollEvents();//检查有没有触发什么事件
}

绘制三角形完成!

LearnOpenGL学习笔记(一)画个三角形的更多相关文章

  1. OpenGL学习笔记(1) 画一个三角形

    最近找实习有一丢丢蛋疼,沉迷鬼泣5,四周目通关,又不想写代码,写篇笔记复习一下,要好好学图形学啊 用OpenGL画一个三角形 项目的简介 记录一下跟着learnOpenGL学习的过程 笔记里的代码放在 ...

  2. OpenGL学习笔记(2) 画一个正方形

    画一个正方形 其实,画正方形就是画两个三角形,用四个顶点以及使用索引来实现 完整代码在Square项目的Application.cpp里 先贴上窗口初始化代码 void BaseInit() { gl ...

  3. LearnOpenGL学习笔记(二)纹理

    开始学习OpenGL,参考的是著名的LearnOpenGL这个网站,在这里做一些总结性的记录,只是方便自己日后查找或者记录自己的一些拓展思考,关于OpenGL的具体内容请移步: https://lea ...

  4. LearnOpenGL学习笔记(一)——现有代码理解

    首先,给出这次学习的代码原网址.------>原作者的源代码 (黑体是源码,注释是写的.) 引用的库(预编译): #include <glad/glad.h> //控制编译时函数的具 ...

  5. Unity3D学习笔记1——绘制一个三角形

    目录 1. 绪论 2. 概述 3. 详论 3.1. 准备 3.2. 实现 3.3. 解析 3.3.1. 场景树对象 3.3.2. 绘制方法 4. 结果 1. 绪论 最近想学习一下Unity3d,无奈发 ...

  6. 五毛的cocos2d-x学习笔记08-动画

    一个例子就够了,单击文本标签,执行动画.我也是小白,写这个demo的时候遇到了问题,单击文本标签游戏就死掉了.今天为了解决这个问题也是一晚没睡,到学习群里问大神,经过大神的指点解决了问题.原来是Ani ...

  7. openGl超级宝典学习笔记 (1)第一个三角形

    执行效果 代码及解析: // // Triangle.cpp // Triangle // // Created by fengsser on 15/6/20. // Copyright (c) 20 ...

  8. AngularJS1.X学习笔记13-动画和触摸

    本文主要涉及了ngAnimation和ngTouch模块,自由男人讲的比较少,估计要用的时候还要更加系统的学习一下. 一.安装 没错,就是酱紫. 二.玩玩动画 <!DOCTYPE html> ...

  9. LearnOpenGL学习笔记(三)——VBO,VAO,EBO理解

    在opengl中所有的数据都要放在显存中,我们通过一定的手段去管理它,既要提供地方存放它,还要提供方法去正确地提取它们,去使用它们,opengl通过VAO,VBO,EBO这些手段来解决这些问题. (一 ...

随机推荐

  1. Sublime Text 3能用支持的插件推荐

    从二月份用测试版本build 3012开始用sublime text 3,虽然很多插件在sublime text 3不工作了,因为sublime text 3修复了2的一些bug.提升了性能并集成了不 ...

  2. 【C/C++开发】字符串操作

    1)字符串操作 strcpy(p, p1) 复制字符串  strncpy(p, p1, n) 复制指定长度字符串  strcat(p, p1) 附加字符串  strncat(p, p1, n) 附加指 ...

  3. 微信小程序 之页面跳转

    wxml: <view><button bindtap="abc" >跳转</button></view> js: abc: (e) ...

  4. python 爬虫实例(三)

    问题描述 爬取博客园的首页数据URL[https://home.cnblogs.com/blog/page/1/],之后写到自己的Excel里面 环境: OS:Window10 python:3.7 ...

  5. 转:对JavaScript中闭包的理解

    关于 const     let      var  总结: 建议使用 let  ,而不使用var,如果要声明常量,则用const. ES6(ES2015)出现之前,JavaScript中声明变量只有 ...

  6. grpc proto文件生成java、.net实体类以及客户端代码

    背景 工作中对接对方服务是GRPC,对方只提供了proto契约文件,需要自己生成对应的实体类以及客户端代码,故记录下操作流程. Java 代码生成 实体类: 通过protoc插件生成实体类 在 htt ...

  7. STL源码剖析——空间配置器Allocator#1 构造与析构

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,因为它扮演的是幕后的角色,隐藏在一切容器的背后默默工作.但以STL的实现角度而言,最应该首先介绍的就是空间配置器,因为这是这是容器展开一切运作的 ...

  8. python 之 前端开发( DOM操作)

    11.47 DOM操作 查找节点: 11.471 直接查找 document.getElementById //根据ID获取唯一一个标签 document.getElementsByClassName ...

  9. spring中EL解析器的使用

    SpEL对表达式语法解析过程进行了很高的抽象,抽象出解析器.表达式.解析上下文.估值(Evaluate)上下文等对象,非常优雅的表达了解析逻辑.主要的对象如下: 类名 说明 ExpressionPar ...

  10. 使用winsw将springboot打包的jar注册系统本地服务

    1.下载winsw 下载地址:https://github.com/kohsuke/winsw/releases 我这里下载的是2.3.0版. 下载sample-minimal.xml和WinSW.N ...