Note that there are some explanatory texts on larger screens.

plurals
  1. PORepeating OpenGL-es texture bound to hills in cocos2d 2.0
    primarykey
    data
    text
    <h1>ORIGINAL ARTICLE</h1> <p>I am in the process of trying to implement <a href="http://www.raywenderlich.com/3888/how-to-create-a-game-like-tiny-wings-part-1" rel="nofollow noreferrer">raywenderlich's tutorial</a> 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...</p> <p>Here is my code:</p> <p>Sending the hills the texture:</p> <pre><code>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:&amp;tp2]; _terrain.stripes = stripes; _backgroundTerrain.stripes = stripes; </code></pre> <p>Generating texture:</p> <pre><code>-(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&lt;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]; } </code></pre> <p>Getting TexCoords for bounding the stripes to the hill: </p> <pre><code>- (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 &lt; _offsetX-winSize.width/self.scale) { _fromKeyPointI++; } while (_hillKeyPoints[_toKeyPointI].x &lt; _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&lt;_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&lt;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]; } } </code></pre> <p>Drawing the texture:</p> <pre><code>- (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); } </code></pre> <p>The problem I'm having is this: after a certain number of repeats the texture starts to degrade in quality, like so:</p> <p><img src="https://i.stack.imgur.com/unlMB.png" alt="Degradation"></p> <p>Is there any way to get the texture to repeat without degradation?</p> <h1>EDIT 1:</h1> <p>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. </p> <h1>EDIT 2:</h1> <p>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.</p> <p><img src="https://i.stack.imgur.com/qm6JZ.png" alt="Degradation Doubling"></p> <h1>UPDATE 1</h1> <p>I have solved two of the problems that were occurring with my texture generation...</p> <h2>Solving Misalignment</h2> <p>IN the sprite generation method this: </p> <pre><code>float x1 = -textureSize; float x2; float y1 = textureSize; float y2 = 0; float dx = textureSize / nStripes * 2; </code></pre> <p>to this: </p> <pre><code>float x1 = -winSize.width; float x2; float y1 = winSize.height; float y2 = 0; float dx = winSize.width / nStripes * 2; </code></pre> <p>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.</p> <h2>Solving Degradation (Kind of)</h2> <p>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 <a href="https://stackoverflow.com/questions/11370074/opengl-es-2-0-texture-distortion-on-large-geometry-gl-repeat/11371462#comment19333122_11371462">this</a> Although I may be wrong on that front!</p> <p>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:</p> <pre><code>for (int j=1; j&lt;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 &gt; 1) { // makes sure texture coordinates are always within the first repetition of texture xTex0 -= 1; } while (xTex1 &gt; 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; } </code></pre> <p>This almost fixed everything, the only two problems I still have are:</p> <ul> <li>A few pixel columns between joins of textures are rendered strangely </li> <li>There is still a memory issue with drawing the texPos and Pos triangles</li> </ul> <p>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.</p> <p><img src="https://i.stack.imgur.com/lpRxf.png" alt="Still Have Memory Issue"></p> <h1>UPDATE 2</h1> <p>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: <code>int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth);</code> 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: </p> <pre><code>float xTex0 = pt0.x/512; float xTex1 = pt1.x/512; while (xTex0 &gt; 1.0) { xTex0 -= 1.0; } while (xTex1 &gt; 1.0) { xTex1 -= 1.0; } if (xTex1 &lt; 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); </code></pre> <p>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: <img src="https://i.stack.imgur.com/eBVvM.png" alt="enter image description here"></p> <p>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!) </p> <pre><code>[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) </code></pre> <p>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!</p> <h1>Update 3</h1> <p>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 <a href="https://stackoverflow.com/questions/14023737/box2d-memory-not-freed-when-destroy-body">separate question</a> for this... I am still however, looking for a fix to the texture degradation problem!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload