OpenGL的Draw函数

转载自:http://blog.csdn.net/patient16/article/details/50540011

前言


初学OpenGL时会发现各种各样的Draw*函数,每种Draw*的功能和适合使用场景是什么,在这里做一下整理。对于老式的Draw(OpengGL1,2的glBegin)不做讨论,其实理解OpenGL3,4的方法就够了。

1.图元类型


Draw是用于画图元的,这些图元包括的类型有:

基本图元 包含类型
Point Point list
Line Line list, Line strip, Line loop
triangle Triangle list, Triangle strip, Triangle fan

假设Draw输入了N个点结合它们的拓扑结构看,这些图元类型特点很好理解: 
这里写图片描述

  • list:独立的图元
    取每k个点作为一个单独的图元(point,k=1;line k=2;triangle k=3),每个图元没有公共顶点,如N=8时,能画出8个point,4条line;N=9时,画出3个triangle。
  • strip:相连着的图元,line和triangle才有。
    1)对于line,前面2个顶点组成一条线后,后面的每个顶点都与其前一个顶点组成一条线,因此共N-1条线;
    2)对于triangle,前面3个顶点组成一个三角形后,后面的每个顶点与其前两个顶点组成一个三角形,因此共N-2个三角形
  • Line loop:类似Line strip,但首尾两个顶点也构成一条线。
  • Triangle fan:有一个公共顶点,前面三个顶点组成一个三角形后,后面的顶点于前一个顶点及公共顶点组成新的三角形。

谈谈strip的特点

  • 当模型由许多相连的三角形构成时,使用strip较于list可以显著减少带宽。
  • triangle strip或line strip相邻两个图元会有重合的顶点或线,但渲染时不会重复画像素。
  • triangle strip的所有三角形的绕向都是一致,这样保证了整个strip都被cull或都不被cull。
  • 当渲染模式为flat shading时,一个三角形内部所有像素将被渲染成同一种颜色,颜色默认来自三角形的最后一个顶点(也可通过glProvokingVertex选择第一个顶点)。因此结合上一个特点,可知strip中每个三角形分别为:(0,1,2),(2,1,3),(2,3,4),(4,3,5),(4,5,6)……flat shading的颜色分别来自顶点2,3,4,5,6……通过一个实验验证,定义6个顶点,其颜色分别为红,绿,蓝,红,绿,蓝。

    void init()
    {
        glViewport(0,0,128,128);
    
        glGenVertexArrays(NumVAOs, VAOs);//生成VAO
        glBindVertexArray(VAOs[Triangles]);//绑定VAO
        GLfloat positions[NumVertex][2]={//6个顶点的位置坐标
            {0.0f, 32.0f},
            {16.0f, 0.0f},
            {32.0f, 32.0f},
            {48.0f, 0.0f},
            {64.0f,  32.0f},
            {80.85f, 0.0f}
        };
        GLfloat colors[NumVertex][4]={//6个顶点的Color
            {1.0f, 0.0f, 0.0f, 1.0f},
            {0.0f, 1.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f, 1.0f},
            {1.0f, 0.0f, 0.0f, 1.0f},
            {0.0f, 1.0f, 0.0f, 1.0f},
            {0.0f, 0.0f, 1.0f, 1.0f},
        };
    
        glGenBuffers(NumBuffers, Buffers);//生成VBO
        glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);//绑定VBO
        glBufferData(GL_ARRAY_BUFFER, sizeof(positions)+sizeof(colors), NULL, GL_STATIC_DRAW);//为VBO分配大小刚好填充position和color数据的空间
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), positions);//填充position数据
        glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(colors), colors);//填充color数据
    
        ShaderInfo shaders[]={
            {GL_VERTEX_SHADER,"triangles.vert"},
            {GL_FRAGMENT_SHADER,"triangles.frag"},
            {GL_NONE,NULL}
        };
    
        GLuint program = LoadShaders(shaders);//编译,链接vs和fs
        glUseProgram(program);
    
        int vPosition_loc = glGetAttribLocation(program, "vPosition");
        int vColor_loc = glGetAttribLocation(program, "vColor");
        //cout<<vPosition_loc<<" "<<vColor_loc<<endl;
        glVertexAttribPointer(vPosition_loc, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));//将VBO中的position数据plumbing到vs
        glEnableVertexAttribArray(vPosition_loc);
        glVertexAttribPointer(vColor_loc, 4, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(positions));//将VBO中的color数据plumbing到vs
        glEnableVertexAttribArray(vColor_loc);
    }
    

    当按正常方式渲染时,vertex Shader的代码如下:

    #version 110
    in vec4 vPosition;
    in vec4 vColor;
    out vec4 vs_fs_color;
    void main()
    {
        gl_Position.x = (vPosition.x-64)/64;
        gl_Position.y = (vPosition.y-64)/64;
        gl_Position.zw = vPosition.zw;
        vs_fs_color = vColor;
    }
    

    fragment shader:

    #version 110
    out vec4 fColor;
    in vec4 vs_fs_color;
    void main()
    {
        fColor = vs_fs_color;
    }
    

    程序中将viewport设为(0,0,128,128),VS中的变换是为了抵消viewport transform,让vertex buffer中的position即屏幕坐标(原点位于Render Target的左下角)。结果为
    这里写图片描述

