GLSL(6)分别使用可编程渲染管线编程和固定渲染管线编程来画点(理解VAO&VBO)

来源:互联网 发布:高中物理网络课程 编辑:程序博客网 时间:2024/06/05 20:22

因为之前在用c++写ray tracing(在CPU上跑),后来因为在CPU上跑太慢了,于是又上cuda,但是用cuda实现完成到30%,各种不能理解的错误,我这个cuda新手也是被折腾的惨,代码调试也很苦难。于是打算后面用compute shader来实现ray tracing。所以最近在狂学opengl和glsl。目前是glsl新手,希望各位多多指教。

不得不承认opengl的API设计比较复杂,至少我是这么感觉,花了一些时间来理它的脉络。

一定要清楚的一点是,在opengl中,可编程渲染管线的编程结构和固定渲染管线方式下的code结构其实差别蛮大的。

假设我们要使用glsl,也就是要使用可编程渲染管线来实现如下效果:

使用可编程渲染管线的coding方式,应该怎么做呢?

首先,我们要知道,可编程,主要是指,我们可以编写自己的顶点着色器和片段着色器,而这两个着色器是分别对应着渲染管线中的两个阶段,我们现在可以操纵这两个阶段,所以叫做可编程渲染管线。OK。

顶点着色器里面可以定义很多的顶点属性,比如顶点的坐标、颜色、法线等等,这些属性下的数据叫做vertex attribute data.这个很重要。和vertex attribute data挂钩的又出现了传说中的VAO、VBO。

哇,要理解好多东西!确实如此!

这两个又是什么鬼??-_-....

VAO:vertex array object.

VBO:vertex buffer object.

这两个有啥区别?分别是干嘛的?

首先要明确的是,VAO,VBO都和vertex shader有关系,你如果要在opengl中使用glsl来实现你想要的效果,那么你必定会接触到这两个东西,也就是说,你要使用和VAO,VBO相关的函数。而在固定渲染管线中不会用到这些函数,这里就是一个界限!这一点很重要。

要在opengl中使用glsl来画图,需要这样来组织代码(理解并记住这个结构很重要):

initialization:

for each batch:

generate,store,and bind a VAO,

bind all the buffers needed for a draw call.

unbind the VAO.

main loop/whenever you render:

for each batch

bind VAO

glDrawArrays(...);or gl DrawElements(...);

unbind VAO.

问:VAO、VBO和shader的关系是啥?

答:VAO stores data about vertex attribute locations。很好理解,VAO保存的是数据的location。当你调用glVertexAttribPointer(...)的时候,VAO就记住了当前VBO绑定的是啥东西,当在执行渲染的时候,VAO就会从VBO中获取 vertex attribute data。即便当前VBO没被绑定!

其实,当我们在调用glUseProgram()这个函数的时候,就表明告诉opengl,我现在已经从固定渲染管线切换到可编程渲染管线了,我要用glsl来搞事情。不能同时既搞fixed又搞programable。

还要注意的是,一次只能将一个target绑定到VAO!你要使用谁,你就绑定谁,把它调到前台来drawarray。

关于VBO。

VBO是一个buffer object。它作为vertex attribute data的数据来源。我们可以通过以下方法来让一个VBO做为vertex attribute data的数据源:

首先,一定要将这个VBO绑定到GL_ARRAY_BUFFER上,然后,调用glVertexAttribPointer(index,....)(如果对这些函数不理解,可以看我之前的文章),调用这个函数,就指定,vertex shader中layout 中index索引处的data的数据来自绑定到GL_ARRAY_BUFFER上的VBO。当这个函数被调用的时候,这个关系就被确定下来了.。假设在这句函数后面再调用glBindBuffer(0),这时是不是我们的数据源就被切断了呢(因为之前是glBindBuffer(VBO),现在这个VBO变成0了)?不是得,源头还是不变!要改变这个,必须要调用glVertexAttribPointer(index,....),理解这个很重要。

