OpenGL ES 2.0 中简单三角形的问题

Trouble with Simple Triangle in OpenGL ES 2.0

我在绘制简单的三角形时遇到了问题。我在 Android 本机 Activity 中正确设置了 OpenGL ES 2.0,因为我可以执行 glClear() 并且它有效。

当我尝试绘制具有 3 个顶点的三角形时,它要么在调用 glDrawArrays 时崩溃,要么在我尝试执行 glDrawElements 时崩溃,但没有任何反应,但我收到以下消息:GLES_V2//GLESv2Imp.cpp:glDrawElements:669 错误 0x500

我查了一下,但我不认为我有任何无效的枚举,我已经检查了我的代码并试验了几天,但绘制调用不起作用。我还读到如果 glDrawArrays 试图访问错误的内存块,它会崩溃,但据我所知,我的设置是正确的。谁能帮我理解为什么它可能会崩溃?

这就是我的三角函数的调用方式。这部分只是一个模型

start()
{
  setTriangle();
}

renderLoop()
{
  drawTriangle();
}

这是我的着色器:

static const std::string triVertShader = "\
attribute vec4 vPosition;\
void main()\
{\
    gl_Position = vPosition;\
}\
";

static const std::string triFragShader = "\
    precision mediump float;\
    uniform vec4 vColor;\
    void main()\
    {\
        gl_FragColor = vColor;\
    }\
";

这是我编译它们和Link程序的地方。我没有包括我的检查,但着色器编译成功,程序链接成功:

GLuint LoadShaders(const std::string &vertexShader, const std::string &fragmentShader)
{
    GLint result = GL_TRUE;
    int infoLogLength;

    //Create the shaders
    GLuint vertShaderID = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragShaderID = glCreateShader(GL_FRAGMENT_SHADER);

    const char * vertShaderSource = vertexShader.data();
    const char * fragShaderSource = fragmentShader.data();

    //Compile Vertex Shader
    glShaderSource(vertShaderID, 1, &vertShaderSource, NULL);
    glCompileShader(vertShaderID);

    //Compile Fragment Shader
    glShaderSource(fragShaderID, 1, &fragShaderSource, NULL);
    glCompileShader(fragShaderID);

    //Link the program
    GLuint programID = glCreateProgram();
    glAttachShader(programID, vertShaderID);
    glAttachShader(programID, fragShaderID);

    glBindAttribLocation(programID, 0, "vPosition");

    glLinkProgram(programID);

   // glDeleteShader(vertShaderID);
   // glDeleteShader(fragShaderID);

    return programID;
}

这是我设置缓冲区对象的地方:

void setTriangle()
{
  glClearColor(0, 1, 0, 1);

  GLfloat triVerts[] = {  0.0f,  0.5f, 0.0f,
                         -0.5f, -0.5f, 0.0f,
                          0.5f, -0.5f, 0.0f };

  GLuint triVertBufferID;  
  glGenBuffers(1, &triVertBufferID); 

  glBindBuffer(GL_ARRAY_BUFFER, triVertBufferID); 

  glBufferData(GL_ARRAY_BUFFER,   
               sizeof(triVerts),  
               triVerts,          
               GL_STATIC_DRAW);   

  program = LoadShaders(triVertShader, triFragShader); //note: "program" is a global variable of type GLuint

}

这是绘图调用:

void drawTriangle()
{

  glClear(GL_COLOR_BUFFER_BIT);

  glUseProgram(program);

  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

  glEnableVertexAttribArray(0);   

  //glDrawElements(GL_TRIANGLES, 3, GL_FLOAT, (void*)0 );

  //glDrawArrays always crashes immediately
  glDrawArrays(GL_TRIANGLES, 0, 3);          

}

此调用无效:

glDrawElements(GL_TRIANGLES, 3, GL_FLOAT, (void*)0 );

参数GL_FLOAT不是索引数据的有效数据类型,GLES 2.x仅支持GL_UNSIGNED_BYTEGL_UNSIGNED_SHORT

但是,我认为您根本不打算使用 glDrawElements()。那是 indexed rendering。在您的代码中,您没有准备任何 GL_ELEMENT_ARRAY_BUFFER,也不需要。

您可能想使用

glDrawArrays(GL_TRIANGLES, 0, 3);

还要确保 triVertBufferIDdrawTriangle() 中调用 glVertexAttribPointer() 时仍然是当前绑定的 GL_ARRAY_BUFFER。目前,您只能在程序初始化时绑定它 setTriangle()。只要您从不将任何其他内容绑定到该缓冲区目标(或明确取消绑定),这就会起作用。如果您想稍后添加不同的对象,这将导致问题。

简而言之,您没有索引和颜色数据,也没有绑定作为顶点缓冲区制作的 VBO。如果您在片段着色器代码中取消颜色,则不需要颜色。放 gl_FragColor = vec(1.0,0.0,0.0,1.0);而是为了测试它是否运作良好。

已更新)

  1. 使局部顶点数组成为全局数组。

    GLfloat triVerts[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f };

  2. 在渲染循环中的函数中使用变量 triVertBufferID 绑定 vbo

  3. 当你使用 VBO 并且它是静态的(永远不会改变)时,不要每次都调用 glVertexAttribPointer(), glEnableVertexAttribArray()。你可以在setTriangle()中设置一次,你不确定它的handle id是否为'0'。你应该调用 glGetAttribLocation() 来了解它。

    当你不使用VBO时,每次用顶点数组的指针调用那些函数。

我发现有一些小问题导致您的应用无法呈现:

您已将 vPosition 属性声明为 vec4,但您正在使用没有足够的数据对其进行初始化。 OpenGL 将使用默认值填充缺失的数据,但这仍可能导致意外行为。

//call me after programID = loadShaders(...)
int vPosLoc = GLES20.getAttributeLocation(programID,"vPosition"); //pass me to EnableVertexAttribArray()!

正如您在初始化数据时所做的那样,在渲染时您需要绑定一个缓冲区以将其连接到着色器输入 - 所以:

glUseProgram(program);
glEnableVertexAttribArray(vPosLoc); //you use 0 for vPosLoc, which only works because you called glBindAttributeLocation()
glBindBuffer(GL_ARRAY_BUFFER, triVertBufferID); //triVertBufferID - make it a class member?
glVertexAttribPointer(vPosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); //you might need to change the second argument from 3 to 4, if you're gonna stick with vec4 for vPosition in the shader.

请注意,我将 vertexAttribPointer 和 enableVertexAttribArray() 中的 0 更改为 vPosLoc,只是为了使用变量而不是常量。

您(在您显示的代码中)从未设置 vColor 统一变量 - 您需要这样做,并且您需要查找其着色器位置:

//set me up right after loadShaders()
int vColorLoc = glGetUniformLocation(programID,"vColor");


//and later, in draw
glUniform4f(vColorLoc,1.0,0.0,0.0,1.0); //red!

现在,您应该可以调用 glDrawArrays() 了!