OpenGL学习 GLFW 绘制三角型 (三)

来,首先我们学习三个英文单词;后面能用到

顶点数组对象Vertex Array ObjectVAO
顶点缓冲对象Vertex Buffer ObjectVBO
索引缓冲对象Element Buffer ObjectEBO或Index Buffer Object,IBO

在OpenGL中,任何事物都是处在3D的,然而我们肉眼看见的都是2D的像素块拼合出来的,也正因如此OpenGL的大部分工作都是在做把3D坐标转换到2D,这里就是由OpenGL的图像渲染管线(Graphics pipeline ,翻译指管线,实际指的是一堆的原始的图形数据经过一个输送管道,在此期间经过各种变化处理最终出现在屏幕中的过程)管理的。它主要包括两个部分,第一3d的坐标转换成2d的坐标第二把2d的坐标转换成有颜色的像素。下面简单的理解一下图形渲染管线,并且如何用它来创建一些漂亮的像素。

先传入一系列顶点(vertex)坐标,顶点数据是一系列顶点的集合,里面还包括了颜色值,表示顶点的颜色组成。而区分到底是绘制怎样的图形由图元(primitive)来表示,常见的值有,GL_POINTSGL_TRIANGLESGL_LINE_STRIP,分别表示点,面,线。

一,顶点着色器(必有

把一个单独的顶点作为输入,目的是改变3d坐标的类型。同时对顶点的属性做基本的处理。

二,图元装配(primitive Assembly)

以上一次的处理结果为输入,按照相应的图元指令,装配成指定的形式。

三,集合着色器

将一系列的点互相链接起来,生成许多的其他的图形。

四,光栅化阶段

把图元处理成像素,生成供片段着色器(fragment shader)使用的片段,还会裁切(clipping)出多余的像素。

五,片段着色器(必有

计算最终的像素的颜色。

六,Alpha测试与Blending混合

检测alpha值--代表透明度,还会检测片段对应的深度值(还有模板(stencil)值)。

开始绘制三角型

一,顶点输入

顶点为一个三维的xyz坐标系中的点,但是取值范围为OpenGL3D坐标(-1)~(1),我们采用数组来储存这些值。但是实际呈现内容到屏幕上的时候只会呈现标准化设备坐标(顶点着色器处理后就是这种坐标)(normalized device coordinate)范围(-0.5~~0.5)里面的坐标才会呈现在屏幕上。

float vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f
    //z轴的值全部都为0,代表是一个平面的二D图形,其实这个值也可以看作就是图形的深度。
};

 

接下来通过顶点缓冲对象(VBO)管理储存这些顶点的数据,将数据发送到GPU内存中(显存),而它存在一个唯一的ID,可以使用函数glGenBuffers和一个缓冲ID生成一个VBO对象。

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

 

OpenGL的缓冲对象有很多类型,由于上面用的是数组来储存的顶点数据,可以将上面创建的VBO对象绑定到GL_ARRAY_BUFFER目标上面。

//也就是给这个顶点缓冲对象绑定到自带的缓冲对象上面(这里采用GL_ARRAY_BUFFER)
glBindBuffer(GL_ARRAY_BUFFER, VBO); 

指定好了类型就可以向缓冲内存中读入了。

/*
参数一:    代表目标缓冲的类型
参数二:    用sizeof计算顶点数据大小,单位是行
参数三:    实际发送的数据
参数四:    告诉显卡怎样管理这些数据,有三个值可选
            GL_STATIC_DRAW :数据不会或几乎不会改变。
            GL_DYNAMIC_DRAW:数据会被改变很多。(动态的)
            GL_STREAM_DRAW :数据每次绘制时都会改变。(流动的)
*/
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

2,创建顶点着色器

我们先创建一个简单的着色器,然而这需要使用着色器语言GLSL(OpenGL shader language)编写顶点着色器,下面是一个简单的example

#version 330 core//版本声明 核心模式
layout (location = 0) in vec3 aPos;//设定输入变量的位置值

void main()
{
    //可以传入坐标数据,用in关键字实现传入顶点属性
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    /*由于不是使用的4维的,目前暂不深究,默认为1.0f*/
}

3,编译着色器

上一步我们只是写出了着色器的代码,还需要OpenGL来执行这个代码,然而实现这个就需要OpenGL在运行的时候动态的编译这个源码。

1,我们需要创建一个顶点着色器对象,同理还是用ID来引用这个对象。

unsigned int vertexShader;//储存ID的值引用
vertexShader = glCreateShader(GL_VERTEX_SHADER);//先创建在将这个值赋给相应的ID引用
//参数类型是我们需要创建的着色器的类型,是可选的值。这里创建的是顶点着色器所以我们使用GL_VERTEX_SHADER

下一步就需要给这个着色器传递源码。然后编译这个着色器

//参数一    着色器对象
//参数二    指定了所传递的源码字符串数量
//参数三    顶点着色器真正的源码
//参数四    暂且设置为NULL
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);

//编译这个着色器
glCompileShader(vertexShader);

对于这个glCompileShader     对于最终的编译结果我们怎样才能知道呢,到底成功没有,还是失败了,错误信息是什么

//这个整形变量储存编译是否成功
int  success;
//在失败的情况下用于储存错误信息的容器
char infoLog[512];
//使用glGetShaderiv来检测是否编译成功,会给success赋值
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

//接下来就用if来判断
if(!success)
{
    //失败了的话可以通过glGetShaderInfoLog获取失败的调试信息
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}

到此没有报错的话就说明我们的顶点着色器已经是创建编译成功了

片段着色器

这个是我们知道的默认系统是不会自己创建的另一个着色器,我们也需要自己来创建。他是用于计算像素最后的颜色输出的,考虑为初学,我们只是创建一个纯色的片段着色器。

