經過OpenGL ES在iOS平臺實踐加強現實(一)

  1. 本文采用OpenGL ES 1固定渲染管線實現,目標爲在設備拍攝到的現實世界中,繪製世界座標軸,並根據設備所在位置和朝向,繪製周圍必定範圍內的指定目標(好比餐廳,咖啡館等)。首先說明幾個OpenGL的容易混淆的基礎知識
    • OpenGL採用右手座標系(伸出你的右手,拇指和食指垂直,中指分別和拇指,食指垂直,此時拇指表明x座標軸,食指表明y座標軸,中指表明z座標軸,這就是右手座標系)測試

    • OpenGL採用列向量,因此矩陣與向量運算爲矩陣左乘spa

    • OpenGLglMutMatrixf等操做爲右乘指針

    • OpenGL採用列主序存儲矩陣code

  2. 下面爲在iOS平臺初始化繪製環境的代碼
    1. EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
      self.context = context;
              
      [EAGLContext setCurrentContext:context];
              
      glGenFramebuffersOES(1, &_framebuffer); // 建立幀緩衝區
      glGenRenderbuffersOES(1, &_renderbuffer);   // 建立繪製緩衝區
      glBindFramebufferOES(GL_FRAMEBUFFER_OES, _framebuffer); // 綁定幀緩衝區到渲染管線
      glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer);  // 綁定繪製緩衝區到渲染管線
      glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _renderbuffer); // 綁定繪製緩衝區到幀緩衝區
              
      GLint width;
      GLint height;
      [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];   // 爲繪製緩衝區分配存儲區,此處將CAEAGLLayer的繪製存儲區做爲繪製緩衝區的存儲區
      glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);    // 獲取繪製緩衝區的像素寬度
      glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);  // 獲取繪製緩衝區的像素高度
              
      glGenRenderbuffersOES(1, &_depthbuffer);    // 建立深度緩衝區
      glBindRenderbufferOES(GL_RENDERBUFFER_OES, _depthbuffer);   // 綁定深度緩衝區到渲染管線
      glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);    // 爲深度緩衝區分配存儲區
      glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, _depthbuffer);   // 綁定深度緩衝區到幀緩衝區
              
      glMatrixMode(GL_PROJECTION);    // 改變矩陣變換模式到投影矩陣,之後的矩陣操做都會是對投影矩陣操做
              
      GLfloat w = 0.5 * tanf(M_PI / 8);
      glFrustumf(-w, w, -w*height/width, w*height/width, 0.5, 3000);  // 視錐定義
      glViewport(0, 0, width, height);    // 視口定義

       

  3. 本文實現了一個攝像機類,根據攝像機的位置和朝向得到攝像機世界矩陣的逆矩陣
    1. - (void)getViewMatrix:(GLfloat *)matrix
      {
          GLfloat x = _orientation.x;
          GLfloat y = _orientation.y;
          GLfloat z = _orientation.z;
          GLfloat w = _orientation.w;
          GLfloat *rot = malloc(sizeof(GLfloat) * 16);
          rot[0] = 1-2*y*y-2*z*z;
          rot[1] = 2*x*y-2*w*z;
          rot[2] = 2*x*z+2*w*y;
          rot[3] = 0.0;
          rot[4] = 2*x*y+2*w*z;
          rot[5] = 1-2*x*2-2*z*z;
          rot[6] = 2*y*z-2*w*x;
          rot[7] = 0.0;
          rot[8] = 2*x*z-2*w*y;
          rot[9] = 2*y*z+2*w*z;
          rot[10] = 1-2*x*x-2*y*y;
          rot[11] = 0.0;
          rot[12] = 0;
          rot[13] = 0;
          rot[14] = 0;
          rot[15] = 1.0;
          
          GLfloat transX = -rot[0]*_position.x - rot[4]*_position.y - rot[8]*_position.z;
          GLfloat transY = -rot[1]*_position.x - rot[5]*_position.y - rot[9]*_position.z;
          GLfloat transZ = -rot[2]*_position.x - rot[6]*_position.y - rot[10]*_position.z;
          
          rot[12] = transX;
          rot[13] = transY;
          rot[14] = transZ;
          
          memcpy(matrix, rot, sizeof(GLfloat)*16);
          free(rot);
      }

       

  4. 本文實現了一個紋理類,用來簡化紋理操做
    1. - (void)getViewMatrix:(GLfloat *)matrix
      {
          GLfloat x = _orientation.x;
          GLfloat y = _orientation.y;
          GLfloat z = _orientation.z;
          GLfloat w = _orientation.w;
          GLfloat *rot = malloc(sizeof(GLfloat) * 16);
          rot[0] = 1-2*y*y-2*z*z;
          rot[1] = 2*x*y-2*w*z;
          rot[2] = 2*x*z+2*w*y;
          rot[3] = 0.0;
          rot[4] = 2*x*y+2*w*z;
          rot[5] = 1-2*x*2-2*z*z;
          rot[6] = 2*y*z-2*w*x;
          rot[7] = 0.0;
          rot[8] = 2*x*z-2*w*y;
          rot[9] = 2*y*z+2*w*z;
          rot[10] = 1-2*x*x-2*y*y;
          rot[11] = 0.0;
          rot[12] = 0;
          rot[13] = 0;
          rot[14] = 0;
          rot[15] = 1.0;
          
          GLfloat transX = -rot[0]*_position.x - rot[4]*_position.y - rot[8]*_position.z;
          GLfloat transY = -rot[1]*_position.x - rot[5]*_position.y - rot[9]*_position.z;
          GLfloat transZ = -rot[2]*_position.x - rot[6]*_position.y - rot[10]*_position.z;
          
          rot[12] = transX;
          rot[13] = transY;
          rot[14] = transZ;
          
          memcpy(matrix, rot, sizeof(GLfloat)*16);
          free(rot);
      }

       

  5. 下面是繪製過程的代碼
    1. - (void)render
      {
          glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer);  //  綁定繪製緩衝區到渲染管線
          //glClearColor(0.0, 0.0, 0.0, 0.0);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空繪製緩衝區和深度緩衝區
          glEnableClientState(GL_VERTEX_ARRAY);
          
          MZGLCamera *camera = self.camera;
          GLfloat *matrix = malloc(sizeof(GLfloat) * 16);
          [camera getViewMatrix:matrix];
      
          glMatrixMode(GL_MODELVIEW_MATRIX);  // 改變矩陣變換模式到模型矩陣
          glLoadIdentity();   // 將模型矩陣更新爲單位矩陣
          glEnable(GL_DEPTH_TEST);    // 開始深度測試
          glDepthFunc(GL_LESS);   // 切換深度測試模式爲待繪製像素距離屏幕距離小於深度緩衝區當前值則繪製,不然不繪製
          
          glLoadMatrixf(matrix);  // 根據攝像機位置設置模型矩陣,此處的矩陣爲攝像機世界矩陣的逆矩陣
          
          glVertexPointer(3, GL_FLOAT, 0, _lineVertexBuffer);
          glColor4f(1.0, 1.0, 0.0, 1.0);
          glDrawElements(GL_LINES, _lineVertexCount, GL_UNSIGNED_BYTE, _lineIndexBuffer); // 繪製世界座標系的座標軸
          
          glEnableClientState(GL_TEXTURE_COORD_ARRAY);
          glEnable(GL_TEXTURE_2D);    // 開啓紋理繪製
          glEnable(GL_ALPHA_TEST);    // 開啓Alpha測試
          glAlphaFunc(GL_GREATER, 0.5f);  // 切換Alpha測試模式爲不透明度大於0.5則繪製,不然不繪製
          glColor4f(1.0, 1.0, 1.0, 1.0);  // 填充繪製緩衝區
          NSArray *values = [self.entityDictionary allValues];
          for (NSObject *entity in values) {
              if ([entity conformsToProtocol:@protocol(MZGLRenderable)]) {
                  if ([entity isKindOfClass:[MZGLBillboard class]]) {
                      MZGLBillboard *billboard = (MZGLBillboard *)entity;
                      
                      glVertexPointer(3, GL_FLOAT, 0, billboard.vertexBuffer);    // 設置頂點緩衝指針
                      glTexCoordPointer(2, GL_FLOAT, 0, billboard.coordinates);   // 設置紋理座標指針
                      [billboard preRender:self];
                      glLoadIdentity();
                      GLfloat *transform = [billboard worldTrasform]; // 設置模型世界矩陣
                      glLoadMatrixf(matrix);  // 將模型矩陣設置爲攝像機世界矩陣的逆矩陣
                      glMultMatrixf(transform);   // 右乘模型的世界矩陣
                      [billboard.texure bind];    // 綁定紋理
                      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  // 繪製頂點
                  }
              }
          }
      
          [_context presentRenderbuffer:GL_RENDERBUFFER]; // 繪製到繪製緩衝區
      }

       

  6. 下面是在測試數據在模擬器上的效果,後續會說明如何結合陀螺儀去將虛擬世界中的攝像頭和設備綁定到一塊兒
相關文章
相關標籤/搜索