案例要求:使用GLKit 加载一个带纹理的正方体,并让该正方体有个旋转的效果。效果图如下:
Aug-24-2020 00-18-18.gif
要完成这个效果,主要思路如下:
image.png
代码的实现分为4个部分:
- 准备工作
- 设置顶点数据,顶点坐标/纹理坐标/法线坐标
- GLKViewDelegate代理实现
- 定时旋转
准备工作
这是使用GLKit绘制图形的基本步骤,这在前一个OpenGL ES案例01 - 加载纹理已经做过一次,此次复习并加深印象。
- 初始化上下文,并设置当前上下文
EAGLContext* context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
[EAGLContext setCurrentContext:context];
- 创建GLKView,并设置代理。在前一个案例中直接将self.view的继承由UIView修改成GLKView。在这里换一种写法,创建一个GLKView,并将它以子视图的形式加到self.view中。
self.glkView = [[GLKView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) context:context];
self.glkView.backgroundColor = [UIColor blackColor];
self.glkView.delegate = self;
[self.view addSubview:self.glkView];
- 设置深度缓冲区格式,此处若不设置,将采用默认格式GLKViewDrawableDepthFormat24。
self.glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
glDepthRangef(1, 0);
- 获取纹理地址
NSString* path = [[NSBundle mainBundle] pathForResource:@"nn" ofType:@"jpg"];
UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
- 设置纹理参数并加载纹理
NSDictionary* options = @{GLKTextureLoaderOriginBottomLeft:@(YES)};
GLKTextureInfo* textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:nil];
- 使用effect
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.texture2d0.enabled = YES;
self.baseEffect.texture2d0.target = textureInfo.target;
self.baseEffect.texture2d0.name = textureInfo.name;
- 开启光照效果,此处若不开启光照效果,会略显不真实
self.baseEffect.light0.enabled = YES;
- 设置漫反射颜色,这主要是营造一种光照的效果
self.baseEffect.light0.diffuseColor = GLKVector4Make(1, 1, 1, 1);
- 设置光源位置,可将光源放置任何想放置的位置
self.baseEffect.light0.position = GLKVector4Make(-0.5, -0.5, 5, 1);
至此,准备工作就完成了。
设置顶点数据
这部分的工作主要是设置顶点数据(顶点坐标/纹理坐标/法线坐标),并将这些数据从CPU拷贝至GPU。
- 设置顶点数据,在本案例中,定义了顶点数据的结构体,通过分别对结构体成员变量赋值的方法来完成每个顶点数据的赋值。
typedef struct {
GLKVector3 positionCoord; //顶点坐标
GLKVector2 textureCoord; //纹理坐标
GLKVector3 normal; //法线
} CCVertex;
//初始化--这里数据的初始化方式是c语言中的结构体赋值
(CCVertex){{-0.5, 0.5, 0.5}, {0, 1}, {0, 0, 1}};
顶点数据设置不是本案例重点,详细代码可参见完整Demo。
- 开辟顶点缓冲区,将顶点数据从CPU拷贝至GPU
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(CCVertex)*kCoordCount, self.vertices, GL_STATIC_DRAW);
- 打开读取通道,指定要访问的属性,才能让顶点着色器访问到从CPU复制到GPU的数据
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL+offsetof(CCVertex, positionCoord));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL+offsetof(CCVertex, textureCoord));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL+offsetof(CCVertex, normal));
至此,完成了顶点数据的设置,并将顶点数据从CPU拷贝至GPU,指定顶点着色器要访问属性,这样顶点着色器就可以访问GPU中的相应数据了。
实现GLKViewDelegate代理
这个代理必须实现,否则无法完成绘制,代理方法的主要目的是绘制视图的内容,并根据定时器的旋转变换,重新渲染视图。
- 清空颜色/深度缓冲区
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
- 由于在旋转过程加入光照效果,因此需要打开深度测试
glEnable(GL_DEPTH_TEST);
- 准备绘制
[self.baseEffect prepareToDraw];
- 开始绘制
glDrawArrays(GL_TRIANGLES, 0, kCoordCount);
启动定时旋转
要实现效果图,需要启用一个定时器,当定时器被触发时,将整个模型视图矩阵沿某个方向旋转指定度数。
- 启动定时器
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- 指定旋转角度
self.angle = (self.angle + 5) % 360;
- 修改模型视图矩阵,进行旋转操作
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(self.angle), 0.3, 1, 0.7);
- 提交重新渲染
[self.glkView display];
至此,按照思路完成了代码的编写,经过这次案例的实现,加深了使用GLKit加载纹理的步骤印象。
完整Demo地址
网友评论