sim*_*per 13 opengl-es objective-c cocos2d-iphone opengl-es-2.0
我正在尝试实现raywenderlich的教程,使用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;
Run Code Online (Sandbox Code Playgroud)
生成纹理:
-(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];
}
Run Code Online (Sandbox Code Playgroud)
让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];
}
}
Run Code Online (Sandbox Code Playgroud)
绘制纹理:
- (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);
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是:经过一定数量的重复后,纹理质量开始下降,如下所示:
有没有办法让纹理重复而不降解?
我已经对纹理如何降级进行了更多的分析,事实证明它不是连续不断地进行,而是随着2次重复的功率而降级,因此它在第一次重复时首次降级,然后在2次重复之后降低,然后是4次, 8,16,32等......似乎每次图像质量下降时,在图像中可以看到的垂直条带开始出现,宽度加倍.同样在每次降级时,游戏的帧速率大幅下降,所以我开始认为这可能是一个内存问题.
我最好猜测为什么到目前为止发生这种情况是因为地形的-draw方法不断产生GL_TRAINGLE_STRIP,并且一旦它们在屏幕外就不会删除它们,导致地形的内存使用量增加,导致降级和帧率下降.
我已经解决了纹理生成中出现的两个问题......
在精灵生成方法中:
float x1 = -textureSize;
float x2;
float y1 = textureSize;
float y2 = 0;
float dx = textureSize / nStripes * 2;
Run Code Online (Sandbox Code Playgroud)
对此:
float x1 = -winSize.width;
float x2;
float y1 = winSize.height;
float y2 = 0;
float dx = winSize.width / nStripes * 2;
Run Code Online (Sandbox Code Playgroud)
我意识到这与主要错误完全无关,而是由于我的条纹由于某种原因没有出现在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;
}
Run Code Online (Sandbox Code Playgroud)
这几乎解决了所有问题,我仍然遇到的唯一两个问题是:
这些可以在这张照片中看到:正如你所看到的那样,帧速率大幅下降并且在整个游戏过程中继续这样做.
我减少了每个三角形条带的宽度,试图找到纹理重复处理的内容,并发现由于某种原因条带被整个背景纹理填充但反转.经过一番思考之后,我意识到这是因为在这里因为地板: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);
Run Code Online (Sandbox Code Playgroud)
这适用于那个条带中的第一个三角形,但不是因为某些特殊原因的第二个三角形,我根本无法理解!它看起来像这样:
然而,_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)
Run Code Online (Sandbox Code Playgroud)
很容易看出从一个纹理到纹理开始的重叠遵循与其他纹理相同的模式,但它仍然无法正确渲染!
事实证明我的内存问题与使用Opengl-es 2.0的绘图完全无关,它实际上与我的游戏中的box2D元素没有在内存中解除分配有关,所以我为此创建了一个单独的问题.然而,我仍然在寻找纹理退化问题的修复方法!
为地形的可见部分生成的三角形条带将具有从左到右增加的纹理坐标.当这些变得非常大时,您将遇到精确问题.
对于UV坐标,您需要从三角形条带中的所有坐标中减去相同的值.通常,取最低坐标的整数部分(可能是您的情况下的最左边或第一个坐标),然后从您生成的所有UV中减去该坐标.或者使用它作为基线来生成其他人,无论您喜欢哪个.
因此,如果屏幕左侧的U值为100.7且右侧的U值为129.5,则实际上您希望输出的值范围为0.7到29.5.(如果你仍然有精度问题,你可以通过使用负数合并将范围居中于零来吱吱作响).
另一种方法是,如果您需要在单个条带内的纹理坐标中具有不连续性并获得最大精度,则引入退化三角形,这是零区域,因此在您更改tex时不会渲染-coords.你可以通过重复顶点来做到这一点,但是在继续之前使用调整后的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;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3040 次 |
最近记录: |