原著
私は、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 要素がメモリ内で割り当て解除されていないことに関連しているため、これについて別の質問を作成しました.. . しかし、私はまだ、テクスチャの劣化の問題の修正を探しています!