如何建立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.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();
好了, 下面介绍一下画图的基本步骤与原理。简单的画图我就不介绍了,这里主要介绍就是怎么把一个图像画到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.0, 1.0, -0.0,1.0, 1.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_STRIP, 0, 4) 就可以画出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) |