本文為L_Ares個人寫作片迅,如需轉載請表明原文出處浪谴。
邏輯和GLSL是一樣的值骇,只不過GLKit提供了GLKBaseEffect道盏,所以不用自己來實現(xiàn)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;
//要繪制的三角形的數(shù)量
@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 - 初始化數(shù)據(jù)
- (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,創(chuàng)建GLKView
GLKView *view = (GLKView *)self.view;
//設置view的上下文
view.context = self.myContext;
//設置圖層的顏色格式
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
//設置圖層的深度緩沖區(qū)大小
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
//設置當前圖形的上下文是myContext
[EAGLContext setCurrentContext:self.myContext];
//開啟深度測試
glEnable(GL_DEPTH_TEST);
}
#pragma mark - 渲染圖形
- (void)toRenderScence
{
//設置頂點數(shù)組
/**
前3個數(shù)據(jù)是頂點坐標
中間3個數(shù)據(jù)是頂點顏色
最后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
};
//索引數(shù)組
GLuint indexArr[] =
{
0,3,2,
0,1,3,
0,2,4,
0,4,1,
1,4,3,
2,3,4
};
//要繪制的三角形個數(shù)
self.count = sizeof(indexArr) / sizeof(GLuint);
//將頂點數(shù)組放入數(shù)組類型的頂點緩沖區(qū)
GLuint vertexBuffer;
//申請緩沖區(qū)
glGenBuffers(1, &vertexBuffer);
//綁定緩沖區(qū)
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
//創(chuàng)建并初始化緩沖區(qū)對象
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArr), &vertexArr, GL_STATIC_DRAW);
//將索引數(shù)組放入索引緩沖區(qū)
GLuint indexBuffer;
//申請緩沖區(qū)
glGenBuffers(1, &indexBuffer);
//綁定緩沖區(qū)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
//創(chuàng)建并初始化緩沖區(qū)對象
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexArr), indexArr, GL_STATIC_DRAW);
//打開頂點屬性通道
glEnableVertexAttribArray(GLKVertexAttribPosition);
//設置頂點數(shù)據(jù)的讀取方式
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, NULL);
//打開顏色屬性通道
glEnableVertexAttribArray(GLKVertexAttribColor);
//設置顏色數(shù)據(jù)的讀取方式
glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 8, (GLfloat *)NULL + 3);
//打開紋理屬性通道
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
//設置紋理數(shù)據(jù)的讀取方式
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函數(shù)
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的子類,并且提供了實現(xiàn)弄抬,那么就用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);
//清空緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//一定要記得GLKBaseEffect在繪制前要使用prepareToDraw
[self.myEffect prepareToDraw];
//利用索引繪圖開始繪制
//最后一個參數(shù)是索引數(shù)組的地址懊亡,但是我們直接把索引都傳入了索引緩沖區(qū)里面了依啰,所以
//直接寫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所示: