OpenGL ES學(xué)習(xí)之路(7.0) 地球與月亮旋轉(zhuǎn)

目的

本案例主要實(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容