适用于iOS的OpenGL ES 2.0 - 多次调用glDrawElements导致EXC_BAD_ACCESS

时间:2012-05-11 02:25:41

标签: iphone ios opengl-es opengl-es-2.0 3d

几年前,我为OpenGL ES 1.1和iPhone编写了一个小型Cocoa / Obj-C游戏框架。当iOS 3.x受欢迎时,这又回来了。我的OpenGL ES 1.1 / iOS 3.x实现的这一切都运行良好。时间过去了,现在我们用iOS 5.1,OpenGL ES 2.0,ARC,块和其他东西。我认为现在是将项目移植到更现代标准的时候了。

编辑:解决了我自己的一个问题 - 这就是它在模拟器上崩溃的原因。排序 - 我现在能够绘制较小的模型,但较大的模型(如测试警车)仍会导致EXC_BAD_ACCESS - 即使这是对glDrawElements的唯一单次调用。我还能够在模拟器上修复绘制多个网格 - 但是,我不知道这是否会在明天早上在设备上运行。 (我的5.0测试设备是我朋友的iPhone,不要)。所以我想主要的问题是,为什么较大的模型会在模拟器上造成EXC_BAD_ACCESS?

以下原帖

然而,在将它移动到5.0时,我遇到了一些OpenGL ES 2.0错误 - 其中两个,特别是,尽管它们可能是相关的。 第一个是简单的 - 如果我尝试在设备上渲染我的模型(iPhone 4S运行5.0.1),它会显示,但如果我尝试在模拟器上显示它(iPhone模拟器正在运行) 5.0),它在glDrawElements上抛出一个EXC_BAD_ACCESS。 第二个,也很简单。我无法绘制多个网格物体。当我将模型绘制为一个大组(一个顶点数组/索引数组组合)时,它绘制得很好 - 但是当我将模型绘制为多个部分(例如,多次调用drawElements)时,它会失败,并显示一个大黑屏 - 黑色与正在绘制的模型(我已经验证了这一点,如下所述)。

在更详细的部分之前总结一下,试图在模拟器上渲染我的模型崩溃

警告:一切都适用于小网格。即使在模拟器上,我一遍又一遍地绘制我的小型静态声明立方体也没有问题。当我说静态声明时,我的意思是一个硬编码的const结构数组,它被绑定并加载到顶点缓冲区中,并且GLushorts的const数组绑定并加载到索引数组中。

注意:当我说' model'我的意思是整体模型,可能由多个顶点和索引缓冲区组成。在代码中,这意味着模型只包含一个网格数组或模型组。网格或模型组是模型的子单元,例如模型的一个连续部分,具有一个顶点数组和一个索引数组,并且还存储两者的长度。在我使用的模型的情况下,汽车的车身是一个网格,窗户是另一个,灯光是第三个。总之,他们构成了模型。

我使用的模型是一辆警车,有几千个顶点和面,并分成多个部分(车身,灯,窗等) - 车身约有3000个面,车窗约100个,车灯少一点。

