目的
本案例主要實(shí)現(xiàn)月亮圍繞著地球自轉(zhuǎn)功能,同時(shí)可以切換透視投影或正投影,通過(guò)GLKit來(lái)實(shí)現(xiàn)效果
效果

image.png
案例流程圖

image.png
案例源碼
案例主要分為7個(gè)步驟:在viewDidLoad當(dāng)中設(shè)置步驟
步驟的配置
#import "AGLKVertexAttribArrayBuffer.h"
#import "sphere.h"
//場(chǎng)景地球軸傾斜度
static const GLfloat SceneEarthAxialTiltDeg = 23.5f;
//月球軌道日數(shù)
static const GLfloat SceneDaysPerMoonOrbit = 28.0f;
//半徑
static const GLfloat SceneMoonRadiusFractionOfEarth = 0.25;
//月球距離地球的距離
static const GLfloat SceneMoonDistanceFromEarth = 2.0f;
@interface CCViewController ()
@property(nonatomic,strong)EAGLContext *mContext;
//頂點(diǎn)positionBuffer
@property(nonatomic,strong)AGLKVertexAttribArrayBuffer *vertexPositionBuffer;
//頂點(diǎn)NormalBuffer
@property(nonatomic,strong)AGLKVertexAttribArrayBuffer *vertexNormalBuffer;
//頂點(diǎn)TextureCoordBuffer
@property(nonatomic,strong)AGLKVertexAttribArrayBuffer *vertextTextureCoordBuffer;
//光照、紋理
@property(nonatomic,strong)GLKBaseEffect *baseEffect;
//不可變紋理對(duì)象數(shù)據(jù),地球紋理對(duì)象
@property(nonatomic,strong)GLKTextureInfo *earchTextureInfo;
//月亮紋理對(duì)象
@property(nonatomic,strong)GLKTextureInfo *moomTextureInfo;
//模型視圖矩陣
//GLKMatrixStackRef CFType 允許一個(gè)4*4 矩陣堆棧
@property(nonatomic,assign)GLKMatrixStackRef modelViewMatrixStack;
//地球的旋轉(zhuǎn)角度
@property(nonatomic,assign)GLfloat earthRotationAngleDegress;
//月亮旋轉(zhuǎn)的角度
@property(nonatomic,assign)GLfloat moonRotationAngleDegress;
@end
- 1.設(shè)置新建OpenGl ES上下文
//1.新建OpenGL ES 上下文
self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- 2.獲取GLkView并設(shè)置屬性,屬性分為顏色格式(drawableColorFormat)和深度緩沖區(qū)的格式(drawableDepthFormat),并開啟深度測(cè)試
//2.獲取GLKView
GLKView *view = (GLKView *)self.view;
view.context = self.mContext;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24; //設(shè)置深度緩沖區(qū)的格式
[EAGLContext setCurrentContext:self.mContext];
//開啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
3.創(chuàng)建GLKBaseEffect 光照信息
self.baseEffect = [[GLKBaseEffect alloc] init];
//配置baseEffect光照信息
[self configureLight];
-(void)configureLight
{
//開啟light0光照
self.baseEffect.light0.enabled = GL_TRUE;
/*
union _GLKVector4
{
struct { float x, y, z, w; };
struct { float r, g, b, a; };
struct { float s, t, p, q; };
float v[4];
} __attribute__((aligned(16)));
typedef union _GLKVector4 GLKVector4;
union共用體
有3個(gè)結(jié)構(gòu)體,
比如表示頂點(diǎn)坐標(biāo)的x,y,z,w
比如表示顏色的,RGBA;
表示紋理的stpq
*/
//2.設(shè)置漫射光顏色,分別是red,green,blue,alpha
self.baseEffect.light0.diffuseColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
/*
The position of the light in world coordinates.
世界坐標(biāo)中的光的位置。
If the w component of the position is 0.0, the light is calculated using the directional light formula. The x, y, and z components of the vector specify the direction the light shines. The light is assumed to be infinitely far away; attenuation and spotlight properties are ignored.
如果位置的w分量為0,則使用定向光公式計(jì)算光。向量的x、y和z分量指定光的方向。光被認(rèn)為是無(wú)限遠(yuǎn)的,衰減和聚光燈屬性被忽略。
If the w component of the position is a non-zero value, the coordinates specify the position of the light in homogenous coordinates, and the light is either calculated as a point light or a spotlight, depending on the value of the spotCutoff property.
如果該位置的W組件是一個(gè)非零的值,指定的坐標(biāo)的光在齊次坐標(biāo)的位置,和光是一個(gè)點(diǎn)光源和聚光燈計(jì)算,根據(jù)不同的spotcutoff屬性的值
The default value is [0.0, 0.0, 1.0, 0.0].
默認(rèn)值[0.0f,0.0f,1.0f,0.0f];
*/
//燈光位置
self.baseEffect.light0.position = GLKVector4Make(1.0f, 0.0f, 0.2f, 0.0f);
//光的環(huán)境部分,分別是red,green,blue,alpha
self.baseEffect.light0.ambientColor = GLKVector4Make(0.2, 0.2, 0.2, 1.0f);
}
4.創(chuàng)建投影矩陣 -> 透視投影矩陣
//獲取屏幕縱橫比
GLfloat aspectRatio = self.view.frame.size.width / self.view.frame.size.height;
self.baseEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(-1.0f * aspectRatio, 1.0f * aspectRatio, -1.0, 1.0, 1.0, 120.0f);
5.設(shè)置模型矩形 -5.0f 表示往屏幕內(nèi)移動(dòng)-5.0f距離
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -5.0f);
6.設(shè)置清屏顏色
GLKVector4 colorVector4 = GLKVector4Make(0.0f, 0.0f, 0.0f, 1.0f);
[self setClearColor:colorVector4];
-(void)setClearColor:(GLKVector4)clearColorRGBA
{
glClearColor(clearColorRGBA.r, clearColorRGBA.g, clearColorRGBA.b, clearColorRGBA.a);
}
7.設(shè)置頂點(diǎn)數(shù)據(jù)
-(void)bufferData
{
//1、GLKMatrixStackCreate()創(chuàng)建一個(gè)新的空矩陣
self.modelViewMatrixStack = GLKMatrixStackCreate(kCFAllocatorDefault);
//2、為將要緩存區(qū)數(shù)據(jù)開辟空間
//sphereVerts 在sphere.h文件中存在
/*
參數(shù)1:數(shù)據(jù)大小 3個(gè)GLFloat類型,x,y,z
參數(shù)2:有多少個(gè)數(shù)據(jù),count
參數(shù)3:數(shù)據(jù)大小
參數(shù)4:用途 GL_STATIC_DRAW,
*/
//頂點(diǎn)數(shù)據(jù)緩存,頂點(diǎn)數(shù)據(jù)從sphere.h文件的sphereVerts數(shù)組中獲取頂點(diǎn)數(shù)據(jù)x,y,z
self.vertexPositionBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:(3 * sizeof(GLfloat)) numberOfVertices:sizeof(sphereVerts)/(3 * sizeof(GLfloat)) bytes:sphereVerts usage:GL_STATIC_DRAW];
//法線,光照坐標(biāo) sphereNormals數(shù)組 x,y,z
self.vertexNormalBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:(3 * sizeof(GLfloat)) numberOfVertices:sizeof(sphereNormals)/(3 * sizeof(GLfloat)) bytes:sphereNormals usage:GL_STATIC_DRAW];
//紋理坐標(biāo) sphereTexCoords數(shù)組 x,y
self.vertextTextureCoordBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:(2 * sizeof(GLfloat)) numberOfVertices:sizeof(sphereTexCoords)/ (2 * sizeof(GLfloat)) bytes:sphereTexCoords usage:GL_STATIC_DRAW];
//3.獲取地球紋理
CGImageRef earthImageRef = [UIImage imageNamed:@"Earth512x256.jpg"].CGImage;
//控制圖像加載方式的選項(xiàng)
NSDictionary *earthOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],GLKTextureLoaderOriginBottomLeft, nil];
//將紋理圖片加載到紋理數(shù)據(jù)對(duì)象earchTextureInfo中
/*
參數(shù)1:加載的紋理圖片
參數(shù)2:控制圖像加載的方式的選項(xiàng)-字典
參數(shù)3:錯(cuò)誤信息
*/
self.earchTextureInfo = [GLKTextureLoader textureWithCGImage:earthImageRef options:earthOptions error:NULL];
//4.獲取月亮紋理
CGImageRef moonImageRef = [UIImage imageNamed:@"Moon256x128"].CGImage;
NSDictionary *moonOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],GLKTextureLoaderOriginBottomLeft, nil];
self.moomTextureInfo = [GLKTextureLoader textureWithCGImage:moonImageRef options:moonOptions error:NULL];
//矩陣堆
//用所提供的矩陣替換最頂層矩陣,將self.baseEffect.transform.modelviewMatrix,替換self.modelViewMatrixStack
GLKMatrixStackLoadMatrix4(self.modelViewMatrixStack, self.baseEffect.transform.modelviewMatrix);
//初始化在軌道上月球位置
self.moonRotationAngleDegress = -20.0f;
}
開始代理繪制渲染操作 - GLKViewDelegate
//渲染場(chǎng)景
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
//設(shè)置清屏顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
//清空顏色緩存區(qū)和深度緩存區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//地球旋轉(zhuǎn)角度
_earthRotationAngleDegress += 360.0f/60.0f;
//月球旋轉(zhuǎn)角度
_moonRotationAngleDegress += (360.0f/60.0f)/SceneDaysPerMoonOrbit;
//2、準(zhǔn)備繪制
/*
其實(shí)就是把數(shù)據(jù)傳遞過(guò)去,然后指定讀取方式
參數(shù)1:數(shù)據(jù)是做什么用的
參數(shù)2:數(shù)據(jù)讀取個(gè)數(shù)
參數(shù)3:數(shù)據(jù)讀取索引
參數(shù)4:是否調(diào)用glEnableVertexAttribArray
著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
默認(rèn)情況下,出于性能考慮,所有頂點(diǎn)著色器的屬性(Attribute)變量都是關(guān)閉的,意味著數(shù)據(jù)在著色器端是不可見(jiàn)的,哪怕數(shù)據(jù)已經(jīng)上傳到GPU,由glEnableVertexAttribArray啟用指定屬性,才可在頂點(diǎn)著色器中訪問(wèn)逐頂點(diǎn)的屬性數(shù)據(jù)。glVertexAttribPointer或VBO只是建立CPU和GPU之間的邏輯連接,從而實(shí)現(xiàn)了CPU數(shù)據(jù)上傳至GPU。但是,數(shù)據(jù)在GPU端是否可見(jiàn),即,著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
那么,glEnableVertexAttribArray應(yīng)該在glVertexAttribPointer之前還是之后調(diào)用?答案是都可以,只要在繪圖調(diào)用(glDraw*系列函數(shù))前調(diào)用即可。
*/
[self.vertexPositionBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:0 shouldEnable:YES];
[self.vertexNormalBuffer prepareToDrawWithAttrib:GLKVertexAttribNormal numberOfCoordinates:3 attribOffset:0 shouldEnable:YES];
[self.vertextTextureCoordBuffer prepareToDrawWithAttrib:GLKVertexAttribTexCoord0 numberOfCoordinates:2 attribOffset:0 shouldEnable:YES];
//3.開始繪制
[self drawEarth];
[self drawMoon];
}
開始繪制地球
-(void)drawEarth
{
//獲取紋理的name、target
self.baseEffect.texture2d0.name = self.earchTextureInfo.name;
self.baseEffect.texture2d0.target = self.earchTextureInfo.target;
/*
current matrix:
1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
0.000000 0.000000 -5.000000 1.000000
為什么?因?yàn)槟阍趘iewDidLoad中設(shè)置的
//5.設(shè)置模型矩形 -5.0f表示往屏幕內(nèi)移動(dòng)-5.0f距離
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -5.0f);
*/
//將當(dāng)前的modelViewMatrixStack 壓棧
GLKMatrixStackPush(self.modelViewMatrixStack);
//在指定的軸上旋轉(zhuǎn)最上面的矩陣,圍繞x軸旋轉(zhuǎn)
GLKMatrixStackRotate(self.modelViewMatrixStack, GLKMathDegreesToRadians(SceneEarthAxialTiltDeg), 1.0f, 0.0f, 0.0f);
/*
current matrix:
1.000000 0.000000 0.000000 0.000000
0.000000 0.917060 0.398749 0.000000
0.000000 -0.398749 0.917060 0.000000
0.000000 0.000000 -5.000000 1.000000
為什么?
將矩陣與圍繞X旋轉(zhuǎn)的旋轉(zhuǎn)矩陣相乘,即可得上述結(jié)果
*/
self.baseEffect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(self.modelViewMatrixStack);
//準(zhǔn)備繪制
[self.baseEffect prepareToDraw];
//調(diào)用AGLKVertexAttribArrayBuffer,繪制圖形
/*
參數(shù)1:繪制的方式,三角形
參數(shù)2:繪制數(shù)據(jù)讀取的索引
參數(shù)3:繪制數(shù)據(jù)的大小
*/
[AGLKVertexAttribArrayBuffer drawPreparedArraysWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:sphereNumVerts];
//繪制完畢,則出棧
/*
current matrix:
0.994522 0.041681 -0.095859 0.000000
0.000000 0.917060 0.398749 0.000000
0.104528 -0.396565 0.912036 0.000000
0.000000 0.000000 -5.000000 1.000000
*/
GLKMatrixStackPop(self.modelViewMatrixStack);
/*
current matrix:
1.000000 0.000000 0.000000 0.000000
0.000000 1.000000 0.000000 0.000000
0.000000 0.000000 1.000000 0.000000
0.000000 0.000000 -5.000000 1.000000
*/
self.baseEffect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(self.modelViewMatrixStack);
}
繪制月亮
-(void)drawMoon
{
//獲取紋理的name、target
self.baseEffect.texture2d0.name = self.moomTextureInfo.name;
self.baseEffect.texture2d0.target = self.moomTextureInfo.target;
//壓棧
GLKMatrixStackPush(self.modelViewMatrixStack);
//圍繞Y軸旋轉(zhuǎn)moonRotationAngleDegress角度
//自轉(zhuǎn)
GLKMatrixStackRotate(self.modelViewMatrixStack, GLKMathDegreesToRadians(self.moonRotationAngleDegress), 0.0f, 1.0f, 0.0f);
//平移 - 月球距離地球的距離
GLKMatrixStackTranslate(self.modelViewMatrixStack, 0.0f, 0.0f, SceneMoonDistanceFromEarth);
//縮放,把月球縮放
GLKMatrixStackScale(self.modelViewMatrixStack, SceneMoonRadiusFractionOfEarth, SceneMoonRadiusFractionOfEarth, SceneMoonRadiusFractionOfEarth);
//旋轉(zhuǎn) 圍繞Y軸旋轉(zhuǎn)
GLKMatrixStackRotate(self.modelViewMatrixStack, GLKMathDegreesToRadians(self.moonRotationAngleDegress), 0.0f, 1.0f, 0.0f);
self.baseEffect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(self.modelViewMatrixStack);
[self.baseEffect prepareToDraw];
[AGLKVertexAttribArrayBuffer drawPreparedArraysWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:sphereNumVerts];
//設(shè)置完畢出棧
GLKMatrixStackPop(self.modelViewMatrixStack);
self.baseEffect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(self.modelViewMatrixStack);
}
Switch切換正投影或透視投影
#pragma mark -Switch Click
//切換正投影效果或透視投影效果
- (IBAction)switchClick:(UISwitch *)sender {
GLfloat aspect = self.view.bounds.size.width / self.view.bounds.size.height;
if ([sender isOn]) {
//正投影
self.baseEffect.transform.projectionMatrix = GLKMatrix4MakeFrustum(-1.0f * aspect, 1.0f * aspect, -1.0, 1.0, 2.0f, 120.0);
}else {
//透視投影
self.baseEffect.transform.projectionMatrix = GLKMatrix4MakeOrtho(-1.0f * aspect, 1.0f * aspect, -1.0, 1.0, 2.0f, 120.0);
}
}
github地址:YSEarthAndMoon.git