再试试使用flat shading,vertex shader:

    #version 110
    in vec4 vPosition;
    in vec4 vColor;
    flat out vec4 vs_fs_color;
    void main()
    {
        gl_Position.x = (vPosition.x-64)/64;
        gl_Position.y = (vPosition.y-64)/64;
        gl_Position.zw = vPosition.zw;
        vs_fs_color = vColor;
    }

fragment shader:

    #version 110
    out vec4 fColor;
    flat in vec4 vs_fs_color;
    void main()
    {
        fColor = vs_fs_color;
    }

这里使用flat qualifier,指定color为flat shading,注意vs的输出和vs的对应输入要同时指定为flat,结果为:
这里写图片描述

可见每个三角形都以最后一个顶点的颜色做flat shading。

2.OpengGL的Draw函数


2.1认识两种最基本的Draw函数

  • glDrawArrays(GLenum mode, GLint first, GLsizei count)
  • glDrawElements(GLenum mode, GLsizei count, GLenum type, GLvoid* indices)

DrawArrays和DrawElements是两种最基本的Draw函数。

2.1.1 glDrawArrays

为最常用的Draw函数,按mode指定的图元类型,画出vertex buffer中第first个起的count个顶点。

2.1.2 glDrawElements

使用glDrawElements前,除了类似glDrawArrays一样准备好VAO和VBO外,还需要产生并绑定EBO(Element Buffer Object),注入的数据为一个类型为type的数组,其元素表示vertex buffer中顶点索引。glDrawElement以按照指定的图元类型mode,以其前count个元素作为VBO中顶点数据的索引。

init函数

void init()
{
    glViewport(0,0,128,128);

    glGenVertexArrays(NumVAOs, VAOs);
    glBindVertexArray(VAOs[Triangles]);
    GLfloat positions[NumVertex][2]={
        {0.0f, 32.0f},
        {16.0f, 0.0f},
        {32.0f, 32.0f},
        {48.0f, 0.0f},
        {64.0f,  32.0f},
        {80.85f, 0.0f}
    };
    GLfloat colors[NumVertex][4]={
        {1.0f, 0.0f, 0.0f, 1.0f},
        {0.0f, 1.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f},
        {0.0f, 1.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f},
    };

    GLushort indices [] =
    {
        0,1,2,1,2,3,2,3,4,3,4,5
    };

    glGenBuffers(1, EBOs);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glGenBuffers(NumBuffers, Buffers);
    glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(positions)+sizeof(colors), NULL, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), positions);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(colors), colors);

    ShaderInfo shaders[]={
        {GL_VERTEX_SHADER,"triangles.vert"},
        {GL_FRAGMENT_SHADER,"triangles.frag"},
        {GL_NONE,NULL}
    };

    GLuint program = LoadShaders(shaders);
    glUseProgram(program);

    int vPosition_loc = glGetAttribLocation(program, "vPosition");
    int vColor_loc = glGetAttribLocation(program, "vColor");
    //cout<<vPosition_loc<<" "<<vColor_loc<<endl;
    glVertexAttribPointer(vPosition_loc, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
    glEnableVertexAttribArray(vPosition_loc);
    glVertexAttribPointer(vColor_loc, 4, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(positions));
    glEnableVertexAttribArray(vColor_loc);
}

dispaly函数

