13

原著

私は、cocos2d を使用して縞模様の座標を繰り返すことで丘を生成するraywenderlich のチュートリアルを実装しようとしている最中です。これまでのところ、すべてが完璧に機能していますが、丘のテクスチャを適切に繰り返すのに問題があります...

これが私のコードです:

丘にテクスチャを送る:

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 倍の幅になっているようです。また、劣化のたびにゲームのフレームレートが大幅に低下するため、これはおそらくメモリの問題であると考え始めています.

編集2:

なぜこれがこれまでに起こっているのかについての私の最善の推測は、テレインの -draw メソッドが継続的に GL_TRAINGLE_STRIP を作成し、オフスクリーンになったら削除せず、テレインのメモリ使用量を増やし、劣化とフレームレート低下。

劣化倍増

更新 1

テクスチャ生成で発生していた 2 つの問題を解決しました...

位置ずれの解決

スプライト生成メソッドでこれ:

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

これでほぼすべてが修正されましたが、まだ残っている2つの問題は次のとおりです。

  • テクスチャの結合間のいくつかのピクセル列が正しくレンダリングされない
  • texPos および Pos 三角形の描画にはまだメモリの問題があります。

これらはこの写真で見ることができます: ご覧のとおり、フレーム レートが大幅に低下し、ゲーム全体を通して低下し続けています。

まだメモリの問題がある

更新 2

各三角形ストリップの幅を縮小して、テクスチャの繰り返しで何が起こっているかを調べてみたところ、何らかの理由でそのストリップが背景テクスチャ全体で埋められていたのに反転していたことがわかりました。少し考えた後、これはフローリングが原因であることに気付きました:int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth);各繰り返しの最後のストリップがテクスチャの幅をちょうど超えていることがわかりますが、xTex1 が 1 より大きいときに 1 を削除すると、この texCoords が設定されます実際には 1.02 になるはずの 0.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);

これは、そのストリップの最初の三角形ではうまく機能しますが、2 番目の三角形では、私にはまったく理解できない奇妙な理由で機能しません! 次のようになります。ここに画像の説明を入力

ただし、_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)

1 つのテクスチャからテクスチャの開始点までのオーバーラップが他のテクスチャと同じパターンに従っていることは簡単にわかりますが、それでも正しくレンダリングされません!

アップデート 3

私のメモリの問題は、Opengl-es 2.0 を使用した描画とはまったく関係がないことが判明しました。実際には、ゲームの box2D 要素がメモリ内で割り当て解除されていないことに関連しているため、これについて別の質問を作成しました.. . しかし、私はまだ、テクスチャの劣化の問題の修正を探しています!

4

2 に答える 2

6

地形の可視部分用に生成した三角形ストリップには、左から右に増加するテクスチャ座標があります。これらが非常に大きくなると、精度の問題が発生します。

UV 座標については、三角形ストリップのすべての座標から同じ値を減算する必要があります。一般に、最も低い座標の整数部分 (おそらく、この場合は左端または最初の座標) を取得し、生成するすべての 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;
}
于 2012-12-26T22:38:16.957 に答える
4

これは私の推測です...解決策ではありませんが、考えられる説明です。

メッシュの生成方法を追跡しようとしました。最初は、何らかの意図的な縮退を行っていたと思います (三角形のストリップを使用し、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 はそれを隠しているに違いありません (他の後にレンダリングするためです。?)私がこれに正しければ)。345 は正しくありません。テクスチャを x= 0.07 から 1.00 にマップします。したがって、234 を修正しても、345 は正しい三角形の上に描画されます。それはあなたの背景が逆になったことを説明するはずです。

それで、それが問題だと思います(チュートリアルのようにテクスチャ座標を正規化しないと起こりません)。

まだ解決策を書く必要がありますか?? :/

まず、2 番目に描いたようなメッシュ (a、b、c...) を生成することをお勧めします。次に、解決策を続行します。汚い解決策は、悪の三角形 345 を退化させることです。それは、ポイント 4 を 1 回繰り返すことです (私は今、少しめまいがします。間違っている可能性があります)- とにかく、これは良い解決策ではありません。三角形。反復ごとに追加するだけです

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

きれいな解決策を考えるか、誰かに書いてもらいましょう - これが実際に問題であることを条件に。

于 2012-12-27T00:12:50.943 に答える