VAO和VBO的区别:VAO stores all of the state needed to supply data.VBO才是真正的存储着数据。

好了,按照上面结构,我们用glsl实现上图的效果,代码如下:

void InitGL(int argc,char* argv[]){    glutInit(&argc,argv);          glEnable(GL_DEBUG_OUTPUT);    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE ); //设置显示方式,RGB、双缓冲    glutInitWindowPosition(100,100);   //位置    glutInitWindowSize(WIN_WIDTH,WIN_HEIGHT);//窗口大小    glutCreateWindow("GLSL FUN!");  //创建窗口,设置标题    glutDisplayFunc(renderScene);  // 当绘制窗口时调用myDisplay    glutIdleFunc(renderScene);    glutReshapeFunc(changeSize);    glutKeyboardFunc(processNormalKeys);    glClearColor(0.0,0.0,0.0,0.0);//清除颜色设置为白色,这样glclear后面clear color就用白色clear    glewInit();    projection = glm::perspective( glm::radians(50.0f),(float)1024/1024,1.0f,100.0f);}void InitPosArrayandColorArray(){    float dx = 1.0f/(float)WIN_WIDTH;    float dy = 1.0f/(float)WIN_HEIGHT;    int index=0;    for(int i = 0; i < WIN_WIDTH; i++)        for(int j = 0; j < WIN_HEIGHT; j++)        {            float screenPosX = (float)i * dx * 2 - 1;            float screenPosY = (float)j * dy * 2 - 1;            int add1 = index + 1;            int add2 = index + 2;            positionData[ index ]    = screenPosX;            positionData[ add1 ] = screenPosY;            positionData[ add2 ] = .0f;            colorData[ index ] = screenPosX;            colorData[ add1 ] = screenPosY;            colorData[ add2 ] = .2f;            index+=3;        }}
}void InitBuffer(){    GLuint vboHandles[2];    glGenBuffers(2,vboHandles);    GLuint positionBufferHandle = vboHandles[0];    GLuint colorBufferHandle = vboHandles[1];    //    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);    glBufferData(GL_ARRAY_BUFFER, TOTAL_POINTS_NUM * sizeof(float), positionData, GL_STATIC_DRAW);    //    glBindBuffer(GL_ARRAY_BUFFER,colorBufferHandle);    glBufferData(GL_ARRAY_BUFFER, TOTAL_POINTS_NUM * sizeof(float), colorData, GL_STATIC_DRAW);    //VAO    glGenVertexArrays(1,&vaoHandle);    glBindVertexArray(vaoHandle);    //    glEnableVertexAttribArray(0);    glEnableVertexAttribArray(1);    //    glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(GLubyte*)NULL);    glBindBuffer(GL_ARRAY_BUFFER, colorBufferHandle);    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,(GLubyte*)NULL);    //    glBindVertexArray(0);}
void renderScene(void){    glClear(GL_COLOR_BUFFER_BIT);       glBindVertexArray(vaoHandle);    glDrawArrays(GL_POINTS,0,TOTAL_POINTS_NUM);    glBindVertexArray(0);    glutSwapBuffers();}int main(int argc,char* argv[]){    //1.    InitGL(argc,argv);    InitPosArrayandColorArray();    //2.steshader其实也属于initScene的一部分,只是下个阶段而已    setShaders();    //after linking,use the following steps to assign data to uniform block in the frag shader   // InitUniformBlockBuffer();    InitBuffer();   // LogGLVersion();     //4.    glutMainLoop();  //消息循环    //    //check gl version    return 0;}
虽然效果很简单,但是理解材才是最重要的。

下面是着色器的代码:

顶点着色器:

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

片单着色器:

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

如果是使用固定管线的话,就很简单了,伪代码如下:

glPointSize(1.0f);

glBegin(GL_POINTS);

for(i=0; i < winwidth;i++)

for(int j = 0; j  < winheight ; j++)

{

glcolor(....);

glvertex3f(x,y,0);

}

glend();