以下是一些需要了解的事项:

  1. 我的模型正确加载。我已经用两种方式证实了这一点 - 打印出模型顶点并手动检查它们,以及 如2)中所述,单独显示每个模型组。我发布了图片,但声誉限制了#39;这是我的第一个问题,我不能。我还从头开始重新构建模型加载器两次,没有任何变化,所以我知道顶点和索引缓冲区的顺序/格式正确。

  2. 当我将模型加载为单个模型组(即一个顶点)时 缓冲区/索引缓冲区)它正确显示整个模型。当我 将模型加载为多个模型组,并显示任何给定的模型组 单独的模型组,它正确显示。当我试图画画 多个模型组(多次调用glDrawElements)的大 黑屏发生。

  3. 黑色屏幕不是因为正在绘制模型。 我 通过更改我的片段着色器来绘制每个像素来验证这一点 红色无论如何。我总是将颜色缓冲区清除为中灰色(我也明确清除深度缓冲区),但尝试绘制多个网格/模型组结果在黑色的屏幕上。我们知道模型不是简单地模糊视图,因为它是黑色而不是红色。这发生在设备上,我不知道在模拟器上会发生什么,因为我无法将其绘制。

  4. 我的模型不会在模拟器中绘制。它不会绘制为单个网格/模型组,也不会绘制多个网格/模型组。应用程序正确加载,但是 尝试绘制网格/模型组会导致EXC_BAD_ACCESS glDrawElements。回溯的相关部分是:

     thread #1: tid = 0x1f03, 0x10b002b5, stop reason = EXC_BAD_ACCESS (code=1, address=0x94fd020)
        frame #0: 0x10b002b5
        frame #1: 0x09744392 GLEngine`gleDrawArraysOrElements_ExecCore + 883
        frame #2: 0x09742a9b GLEngine`glDrawElements_ES2Exec + 505
        frame #3: 0x00f43c3c OpenGLES`glDrawElements + 64
        frame #4: 0x0001cb11 MochaARC`-[Mesh draw] + 177 at Mesh.m:81
    

    编辑:它始终能够绘制较小的动态创建模型(~100个面),但整个模型中的3000个

  5. 我能够让它渲染一个更小,更简单但仍然动态加载的模型,该模型由192个面/ 576个顶点组成。我能够将它显示为单个顶点和索引缓冲区,以及分割成部分并呈现为多个较小的顶点和索引缓冲区。尝试在模拟器中绘制单网格模型导致仍然抛出EXC_BAD_ACCESS,但仅在第一帧上抛出。如果我强行继续它,它会显示一个非常搞砸的模型,然后在每一帧之后,它显示出100%的精确度。

  6. 我的着色器没有错误。当我使用一个小的,静态声明的顶点缓冲区时,它们可以正确编译和显示。但是,为了完整起见,我会将它们发布在底部。


  7. 我的代码如下:

    渲染循环:

    glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        //muShader is a subclass of a shader-handler I've written that tracks the active shader
        //and handles attributes/uniforms
        //[muShader use] just does glUseProgram(muShader.program); then
        //disables the previous shader's attributes (if needed) and then
        //activates its own attributes - in this case:
        //it does:
        //    glEnableVertexAttribArray(self.position);
        //    glEnableVertexAttribArray(self.uv);
        //where position and uv are handles to the position and texture coordinate attributes
    [self.muShader use];
    
    GLKMatrix4 model = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(_rotation), 0, 1, 0);
    GLKMatrix4 world = GLKMatrix4Identity;
    GLKMatrix4 mvp = GLKMatrix4Multiply(_camera.projection, _camera.view);
    mvp = GLKMatrix4Multiply(mvp,world);
    mvp = GLKMatrix4Multiply(mvp, model);
    
        //muShader.modelViewProjection is a handle to the shader's model-view-projection matrix uniform
    glUniformMatrix4fv(self.muShader.modelViewProjection,1,0,mvp.m);
    
    glActiveTexture(GL_TEXTURE0); 
    glBindTexture(GL_TEXTURE_2D, self.policeTextureID);
        //ditto on muShader.texture
    glUniform1i(self.muShader.texture, 0);
    
    for(int i=0; i < self.policeModel.count; i++)
    {
            //I'll expand muShader readyForFormat after this
        [self.muShader readyForFormat:ModelVertexFormat];
            //I'll expand mesh draw after this
        [[self.policeModel meshAtIndex:i] draw];
    }
    

    muShader的东西

    muShader绑定属性和制服

    我不会发布整个muShader的课程,这是不必要的,足以说它有效,或者它根本不显示任何东西。

    //here is where we bind the attribute locations when the shader is created
    -(void)bindAttributeLocations
    {
        _position = glGetAttribLocation(self.program, "position");
        _uv = glGetAttribLocation(self.program, "uv");
    }
    //ditto for uniforms
    -(void)bindUniformLocations
    {
        _modelViewProjection = glGetUniformLocation(self.program, "modelViewProjection");
        _texture = glGetUniformLocation(self.program, "texture");
    }
    

    muShader readyForFormat

    -(void)readyForFormat:(VertexFormat)vertexFormat
    {
        switch (vertexFormat)
        {
            //... extra vertex formats removed for brevity
            case ModelVertexFormat:
    
                //ModelVertex is a struct, with the following definition:
                //typedef struct{
                //    GLKVector4 position;
                //    GLKVector4 uv;
                //    GLKVector4 normal;
                //}ModelVertex;
    
                glVertexAttribPointer(_position, 3, GL_FLOAT, GL_FALSE, sizeof(ModelVertex), BUFFER_OFFSET(0));
                glVertexAttribPointer(_uv, 3, GL_FLOAT, GL_FALSE, sizeof(ModelVertex), BUFFER_OFFSET(16));
                break;
            //... extra vertex formats removed for brevity
        }
    }
    

    网格物品

    设置顶点/索引缓冲区

    //this is how I set/create the vertex buffer for a mesh/model-group
    //vertices is a c-array of ModelVertex structs
    //    created with malloc(count * sizeof(ModelVertex))
    //    and freed using free(vertices) - after setVertices is called, of course
    -(void)setVertices:(ModelVertex *)vertices count:(GLushort)count
    {
        //frees previous data if necessary
        [self freeVertices];
        glGenBuffers(1, &_vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(ModelVertex) * count, vertices, GL_STATIC_DRAW);
        _vertexCount = count;
    
    }
    //this is how I set/create the index buffer for a mesh/model-group
    //indices is a c-array of GLushort,
    //    created with malloc(count * sizeof(GLushort);
    //    and freed using free(vertices) - after setVertices is called, of course
    -(void)setIndices:(GLushort *)indices count:(GLushort)count
    {
        [self freeIndices];
        glGenBuffers(1, &_indexBuffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort) * count, indices, GL_STATIC_DRAW);
        _indexCount = count;
    }
    

    网格绘制

    //vertexBuffer and indexBuffer are handles to a vertex/index buffer
    //I have verified that they are loaded properly
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
    glDrawElements(GL_TRIANGLES, _indexCount, GL_UNSIGNED_SHORT, 0);
    

    着色器

    顶点着色器

    attribute highp vec4 position;
    attribute lowp vec3 uv;
    
    varying lowp vec3 fragmentUV;
    
    uniform highp mat4 modelViewProjection;
    uniform lowp sampler2D texture;
    
    void main()
    {
        fragmentUV = uv;
    
        gl_Position = modelViewProjection * position;
    }
    

    片段着色器

    varying lowp vec3 fragmentUV;
    
    uniform highp mat4 modelViewProjection;
    uniform lowp sampler2D texture;
    
    void main()
    {
        gl_FragColor = texture2D(texture,fragmentUV.xy);
        //used below instead to test the aforementioned black screen by setting
        //every pixel of the model being drawn to red
        //the screen stayed black, so the model wasn't covering the whole screen or anything
        //gl_FragColor = vec4(1,0,0,1);
    }
    

1 个答案:

答案 0 :(得分:8)

自己回答,当使用多个缓冲区对象时,每次绑定顶点/索引缓冲区对象时都必须调用glEnableVertexAttribArray,而不是每帧一次(每个着色器)。这是所有问题的原因,包括模拟器崩溃。

关闭。