glDrawArrays 行为异常

3
我花了相当一段时间学习openGL的固定管线,最近开始学习可编程管线。我知道我的painter和shader类不是问题,因为它们可以与固定管线的东西一起工作。我似乎无法让glDrawArrays正常工作。

我不确定我的错误是在如何设置顶点缓冲对象、我的着色器还是其他地方。我也调试了我的代码,并在display函数中设置了断点,它似乎永远无法通过glDrawArrays()(即它在glDrawArrays处触发断点,但在之后没有任何断点被触发,不知道为什么)。

输出的只是一个白屏,什么都没有。

这是我的代码:

float   vertices[] = {   0.75,  0.75, 0.0, 1.0,
                         0.75, -0.75, 0.0, 1.0,
                        -0.75, -0.75, 0.0, 1.0 };

GLuint  vertexBufferObject;
GLuint  positionLocation;
GLuint  vaoObject;

void initVertexBuffer(GLuint& vertexBufferObject, float* vertexData, unsigned int size, GLenum GL_DRAW_TYPE)
{
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, size, vertexData, GL_DRAW_TYPE);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void main(int argc, char* argv[])
{

    painter.initEngine(argc, argv, 500, 500, 0, 0, "2D3D");
    painter.initGlutFuncs(display, resize, Input::MouseButtonClick,   Input::MouseDrag, keyboard);

    defaultShader.init("default.vert", "default.frag"); 
    defaultShader.link();

    initVertexBuffer(vertexBufferObject, vertices, sizeof(vertices), GL_STATIC_DRAW);
    glGenVertexArrays(1, &vaoObject);
    glBindVertexArray(vaoObject);
    positionLocation = glGetAttribLocation(defaultShader.id(), "position");

    painter.startMainLoop();
}

void display()
{
    painter.clearDisplay();

    defaultShader.bind();

    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glEnableVertexAttribArray(positionLocation);
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
    std::cout << Framework::glErrorCheck() << std::endl;

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(positionLocation);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    defaultShader.unbind();

    painter.flushAndSwapBuffers();
}

顶点着色器:

#version 140

in vec4 position; 

void main()
{   
    gl_Position = position; 
}

片元着色器:

#version 140

out vec4 outColor;

void main()
{
    outColor = vec4(1.0, 0.0, 1.0, 1.0);
}

编辑:代码已经更新,采纳了Joey Dewd、keltar和genpfault的建议。 现在我不再卡在glDrawArrays这里了,也就是说不再出现白屏,但是出现了黑屏。这让我觉得我的缓冲区可能仍然没有正确设置。或者,我可能还缺少其他用于顶点数组缓冲区初始化(vaoObject)的东西?


我认为你的问题在于你没有在绘图命令中使用顶点属性数组。如果我没记错的话,可编程管线要求在使用glDrawArraysglDrawElements等调用之前设置VAO。 - Joey Dewd
@JoeyDewd 谢谢,这解决了我的程序在glDrawArrays上挂起的问题,但三角形仍然没有动作。 - hbdavehb
@hdavehb:我相信你已经接近成功了,但是目前你的顺序有误。正确的顺序应该是:1. 绑定VAO对象。2. 绑定缓冲区并设置其数据和VertexAttribPointers。 VAO存储了所有绑定到它的VAO的缓冲区命令。这意味着,一旦你绑定了VAO,任何后续的VBO/EBO和指针调用都将被存储,因此下次需要绘制时,你只需要调用glBindVertexArray(VAO)即可。尝试先绑定VAO,然后再进行后续的缓冲区和属性指针调用。 - Joey Dewd
2个回答

4
void initVertexBuffer(GLuint vertexBufferObject, float* vertexData, GLenum GL_DRAW_TYPE)
{
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_DRAW_TYPE);
                                  ^^^^^^^^^^^^^^^^^^ nnnnope
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

在这个上下文中,sizeof(vertexData)并不是你所期望的那样。

它可能是4或8,取决于你是否使用64位系统。也就是说,它是指指向浮点数的指针的sizeof,而不是sizeof(vertices)

你需要传入一个单独的大小参数:

void initVertexBuffer(GLuint& vertexBufferObject, float* vertexData, unsigned int size, GLenum GL_DRAW_TYPE)
{
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, size, vertexData, GL_DRAW_TYPE);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

...

void main(int argc, char* argv[])
{
    ...
    initVertexBuffer(vertexBufferObject, vertices, sizeof(vertices), GL_STATIC_DRAW);
    ...    
}

或者一个连续的(重要!)标准容器,比如std::vector

