OpenGL日常-三角形(上)

大家好,欢迎来到听风的OpenGL日常。本文代码

写在前面

本节我们的理解核心:

VBO,Vertex Buffer Object 顶点缓冲对象
VAO,Vertex Array Object 顶点数组对象
EBO,Element Buffer Ojbect 索引缓冲对象

理解了这三个概念,就理解了什么叫做图形渲染管线(Graphics Pipeline)。

画三角形

本篇我们接02的glad配置环境与视口,我想你一定不会忘记窗口与视口的区别。

所以如果你不会如何使用glfw去画一个OpenGL窗口,那么请看听风的02-GLAD一节,这里就不给出链接了,自己想办法找。

在上篇中我们给视口区域渲染了底色,如果你做的足够好,那么我们会看到启动的程序背景会不停的发生变化。毫无疑问,画三角形的位置应该与改变视口底色的位置是一样的。

OpenGL会知道在某次渲染循环中改变视口区域的所有颜色值,但是对于一个三角形,OpenGL需要知道在什么位置渲染以及渲染什么样的颜色值。

于是。。。

pipeline.png

这张图讲了什么

对于上面我们提出的问题,答案就在这张图里,我们一点点探索。对于图中写的着色器(Shader),虽然现在不理解这个是什么意思,但是我们只要知道,这里的着色不仅仅代表颜色就够了,因为这个翻译会影响我们的理解。

上面这张图就是整个OpenGL渲染管线的全过程,你会发现这个过程就是一条流水线而矣,没错它就是如此憨憨的一条流水作业罢了。

从顶点数据开始说起

首先一个三角形是由三个顶点组成,OpenGL得知道三个顶点的位置,但是由于OpenGL是与硬件无关的,它并不知道实际应用中窗口的像素是多少,因此在它自己的理解里,它只知道顶点的概念。于是在它自己的认知内,它得为自己的顶点设定一个范围,这个范围就是标准化设备坐标(Normailzed Device Coordinates),它是一个三维笛卡尔坐标系(百度左转),x,y,z轴的范围都是-1~1。所以如果假想我们的窗口里的物体是在这个范围内,那么我们可以定义一些基础图形:

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
};
//今天的例子是一个2D图形,先忽略z

用一张图来理解:

tri01.png

记录位置

顶点有了,OpenGL需要把这些位置记录下来,然后发送并告诉GPU,这样GPU才能把正确的位置信息最终输出到屏幕上。

于是第一个概念顶点缓冲对象(VBO)开始工作,

首先生成一个顶点缓冲对象,用它来对这些数据进行管理:

unsigned int VBO;
glGenBuffers(1, &VBO);

//这里第一个参数表示缓存对象个数,第二个参数是唯一ID用于标识绑定的VBO对象;

OpenGL还需要知道绑定的对象是什么类型,这样方便它去管理,这里我们的VBO是一个GL_ARRAY_BUFFER

glBindBuffer(GL_ARRAY_BUFFER, VBO);

之后我们要把顶点数据存储到VBO对象中,

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

这里会把我们的顶点数据复制到内存里,第一个参数如上个绑定所说,第二个参数是数据大小,第三个是顶点数据,第四个解释如下,作区分的话个人理解也是OpenGL便于管理,对于要进行频繁修改的一些缓冲数据,要放置在一些便于访问的位置(臆断);

  • GL_STATIC_DRAW :数据不会或几乎不会改变。
  • GL_DYNAMIC_DRAW:数据会被改变很多。
  • GL_STREAM_DRAW :数据每次绘制时都会改变。

顶点着色器

接下来我们有了这些顶点数据,OpenGL应该知道这些点应该画在什么地方,因为数据虽然已经到达内存,但是OpenGL并不知道,这里就需要用到顶点着色器了,着色器由类似C语言写法的GLSL(OpenGL Shading Language),直接作用在GPU上,是OpenGL与我们写的程序的一个桥梁。这是目前我能理解到的程度。

#version 330 core
layout (location = 0) in vec3 aPos;

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

这里我们不对其语法作过多的说明,后续慢慢了解,我们现在看它是如何被连接到我们程序里的。

上面说到VBO对象得将顶点数据发送到OpenGL里,那么绑定在VBO的顶点数据就作为顶点着色器的输入直接传入到着色器中,所以OpenGL必须先定义一个着色器;

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

可以看到这里的着色器类型是GL_VERTEX_SHADER,之后着色器是由GLSL编写的,那么:

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";

着色器程序是由字符串组织的,使用下面的代码绑定到着色器上并编译:

glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

下面这一段是检查着色器编译是否成功的,从这里可以看到,OpenGL的许多功能都是内置的,我们在外部可以通过某些函数对其内部的状态进行窥视,有种偷偷摸摸的感脚,此处省略画面:

//检查编译错误
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;
}

gl_Position

所以是怎么传到OpenGL里的呢,一张图自己体会:

tri02.png

这里的gl_Position很重要,这个是最终OpenGL接收数据的地方,是它的一个内置变量,它的值将会让OpenGL知道顶点的位置;

片段着色器

接下来我们先跳过开头图中其他的着色器环节,来给三角形设置颜色,这里我们在片段着色器阶段搞一些事情,同前面的顶点着色器类似的过程;

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
} 

这次我们定义的着色器类型是GL_FRAGMENT_SHADER

const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"
"}\n\0";
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
if (!success)
{
    glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}

着色器程序

接下来我们要把最开始的那张图的所有过程链接起来,需要使用OpenGL定义一个着色器程序,并把我们写好的两个着色器也链接进去:

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);

如何解析顶点数据

这个部分我觉得需要充分展开说明,涉及到了下面代码段,这个留在下一节中吧,我们就快要完成了,三角形很快要出现。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);

可以画三角形了

上节说到我们渲染了视口的颜色,前面我们已经写好了渲染程序,所以在改变视口颜色的位置调用着色器程序渲染三角形:

glUseProgram(shaderProgram);
glBindVertexArray(VBO); 
glDrawArrays(GL_TRIANGLES, 0, 3);

很不幸今年的Demo过程并没有得到最终的结果,那么问题出在什么地方呢?上面说的解析顶点数据到底做了些什么?为什么顶点跟颜色都绑定好了内存却没有在Draw时画出三角形?

下一篇继续讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342