【问题标题】:Repeating OpenGL-es texture bound to hills in cocos2d 2.0在 cocos2d 2.0 中重复绑定到山丘的 OpenGL-es 纹理
【发布时间】:2012-12-17 13:57:08
【问题描述】:

原创文章

我正在尝试实现raywenderlich's tutorial 以使用 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);
}

我遇到的问题是:经过一定次数的重复后,纹理的质量开始下降,如下所示:

有什么方法可以让纹理重复而不退化?

编辑 1:

我已经对纹理如何退化进行了更多分析,结果发现它不会连续退化,而是以 2 次重复的功率退化,因此它在第一次重复时第一次退化,然后在 2 次重复后退化,然后是 4、8、16、32 等等……似乎每次图像质量下降时,在图像中可以看到的开始出现的垂直条带的宽度会加倍。同样在每次降级时,游戏的帧速率都会大大降低,所以我开始认为这可能是内存问题。

编辑 2:

到目前为止,我对为什么会发生这种情况的最佳猜测是因为地形的 -draw 方法不断生成 GL_TRAINGLE_STRIP,并且一旦它们离开屏幕就不会删除它们,导致地形的内存使用量增加,导致降级和帧率下降。

更新 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 度角出现,这导致它们在重复时无法对齐。我试图思考造成这种情况的原因,最后通过假设纹理坐标原点位于屏幕的左上角而不是纹理的左上角来修复它。

解决退化(种类)

我有一种预感,由于纹理的大量重复导致图像质量下降,原因与 this 类似,尽管我可能在这方面错了!

为了在 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 三角形时仍然存在内存问题

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

更新 2

我减小了每个三角形条带的宽度以尝试找出纹理重复处发生的情况,并发现由于某种原因该条带被整个背景纹理填充但反转了。经过一番思考后,我意识到这是因为这里的地板:int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth); 我们得到每次重复的最后一条带刚好超过纹理的宽度,但是当我们删除 1 而 xTex1 大于 1 时将此 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);

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

但是 _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元素没有在内存中被释放有关,所以我创建了一个separate question为此...但是,我仍在寻找解决纹理退化问题的方法!

【问题讨论】:

  • 我在网上找不到任何答案,也找不到任何人也尝试将其移植到 Cocos2D 2.0。那里一定有一些兴趣吗?使用 Cocos2D 1.0 的这种方法肯定会引起很大的兴趣......如果有人甚至有一个想法,请帮助:D
  • 这可能不是你的问题,但我认为你不应该在浮点数上使用增量运算符。
  • 非常好的点...希望天堂是我的问题!!
  • 看起来很可疑,好像您正试图将纹理坐标包裹在连续的 TRIANGLE_STRIP 中。
  • 很抱歉删除了以前的 cmets。我不想溢出cmets。你最后的代码 sn-p 是故意的吗?去掉它。这打破了你的三角形退化模式(你知道你正在退化你的三角形,不是吗?)而且我猜它会按照你展示它的方式创建一个三角形 =D

标签: objective-c opengl-es cocos2d-iphone opengl-es-2.0


【解决方案1】:

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

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

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

如果您需要在单个条带内的纹理坐标不连续并获得最大精度,则另一种方法是引入退化三角形,它们是零面积,因此不会被渲染,而您改变tex坐标。在继续之前,您可以通过重复顶点但使用调整后的 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;
}

【讨论】:

  • UV坐标是指存储在_hillVertices[]中的坐标吗? - 如果可以肯定地从这些值中取出值会导致纹理不与地形一起移动?因为它会使纹理在球上变得不稳定...... - 如果不是,我的 UV 坐标是什么意思?你能不能给我写一些代码来告诉我你的意思? :)
  • UV 坐标是指纹理坐标,即 _hillTexCoords[] 中的坐标。您只需减去 UV 坐标的整数部分。纹理从 0 -> 1 映射,并在该范围之外重复。
  • 我正在从 0 -> 1 映射纹理(请参阅更新 1)我可能只是累了::P 但我看不出与将纹理从 0 映射到 1 有什么不同我在做什么,你在说什么:/
  • 嗯,我想我可能已经让你真正考虑过了......给我一秒钟
  • 是的,基本上。在某些时候,有一个三角形,tex-coords miles 分开。
【解决方案2】:

这只是我的猜测...不是解决方案,而是可能的解释。

我试图追踪您是如何生成网格的。起初我以为你在做某种故意的退化(因为你正在使用三角形条,并且每 2 个三角形使用 4 个顶点)。但是您正在像这样创建网格,对吧?

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

这将创建三角形 123、[234]、345、[456]、567、[678]... 等等(注意 [] 表示反转)。所以,你看到你重叠了一半的三角形。我想最后一个三角形被看到了,另一个被隐藏了……如果 z 正好是 0,你就没有任何伪影。 当 4 = 7 和 2 = 5 时,即当您不修改纹理坐标时,一切正常,因为,一种或另一种方式减少到以下情况(如果您将其移置,这将是正确的做法-acbdef)。翻译成坐标——同一个坐标,同一个点是这样的:

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

在您发布的日志中,您一次写 4 个点,对吗?因此,在“for”的第 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...)绘制的那样。然后继续解决。 dirty 解决方案是退化邪恶三角形 345。那就是重复第 4 点一次(我现在有点头晕,可能是错的)-无论如何这不是一个好的解决方案,您正在混合方向和重叠三角形。每个 for 迭代你应该只添加

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

让我想一个干净的解决方案,或者让别人为我写 - 如果这确实是问题所在。

【讨论】:

  • 啊,是的,这解释了很多!恐怕我已将 JasonD 的答案标记为正确,因为他给了我一个更易于实施的解决方案。我自己解决这个问题的问题是我没有意识到我正在绘制三角形 123、[234]、345、[456]、567、[678] 正如你正确指出的那样,我有点假设它是在绘制 123,[ 234],567,[678] 等等,这就是为什么,我想,我错了!感谢您的帮助:)
  • 我很高兴能帮上忙。我知道这不是解决方案,只是对好奇的人的解释。事实上,你可以像我在这里说的那样重绘你的网格(一半的三角形受益),并像 JasonD 指出的那样设置纹理坐标。那将是一个几乎最佳的解决方案! ;)
猜你喜欢
  • 1970-01-01
  • 2011-05-19
  • 2014-07-14
  • 2012-01-16
  • 1970-01-01
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
相关资源
最近更新 更多