在计算机中图形的颜色表示有四个元素,红色,绿色,蓝色和alpha(透明度)分量。RGBA。在OpenGL中每个颜色的取值都是在0~~1之间的浮点数。

同理我们也需要一个着色器源码

#version 330 core//OpenGL版本和渲染模式,核心模式

//由于是输出,里面有4个分量,RGBA.
//我们可以用out关键字来声明输出变量
out vec4 FragColor;

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

下面同样的就是编译片段着色器,和编译顶点着色器类似

unsigned int fragmentShader;//ID引用        fragment碎片,片段
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//赋值
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//传递源码
glCompileShader(fragmentShader);//编译

到此,两个着色器就已经编译好了,但是还需要一个着色器程序(shader program)来链接这些着色器,管理他们来进行渲染

着色器程序

他也是一个对象,由多个着色器链接后形成。如果要使用上面我们创建的着色器,我们就需要把他们整合到一个着色器程序中来,然后在渲染的时候调用。

创建步骤很简单

unsigned int shaderProgram;
shaderProgram = glCreateProgram();//glCreateProgram函数创建一个程序

接下来就是来链接着色器了

glAttachShader(shaderProgram, vertexShader);//glAttachShader附加着色器到程序上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//着色器程序链接这些着色器

同样我们需要编译这个着色器程序,并且判断是否编译成功但是还是有区别的

glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if(!success) {
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    //没有成功就将错误的调试信息装入容器
}

得到这个程序对象吼我们可以通过调用glUseProgram函数,传入刚刚创建的对象,就可以激活这个程序对象了。

glUseProgram(shaderProgram);

注意:我们在激活了这个程序对象后我们就不再需要之前的着色器对象了。我们要记得删除着色器对象

glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

至此,我们已经将顶点数据发送给了GPU(显存),并且指示了GPU如何在顶点和片段着色器中处理他。接下来我们要让OpenGL知道如何解释内存中的顶点数据,以及如何将顶点数据链接到顶点着色器上。我们需要给与指令。

链接顶点属性

 

顶点着色器允许我们指定任何以顶点属性形式的输入,这使得具有很强的灵活性的同时我们就需要手动的输入数据的那一部分对应着着色器的哪一个顶点属性。所以我们要在渲染OpenGL渲染前指示OpenGL如何解释顶点数据。

我们的顶点缓冲数据会被解析成如图格式

  • 位置数据储存为32位(4个字节)浮点值。
  • 每个位置包含三个这样的值
  • 在这三个之间没有空隙(或者其他的值),这几个值在数组中紧密的排列(tightly packed)。
  • 数据中第一个值在缓冲区开始的位置。

理解了上面的知识后,我们就可以开始正式的指示OpenGL如何解析这些顶点数据(应用到每个顶点

属性上)

//glVertexAttribPointer函数的参数非常的多,下面逐一介绍
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
//暂时不知到具体的用途
glEnableVertexAttribArray(0);

 

glVertexAttribPointer 参数解释

参数一顶点属性,在layout(location = 0)中定义了position顶点属性的位置为0,我们希望传入顶点属性到这一个顶点数据中,所以在这里传入0
参数二表示顶点属性的大小,由于是ve3三个方向的向量,我们就传入3
参数三指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。
参数四表示数据是否被标准化(normalize)如果设置成GL_TRUE,所有的数据都会被映射成0~1之间,我们设置为GL_FALSE
参数五

步长(stide),他告诉我们连续的顶点数组间的间隔,由于每个数组的大小是三个float,所以我们设置步长为 3*sizeof(float).

其实我们的数组是连续排列的没有空隙,我们也可以设置0来让OpenGL自己决定具体的步长是多少(仅当数据是紧密的联系在一起的时候可以采用)。

参数六这个参数的类型是void* ,这里需要进行强制类型转换。他表示位置数据在缓冲中起始位置的偏移量(offset)。位置数据在数组的开头,所以这里写0.

每个顶点属性从一个VBO管理的内存中获取它的数据,二具体是哪一个VBO(程序中可以有多个VBO)获取则是通过在调用glVertexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。由于在调用glVertexAttribPointer之前绑定的是先前定义的VBO对象,顶点属性0 现在会链接到它的顶点数据。

到此   我们已经定义好了OpenGL该如何解释顶点数据,现在就需要用glEnableVertexAttribArray,来根据顶点属性的值启用顶点属性,它默认是禁用的。到这里我们需要的所有的东西就已经设置好了 经历了如下步骤

  1. 使用一个顶点缓冲对象将顶点数据初始化至缓冲中
  2. 建立了一个顶点和一个片段着色器
  3. 告诉OpenGL如何将顶点数据链接到顶点着色器的顶点属性上面

下面是OpenGL中绘制一个物体的代码

// 0. 复制顶点数组到缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 2. 当我们渲染一个物体时要使用着色器程序
glUseProgram(shaderProgram);
// 3. 绘制物体
someOpenGLFunctionThatDrawsOurTriangle();

每当我们需要绘制这样一个物体的时候就要重复执行这个过程,这看起来可能不是很多,但是如果有超过5个顶点属性,上百个不同物体呢(很常见),绑定正确的缓冲对象,为每个物体配置所有顶点属性很快就会成为一件麻烦事。下面就有一种方法可以让我们把所有的这些状态装配储存到一个对象中,并且可以通过绑定这个对象来恢复状态。

顶点数组对象

(vertex array object)VAO,它可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当你配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中

一个顶点数组对象VAO包含下列内容

  • glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
  • 通过glVertexAttribPointer设置的顶点属性配置。
  • 通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不懂电脑的小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值