Подтвердить что ты не робот

Повторение текстуры OpenGL-es, связанной с холмами в cocos2d 2.0

ОРИГИНАЛЬНАЯ СТАТЬЯ

Я пытаюсь внедрить учебник raywenderlich о создании холмов с повторяющимися полосатыми координатами с использованием cocos2d. Эта статья была написана для Cocos2D 1.0, и поскольку я пытаюсь перенести его в Cocos2D 2.0, это означает его обновление для openGl-es 2. До сих пор все работало отлично, однако у меня возникают проблемы с тем, чтобы текстура холма правильно повторялась...

Вот мой код:

Отправка холмов текстуры:

CCSprite *stripes = [self stripedSpriteWithColor1:color3 color2:color4 textureSize:512 stripes:nStripes];
    stripes.position = ccp(winSize.width/2,winSize.height/2);
    ccTexParams tp2 = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_CLAMP_TO_EDGE};
    [stripes.texture setTexParameters:&tp2];
    _terrain.stripes = stripes;
    _backgroundTerrain.stripes = stripes;

Генерация текстуры:

-(CCSprite *)stripedSpriteWithColor1:(ccColor4F)c1 color2:(ccColor4F)c2 textureSize:(float)textureSize stripes:(int) nStripes {
    // 1: Create new CCRenderTexture
    CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureSize height:textureSize];

    // 2: Call CCRenderTexture:begin

    [rt beginWithClear:c1.r g:c1.g b:c1.b a:c1.a];

    // 3: Draw into texture
    //OpenGL gradient

    NSLog(@"Strip color is: %f : %f : %f", c2.r,c2.g,c2.b);

    CGPoint vertices[nStripes*6];
    ccColor4F colors[nStripes*6];
    int nVertices = 0;
    float x1 = -textureSize;
    float x2;
    float y1 = textureSize;
    float y2 = 0;
    float dx = textureSize / nStripes * 2;
    float stripeWidth = dx/2;
    ccColor4F stripColor = (ccColor4F){c2.r,c2.g,c2.b,c2.a};
    for (int i=0; i<nStripes; i++) {
        x2 = x1 + textureSize;
        colors[nVertices] = stripColor;
        vertices[nVertices++] = ccpMult(CGPointMake(x1, y1), CC_CONTENT_SCALE_FACTOR());
        colors[nVertices] = stripColor;
        vertices[nVertices++] = ccpMult(CGPointMake(x1+stripeWidth, y1), CC_CONTENT_SCALE_FACTOR());
        colors[nVertices] = stripColor;
        vertices[nVertices++] = ccpMult(CGPointMake(x2, y2), CC_CONTENT_SCALE_FACTOR());
        colors[nVertices] = stripColor;
        vertices[nVertices++] = vertices[nVertices-3];
        colors[nVertices] = stripColor;
        vertices[nVertices++] = vertices[nVertices-3];
        colors[nVertices] = stripColor;
        vertices[nVertices++] = ccpMult(CGPointMake(x2+stripeWidth, y2), CC_CONTENT_SCALE_FACTOR());
        x1 += dx;
    }


    [self.shaderProgram use];


    ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position  | kCCVertexAttribFlag_Color);

    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colors);

    glDrawArrays(GL_TRIANGLES, 0, (GLsizei)nVertices);

    //Gradient

    float gradientAlpha = 0.2;
    nVertices = 0;

    vertices[nVertices] = CGPointMake(0, 0);
    colors[nVertices++] = (ccColor4F){0,0,0,0};
    vertices[nVertices] = CGPointMake(textureSize, 0);
    colors[nVertices++] = (ccColor4F){0,0,0,0};
    vertices[nVertices] = CGPointMake(0, textureSize);
    colors[nVertices++] = (ccColor4F){0,0,0,gradientAlpha};
    vertices[nVertices] = CGPointMake(textureSize, textureSize);
    colors[nVertices++] = (ccColor4F){0,0,0,gradientAlpha};





    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colors);

    glDrawArrays(GL_TRIANGLE_STRIP,0, (GLsizei)nVertices);



    // Highlighting

    float borderWidth = textureSize/8;
    float borderAlpha = 0.1f;
    nVertices = 0;

    vertices[nVertices] = CGPointMake(0, 0);
    colors [nVertices++] = (ccColor4F){1,1,1,borderAlpha};
    vertices[nVertices] = CGPointMake(textureSize*CC_CONTENT_SCALE_FACTOR(),0);
    colors [nVertices++] = (ccColor4F){1,1,1,borderAlpha};

    vertices[nVertices] = CGPointMake(0, borderWidth*CC_CONTENT_SCALE_FACTOR());
    colors [nVertices++] = (ccColor4F){0,0,0,0};
    vertices[nVertices] = CGPointMake(textureSize*CC_CONTENT_SCALE_FACTOR(),borderWidth*CC_CONTENT_SCALE_FACTOR());
    colors [nVertices++] = (ccColor4F){0,0,0,0};

    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colors);
    glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);



    //Noise 
    CCSprite *noise = [CCSprite spriteWithFile:@"noise.png"];
    [noise setBlendFunc:(ccBlendFunc){GL_DST_COLOR, GL_ZERO}];
    noise.position = ccp(textureSize/2, textureSize/2);
    [noise visit];


    [rt end];
    // Return texture sprite
    return [CCSprite spriteWithTexture:rt.sprite.texture];

}

