• OpenGL 笔记<3> 数据传递 一


    Sending data to a shader using vertex attributes and vertex buffer object

    上次我们说到着色器的编译和连接,后面的事情没有做过多的阐述,所以我们今天继续上次的。

    今天所讲的也是OpenGL着色器编程中非常重要的一个环节,叫做:数据传递

    它主要承担的任务呢就是把opengl主程序中的数据传递到着色器中,上一节,我们已经把着色器准备好了,本节,我们将把主程序中的数据传递到着色器中进行处理。

    说到数据传递,它其实分两种:

    1.使用顶点属性和顶点缓冲对象(VBO)进行数据传递

    2.使用统一变量(uniform)进行数据传递

    上述两者均为非常重要的内容,我们今天先阐述第一种。

    Getting Ready

    为了方便,我们把上次的代码贴过来

    顶点着色器 vertShader

    #version 430
    
    layout (location=0) in vec3 VertexPosition;
    layout (location=1) in vec3 VertexColor;
    
    out vec3 Color;
    
    void main()
    {
        Color = VertexColor;
        
        gl_Position = vec4(VertexPosition,1.0);
    }

    片元着色器 fragShader

    #version 430
    
    in vec3 Color;
    
    out vec4 FragColor;
    
    void main()
    {
        FragColor = vec4(Color, 1.0);
    }

    opengl 主程序(应用程序)

      1 //配置代码
      2 #if _MSC_VER>=1900
      3 #include "stdio.h"
      4 _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
      5 #ifdef __cplusplus
      6 extern "C"
      7 #endif
      8 FILE* __cdecl __iob_func(unsigned i) {
      9     return __acrt_iob_func(i);
     10 }
     11 #endif /* _MSC_VER>=1900 */
     12 
     13 //code-list
     14 //using namespace std;
     15 #include <iostream>
     16 #include <fstream>
     17 using namespace std;
     18 #include <vgl.h>
     19 
     20 GLint vertShader, fragShader;
     21 GLuint vaoHandle;
     22 
     23 float positionDate[] =
     24 {
     25     -0.8f,-0.8f,0.0f,
     26     0.8f,-0.8f,0.0f,
     27     0.0f,0.8f,0.0f,
     28 };
     29 float colorDate[] =
     30 {
     31     1.0f,0.0f,0.0f,
     32     0.0f,1.0f,0.0f,
     33     0.0f,0.0f,1.0f,
     34 };
     35 
     36 void init();
     37 void Display();
     38 void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName);  //编译着色器
     39 void _Link_Shader_();                          //链接着色器
     40 bool readFile(const char*, string&);           //读取文件内容的函数
     41 
     42 int main(int argc, char** argv)
     43 {
     44     glutInit(&argc, argv);
     45     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
     46     glutInitWindowSize(1024, 768);
     47     glutInitWindowPosition(20, 20);
     48     glutCreateWindow("test");
     49 
     50     if (glewInit())
     51     {
     52         cout << "Error!" << glGetString(glewInit()) << endl;
     53         exit(EXIT_FAILURE);
     54     }
     55 
     56     cout << "GL version:" << glGetString(GL_VERSION) << endl;         //查询本机OpenGL版本
     57 
     58     init();
     59 
     60 
     61     _Compiling_Shader_(vertShader, GL_VERTEX_SHADER,"basic.vert");
     62     _Compiling_Shader_(fragShader, GL_FRAGMENT_SHADER, "basic.frag");
     63 
     64     _Link_Shader_();
     65 
     66     glutDisplayFunc(Display);
     67     
     68     glutMainLoop();
     69 }
     70 
     71 
     72 void init()
     73 {
     74     GLuint vboHandles[2];
     75 
     76     glGenBuffers(2, vboHandles);
     77 
     78     GLuint postionBufferHandle = vboHandles[0];
     79     GLuint colorBufferHanle = vboHandles[1];
     80 
     81     glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
     82     glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), positionDate, GL_STATIC_DRAW);
     83 
     84     glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
     85     glBufferData(GL_ARRAY_BUFFER, 9 * sizeof(float), colorDate, GL_STATIC_DRAW);
     86 
     87     glGenVertexArrays(1, &vaoHandle);
     88     glBindVertexArray(vaoHandle);
     89 
     90     glEnableVertexAttribArray(0);
     91     glEnableVertexAttribArray(1);
     92 
     93     glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
     94     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
     95 
     96     glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
     97     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
     98 }
     99 
    100 void Display()
    101 {
    102     glClear(GL_COLOR_BUFFER_BIT);
    103 
    104     glBindVertexArray(vaoHandle);
    105     glDrawArrays(GL_TRIANGLES, 0, 3);
    106     glutSwapBuffers();
    107 }
    108 
    109 bool readFile(const char* filename, string& content)
    110 {
    111     ifstream infile;
    112     infile.open(filename);
    113     if (!infile.is_open())return false;
    114 
    115     char ch;
    116     infile >> noskipws;
    117     while (!infile.eof())
    118     {
    119         infile >> ch;
    120         content += ch;
    121     }
    122     infile.close();
    123     content += '';
    124     return true;
    125 }
    126 
    127 void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName)
    128 {
    129     shaderHandle = glCreateShader(GL_Shader_type);
    130     //检查编译情况
    131     if (0 == shaderHandle)
    132     {
    133         fprintf(stderr, "Error creating shader.
    ");
    134         exit(EXIT_FAILURE);
    135     }
    136     string ShaderCode;
    137     if (!readFile(shaderName, ShaderCode))
    138     {
    139         cout << "readFile Error!" << endl;
    140         exit(EXIT_FAILURE);
    141     }
    142     const GLchar* shaderSource = ShaderCode.c_str();
    143     const GLchar* codeArray[] = { shaderSource };
    144     glShaderSource(shaderHandle, 1, codeArray, NULL);
    145 
    146     glCompileShader(shaderHandle);
    147 
    148     GLint result;
    149     glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result);
    150     if (GL_FALSE == result)
    151     {
    152         fprintf(stderr, "shader compilation failed!
    ");
    153 
    154         GLint logLen;
    155         glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen);
    156 
    157         if (logLen > 0)
    158         {
    159             char* log = new char[logLen];
    160 
    161             GLsizei written;
    162             glGetShaderInfoLog(shaderHandle, logLen, &written, log);
    163 
    164             fprintf(stderr, "Shader log:
    %s", log);
    165             delete[] log;
    166         }
    167     }
    168 }
    169 void _Link_Shader_()
    170 {
    171     GLuint programHandle = glCreateProgram();
    172     if (0 == programHandle)
    173     {
    174         fprintf(stderr, "Error creating program object.
    ");
    175         exit(1);
    176     }
    177 
    178     glAttachShader(programHandle, vertShader);
    179     glAttachShader(programHandle, fragShader);
    180 
    181     glLinkProgram(programHandle);
    182 
    183     GLint status;
    184     glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
    185     if (GL_FALSE == status)
    186     {
    187         fprintf(stderr, "Failed to link shader program!
    ");
    188 
    189         GLint logLen;
    190         glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
    191         if (logLen > 0)
    192         {
    193             char* log = new char[logLen];
    194 
    195             GLsizei written;
    196             glGetShaderInfoLog(programHandle, logLen, &written, log);
    197 
    198             fprintf(stderr, "Program log:
    %s", log);
    199             delete[] log;
    200         }
    201     }
    202     else
    203         glUseProgram(programHandle);
    204 }
    View Code

    How it works...

    1.glsl 相关

    关于in、out 等简单的关键字上次已经做过阐述。

    location 关键字用于设置glsl 变量的下标,以便于在opengl 主程序对其进行访问。

    设定glsl 变量下标(index)还可以在opengl主程序中进行:

    //假定着色器程序(glsl程序)的句柄额为programHandle
    glBindAttribLocation(programHandle, 0,"VertexPosition");
    glBindAttribLocation(programHandle, 1, "VertexColor");

    当然这个index也可以不设置,系统会默认进行,我们进行明确设定一下方便后面的数据传递。

    gl_Position 是opengl内置变量,我们只需要重置其值即可,至于后面的扩展维度,属于CG中的知识,是在后面的三维变换中用到的,我们这里设置为1.0,即限定于普通二维平面。

    流程:

    ·vertShader从opengl程序中接收读入VertexPosition和VertexColor,重置刷新gl_Positin ,然后将VertexColor 以 unchanged copy 的方式传递给Color。

    ·fragShader接收来自vertShader中的out数据,产生像素信息,输出

    2.opengl 相关

    流程:

    1)创建一个VAO(vertex array object)    line:21(代码第21行)

    2)创建属性数据信息                                 line:23、29

    3)创建VBO(vertex buffer object)         line:74~79

    4)用创建好的属性数据填充VBO缓冲区     line:81~85

    5)激活VAO               line:87、88

    6)激活glsl 顶点属性数据                           line:90、91

    7)VBO与glsl 顶点属性量作映射         line:93~97

    8)激活VAO进行渲染绘制                           line:104~105

    那么,我们来分析一下每一步的工作

    我们做之前一定是要准备好坐标以及颜色等属性信息的,这个就不多说了

    3)创建VBO来存储我们的属性信息,为了方便,我们将vboHandle数组中的每一个量都重新赋值是方便后面的代码书写,没有引用是因为我们的句柄只是数字,数字即可识别,不需要地址。

    4)我们需要将VBO与opengl的枚举量GL_ARRAY_BUFFER创建映射,然后通过它进行数据载入。

    那个枚举量还有其他,我们将其称为binding point(暂时不知道该如何翻译,就先这样叫着吧)。

    我们通过GLBindBuffer将VBO对象与一个binding point建立映射,然后通过GLBufferData将创建好的属性数据填充到VBO缓冲中,函数的第三个参数是告诉opengl应该如何使用该数据,在我们的例子中,数据被指定一次,不会被修改,并且将被多次用于绘制操作,因此这种使用模式最好对应于值GL_STATIC_DRAW。

    既然我们都建立好了我们的缓冲区对象了,我们就把它们联系起来,然后记录在VAO中。

    于是就有了第5)步,VAO记录的是我们的缓冲区数据和glsl 顶点属性量之间的关联

    然后第6),我们通过下标index值(location设定的值)访问glsl 顶点属性量,进行激活。

    这样做的目的是:打开属性量的通道,代表着该属性量将被用于之后的渲染

    7)void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer);   顶点属性指针函数

    第一个参数为glsl 属性量的index(或是location值);

    第二个参数为该属性量的维度数,比如,position数据就是x,y,z组成的,是三维数据;

    第三个参数为数据类型;

    第四个参数为需要规格化,即将数据范围限定在【-1,1】;

    第五个是相邻连续的属性量之间的字节距离;

    最后一个参数是一个指针,但不被视为一个指针! 相反,它的值被解释为从缓冲区的开头到缓冲区中的第一个属性的字节偏移量。 在这种情况下,在第一个元素之前的任一缓冲区中都没有附加数据,因此我们使用零值

    glVertexAttribPointer函数存储(在VAO的状态下)指向当前绑定到GL_ARRAY_BUFFER绑定点的缓冲区的指针。 当另一个缓冲区绑定到该绑定点(binding point)时,它不会更改指针的值。

    More

    如果上面的glVertexAttribPointer没看懂可以看下面这个详细版(下面的第一块)。

    Separate attribute format    分离属性格式  技术

    在opengl 4.3中,我们有着更好的方式去指定VAO状态(属性格式,属性激活与否,vbo缓冲区)。

    在之前的例子中,glVertexAttribPointer干了两件事。

    1.它间接指定了含有属性数据信息的缓冲区与binding point 之间的关联

    2.它直接指定了数据的格式(类型,偏移量,间隔,等等)

    我们可以将其划分为各自的函数进行设定,这样,我们将会看的更加清晰一点。

    我们可以这样做:程序可以正常执行

    //以前的87行处

    glGenVertexArrays(1, &vaoHandle); glBindVertexArray(vaoHandle); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); //glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle); //glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindVertexBuffer(0, postionBufferHandle, 0, sizeof(GLfloat) * 3); glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); glVertexAttribBinding(0, 0); //glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle); //glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); glBindVertexBuffer(1, colorBufferHandle, 0, sizeof(GLfloat) * 3); glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, 0); glVertexAttribBinding(1, 1);

    这样就很清楚了

    从glBindVertexBuffer开始讲,我们之前提到过,同一个binding point可以绑定多个buffer缓冲区,比如,colorBufferHandle和positionBUfferHandle都与同一个binding point——GL_ARRAY_BUFFER建立映射(绑定)。其实呢,在opengl中binding point也有多个下标(通常标号为0~15)即16个,所以我们在这里通过binding index来建立VBO 缓冲区与binding point之间的映射。

    注意:这里的binding index是binding point的下标,比如GL_ARRAY_BUFFER一般有16个位置可供绑定,以支持同一个binding point 与多个buffer建立映射

    之前的location 设定的属性index与之不同,它代表的是glsl 属性量的访问代号

    所以,该函数第一个参数为binding index;第二个参数为VBO buffer(缓冲区),这里用VBO句柄代替;第三个参数为buffer起始位置与第一个数据之间的字节距离;

    第四个参数为连续两个数据元素之间的字节距离,这里的值不是0,而glVertexAttribPointer中的该参数为0,是因为:例如,position数据中,每一个数据元素为一个三维的坐标,我们让opengl去解析这个数据,后者提供了足够的信息,包括每个数据元素的维度,以及数据类型等共6个参数,而前者函数并没有提供数据元素的维度数,即没有说明每个数据元素有多大,所以需要提供每两个数据元素中间的字节距离,即3个float。

    这个函数就非常明确的实现了之前提到的第一件事,它指定了binding index,buffer,buffer中第一个数据的初始位置,以及每两个数据元素中间相隔的字节距离(或者可以认为是每个数据元素占多大,即如何解析数据),可以说是非常明确了

    glVertexAttribFormat,第一个参数为location值,即属性量的index,第二个为数据元素的维度,第三个为数据类型,第四个指是否需要规格化,第五个参数为相对偏移量

    glVertexAttribBinding,绑定binding index与属性量的index

    另请注意,顶点缓冲区绑定点(binding point)(由glBindVertexBuffer指定)的缓冲区绑定是VAO状态的一部分,与GL_ARRAY_BUFFER的绑定不同,后者不是。
    这个版本可以说更清晰易懂。 它消除了我们对VAO中管理的“不可见”指针的疑惑,并且glVertexAttribBinding使属性和缓冲区之间的关系更加清晰。 此外,它分离了真正不需要组合的问题。

    后面还有一些其他的技术,比如:绘制方面用到的glDrawElements 等等,之后测试完成在介绍给大家,今天就到这里啦。

     

  • 相关阅读:
    第三题 bfw在睡觉
    第二题 bfw和zhk的故事
    第一题 奶牛散步
    AC加油站7月比赛总结
    暑期机房联赛
    题解 P5663 【加工零件【民间数据】】
    题解 P1052 【过河】
    并发编程之进程
    网络编程socketserver
    网络编程之黏包
  • 原文地址:https://www.cnblogs.com/lv-anchoret/p/9523191.html
Copyright © 2020-2023  润新知