Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5542941
  • 博文数量: 763
  • 博客积分: 12108
  • 博客等级: 上将
  • 技术积分: 15717
  • 用 户 组: 普通用户
  • 注册时间: 2007-09-28 21:21
个人简介

业精于勤,荒于嬉

文章分类

全部博文(763)

文章存档

2018年(6)

2017年(15)

2016年(2)

2015年(31)

2014年(14)

2013年(87)

2012年(75)

2011年(94)

2010年(190)

2009年(38)

2008年(183)

2007年(28)

分类: C/C++

2010-01-28 20:35:50

如何建立iPhone的OpenGL ES项目,请参考本站的游戏是这样写成的系列,非常清楚。

1 将EAGLView设置成一个单例模式,这样可以用别的类来调用其的 swapBuffers 来画图。

- (void) swapBuffers
{
EAGLContext *oldContext = [EAGLContext currentContext];
GLuint oldRenderbuffer;

   if(oldContext != context)
[EAGLContext setCurrentContext:context];



     glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

if(![context presentRenderbuffer:GL_RENDERBUFFER_OES])
printf("Failed to swap renderbuffer in %s\n", __FUNCTION__);

   if(oldContext != context)
[EAGLContext setCurrentContext:oldContext];
}



swapBuffers是一个很好的方法,利用双缓冲,先将图画到一个context上,然后交换当前context和画好的,这样避免屏幕闪烁。

2. 画图原理以及如何一次画多图



因为很多游戏一次要画很多图,所以不要每画一个小的就画到View上,这样屏幕会频繁闪动。我将下面代码使用了99次,也就是画了99张图,然后再调用 swapBuffers 这个方法来一次性画出来。千万避免每画一张图,就swapBuffer一次。



glLoadIdentity(); //必须使用这个再每一次新画一个图之前,否则原始坐标会丢失

glColor4f(1.0f1.0f1.0f1.0f);

glPushMatrix();
glTranslatef(x, y, 0.0f);//移位
glRotatef(angle, 0.0f, 0.0f, 1.0f);//旋转
glScalef(xScale, yScale, 1.0f);//缩放
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glPopMatrix();

好了, 下面介绍一下画图的基本步骤与原理。简单的画图我就不介绍了,这里主要介绍就是怎么把一个图像画到View上,就是所谓的纹理映射。在EAGLView的drawView方法里用如下代码实现 画一幅简单的图:bg.png(可以在游戏是这样写成的里面找到)


- (void)drawView {
[EAGLContext setCurrentContext:context];

   const GLfloat zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0
GLfloat size; 

//glEnable可以用来实现很多东西,具体的参数可以去opengl网站查
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION); 
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
CGRect rect = self.bounds; 
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / (rect.size.width / rect.size.height), zNear, zFar); 
glViewport(0, 0, rect.size.width, rect.size.height);  
glMatrixMode(GL_MODELVIEW);

glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_SRC_COLOR);

//申明一个图像,并给这个图像编号

GLuint      texture[1];
glGenTextures(1, &texture[0]);
//将这个图像用2D方式纹理映射
glBindTexture(GL_TEXTURE_2D, texture[0]);
//设置过滤器
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
//读取一个图像 bg.png
NSString *path = [[NSBundle mainBundle] pathForResource:@"bg" ofType:@"png"];
        
NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
        
UIImage *image = [[UIImage alloc] initWithData:texData];
        
if (image == nil)
             NSLog(@"Do real error checking here");
//读取这个图像的长与高
        
GLuint width = CGImageGetWidth(image.CGImage);
        
GLuint height = CGImageGetHeight(image.CGImage);
//设置颜色

        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//分配内存用于画这个图像

        
void *imageData = malloc( height * width * 4 );
//准备一个context用于映射这个图像

        
CGContextRef contextX = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
        

        CGColorSpaceRelease( colorSpace );
        
CGContextClearRect( contextX, CGRectMake( 0, 0, width, height ) );
        
CGContextTranslateCTM( contextX, 0, height - height );
//将图像画到context上,实际上就是画到imageData里

        
CGContextDrawImage( contextX, CGRectMake( 0, 0, width, height ), image.CGImage );
//映射,将图像映射成纹理
        
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

//释放内存
        CGContextRelease(contextX);
        
free(imageData);
        [image
release];
        [texData
release];

//设置光亮
glEnable(GL_LIGHTING);
        // Turn the first light on
        glEnable(GL_LIGHT0);
    
        // Define the ambient component of the first light
        
static const Color3D light0Ambient[] = {{1.0, 1.0, 1.0, 1.0}};
glLightfv(GL_LIGHT0, GL_AMBIENT, (const GLfloat *)light0Ambient);
    
        // Define the diffuse component of the first light
        
static const Color3D light0Diffuse[] = {{0.8, 0.8, 0.8, 1.0}};
glLightfv(GL_LIGHT0, GL_DIFFUSE, (const GLfloat *)light0Diffuse);
    
        // Define the position of the first light 
        
static const Vertex3D light0Position[] = {{10.0, 10.0, 10.0}};
glLightfv(GL_LIGHT0, GL_POSITION, (const GLfloat *)light0Position); 

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

/*------------------------------------以上是图像映射以下是画图----------------------------------------*/
static GLfloat rot = 0.0;
    
    
//清楚颜色等参数
        
