在 cocos2d 2.0 中重复绑定到山丘的 OpenGL-es 纹理 [英] Repeating OpenGL-es texture bound to hills in cocos2d 2.0

查看:17
本文介绍了在 cocos2d 2.0 中重复绑定到山丘的 OpenGL-es 纹理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原创文章

我正在尝试实现 虽然我可能在这方面错了!

为了在 resetHillVertices 中解决这个问题,我将其设置为 texCoords 始终介于 0 和 1 之间,这意味着绑定到山丘的纹理始终是纹理的第一次重复.我是这样实现的:

for (int j=1; j 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 三角形仍然存在内存问题

在这张照片中可以看到:正如您所见,帧速率急剧下降,并且在整个游戏过程中继续下降.

更新 2

我减小了每个三角形条的宽度以尝试找出纹理重复处发生的情况,并发现由于某种原因该条被整个背景纹理填充但反转了.经过一番思考,我意识到这是因为这里的地板: int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth); 我们得到了每次重复的最后一条刚好超过纹理的宽度,但是当我们删除 1 而 xTex1 大于 1 时,这会将这个 texCoords 设置为 0.02(或其他一些小数字),它实际上应该是 1.02(这很难理解,但是它是正确的).我认为这可以通过使用另一个 if 语句来解决:

float xTex0 = pt0.x/512;浮动 xTex1 = pt1.x/512;而 (xTex0 > 1.0) {xTex0 -= 1.0;}而(xTex1 > 1.0){xTex1 -= 1.0;}如果(xTex1 

这适用于该条带中的第一个三角形,但由于某些特殊原因,第二个三角形不起作用,我根本无法理解!它看起来像这样:

但是 _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 元素没有在内存中解除分配有关,所以我创建了一个 单独的问题...但是,我仍在寻找解决方法纹理退化问题!

解决方案

为地形的可见部分生成的三角形带将具有从左到右增加的纹理坐标.当这些变得非常大时,您将遇到精度问题.

对于您的 UV 坐标,您需要从三角带中的所有坐标中减去 same 值.通常,取最低坐标的整数部分(在您的情况下可能是最左边或第一个坐标),然后从您生成的所有 UV 中减去它.或者将其用作生成其他基线的基线,无论您喜欢哪一个.

因此,如果屏幕左侧的 U 值为 100.7,右侧的 U 值为 129.5,那么您实际上希望输出的值范围为 0.7 到 29.5.(如果您仍然有精度问题,您可能会通过将范围集中在零上,使用负坐标来增加一点).

如果您需要在单个条带内的纹理坐标中存在不连续性并获得最大精度,则另一种方法是引入退化三角形,它们是零面积,因此不会被渲染,而您改变tex坐标.在继续之前,您可以通过重复顶点但使用调整后的 tex-coords 来做到这一点.

根据您上面的代码,我建议如下:

//此行在每个条带中应该只出现一次.浮动 U_Off = 地板(pt0.x/512);//内部循环如下所示:for (int j=1; j

ORIGINAL ARTICLE

I am in the process of trying to implement raywenderlich's tutorial on generating hills with repeating striped coordinates using cocos2d, This article was written for Cocos2D 1.0, and as I am trying to port it to Cocos2D 2.0 This means updating it for openGl-es 2. So far everything has worked perfectly, However I am having problems with getting the texture of the hill to repeat properly...

Here is my code:

Sending the hills the texture:

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;

Generating texture:

-(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];

}

Getting TexCoords for bounding the stripes to the hill:

- (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];
    }

}

Drawing the texture:

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

The problem I'm having is this: after a certain number of repeats the texture starts to degrade in quality, like so:

Is there any way to get the texture to repeat without degradation?

EDIT 1:

I've veen doing more analysis into how the texture degrades, it turns out it doesn't do it continuously, but degrades with power of 2 repetitions so it degrades for the first time on the first repeat then after 2 repeats, then 4, 8, 16, 32 and so on... It also seems that the vertical bands that start to appear that can be seen in the image double in width each time the image degrades in quality. Also on each degradation the frame rate of the game decreases substantially so I'm starting to think this is probably a memory issue.

EDIT 2:

My best guess at why this is happening so far is because the -draw method for the terrain is continually making GL_TRAINGLE_STRIP, and not deleting them once they are off-screen causing a build up in the memory usage of the terrain, causing the degradation and frame rate drop.

UPDATE 1

I have solved two of the problems that were occurring with my texture generation...

Solving Misalignment

IN the sprite generation method this:

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

to this:

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

I realised that this was totally unrelated to the main error, rather it was due to my stripes for some reason not appearing at a 45 degree angle, which causes them to misalign on repeat. I tried to think of reasons for this, and finally fixed it by assuming that the textures coordinate origin was at the top left corner of the screen as opposed to the top left corner of the texture.

Solving Degradation (Kind of)

I had an inkling that the image degradation was occurring due to the large amounts of repetitions of the texture, due to a similar reason as this Although I may be wrong on that front!

To solve this in the resetHillVertices I set it up so the texCoords are always between 0 and 1 meaning that the texture bound to the hills is always the first repetition of the texture. I implemented this like so:

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

This almost fixed everything, the only two problems I still have are:

  • A few pixel columns between joins of textures are rendered strangely
  • There is still a memory issue with drawing the texPos and Pos triangles

These can be seen in this photo: As you can see the frame rate has dropped drastically and continues to do so all through the game.

UPDATE 2

I reduced the width of each triangle strip to try and find what was going on at the texture repeat, and found out that for some reason that strip was filled with the whole of the background texture but reversed. After a small amount of thinking I realised this was because due to flooring here: int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth); we get that the last strip for each repetition goes just past the width of the texture, however as we are remove 1 while xTex1 is greater than one this sets this texCoords to 0.02 (or some other small number) where it should actually be 1.02 (This is difficult to understand, however it is correct). I thought this could be solved by using another if statement like so:

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

This works fine for the first triangle in that strip, but not for the second for some peculiar reason, which I can't fathom at all! It looks like this:

However the setup within _hillTexCoords seems correct, when I set a break point within the app this is the result I get for the _hillTexCoords array, and it looks like it should be pinning the texture correctly, but it still isn't (Incredibly frustrating!)

[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)

It's easy to see that the overlap from one texture back to the start of the texture follows the same pattern as the others, but it still doesn't render correctly!

Update 3

It turns out that my memory issue is entirely unrelated to drawing using Opengl-es 2.0, it is in fact related to the box2D elements of my game not being de-allocated in the memory, so I have created a separate question for this... I am still however, looking for a fix to the texture degradation problem!

解决方案

The triangle strip you generate for the visible part of the terrain will have texture coordinates which increase from left to right. When these get very large, you will have precision issues.

For your UV coordinates, you need to subtract the same value from all the coordinates in your triangle strip. Generally, take the integer part of the lowest coordinate (probably the far-left or first coordinate in your case), and subtract that from all the UVs you generate. Or use it as a baseline to generate the others, whichever you prefer.

So if the left hand side of the screen has a U value of 100.7 and the right hand side has a U value of 129.5, you actually want to output values ranging from 0.7 to 29.5. (if you still have precision issues, you might squeak out a bit more by centring the range on zero, using negative co-ords).

The alternative, if you need to have a discontinuity in the texture coordinates within a single strip, and to get maximum precision, is to introduce degenerate triangles, which are zero-area, and thus don't get rendered, while you change the tex-coords. You do that by repeating vertices, but with the adjusted tex-coords, before continuing.

Based on your code above, I'd suggest something like this:

// 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;
}

这篇关于在 cocos2d 2.0 中重复绑定到山丘的 OpenGL-es 纹理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