一个三角形

作者: 大旺旺的弟弟小旺旺 | 来源:发表于2022-07-13 06:54 被阅读0次

    三个单词:

    • 顶点数组对象:Vertex Array Object,VAO
    • 顶点缓冲对象:Vertex Buffer Object,VBO 使用步骤:create 存储数据 绑定 使用
    • 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO

    屏幕都是2d的,但是绘制都是3d的,那么需要将2d转换为3D,这个通过opengl的图形渲染管线完成的。
    最终就是将3d转换为2D,然后转换为具体的像素。

    · 2d像素和2d不一样,这个颜色值会受到分辨率的影响。opengl坐标变换到屏幕坐标上,一个像素不一定对应一个颜色值(线性 临近 mipmap等)。

    多重采样,来处理锯齿。

    图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做着色器(Shader)。

    有些部分是可以使用用户自己定义的。

    每个部分的输出作为下一个部分的输入,每个阶段都是一段程序,有的可以自己定义,有的不可以自己定义。

    图形绘制运行在GPU上,节约了cpu资源。

    每个部分都是将点数据转换为像素的过程。

    过程

    1.3d顶点{顶点数据}传递给管线 ,每个顶点{顶点属性}是一个3d集合
    2.告诉它是需要绘制什么 线 三角形??

    一般地,传递3个3D坐标表示三角形(顶点数据),它是一系列顶点的集合。

    顶点着色器

    将一个单独的顶点作为输入,目的是将3d坐标转换为另一种3d坐标,并且运行对3d坐标进行一些处理。

    图元装配

    顶点着色器的输出作为输入,将所有的点装配为一个指定的图元形状

    这个部分的输出传递给几何着色器,将图元形式的一些顶点作为输入,构造出新的图元形状。

    几何着色器

    几何着色器图元一系列顶点的集合作为输入,可以通过新图元构造出一个基本图形。

    裁剪

    着色器之前会进行裁剪,裁剪去屏幕之外的部分。

    光栅化

    几何着色器输出回传入光栅化阶段,它会把图元映射到屏幕的相应像素,供给片段着色器使用的片段,

    open GL的每一个片段是Opengl渲染一个像素所需要的所有数据。(颜色、深度等值)

    片段着色器

    片段着色器是计算一个最终的像素,包含了光照,阴影,光照颜色等,计算出来的一个最总颜色。

    颜色确定之后会进行一个alpha和混合阶段。深度值来判断在前还是在后,决定是否应该丢弃

    可以对alpha值进行判断,丢弃掉一些不需要的片段。

    alpha测试

    会检查alpha,对物体进行混合,所有在片段着色器中计算出一个值,

    一般的情况,我们只关心片段和片元即可,其他的使用默认。

    顶点输入

    开始绘制,需要先给一些顶点数据,opengl不是简单的将3d坐标转换为2d坐标,它只能处理-1到1范围内的,这个范围叫做标准化设备坐标,最终会显示在屏幕上。

    三角形3个顶点以标准化形式展示,给定一个float数组。

    -0.5F,-0.5F,0.0f,
    0.5f,-0.5f,0.0f,
    0.0f,0.5f,0f
    

    渲染一个2维图形,将z设置为0,三角形的深度是一样的(深度z,glfrood来获取),实现一个2d效果。

    一旦顶点经历了顶点着色器处理之后,就变为了标准化坐标了,在-1到1之外的就会被抛弃。

    z一般表示的是深度,空间距离你的距离,距离远的会被抛弃,以节省资源。

    顶点数据准备好之后,将数据发送给第一给处理阶段:顶点着色器,它会在GPU上存储我们的顶点数据,还需要配置open gl怎样解释内存,如何发送到显卡,顶点着色器会处理我们在内存中指定的顶点数据。

    解释一下:
    1.我们给的顶点数据可能是单一的,可能是所有的东西都在一起,所以我们应该如何分配这些数据。
    2.数据如何发送?使用缓冲区?使用的是属性、uniform还是其他的。

    cpu发送到GPU是很慢的,所以我们需要需要一次发送比较多的信息。

    顶点缓冲对象

    会在GPU中存储大量的顶点,好处就是可以一次发送一大批的数据去显卡(之前是每个顶点发送一次)。cpu将数据发送到显卡相对较慢,需要我们要尝试一次发送尽可能多的数据,发送到显卡之后,会被GPU立即访问。

    顶点缓冲区对象,opengl可以存在多个缓存区,只要是不同类型的。可以使用glGenBuffers生成一个缓冲ID。

    OPENGL有许多缓冲区对象类型,比如:GL_ARRAY_BUFFER

    使用

    //创建缓冲区
    int VBO;
    glGenBuffers(1,&VBO);
    //绑定缓冲区数组
    glBindBuffer(GL_ARRAY_BUFFER,VBO);
    下来我们可以对这个缓冲区进行配置
    - 存入数据
    glBufferData(GL_ARRAY_BUFFER,size,vertices,GL_STATIC_DRAW);
    

    glBufferData将用户定义的数据绑定到当前缓冲区函数。

    • 目标类型

    • 数据大小

    • 数据

    • 如果管理

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

    根据顶点数据如何变化,选择一个合适的模式。

    顶点着色器

    第一个可编程的着色器。渲染需要一个顶点着色器一个是片段着色器。
    之前说过,GPU每一个阶段都是一个小的程序代码,有的是可以编程的。

    所以写代码,我们使用的是GLSL语言。

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

    属性是一个4个float组成的东西。

    为了设置顶点着色器的输出,我们需要将position给gl_Position,它是一个vec4类型的东西。
    顶点的数据并不是简单的赋值,他将顶点数据变为着色器顶点

    上面的数据都是0.x的形式,在实际中,这个不是必须的,根据实际情况进行定义,比如宽高为720 x 1280
    就可以将上面的0.5写为360 640.

    编译着色器

    为了pengl使用它,我们动态的编译源代码。

    • 先得到一个着色器的ID.
    int vertexShader;
    vertextShader = glCreateShader(GL_VERTEX_SHADER);
    
    • 将代码附着在着色器上面。
    glShaderSource(vertexShader,1,&vertextShaderSource,NULL);
    
    • 进行编译
    glCompileShader*(vertexShader);
    

    片段着色器

    第二个可以更改的部分。计算像素最终的颜色输出。片段着色器需要一个输出变量,表示最终输出的颜色,将最终的结果,给Fragcolor

    void main(){
        FragColor = vec4(1.0f,1.0f,1.0f,1.0f);
    }
    
    • 创建片段着色器
    • 附着程序
    • 编译
    int fragmentShader;
    glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader,1,&fragmentShaderSource,null);
    glCompileShader(fragmentShader);
    

    将两个着色器对象连接到一个着色器程序中。

    着色器程序

    着色器程序是合并之后并最终连接完成的版本,如果要使用我们还需要将他们连接为一个可执行程序对象,然后激活它

    它会将每个着色器连接到下一个着色器的输入。

    int shaderProgram;
    shaderProgram = glCreateProgram();
    将两个部分附着到程序对象上,并连接他们
    
    glAttachShader(shaderProgram,vertexShader);
    glAttachShader(shaderProgram,fragmentShader);
    glLinkProgram(shaderProgram);
    

    下一步就是使用它.

    glUseProgram(shaderProgram);
    

    每一个着色器调用和渲染调用都会使用这个程序。

    · 注意这个时候可以删除我们的着色器对象了,因为不需要了。

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    

    发送数据给GPU

    • 指示在GPU中如何使用顶点和片段着色器
    • 如何使用内存
    • 顶点数据给属性

    连接顶点属性

    顶点着色器允许我们指定任何以顶点属性为形式的输入。所以我们需要指定那一部分属于什么。如何解释数据。

    • 位置数据存储为32位浮点值
    • 每个位置包含3个值
    • 值之间没有间隙
    • 数据中第一值在缓冲的开始。

    如何解释?

    1.位置顶点属性
    2.指定属性的大小
    3.值当数据的类型
    4.是否希望数据标准化(所有数据都会映射到0到1之间)
    5.步长
    6.偏移量(或者buffer)
    glVertexAttribPointer(glPosition,3,GL_FLOAT,GL_FALSE,3*sizeof(float),0);
    

    告诉了怎么解释数据,下一步就是启用顶点属性。

    glEnableVextexAttribArray(0);
    

    使用顶点缓冲区使用的一个完整案例:

    1.生成一个VBO
    2.绑定一个ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    3.绑定数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    4. 设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    5.启动顶点属性
    glEnableVertexAttribArray(0);
    6. 当我们渲染一个物体时要使用着色器程序
    glUseProgram(shaderProgram);
    7. 绘制物体
    someOpenGLFunctionThatDrawsOurTriangle();
    

    上面的缺点是,每次每一个属性都需要这么写,可以使用下面的顶点数组对象

    顶点数组对象

    和顶点缓冲对象那样,任意的顶点属性调用都会存储在vao中,只需要调用一次,以后只需要绑定就可以了。
    好处就是在切换顶点的时候比较的快。

    使用:

    • 创建vao
    unsigned int VAO;
    glGenVertexArrays(1, &VAO);
    
    • 绑定和配置对于的vao和属性指针
    glBindVertexArray(VAO);
    
    • 设置值
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 设置顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    • render中
    // 4. 绘制物体
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    someOpenGLFunctionThatDrawsOurTriangle();
    

    元素缓冲对象

    元素缓冲对象,索引缓冲对象。简单说就是通过索引知道点如何进行绘制,去除重复的点。

    1.创建

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

    先绑定glBufferData,将索引放入缓冲区中

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
    glBufferData(GL_ELEMENT_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
    

    绘制

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    

    缓冲数组做了什么?

    // ..:: 初始化代码 :: ..
    // 1. 绑定顶点数组对象
    glBindVertexArray(VAO);
    // 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // 3. 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // 4. 设定顶点属性指针
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    [...]
    
    // ..:: 绘制代码(渲染循环中) :: ..
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    

    相关文章

      网友评论

        本文标题:一个三角形

        本文链接:https://www.haomeiwen.com/subject/fmezbrtx.html