glColor4f(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
//准备画图,开启vertex和coord
        glEnableClientState(GL_VERTEX_ARRAY);
        //glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
//准备好vertice和coord准备画图,给图像的位置定位
        
Vertex3D vertices[] = {-1.01.0, -0.0,1.01.0, -0.0,-1.0, -1.0, -0.0, 1.0, -1.0, -0.0};
    
        //Vector3D normals[] = {0.0, 0.0, 1.0,0.0, 0.0, 1.0,0.0, 0.0, 1.0,0.0, 0.0, 1.0};

      // 测试不同的坐标参数就知道画图是如何画的,第一组只画1/4的图,从原点到2边的中心截取,第2组也是,但是截取的位置不一样,第3组画整个图


      //static const GLfloat texCoords[] = {
//       
0.0, 0.5,
//       
0.5, 0.5,
//       
0.0, 0.0,
//       
0.5, 0.0
//     
};

//    static const GLfloat texCoords[] = {
//        0.25, 0.75,
//        0.75, 0.75,
//        0.25, 0.25,
//        0.75, 0.25
//    };
//    
   
static const GLfloat texCoords[] = {
       
0.0, 1.0,
       
1.0, 1.0,
       
0.0, 0.0,
       
1.0, 0.0
   
};
    
//这个每画一个新图都要用,否则会迷失原点
      glLoadIdentity();
     
glTranslatef(0.0, 0.0, -1.0);//移位
     
glRotatef(rot, 1.0, 1.0, 1.0);//旋转
    
     
glBindTexture(GL_TEXTURE_2D, texture[0]);
     
glVertexPointer(3, GL_FLOAT, 0, vertices);//将要画图位置的顶点数组传递进去
      //glNormalPointer(GL_FLOAT, 0, normals);
     
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);//将要画的图像的截图顶点位置传递进去
      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//用triangle strip方式画图 顶点数目为4
    
    
//相应的 开启就要关闭
      glDisableClientState(GL_VERTEX_ARRAY);
      //glDisableClientState(GL_NORMAL_ARRAY);
      glDisableClientState(GL_TEXTURE_COORD_ARRAY);

/*-------------------------画图完成,如果要画到View上就可以用下面的方法,如果想继续画其他图,就继续重复上面代码,但是图片要改变------------------------*/

    
      glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}




不难看出,开启功能,然后声明一个图像并给予编号,然后读取图像,读取图像参数,根据参数分配内存,设置画图的一些颜色,大小亮度等参数,用glTexImage2D将 图像映射到分配的空间,映射的步骤就完成了。在画图的步骤中,首先要准备好2组坐标,一个用于存放要画出来的图像的位置,一个用于存放要截取的图像的部 位,然后glLoadIdentily,这个时候要移位的移位,要旋转的旋转,要缩放的缩放,然后将2组坐标传递进去,当然还可以有其他坐标,这里不详细 介绍了,在就是用glDrawArrays将图画出来。但是这个时候并没有呈现在屏幕上,因为没有呈现在EAGLView的context里,所以继续画 图的话就不要调用方法,将所有图全画完再一次调用[context presentRenderbuffer:GL_RENDERBUFFER_OES] 将所有图像呈现出来。其他一些函数这里不详细介绍,函数的参数也不详细介绍,这里主要介绍的是用OpenGL ES 进行纹理映射的大致步骤。

简单的说,就是要画99个图的话,把红色代码重复99次就可以,蓝色代码只要1次就行。但是注意红色代码部分要改图片和位置,否则就是在同一位置画99次一样的图了。
如果图很多很耗资源而对图的要求不是特别高的话,可以用 glSubTexImage2D 或者 glCompressedTexImage2D 来映射纹理。 




3. 如何设置旋转中心使图像围绕自己想要旋转的点旋转



假设要画一个32*32的图像在点(x, y), 并让此点以图像的中心旋转,那么用如下代码就可以实现
// 围绕中心旋转
GLfloat vertices[] = 
{
imageWidth/2, - imageHeight/2,
  imageWidth/2, - imageHeight/2,
imageWidth/2,   imageHeight/2,
  imageWidth/2,   imageHeight/2
};

// 围绕底部中心旋转
//GLfloat vertices[] = 
// {
// imageWidth/2, 0.0,
//   imageWidth/2, 0.0,
// imageWidth/2, imageHeight,
//   imageWidth/2, imageHeight
// };

glLoadIdentity();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glPushMatrix();
glTranslatef(x, y, 0.0f);
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glScalef(xScale, yScale, 1.0f);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glPopMatrix();

代码不完整,主要使告诉如何设置 vertices ,这样可以调整你想旋转的位置。一定要以你想旋转的点为中心,按照图像的长宽来设置好4个顶点的位置,4个顶点的位置顺序,这里依次是左下,右下,左上,右上。这样用glDrawArrays(GL_TRIANGLE_STRIP04就可以画出4边形了。然后translate (x,y) 就可以把图像放到你想要放的位置了,再旋转就可以按照你想要旋转的点来旋转。本例缺少的coordinates请参照2里面红色部分的代码,截取你想要画的图片的部分的顶点坐标。





//////////////////////////



//读取这个图像的长与高
        GLuint width = CGImageGetWidth(image.CGImage);
        GLuint height = CGImageGetHeight(image.CGImage);
//设置颜色

        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//分配内存用于画这个图像

        void *imageData = malloc( height * width * 4 );
//准备一个context用于映射这个图像

        CGContextRef contextX = CGBitmapContextCreate( imageData, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big );
        
        CGColorSpaceRelease( colorSpace );
        CGContextClearRect( contextX, CGRectMake( 0, 0, width, height ) );
        CGContextTranslateCTM( contextX, 0, height - height );
//将图像画到context上,实际上就是画到imageData里

        CGContextDrawImage( contextX, CGRectMake( 0, 0, width, height ), image.CGImage );
//映射,将图像映射成纹理
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);


glTexImage2D的 width height不是不是要求2的n次方么
直接用图的width和height怎么会画的出来?
阅读(2500) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~