template< typename Container >
void initVertexBuffer(GLuint& vertexBufferObject, const Container& vertexData, GLenum GL_DRAW_TYPE)
{
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof( typename Container::value_type ), &vertexData[0], GL_DRAW_TYPE);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

完整示例:

#include <GL/glew.h>
#include <GL/freeglut.h>

#include <iostream>
#include <vector>
using namespace std;

struct Program
{
    static GLuint Load( const char* vert, const char* geom, const char* frag )
    {
        GLuint prog = glCreateProgram();
        if( vert ) AttachShader( prog, GL_VERTEX_SHADER, vert );
        if( geom ) AttachShader( prog, GL_GEOMETRY_SHADER, geom );
        if( frag ) AttachShader( prog, GL_FRAGMENT_SHADER, frag );
        glLinkProgram( prog );
        CheckStatus( prog );
        return prog;
    }

private:
    static void CheckStatus( GLuint obj )
    {
        GLint status = GL_FALSE;
        if( glIsShader(obj) ) glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
        if( glIsProgram(obj) ) glGetProgramiv( obj, GL_LINK_STATUS, &status );
        if( status == GL_TRUE ) return;
        GLchar log[ 1 << 15 ] = { 0 };
        if( glIsShader(obj) ) glGetShaderInfoLog( obj, sizeof(log), NULL, log );
        if( glIsProgram(obj) ) glGetProgramInfoLog( obj, sizeof(log), NULL, log );
        std::cerr << log << std::endl;
        exit( -1 );
    }

    static void AttachShader( GLuint program, GLenum type, const char* src )
    {
        GLuint shader = glCreateShader( type );
        glShaderSource( shader, 1, &src, NULL );
        glCompileShader( shader );
        CheckStatus( shader );
        glAttachShader( program, shader );
        glDeleteShader( shader );
    }
};

#define GLSL(version, shader) "#version " #version "\n" #shader


void initVertexBuffer(GLuint& vertexBufferObject, float* vertexData, unsigned int size, GLenum GL_DRAW_TYPE)
{
    glGenBuffers(1, &vertexBufferObject);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
    glBufferData(GL_ARRAY_BUFFER, size, vertexData, GL_DRAW_TYPE);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

GLuint prog;
GLuint vaoObject;
void init()
{
    const char* vert = GLSL
    (
        140,
        in vec4 position; 
        void main()
        {   
            gl_Position = position; 
        }
    );

    const char* frag = GLSL
    (
        140,
        out vec4 outColor;
        void main()
        {
            outColor = vec4(1.0, 0.0, 1.0, 1.0);
        }
    );

    prog = Program::Load( vert, NULL, frag );
    glUseProgram( prog );

    glGenVertexArrays(1, &vaoObject);
    glBindVertexArray(vaoObject);

    float vertices[] = 
    {   
        0.75,  0.75, 0.0, 1.0,
        0.75, -0.75, 0.0, 1.0,
        -0.75, -0.75, 0.0, 1.0 
    };
    GLuint vertexBufferObject;
    initVertexBuffer(vertexBufferObject, vertices, sizeof(vertices), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);

    GLuint positionLocation = glGetAttribLocation(prog, "position");
    glEnableVertexAttribArray(positionLocation);
    glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram( prog );
    glBindVertexArray(vaoObject);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glutSwapBuffers();
}

int main(int argc, char **argv)
{
    glutInit( &argc, argv );
    glutInitContextVersion( 3, 1 );
    glutInitContextProfile( GLUT_COMPATIBILITY_PROFILE );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutInitWindowSize( 600, 600 );
    glutCreateWindow( "GLUT" );

    glewExperimental = GL_TRUE;
    glewInit();

    init();

    glutDisplayFunc( display );
    glutMainLoop();
    return 0;
}

谢谢,你发现得真好,我完全会忽略这个。不过很遗憾它并没有解决我的问题。 - hbdavehb
@hbdavehb:正如keltar所指出的那样,您需要通过引用而不是值传递vertexBufferObject。编辑以添加必要的“&”符号。 - genpfault
2
谢谢!我非常感激你写了一个完整的例子,它连同@JoeyDewd的建议帮助我意识到我的缓冲区/顶点数组对象的绑定/初始化顺序混乱了。再次感谢。 - hbdavehb
不客气!将来我建议您首先使用完整的程序(可以随意重用它!),因为OpenGL的状态性可能会使得仅凭代码片段很难弄清楚正在发生的事情。 - genpfault

2
glGenBuffers(1, &vertexBufferObject);

将顶点缓冲区的id保存在本地变量中。这个值在绘制调用中不再可用,并且不会修改全局的vertexBufferObject(但缓冲区仍然存在 - 您只是失去了它的id,无法再使用它。甚至无法销毁它)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接