4

几年前,我为 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 错误——特别是其中两个,尽管它们可能是相关的。第一个很简单- 如果我尝试在设备上渲染我的模型(运行 5.0.1 的 iPhone 4S),它会显示,但如果我尝试在模拟器上显示它(运行 5.0 的 iPhone 模拟器),它会抛出 EXC_BAD_ACCESS在 glDrawElements 上。第二个,也很简单。我无法绘制多个网格。当我将模型绘制为一个大组(一个顶点数组/索引数组组合)时,它绘制得很好 - 但是当我将模型绘制为多个部分(例如,多次调用 drawElements)时,它会失败,并显示一个大黑屏 -黑度不是来自正在绘制的模型(我已经验证了这一点,如下所述)。

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

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

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

我使用的模型是一辆警车,有几千个顶点和面,并被分成多个部分(车身、灯光、窗户等)——车身大约有 3000 个面,窗户大约 100 个,灯光少了一点.

这里有一些事情要知道:

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

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

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

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

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


我的代码如下:

渲染循环:

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);
}
4

1 回答 1

8

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

关闭。

于 2012-05-11T16:18:05.660 回答