本文原始地址:OpenCV for Ios 學習筆記(9)-用OPENGL渲染AR場景2
drawFrame的操作步驟是:
1.清除場景。
2.啓動正射投影繪製背景。
3.在視口繪製最後一個從相機獲取到的圖像。
4.根據相機內在參數設置透視投影。
5.把每個偵測到的標記的座標系移動到標記的3維位置(把4x4的變換矩陣應用到opengl的模型矩陣上)。
6.呈現一個任意的3維物體。
7.展示幀緩存。
我們將會在幀準備好時調用drawFrame - 當一個新的相機幀被上傳到視頻內存中,並且標記監測的步驟已經完成。
下面的就是drawFrame的代碼:
- - (void)drawFrame
- {
- //啓動激活的幀緩存
- [m_glview setFramebuffer];
- // 繪製背景
- [self drawBackground];
- // 在檢測到的標記處繪製3維物體
- [self drawAR];
- // 呈現幀緩存
- bool ok = [m_glview presentFramebuffer];
- int glErCode = glGetError();
- if (!ok || glErCode != GL_NO_ERROR)
- {
- std::cerr << "opengl 檢測出錯,錯誤代碼是:" << glErCode << std::endl;
- }
- }
繪製一個背景對我們來說相當容易。我們啓用正視投影並繪製一個全屏的當前幀的圖像紋理。下面是opengl es1的代碼:
- - (void) drawBackground
- {
- //獲取視口的寬高
- GLfloat w = m_glview.bounds.size.width;
- GLfloat h = m_glview.bounds.size.height;
- //四個頂點座標
- const GLfloat squareVertices[] =
- {
- 0, 0,
- w, 0,
- 0, h,
- w, h
- };
- //紋理頂點
- static const GLfloat textureVertices[] =
- {
- 1, 0,
- 1, 1,
- 0, 0,
- 0, 1
- };
- //正視矩陣
- static const GLfloat proj[] =
- {
- 0, -2.f/w, 0, 0,
- -2.f/h, 0, 0, 0,
- 0, 0, 1, 0,
- 1, 1, 0, 1
- };
- /*
- glMatrixMode - 指定哪一個矩陣是當前矩陣
- mode 指定哪一個矩陣堆棧是下一個矩陣操作的目標,可選值: GL_MODELVIEW、GL_PROJECTION、GL_TEXTURE.
- 說明
- glMatrixMode設置當前矩陣模式:
- GL_MODELVIEW,對模型視景矩陣堆棧應用隨後的矩陣操作.
- GL_PROJECTION,對投影矩陣應用隨後的矩陣操作.
- GL_TEXTURE,對紋理矩陣堆棧應用隨後的矩陣操作.
- 與glLoadIdentity()一同使用
- glLoadIdentity():該函數的功能是重置當前指定的矩陣爲單位矩陣。
- 在glLoadIdentity()之後我們爲場景設置了透視圖。glMatrixMode(GL_MODELVIEW)設置當前矩陣爲模型視圖矩陣,模型視圖矩陣儲存了有關物體的信息。
- */
- glMatrixMode(GL_PROJECTION);
- glLoadMatrixf(proj);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glDepthMask(FALSE);
- glDisable(GL_COLOR_MATERIAL);
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, m_backgroundTextureId);
- // 更新屬性值.
- glVertexPointer(2, GL_FLOAT, 0, squareVertices);
- glEnableClientState(GL_VERTEX_ARRAY);
- glTexCoordPointer(2, GL_FLOAT, 0, textureVertices);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glColor4f(1,1,1,1);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisable(GL_TEXTURE_2D);
- }
在場景中渲染虛擬物體是很機靈的事。首先我們需要根據相機內在參數,適應OpenGL投影矩陣。如果沒有這步,我們將得到錯誤的透射投影,這會使虛擬的物體看起來不那麼自然,好像“飄在空中”,沒有真實感!正確的投影是所有增強現實應用的必備。
下面是根據相機內參創建OpenGL投影矩陣的一片代碼:
- - (void)buildProjectionMatrix:(Matrix33)cameraMatrix: (int)screen_width: (int)screen_height: (Matrix44&) projectionMatrix
- {
- float near = 0.01; // 近裁剪距離
- float far = 100; // 遠裁剪距離
- // 相機的參數
- float f_x = cameraMatrix.data[0]; // Focal length in x axis
- float f_y = cameraMatrix.data[4]; // Focal length in y axis (usually the same?)
- float c_x = cameraMatrix.data[2]; // Camera primary point x
- float c_y = cameraMatrix.data[5]; // Camera primary point y
- projectionMatrix.data[0] = - 2.0 * f_x / screen_width;
- projectionMatrix.data[1] = 0.0;
- projectionMatrix.data[2] = 0.0;
- projectionMatrix.data[3] = 0.0;
- projectionMatrix.data[4] = 0.0;
- projectionMatrix.data[5] = 2.0 * f_y / screen_height;
- projectionMatrix.data[6] = 0.0;
- projectionMatrix.data[7] = 0.0;
- projectionMatrix.data[8] = 2.0 * c_x / screen_width - 1.0;
- projectionMatrix.data[9] = 2.0 * c_y / screen_height - 1.0;
- projectionMatrix.data[10] = -( far+near ) / ( far - near );
- projectionMatrix.data[11] = -1.0;
- projectionMatrix.data[12] = 0.0;
- projectionMatrix.data[13] = 0.0;
- projectionMatrix.data[14] = -2.0 * far * near / ( far - near );
- projectionMatrix.data[15] = 0.0;
- }
任何一個變換都能夠被4x4矩陣呈現並且載入到OpenGL模型視圖矩陣,這一步將會把座標系移動到世界座標系中的標記處。
例如,讓我們在每個標記的上方繪製一個座標軸-它將會展示標記的在空間中的方向,並用漸變的矩形填充整個標記
。這個視覺化操作將會像預期一樣給我們視覺上反饋。下面是drawAR函數的實現:
- - (void) drawAR
- {
- Matrix44 projectionMatrix;
- //相機的內參 屏幕的寬高 待輸出的投影矩陣
- [self buildProjectionMatrix:m_calibration.getIntrinsic():m_frameSize.width :m_frameSize.height :projectionMatrix];
- glMatrixMode(GL_PROJECTION);
- glLoadMatrixf(projectionMatrix.data);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glDepthMask(TRUE);
- glEnable(GL_DEPTH_TEST);
- //glDepthFunc(GL_LESS);
- //glDepthFunc(GL_GREATER);
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_NORMAL_ARRAY);
- glPushMatrix();
- glLineWidth(3.0f);
- //三條軸
- float lineX[] = {0,0,0,1,0,0};
- float lineY[] = {0,0,0,0,1,0};
- float lineZ[] = {0,0,0,0,0,1};
- const GLfloat squareVertices[] = {
- -0.5f, -0.5f,
- 0.5f, -0.5f,
- -0.5f, 0.5f,
- 0.5f, 0.5f,
- };
- const GLubyte squareColors[] = {
- 255, 255, 0, 255,
- 0, 255, 255, 255,
- 0, 0, 0, 0,
- 255, 0, 255, 255,
- };
- //遍歷標記變換
- for (size_t transformationIndex=0; transformationIndex<m_transformations.size(); transformationIndex++)
- {
- const Transformation& transformation = m_transformations[transformationIndex];
- Matrix44 glMatrix = transformation.getMat44();
- glLoadMatrixf(reinterpret_cast<const GLfloat*>(&glMatrix.data[0]));
- // 繪製數據
- glVertexPointer(2, GL_FLOAT, 0, squareVertices);
- glEnableClientState(GL_VERTEX_ARRAY);
- glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareColors);
- glEnableClientState(GL_COLOR_ARRAY);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- glDisableClientState(GL_COLOR_ARRAY);
- float scale = 0.5;
- glScalef(scale, scale, scale);
- glTranslatef(0, 0, 0.1f);
- glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
- glVertexPointer(3, GL_FLOAT, 0, lineX);
- glDrawArrays(GL_LINES, 0, 2);
- glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
- glVertexPointer(3, GL_FLOAT, 0, lineY);
- glDrawArrays(GL_LINES, 0, 2);
- glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
- glVertexPointer(3, GL_FLOAT, 0, lineZ);
- glDrawArrays(GL_LINES, 0, 2);
- }
- glPopMatrix();
- glDisableClientState(GL_VERTEX_ARRAY);
- }
作爲一個例子的結尾,我們將演示我們的成果並總結。