Получение TexCoords для ограничения полос на холм:

- (void)resetHillVertices {

    CGSize winSize = [CCDirector sharedDirector].winSize;

    static int prevFromKeyPointI = -1;
    static int prevToKeyPointI = -1;

    // key points interval for drawing
    while (_hillKeyPoints[_fromKeyPointI+1].x < _offsetX-winSize.width/self.scale) {
        _fromKeyPointI++;
    }
    while (_hillKeyPoints[_toKeyPointI].x < _offsetX+winSize.width*3/2/self.scale) {
        _toKeyPointI++;
    }

    if (prevFromKeyPointI != _fromKeyPointI || prevToKeyPointI != _toKeyPointI) {
        _nHillVertices = 0;
        _nBorderVertices = 0;
        CGPoint p0, p1, pt0, pt1;
        p0 = _hillKeyPoints[_fromKeyPointI];
        for (int i=_fromKeyPointI+1; i<_toKeyPointI+1; i++) {
            p1 = _hillKeyPoints[i];

            // triangle strip between p0 and p1
            int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth);
            float dx = (p1.x - p0.x) / hSegments;
            float da = M_PI / hSegments;
            float ymid = (p0.y + p1.y) / 2;
            float ampl = (p0.y - p1.y) / 2;
            pt0 = p0;
            _borderVertices[_nBorderVertices++] = pt0;
            for (int j=1; j<hSegments+1; j++) {
                pt1.x = p0.x + j*dx;
                pt1.y = ymid + ampl * cosf(da*j);
                _borderVertices[_nBorderVertices++] = pt1;

                _hillVertices[_nHillVertices] = CGPointMake(pt0.x, 0);
                _hillTexCoords[_nHillVertices++] = CGPointMake(pt0.x/512, 1.0f);
                _hillVertices[_nHillVertices] = CGPointMake(pt1.x, 0);
                _hillTexCoords[_nHillVertices++] = CGPointMake(pt1.x/512, 1.0f);

                _hillVertices[_nHillVertices] = CGPointMake(pt0.x, pt0.y);
                _hillTexCoords[_nHillVertices++] = CGPointMake(pt0.x/512, 0);
                _hillVertices[_nHillVertices] = CGPointMake(pt1.x, pt1.y);
                _hillTexCoords[_nHillVertices++] = CGPointMake(pt1.x/512, 0);

                pt0 = pt1;
            }

            p0 = p1;
        }

        prevFromKeyPointI = _fromKeyPointI;
        prevToKeyPointI = _toKeyPointI;
        [self resetBox2DBody];
    }

}

Рисование текстуры:

- (void) draw {


    self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTexture];
    CC_NODE_DRAW_SETUP();
    ccGLBlendFunc( CC_BLEND_SRC, CC_BLEND_DST ); //TB 25-08-12: Allows change of blend function

    ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position  | kCCVertexAttribFlag_TexCoords);

    ccGLBindTexture2D(_stripes.texture.name);
    // Assign the vertices array to the 'position' attribute
    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, _hillVertices);

    // Assign the texCoords array to the 'TexCoords' attribute
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, _hillTexCoords);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)_nHillVertices);
}

