教程
OpenGL ES入门教程1-Tutorial01-GLKit
OpenGL ES入门教程2-Tutorial02-shader入门
OpenGL ES入门教程3-Tutorial03-三维变换
这次我们用GLKit,更简单的实现图形变换、纹理贴图、着色、深度测试(代码在这)。
OpenGL ES系列教程在这里。
OpenGL ES系列教程的代码地址 - 你的star和fork是我的源动力,你的意见能让我走得更远。
效果展示
核心思路
使用GLKit来进行图形变换、纹理贴图加载、深度测试,用GLKBaseEffect来管理纹理贴图和进行着色。
具体细节
1、顶点属性
typedef NS_ENUM(GLint, GLKVertexAttrib)
{
GLKVertexAttribPosition,
GLKVertexAttribNormal,
GLKVertexAttribColor,
GLKVertexAttribTexCoord0,
GLKVertexAttribTexCoord1
} NS_ENUM_AVAILABLE(10_8, 5_0);
GLKEffects用到的顶点属性已经定义好,使用时直接用枚举量赋值。如下图,直接对顶点的位置、颜色、纹理坐标进行赋值。
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL);
//顶点颜色
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, 4 * 8, (GLfloat *)NULL + 3);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 4 * 8, (GLfloat *)NULL + 6);
2、纹理
在自定义shader中使用纹理,需要用CoreGraphics把图像转换成bitmapdata,再申请纹理内存,把图像数据传进去,最后还要释放bitmapdata。
在GLKit中,仅仅需要如下三行代码,就可以完成纹理的加载。
//纹理
NSString* filePath = [[NSBundle mainBundle] pathForResource:@"for_test" ofType:@"png"];
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];
GLKTextureInfo* textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
GLKTextureLoaderOriginBottomLeft
参数是避免纹理上下颠倒,原因是纹理坐标系和世界坐标系的原点不同。
最后创建着色器,启用纹理,把刚刚创建的textureInfo的name赋值给着色器。
//着色器
self.mEffect = [[GLKBaseEffect alloc] init];
self.mEffect.texture2d0.enabled = GL_TRUE;
self.mEffect.texture2d0.name = textureInfo.name;
3、图形变换
在OpenGL ES里面,图形变换的表现形式就是矩阵操作,GLKit也提供了很多矩阵操作函数。
//初始的投影
CGSize size = self.view.bounds.size;
float aspect = fabs(size.width / size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.1f, 10.f);
projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
self.mEffect.transform.projectionMatrix = projectionMatrix;
GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
self.mEffect.transform.modelviewMatrix = modelViewMatrix;
GLKMatrix4MakePerspective
是透视投影变换
GLKMatrix4Translate
是平移变换
/**
* 场景数据变化
*/
- (void)update {
GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, self.mDegreeX);
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, self.mDegreeY);
modelViewMatrix = GLKMatrix4RotateZ(modelViewMatrix, self.mDegreeZ);
self.mEffect.transform.modelviewMatrix = modelViewMatrix;
}
在场景变换函数里面,GLKMatrix4RotateY
是绕Y轴旋转,其他的分分别是绕X、Z轴旋转。
4、深度测试
在前面的教程介绍过,开启深度测试需要分配深度测试的缓冲区,并挂载到相应的帧缓冲区。
在GLKit代码中,深度测试的开启十分简单。
在新建上下文时调用glEnable(GL_DEPTH_TEST);
开启深度测试。
//新建OpenGL ES 上下文
self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView* view = (GLKView *)self.view;
view.context = self.mContext;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
[EAGLContext setCurrentContext:self.mContext];
glEnable(GL_DEPTH_TEST);
在渲染场景时,glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear参数加入GL_DEPTH_BUFFER_BIT
即可。
/**
* 渲染场景代码
*/
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
[self.mEffect prepareToDraw];
glDrawElements(GL_TRIANGLES, self.mCount, GL_UNSIGNED_INT, 0);
}
总结
OpenGL ES的入门已经差不多,后面还有很多很多知识,需要时再学习即可。
总结这几篇教程花了一个星期左右。
学习过程中有几部分最难受:
- 第一部分是OpenGL ES的顶点属性、纹理贴图,对顶点到图形的过程不理解,对OpenGL ES的数据缓存机制不了解,无知容易使人知难而退;
- 第二部分是shader和glsl,glsl无法调试、编译信息不会查看、语法不懂等等,一个1.0 + 1都会报错,自己却莫名其妙,只能通过二分注释代码来定位问题,特别让人泄气;
- 第三部分是OpenGL ES的三维图形变换和光照等,这部分更多的是数学知识和物理知识,代码一般都很简单;这部分比前面的都难,但是因为知道哪里缺失,虽然不懂,但是不慌,只是有点难受。
最后的感想,计算机图形学和线性代数毕竟是基础,自己多花点时间学习,培养为核心竞争力非常不错。
网友评论
为什么color 的stride 是4 * 8 ?
最后为了能启动,同时能看懂,再追加了一个Buffer 给color。
我发现在调用点击Y,方法的时候这里self.mDegreeY是一直在增加的,这里不是旋转吗,怎么这个值是一直在增加的,这是什么情况,求指教,谢谢
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.1f, 10.f);
projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
self.mEffect.transform.projectionMatrix = projectionMatrix;
GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
GLKMatrix4MakePerspective 中的near和far这里为何设置成 0.1和10.f,以及modelview的tz为何又设置成了 -2.f 这两者之间有何联系吗?