objective c - Repetición de la textura OpenGL-es vinculada a las colinas en cocos2d 2.0
objective-c cocos2d-iphone (2)
La franja triangular que generes para la parte visible del terreno tendrá coordenadas de textura que aumentarán de izquierda a derecha. Cuando estos sean muy grandes, tendrás problemas de precisión.
Para sus coordenadas UV, necesita restar el mismo valor de todas las coordenadas en su franja triangular. Generalmente, tome la parte entera de la coordenada más baja (probablemente la coordenada más a la izquierda o la primera en su caso) y reste eso de todos los UV que genere. O úsalo como línea de base para generar los demás, lo que prefieras.
Por lo tanto, si el lado izquierdo de la pantalla tiene un valor U de 100.7 y el lado derecho tiene un valor U de 129.5, realmente desea valores de salida que van de 0.7 a 29.5. (Si todavía tiene problemas de precisión, puede chirriar un poco más al centrar el rango en cero, utilizando co-ords negativos).
La alternativa, si necesita tener una discontinuidad en las coordenadas de la textura dentro de una sola banda, y para obtener la máxima precisión, es introducir triángulos degenerados, que son de área cero, y por lo tanto no se renderizan, mientras cambia el texto -coords Para ello, repite los vértices, pero con los tex-coords ajustados, antes de continuar.
De acuerdo con su código anterior, sugeriría algo como esto:
// 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;
}
ARTÍCULO ORIGINAL
Estoy en el proceso de tratar de implementar el tutorial de raywenderlich para generar colinas con coordenadas repetidas a rayas usando cocos2d. Este artículo fue escrito para Cocos2D 1.0, y estoy tratando de portarlo a Cocos2D 2.0. Esto significa actualizarlo para openGl-es 2. Hasta ahora, todo ha funcionado a la perfección. Sin embargo, tengo problemas para que la textura de la colina se repita correctamente ...
Aquí está mi código:
Enviando las colinas la textura:
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;
Generando textura:
-(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];
}
Conseguir TexCoords para delimitar las rayas a la colina:
- (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];
}
}
Dibujando la textura:
- (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);
}
El problema que tengo es esto: después de un cierto número de repeticiones, la textura comienza a degradarse en calidad, así:
¿Hay alguna manera de hacer que la textura se repita sin degradación?
EDIT 1:
He estado haciendo más análisis sobre cómo se degrada la textura, resulta que no lo hace de manera continua, sino que se degrada con una potencia de 2 repeticiones, por lo que se degrada por primera vez en la primera repetición y luego después de 2 repeticiones, luego 4, 8, 16, 32 y así sucesivamente ... También parece que las bandas verticales que comienzan a aparecer que se pueden ver en la imagen tienen un ancho doble cada vez que la imagen se degrada en calidad. También en cada degradación la velocidad de fotogramas del juego disminuye sustancialmente, así que estoy empezando a pensar que esto es probablemente un problema de memoria.
EDICION 2:
Mi mejor conjetura de por qué esto está sucediendo hasta ahora es porque el método de extracción para el terreno está haciendo continuamente GL_TRAINGLE_STRIP, y no borrarlos una vez que están fuera de pantalla, causando una acumulación en el uso de la memoria del terreno, causando la degradación y caída de frecuencia de cuadro.
ACTUALIZACIÓN 1
He resuelto dos de los problemas que estaban ocurriendo con mi generación de texturas ...
Resolviendo la desalineación
EN el método de generación de sprites esto:
float x1 = -textureSize;
float x2;
float y1 = textureSize;
float y2 = 0;
float dx = textureSize / nStripes * 2;
a esto:
float x1 = -winSize.width;
float x2;
float y1 = winSize.height;
float y2 = 0;
float dx = winSize.width / nStripes * 2;
Me di cuenta de que esto no tenía nada que ver con el error principal, sino que se debía a que mis rayas, por algún motivo, no aparecían en un ángulo de 45 grados, lo que causa que se alineen incorrectamente al repetir. Traté de pensar en las razones de esto, y finalmente lo solucioné al asumir que el origen de las coordenadas de las texturas estaba en la esquina superior izquierda de la pantalla en lugar de en la esquina superior izquierda de la textura.
Cómo resolver la degradación (tipo de)
Tenía una idea de que la degradación de la imagen estaba ocurriendo debido a la gran cantidad de repeticiones de la textura, debido a una razón similar a esta, ¡ aunque puedo estar equivocado en ese frente!
Para resolver esto en el resetHillVertices lo configuro para que los texCoords estén siempre entre 0 y 1, lo que significa que la textura ligada a las colinas siempre es la primera repetición de la textura. Implementé esto así:
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;
}
Esto casi solucionó todo, los únicos dos problemas que aún tengo son:
- Algunas columnas de píxeles entre uniones de texturas se representan de forma extraña
- Todavía hay un problema de memoria al dibujar los triángulos texPos y Pos
Esto se puede ver en esta foto: Como puede ver, la velocidad de fotogramas se ha reducido drásticamente y continúa haciéndolo durante todo el juego.
ACTUALIZACIÓN 2
Reduje el ancho de cada tira de triángulo para intentar encontrar lo que estaba sucediendo en la repetición de textura, y descubrí que por alguna razón esa tira se llenaba con toda la textura de fondo pero se invertía. Después de pensar un poco, me di cuenta de que esto se debía a los suelos: int hSegments = floorf((p1.x-p0.x)/kHillSegmentWidth);
obtenemos que la última tira para cada repetición va más allá del ancho de la textura, sin embargo, como removemos 1, mientras que xTex1 es mayor que uno, establece este texCoords en 0.02 (o algún otro número pequeño) donde debería ser 1.02 ( Esto es difícil de entender, sin embargo, es correcto). Pensé que esto podría resolverse usando otra declaración if como esta:
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);
Esto funciona bien para el primer triángulo en esa tira, pero no para el segundo por alguna razón peculiar, que no puedo comprender en absoluto. Se parece a esto:
Sin embargo, la configuración dentro de _hillTexCoords parece correcta, cuando establezco un punto de interrupción dentro de la aplicación, este es el resultado que obtengo para la matriz _hillTexCoords, y parece que debería estar fijando la textura correctamente, pero todavía no es (¡Increíblemente frustrante! )
[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)
Es fácil ver que la superposición de una textura al inicio de la textura sigue el mismo patrón que las otras, ¡pero aún no se procesa correctamente!
Actualización 3
Resulta que mi problema de memoria no está relacionado con el dibujo usando OpenGL-es 2.0, de hecho está relacionado con los elementos de box2D de mi juego que no están desasignados en la memoria, así que he creado una pregunta separada para esto. . Sin embargo, todavía estoy buscando una solución al problema de degradación de la textura.
Esto es solo mi suposición ... No es una solución, sino una posible explicación.
Intenté rastrear cómo estás generando tu malla. Al principio pensé que estabas haciendo una especie de degeneración intencional (ya que estás usando una banda triangular y usando 4 vértices por cada 2 triángulos). Pero estás creando tu malla así, ¿verdad ?.
3 ___ 4-7___ 8
| / | / |
| / | / |
1 ___ 2-5 __ 6
Esto crea los triángulos 123, [234], 345, [456], 567, [678] ... y así sucesivamente (nota [] significa invertido). Entonces, ves que estás superponiendo la mitad de los triángulos. Supongo que se ve el último triángulo dibujado y el otro está oculto ... siempre que z sea exactamente 0 no está teniendo ningún artefacto. Cuando 4 = 7 y 2 = 5, que es cuando no está modificando su coordenada texutre, todo funciona bien, ya que de una forma u otra se reduce a lo siguiente (que sería la forma correcta de hacerlo si lo traspones) - acbdef). Traducido a coordenadas - misma coordenada, el mismo punto es así:
c ___ d ___ f
| / | / |
| / | / |
a ___ b ___ e
En el registro que publicó, escribe 4 puntos a la vez, ¿verdad? Entonces, en el 13er ciclo del "para" generas estos vértices: (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
Eso hace 5 triángulos. En particular, 2 son de interés aquí: [234] y 345. 234 está bien, pero 345 debe estar escondiéndolo (porque se representa después de la otra ...) Trate de usar una función de prueba de profundidad como GL_GREATER solo para evitar que se visualice y vea si estoy en lo cierto) Bueno, 345 no está bien, mapea la textura de x = 0.07 a 1.00. Entonces, incluso si corrigió 234, 345 todavía se dibuja sobre su triángulo correcto. Eso debería explicar tu fondo invertido.
Por lo tanto, ese era el problema que yo creo que sería (no sucedería si no normalizas las coordenadas de tu textura como en el tutorial).
¿Todavía tengo que escribir la solución? : /
Para empezar, sugiero que hayas generado tu malla como lo que dibujo en el segundo lugar (a, b, c ...). Luego continúa con una solución. Una solución sucia sería degenerar el triángulo malvado 345. Eso sería repetir el punto 4 una vez (estoy un poco mareado ahora, podría estar equivocado) - De todos modos esta no es una buena solución, estás mezclando orientaciones y superposición triangulos. Cada uno para iteración solo deberías agregar
_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);
Déjame pensar en una solución limpia, o dejar que alguien la escriba por mí, siempre que este sea el problema.