Проблема, с которой я столкнулась, заключается в следующем: после определенного количества повторений текстура начинает ухудшаться по качеству, например:

Degradation

Есть ли способ заставить текстуру повторять без ухудшения?

РЕДАКТИРОВАТЬ 1:

Я больше занимаюсь анализом того, как текстура деградирует, оказывается, она не делает ее непрерывно, но деградирует с мощностью 2 повторения, так что она впервые ухудшается при первом повторе, а затем после двух повторов, затем 4, 8, 16, 32 и т.д. Также кажется, что появляются вертикальные полосы, которые могут быть видны на изображении двойным по ширине при каждом ухудшении качества изображения. Также при каждом ухудшении частота кадров в игре значительно уменьшается, поэтому я начинаю думать, что это, вероятно, проблема с памятью.

ИЗМЕНИТЬ 2:

Мое лучшее предположение о том, почему это происходит до сих пор, состоит в том, что метод -draw для ландшафта постоянно делает GL_TRAINGLE_STRIP, а не удаляет их, когда они находятся вне экрана, вызывая нарастание памяти при использовании местности, вызывая снижение и снижение частоты кадров.

Degradation Doubling

ОБНОВЛЕНИЕ 1

Я решил две проблемы, связанные с генерацией текстур...

Решение проблемы несоответствия

В методе генерации спрайтов это:

float x1 = -textureSize;
float x2;
float y1 = textureSize;
float y2 = 0;
float dx = textureSize / nStripes * 2;

:

float x1 = -winSize.width;
float x2;
float y1 = winSize.height;
float y2 = 0;
float dx = winSize.width / nStripes * 2;

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

Решение проблемы деградации (вид)

У меня было подозрение, что деградация изображения происходила из-за большого количества повторений текстуры по той же причине, что и . Хотя я могу ошибаться на этом фронте!

Чтобы решить эту проблему в resetHillVertices, я настроил ее так, чтобы texCoords всегда находились между 0 и 1, что означает, что текстура, привязанная к холмам, всегда является первым повторением текстуры. Я реализовал это так:

for (int j=1; j<hSegments+1; j++) {
            pt1.x = p0.x + j*dx;
            pt1.y = ymid + ampl * cosf(da*j);
            _borderVertices[_nBorderVertices++] = pt1;
            float xTex0 = pt0.x/512;
            float xTex1 = pt1.x/512;
            while (xTex0 > 1) { // makes sure texture coordinates are always within the first repetition of texture
                xTex0 -= 1;
            }
            while (xTex1 > 1) {
                xTex1 -= 1;
            }
            _hillVertices[_nHillVertices] = CGPointMake(pt0.x, 0);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 1.0);
            _hillVertices[_nHillVertices] = CGPointMake(pt1.x, 0);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 1.0);

            _hillVertices[_nHillVertices] = CGPointMake(pt0.x, pt0.y);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 0.0);
            _hillVertices[_nHillVertices] = CGPointMake(pt1.x, pt1.y);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 0.0);

            pt0 = pt1;
        }

Это почти фиксированное все, только две проблемы, которые у меня есть:

  • Несколько столбцов пикселей между объединениями текстур отображаются странно
  • По-прежнему сохраняется проблема с памятью при рисовании треугольников texPos и ​​Pos

Это можно увидеть на этой фотографии: как вы можете видеть, частота кадров резко упала и продолжает делать это всю игру.

Still Have Memory Issue

ОБНОВЛЕНИЕ 2

Я уменьшил ширину каждой полосы треугольника, чтобы попытаться найти то, что происходило при повторении текстуры, и выяснил, что по какой-то причине полоска была заполнена всей текстурой фона, но была отменена. После небольшого количества размышлений я понял, что это объясняется тем, что из-за пола здесь: int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth); мы получаем, что последняя полоса для каждого повторения проходит мимо ширины текстуры, однако, поскольку мы удаляем 1, а xTex1 больше, чем один, этот устанавливает это texCoords в 0.02 (или какое-то другое небольшое число), где оно должно быть действительно 1.02 (это трудно понять, однако это правильно). Я думал, что это можно решить, используя другой оператор if:

float xTex0 = pt0.x/512;
            float xTex1 = pt1.x/512;
            while (xTex0 > 1.0) {
                xTex0 -= 1.0;
            }
            while (xTex1 > 1.0) {
                xTex1 -= 1.0;
            }

            if (xTex1 < xTex0) {
                xTex1++;
            }


            _hillVertices[_nHillVertices] = CGPointMake(pt0.x, 0);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 1.0);
            _hillVertices[_nHillVertices] = CGPointMake(pt1.x, 0);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 1.0);

            _hillVertices[_nHillVertices] = CGPointMake(pt0.x, pt0.y);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 0.0);
            _hillVertices[_nHillVertices] = CGPointMake(pt1.x, pt1.y);
            _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 0.0);

Это отлично работает для первого треугольника в этой полосе, но не для второго по какой-то своеобразной причине, которую я не могу понять абсолютно! Это выглядит так: enter image description here

Однако настройка в _hillTexCoords кажется правильной, когда я устанавливаю точку прерывания в приложении, это результат, который я получаю для массива _hillTexCoords, и похоже, что он должен правильно фиксировать текстуру, но это все еще не ( Невероятно расстраивает!)

[44]    CGPoint (x=0.804036,y=1)
[45]    CGPoint (x=0.873047,y=1)
[46]    CGPoint (x=0.804036,y=0)
[47]    CGPoint (x=0.873047,y=0)
[48]    CGPoint (x=0.873047,y=1)
[49]    CGPoint (x=0.939453,y=1)
[50]    CGPoint (x=0.873047,y=0)
[51]    CGPoint (x=0.939453,y=0)
[52]    CGPoint (x=0.939453,y=1)
[53]    CGPoint (x=1.00586,y=1)
[54]    CGPoint (x=0.939453,y=0)
[55]    CGPoint (x=1.00586,y=0)
[56]    CGPoint (x=0.00585938,y=1)
[57]    CGPoint (x=0.0722656,y=1)
[58]    CGPoint (x=0.00585938,y=0)
[59]    CGPoint (x=0.0722656,y=0)
[60]    CGPoint (x=0.0722656,y=1)
[61]    CGPoint (x=0.13737,y=1)
[62]    CGPoint (x=0.0722656,y=0)
[63]    CGPoint (x=0.13737,y=0)

Легко видеть, что перекрытие от одной текстуры до начала текстуры следует тому же шаблону, что и другие, но оно по-прежнему не отображается правильно!

Обновление 3

Оказывается, что проблема с памятью полностью не связана с рисованием с использованием Opengl-es 2.0, на самом деле это связано с тем, что элементы box2D моей игры не выделяются в памяти, поэтому я создал отдельный вопрос для этого... Тем не менее, я все же ищу исправление проблемы деградации текстуры!

4b9b3361

Ответ 1

Треугольная полоса, которую вы создаете для видимой части ландшафта, будет иметь координаты текстуры, которые увеличиваются слева направо. Когда они становятся очень большими, у вас будут проблемы с точностью.

Для ваших UV-координат вам нужно вычесть одно и то же значение из всех координат в вашей треугольной полосе. Как правило, возьмите целочисленную часть нижней координаты (возможно, далекую левую или первую координату в вашем случае) и вычтите из всех генерируемых UV. Или используйте его в качестве базовой линии для создания других, в зависимости от того, что вы предпочитаете.

Итак, если левая сторона экрана имеет значение U 100.7, а правая сторона имеет значение U 129,5, вы действительно хотите выводить значения в диапазоне от 0,7 до 29,5. (если у вас все еще есть проблемы с точностью, вы можете пискнуть немного больше, центрируя диапазон на ноль, используя отрицательные координаты).

Альтернативой, если вам нужно иметь разрыв в координатах текстуры в пределах одной полосы и получить максимальную точность, заключается в том, чтобы ввести вырожденные треугольники, которые являются нулевыми, и, следовательно, не получать визуализацию, тогда как вы измените tex-coords. Вы делаете это, повторяя вершины, но с настроенными текс-коордами, прежде чем продолжить.

Основываясь на вашем коде выше, я бы предложил что-то вроде этого:

// This line should happen only once per-strip. 
float U_Off = floor(pt0.x / 512);

// The inner loop then looks like this:
for (int j=1; j<hSegments+1; j++) {
    pt1.x = p0.x + j*dx;
    pt1.y = ymid + ampl * cosf(da*j);
    _borderVertices[_nBorderVertices++] = pt1;
    float xTex0 = pt0.x/512 - U_Off;
    float xTex1 = pt1.x/512 - U_Off;

    _hillVertices[_nHillVertices] = CGPointMake(pt0.x, 0);
    _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 1.0);
    _hillVertices[_nHillVertices] = CGPointMake(pt1.x, 0);
    _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 1.0);

    _hillVertices[_nHillVertices] = CGPointMake(pt0.x, pt0.y);
    _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 0.0);
    _hillVertices[_nHillVertices] = CGPointMake(pt1.x, pt1.y);
    _hillTexCoords[_nHillVertices++] = CGPointMake(xTex1, 0.0);

    pt0 = pt1;
}

Ответ 2

Это только моя догадка... Не решение, а возможное объяснение.

Я попытался проследить, как вы генерируете свою сетку. Сначала я, хотя вы делали какую-то преднамеренную дегенерацию (поскольку вы используете полосу треугольника и используя 4 вершины для каждых 2 треугольников). Но вы создаете свою сетку, как это, не так ли?.

3 ___ 4-7___ 8
| \    | \   |
|  \   |  \  |
1 ___ 2-5 __ 6

Это создает треугольники 123, [234], 345, [456], 567, [678]... и т.д. (примечание [] означает обратное). Итак, вы видите, что вы перекрываете половину треугольников. Я предполагаю, что последний треугольник нарисован, а другой скрыт... при условии, что z ровно 0, у вас нет артефакта. Когда 4 = 7 и 2 = 5, то есть когда вы не изменяете координату texutre, все работает нормально, так как это так или иначе сводится к следующему (что было бы правильным способом сделать это, если вы его настроите - acbdef). Переведено в координаты - та же самая координата, то же самое он делает так:

c ___ d ___ f
| \   | \   |
|  \  |  \  |
a ___ b ___ e

В журнале, который вы опубликовали, вы пишете 4 балла за раз, не так ли? Итак, в 13-м цикле "для" вы создаете эти вершины: (14 * 3 = 52):

    [52]    CGPoint (x=0.939453,y=1) //1
    [53]    CGPoint (x=1.00586,y=1)  //2
    [54]    CGPoint (x=0.939453,y=0) //3
    [55]    CGPoint (x=1.00586,y=0)  //4

    [56]    CGPoint (x=0.00585938,y=1) //5

Это делает 5 треугольников. В частности, здесь интересны 2: [234] и 345. 234 нормально, но 345 должно скрывать его (потому что оно появляется после другого). Попробуйте использовать функцию проверки глубины, такую ​​как GL_GREATER, чтобы избежать ее визуализации и видеть если я прав на этом). Ну, 345 не прав, он отображает текстуру с x = 0.07 до 1.00. Таким образом, даже если вы исправили 234, 345 по-прежнему нарисован над вашим правильным треугольником. Это должно объяснить ваш фоном обратную вещь.

Итак, я думаю, что это проблема (не произойдет, если вы не нормализуете свои координаты текстуры, как в учебнике).

Должен ли я все-таки написать решение?:/

Начнем с того, что я предлагаю вам создать вашу сетку, как то, что я рисую на втором месте (a, b, c...). Затем продолжите решение. Решение грязное должно было бы дезартировать злой треугольник 345. Это было бы повторение пункта 4 один раз (теперь я немного головокружение, может быть неправильно). Во всяком случае, это нехорошее решение, вы смешиваете ориентацию и перекрывающиеся треугольники. Для каждой итерации вы должны добавлять только

        _hillVertices[_nHillVertices] = CGPointMake(pt0.x, 0);
        _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 1.0);

        _hillVertices[_nHillVertices] = CGPointMake(pt0.x, pt0.y);
        _hillTexCoords[_nHillVertices++] = CGPointMake(xTex0, 0.0);

Позвольте мне подумать о чистом решении, или пусть кто-нибудь напишет это для меня. Предоставление этого на самом деле является проблемой.