void dispaly(void)
{
    //glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);//LAST_VERTEX_CONVENTION
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(VAOs[Triangles]);
    //glDrawArrays(GL_TRIANGLE_STRIP, 0, NumVertex);
    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, NULL);
    glFlush();
}

结果为: 
这里写图片描述

由此,DrawElements同样用6个顶点画出了4个三角形,用DrawArrays则需要12个顶点,省下得不仅是存储顶点数据的buffer。每个顶点都要经由Shader的计算以及Raster的相关处理,当pipeline中cache一定量的已处理顶点并根据index做击中测试时,DrawElements将4个三角形所需要的计算和数据带宽降低到4个顶点(当然还要加上index所占带宽)。
在display函数中,DrawElements的最后一个参数indices并未使用。这里的indices一般在旧式中用到。通常是先用glEnableClientState开启 
GL_VERTEX_ARRAY,当vertex array enble后,使用数组indices直接调用。

2.2基本Draw函数的变种

2.2.1 *Instanced

变种类型 变种函数
*Instanced DrawArraysInstanced

glDrawArraysInstanced(GLenum mode, GLsizei first, GLsizei count, GLsizei primcount)为例进行说明。
当vertex buffer中的顶点会被多个图元频繁使用时,DrawElements比DrawArrays更为高效;
当需要将一个模型多次渲染,并且每次具有不同的位置或某种属性时,DrawArraysInstanced为更为有效简洁。 
在应用中有时需要多次画同一个模型,但每次该模型的部分属性有所不同。例如画楼群,森林时,可以使用同一个模型,每次为该模型赋予不同的坐标变换,颜色等等。这样需要调用多次Draw函数,每次使用新的属性数据等。
DrawArraysInstanced便是为这种需要准备的,它接收5个参数,前4个参数与DrawArrays一致。我们将其中指定的count个顶点构成的模型称为一个Instance,第5个参数primcount表示要重复画多少个instance。使用glVertexAttribDivisor(GLuint index, GLuint divisor)将序数为index的属性指定为instance属性,每隔divisor个instance,vertex shader中注进buffer一个新的属性值。 
在init函数中多添加一句代码:glVertexAttribDivisor(vColor_loc, 1);

glVertexAttribPointer(vColor_loc, 4, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(positions));//将VBO中的color数据plumbing到vs
glEnableVertexAttribArray(vColor_loc);
glVertexAttribDivisor(vColor_loc, 1);

并修改display函数

void dispaly(void)
{       
    //glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(VAOs[Triangles]);
    //glDrawArrays(GL_TRIANGLE_STRIP, 0, NumVertex);
    //glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, NULL);
    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, NumVertex, 5);
    glFlush();
}

及vertex shader:

#version 410
in vec4 vPosition;
in vec4 vColor;
out vec4 vs_fs_color;
void main()
{
    gl_Position.x = (vPosition.x + gl_InstanceID*20-64)/64;
    gl_Position.y = (vPosition.y+ gl_InstanceID*20-64)/64;
    gl_Position.zw = vPosition.zw;
    vs_fs_color = vColor;
}

对于Instance drawing,在vertex shader中gl_InstanceID表示每个顶点的Instance序数,这里使用之作简单地偏移,将不同Instance的位置区分开来。 
结果如下: 
这里写图片描述 
如果用glVertexAttribDivisor(vColor_loc, 2); 
则结果为 
这里写图片描述

2.2.2 *BaseVertex

用于扩展DrawElements,当根据indices作为索引从vertex buffer中读取数据时,有时希望允许一定数目顶点的偏移——例如当vertex buffer中存着动画的多帧,需要按照一定的偏移取每一帧的数据进行渲染。该偏移通过一个额外的参数GLint basevertex指定。

2.2.3 *BaseInstance

用于扩展Draw*Instanced,通过一个额外的参数GLint baseInstance指定按照baseInstace作为偏移从buffer中取出instance属性的数据。

2.2.4 *BaseInstanceBaseVertex

同时具有前面两种作用。

2.2.5 *Indirect

变种类型 变种函数
*Indiret DrawArraysIndiret

* DrawArraysIndiret具有DrawArraysInstanced一样的参数及功能,只不过其参数间接从类型为GL_DRAW_INDIRECT_BUFFER的buffer中读出。 
* DrawElementsIndiret具有DrawElementsInstanceBaseVertex一样的参数及功能,只不过其参数间接从buffer中读出。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值