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

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

问题描述

ORIGINAL ARTICLE



我正在尝试实施 ...我仍然在,但寻找修复纹理退化问题!

解决方案

为地形的可见部分生成的三角形条纹将具有纹理坐标从左到右增加。



对于您的UV坐标,您需要从所有坐标中减去同一值在你的三角形条。一般来说,取最低坐标的整数部分(可能是你的情况下的最左边或第一个坐标),并从你生成的所有UV中减去它。



因此,如果屏幕左侧的U值为100.7,右侧具有129.5的U值,实际上想要输出范围从0.7到29.5的值。 (如果您仍然有精确度问题,您可能会通过使用负向协调将范围置于零上,从而更加尖锐)。



如果您需要在单个条带内具有不连续的纹理坐标,并且为了获得最大精度,是引入简并三角形,其为零区域,并且因此不被渲染,而您改变tex-coords。



根据上面的代码,我建议使用下面的代码:

  //这行应该每条只发生一次。 
float U_Off = floor(pt0.x / 512);

//内循环看起来像这样:
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;
}


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天全站免登陆