本文为L_Ares个人写作,如需转载请表明原文出处。
逻辑和GLSL是一样的,只不过GLKit提供了GLKBaseEffect,所以不用自己来实现shader的代码,其他的逻辑是一模一样的。
直接上代码,如果有需要的小伙伴记得仔细看备注,我觉得非常的详细了。
//
// ViewController.m
// 06GLKit索引绘图
//
// Created by EasonLi on 2020/10/10.
//
#import "ViewController.h"
@interface ViewController ()
//图形上下文
@property (nonatomic,strong) EAGLContext *myContext;
//渲染效果设置器
@property (nonatomic,strong) GLKBaseEffect *myEffect;
//要绘制的三角形的数量
@property (nonatomic,assign) GLint count;
//绕X轴的旋转弧度
@property (nonatomic,assign) GLfloat xDegree;
//绕Y轴的旋转弧度
@property (nonatomic,assign) GLfloat yDegree;
//绕Z轴的旋转弧度
@property (nonatomic,assign) GLfloat zDegree;
//是否绕X轴旋转
@property (nonatomic,assign) BOOL roundX;
//是否绕Y轴旋转
@property (nonatomic,assign) BOOL roundY;
//是否绕Z轴旋转
@property (nonatomic,assign) BOOL roundZ;
//定时器
@property (nonatomic,strong) dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化变量
[self initialData];
//设置图层和上下文
[self setUpContextAndLayer];
[self toRenderScence];
// Do any additional setup after loading the view.
}
#pragma mark - 初始化数据
- (void)initialData
{
self.myContext = nil;
self.myEffect = nil;
self.count = 0;
self.xDegree = 0.f;
self.yDegree = 0.f;
self.zDegree = 0.f;
self.roundX = NO;
self.roundY = NO;
self.roundZ = NO;
double seconds = 0.1;
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
}
#pragma mark - 设置图层和上下文
- (void)setUpContextAndLayer
{
//新建图形上下文
self.myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
//将controller的view直接赋值给GLKView,创建GLKView
GLKView *view = (GLKView *)self.view;
//设置view的上下文
view.context = self.myContext;
//设置图层的颜色格式
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
//设置图层的深度缓冲区大小
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
//设置当前图形的上下文是myContext
[EAGLContext setCurrentContext:self.myContext];
//开启深度测试
glEnable(GL_DEPTH_TEST);
}
#pragma mark - 渲染图形
- (void)toRenderScence
{
//设置顶点数组
/**
前3个数据是顶点坐标
中间3个数据是顶点颜色
最后2个是纹理坐标
*/
GLfloat vertexArr[] =
{
//左上
-0.5f,0.5f,0.f, 0.f,0.f,0.5f, 0.f,1.f,
//右上
0.5f,0.5f,0.f, 0.f,0.5f,0.f, 1.f,1.f,
//左下
-0.5f,-0.5f,0.f, 0.5f,0.f,0.f, 0.f,0.f,
//右下
0.5f,-0.5f,0.f, 0.5f,0.5f,0.f, 1.f,0.f,
//中间顶点
0.f,0.f,0.8f, 0.5f,0.5f,0.5f, 0.5f,0.5f
};
//索引数组
GLuint indexArr[] =
{
0,3,2,
0,1,3,
0,2,4,
0,4,1,
1,4,3,
2,3,4
};
//要绘制的三角形个数
self.count = sizeof(indexArr) / sizeof(GLuint);
//将顶点数组放入数组类型的顶点缓冲区
GLuint vertexBuffer;
//申请缓冲区
glGenBuffers(1, &vertexBuffer);
//绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
//创建并初始化缓冲区对象
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArr), &vertexArr, GL_STATIC_DRAW);
//将索引数组放入索引缓冲区
GLuint indexBuffer;
//申请缓冲区
glGenBuffers(1, &indexBuffer);
//绑定缓冲区
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
//创建并初始化缓冲区对象
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexArr), indexArr, GL_STATIC_DRAW);
//打开顶点属性通道
glEnableVertexAttribArray(GLKVertexAttribPosition);
//设置顶点数据的读取方式
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, NULL);
//打开颜色属性通道
glEnableVertexAttribArray(GLKVertexAttribColor);
//设置颜色数据的读取方式
glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 3);
//打开纹理属性通道
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//设置纹理数据的读取方式
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 6);
//载入纹理并且使用GLKBaseEffect
[self loadTextureAndSetEffect];
//设置投影矩阵和模型视图矩阵
[self setUpModelViewProjectionMatrix];
//开启GCD的定时器
[self startGCDTimer];
}
- (void)loadTextureAndSetEffect
{
//获取纹理文件的路径
NSString *textureFile = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"png"];
//设置纹理坐标的原点为左下角
NSDictionary *optional = @{GLKTextureLoaderOriginBottomLeft:@1};
//初始化纹理信息
GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:textureFile options:optional error:nil];
//初始化GLKBaseEffect对象
self.myEffect = [[GLKBaseEffect alloc] init];
//允许使用第一个纹理
self.myEffect.texture2d0.enabled = GL_TRUE;
//纹理名称
self.myEffect.texture2d0.name = textureInfo.name;
}
- (void)setUpModelViewProjectionMatrix
{
//计算宽高比
CGSize size = self.view.bounds.size;
//fabs就是C中的返回绝对值
float aspect = fabs(size.width/size.height);
//计算投影矩阵
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.f), aspect, 0.1f, 10.f);
/**
就是设置缩放的,用不用都行,随意,当然有想要翻转各轴的的话,可以使用
projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0f, 1.0f, 1.0f);
*/
//设置透视投影的投影矩阵
self.myEffect.transform.projectionMatrix = projectionMatrix;
//设置模型视图矩阵,可以选择稍微平移一下,不想要平移一下的话,可以直接初始化
//那就是一个单元矩阵,平移用GLKMatrix4Translate函数
GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
//设置模型视图矩阵
self.myEffect.transform.modelviewMatrix = modelViewMatrix;
}
- (void)startGCDTimer
{
dispatch_source_set_event_handler(self.timer, ^{
self.xDegree += 0.1 * self.roundX;
self.yDegree += 0.1 * self.roundY;
self.zDegree += 0.1 * self.roundZ;
});
dispatch_resume(self.timer);
}
#pragma mark - GLKViewController的代理
//如果Controller是GLKViewController的子类,并且提供了实现,那么就用update,不然
//就用glkViewControllerUpdate
- (void)update
{
//旋转就是把模型视图矩阵乘以旋转矩阵,然后再把模型视图矩阵重新赋值给
//GLKBaseEffect的modelviewMatrix
//所以先初始化一个新的modelview矩阵
//把金字塔移动一些,不然看不到了
GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -3.0f);
//X轴变换
modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, self.xDegree);
//Y轴变换
modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, self.yDegree);
//Z轴变换
modelViewMatrix = GLKMatrix4RotateZ(modelViewMatrix, self.zDegree);
//赋值给GLKBaseEffect对象的modelViewMatrix
self.myEffect.transform.modelviewMatrix = modelViewMatrix;
}
#pragma mark - GLKView的代理
//用来绘制的,就是渲染,按照渲染的步骤来
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
//设置清屏颜色
glClearColor(0.3f, 0.3f, 0.3f, 1.f);
//清空缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//一定要记得GLKBaseEffect在绘制前要使用prepareToDraw
[self.myEffect prepareToDraw];
//利用索引绘图开始绘制
//最后一个参数是索引数组的地址,但是我们直接把索引都传入了索引缓冲区里面了,所以
//直接写0就得了,反正也不从那里获取。
glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
}
- (IBAction)xClick:(id)sender {
self.roundX = !self.roundX;
}
- (IBAction)yClick:(id)sender {
self.roundY = !self.roundY;
}
- (IBAction)zClick:(id)sender {
self.roundZ = !self.roundZ;
}
#pragma mark - 销毁
- (void)dealloc{
if ([EAGLContext currentContext] == self.myContext) {
[EAGLContext setCurrentContext:nil];
}
dispatch_source_cancel(_timer);
_timer = nil;
}
@end
效果图如下图1.1所示:
1.1.png
网友评论