OpenGL ES (на iOS): EXC_BAD_ACCESS в glDrawArrays вызывает рисование сферы

#iphone #ios #ipad #opengl-es

#iPhone #iOS #iPad #opengl-es

Вопрос:

Мне нужна процедурно сгенерированная сфера OpenGL ES — я погуглил и обнаружил этот код OGL и решил, что он выглядит аккуратно, поэтому я бы изменил его на OGLES вместо этого. Я устранил glBegin (), glEnd (), доступные только для OGL, и изменил исходные drawtri и drawsphere на generatetri и generateSpherePoints. Эти новые функции присваивают значения GLfloat статическим массивам для нормалей и вершин сферы, что означает, что я могу затем вызвать новую функцию drawSphere () по своему усмотрению, и точки не нужно пересчитывать. Я также добавил параметры x, y, z в generateSpherePoints, позволяющие определять ненулевой центр сферы.

Проблема в том, что моя версия OGLES выдает EXC_BAD_ACCESS в XCode в строке glDrawArrays. С помощью отладчика я проверил массивы ‘sphereNormals’ и ‘sphereVertices’, и они выглядят хорошо заполненными, как хотелось бы. Я был бы очень признателен, если бы кто-нибудь мог подсказать, в чем может быть проблема, поскольку я сейчас понятия не имею!

 #define SX .525731112119133606 
#define SZ .850650808352039932

/* Drawing Sphere */
static GLfloat vdata[12][3] = {    
    {-SX, 0.0, SZ}, {SX, 0.0, SZ}, {-SX, 0.0, -SZ}, {SX, 0.0, -SZ},    
    {0.0, SZ, SX}, {0.0, SZ, -SX}, {0.0, -SZ, SX}, {0.0, -SZ, -SX},    
    {SZ, SX, 0.0}, {-SZ, SX, 0.0}, {SZ, -SX, 0.0}, {-SZ, -SX, 0.0} 
};

static GLuint tindices[20][3] = { 
    {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1},    
    {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3},    
    {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6}, 
    {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} };

void normalize(GLfloat *a) {
    GLfloat d=sqrt(a[0]*a[0] a[1]*a[1] a[2]*a[2]);
    a[0]/=d; a[1]/=d; a[2]/=d;
}

static const int ndiv = 5;
static const int vecSize = 3;
static const int isoVertexCount = 3;
static const int spherePointsSectionCount = 20;
static const int pointSectionSize = 9216;// value MUST be updated to be result of calcPointSectionSize(ndiv)
static const int pointCount = 184320;// value MUST be updated to be spherePointsSectionCount*pointSectionSize;
static const int sphereVertsCount = 61440; // value MUST be updated to be pointCount/vecSize;
static GLfloat spherePoints[184320]; // size MUST be updated to be pointCount value
static GLfloat sphereNormals[184320]; // size MUST be updated to be pointCount value

int calcPointSectionSize(int div) {
    return vecSize*isoVertexCount*powf(4.0f,(float)(div));
}

// OpenGL ES doesn't support glBegin(), glEnd() so must use glDrawArrays() instead
void generatetri(GLfloat *a, GLfloat *b, GLfloat *c, int div, float r, int pos) {
    if (div<=0) {
        int X = 0, Y = 1, Z = 2;
        sphereNormals[pos X] = a[X];
        sphereNormals[pos Y] = a[Y];
        sphereNormals[pos Z] = a[Z];
        spherePoints[pos X] = a[X]*r;
        spherePoints[pos Y] = a[Y]*r;
        spherePoints[pos Z] = a[Z]*r;
        sphereNormals[pos vecSize X] = b[X];
        sphereNormals[pos vecSize Y] = b[Y];
        sphereNormals[pos vecSize Z] = b[Z];
        spherePoints[pos vecSize X] = b[X]*r;
        spherePoints[pos vecSize Y] = b[Y]*r;
        spherePoints[pos vecSize Z] = b[Z]*r;
        sphereNormals[pos 2*vecSize X] = c[X];
        sphereNormals[pos 2*vecSize Y] = c[Y];
        sphereNormals[pos 2*vecSize Z] = c[Z];
        spherePoints[pos 2*vecSize X] = c[X]*r;
        spherePoints[pos 2*vecSize Y] = c[Y]*r;
        spherePoints[pos 2*vecSize Z] = c[Z]*r;
        /*
        glNormal3fv(a); glVertex3f(a[0]*r, a[1]*r, a[2]*r);
        glNormal3fv(b); glVertex3f(b[0]*r, b[1]*r, b[2]*r);
        glNormal3fv(c); glVertex3f(c[0]*r, c[1]*r, c[2]*r);
        */
    } else {
        GLfloat ab[3], ac[3], bc[3];
        for (int i=0;i<3;i  ) {
            ab[i]=(a[i] b[i])/2;
            ac[i]=(a[i] c[i])/2;
            bc[i]=(b[i] c[i])/2;
        }
        normalize(ab); normalize(ac); normalize(bc);
        const int pointSectionSize = calcPointSectionSize(div-1);
        generatetri(a, ab, ac, div-1, r, pos 0*pointSectionSize);
        generatetri(b, bc, ab, div-1, r, pos 1*pointSectionSize);
        generatetri(c, ac, bc, div-1, r, pos 2*pointSectionSize);
        generatetri(ab, bc, ac, div-1, r, pos 3*pointSectionSize);
    }  
}

void generateSpherePoints(float x, float y, float z, float radius) {
    for (int i=0;i<20;i  ) {
        GLfloat *va = vdata[tindices[i][0]];
        GLfloat *vb = vdata[tindices[i][1]];
        GLfloat *vc = vdata[tindices[i][2]];
        GLfloat a[3] = {va[0] x,va[1] y,va[2] z};
        GLfloat b[3] = {vb[0] x,vb[1] y,vb[2] z};
        GLfloat c[3] = {vc[0] x,vc[1] y,vc[2] z};
        generatetri(a, b, c, ndiv, radius, i*pointSectionSize);
    }
}

void drawSphere() {
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    glNormalPointer(GL_FLOAT, 0, sphereNormals);
    glVertexPointer(3, GL_FLOAT, 0, spherePoints);
    glDrawArrays(GL_TRIANGLES, 0, sphereVertsCount);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
}
  

Комментарии:

1. Код выглядит хорошо, возможно, вам было бы лучше использовать эквивалентный код iOS, который уже был протестирован: iphonedevelopment.blogspot.com/2009/05 /…

2. Спасибо, Джулио — так получилось, что я видел эту ссылку во время моего первоначального поиска кода OGL в Google, но избегал ее, поскольку автор утверждает, что производительность низкая. Я думаю, не имеет значения, какой метод я использую, пока я кэширую точки тем способом, который у меня есть. Я собираюсь устранить проблему с помощью NSZombie в соответствии с предложением cduhn, приведенным ниже.

Ответ №1:

EXC_BAD_ACCESS означает, что ваш код пытается получить доступ к адресу памяти, который ему не принадлежит. Наиболее распространенной причиной этого является ошибка в коде управления памятью, которая приводит к освобождению объекта, пока он все еще используется. Например, вашему коду, возможно, не удалось сохранить необходимый ему объект, или он, возможно, выпустил объект преждевременно.

В этом случае я бы предположил, что представление, к которому вы рисуете, возможно, было освобождено преждевременно. Но вместо того, чтобы гадать, вам следует использовать средство NSZombieEnabled, чтобы выяснить, к какому освобожденному объекту пытается получить доступ код. Смотрите раздел Руководства по разработке iOS, посвященный отладке приложений.

Комментарии:

1. Спасибо, конечно — на этот раз NSZombie вылетел у меня из головы, хотя я использовал его несколько раз раньше, спасибо, что указали на это. Стоит отметить, что его ручная спецификация в XCode — плохая идея, поскольку она оказывает значительное влияние на производительность, если вы забудете отключить ее при развертывании своего приложения — правильный способ использовать его в Xcode 4 — выбрать Profile вместо Run, затем выберите iOS Simulator -> All -> Zombies. В любом случае, я попробую и посмотрю, идентифицирует ли это проблему.

2. Полезно знать. Я не использовал его с Xcode 4. Спасибо за совет.