ARKit & OpenGL ES - OpenGL實(shí)現(xiàn)篇

點(diǎn)擊獲取本文示例代碼, 本文代碼在分支ARKit中您朽。

如果你想了解ATRKit的基礎(chǔ)知識(shí),請(qǐng)?jiān)L問ARKit & OpenGL ES - ARKit原理篇
如果你想了解更多關(guān)于OpenGL ES的知識(shí)个少,請(qǐng)移步至OpenGL ES相關(guān)文章目錄

本文所用OpenGL基礎(chǔ)代碼來自OpenGL ES系列,具備渲染幾何體潭辈,紋理等基礎(chǔ)功能线罕,實(shí)現(xiàn)細(xì)節(jié)將不贅述。

集成ARKit的關(guān)鍵代碼都在ARGLBaseViewController中般哼。我們來看一下它的代碼吴汪。

處理ARFrame

- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame {
    // 同步Y(jié)UV信息到 yTexture 和 uvTexture
    CVPixelBufferRef pixelBuffer = frame.capturedImage;
    GLsizei imageWidth = (GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
    GLsizei imageHeight = (GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
    void * baseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    
    glBindTexture(GL_TEXTURE_2D, self.yTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, imageWidth, imageHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, baseAddress);
    glBindTexture(GL_TEXTURE_2D, 0);
    
    imageWidth = (GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
    imageHeight = (GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
    void *laAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    glBindTexture(GL_TEXTURE_2D, self.uvTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, imageWidth, imageHeight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, laAddress);
    glBindTexture(GL_TEXTURE_2D, 0);
    
    self.videoPlane.yuv_yTexture = self.yTexture;
    self.videoPlane.yuv_uvTexture = self.uvTexture;
    [self setupViewport: CGSizeMake(imageHeight, imageWidth)];
    
    // 同步攝像機(jī)
    matrix_float4x4 cameraMatrix = matrix_invert([frame.camera transform]);
    GLKMatrix4 newCameraMatrix = GLKMatrix4Identity;
    for (int col = 0; col < 4; ++col) {
        for (int row = 0; row < 4; ++row) {
            newCameraMatrix.m[col * 4 + row] = cameraMatrix.columns[col][row];
        }
    }
    
    self.cameraMatrix = newCameraMatrix;
    GLKVector3 forward = GLKVector3Make(-self.cameraMatrix.m13, -self.cameraMatrix.m23, -self.cameraMatrix.m33);
    GLKMatrix4 rotationMatrix = GLKMatrix4MakeRotation(M_PI / 2, forward.x, forward.y, forward.z);
    self.cameraMatrix = GLKMatrix4Multiply(rotationMatrix, newCameraMatrix);
}

上面的代碼展示了如何處理ARKit捕捉的ARFrame,ARFramecapturedImage存儲(chǔ)了攝像頭捕捉的圖片信息蒸眠,類型是CVPixelBufferRef漾橙。默認(rèn)情況下,圖片信息的格式是YUV楞卡,通過兩個(gè)Plane來存儲(chǔ)霜运,也可以理解為兩張圖片。一張格式是Y(Luminance)蒋腮,保存了明度信息淘捡,另一張是UV(Chrominance、Chroma)池摧,保存了色度和濃度焦除。我們需要把這兩張圖分別綁定到不同的紋理上,然后在Shader中利用算法將YUV轉(zhuǎn)換成RGB作彤。下面是處理紋理的Fragment Shader膘魄,利用公式進(jìn)行顏色轉(zhuǎn)換。

precision highp float;

varying vec3 fragNormal;
varying vec2 fragUV;

uniform float elapsedTime;
uniform mat4 normalMatrix;
uniform sampler2D yMap;
uniform sampler2D uvMap;

void main(void) {
    vec4 Y_planeColor = texture2D(yMap, fragUV);
    vec4 CbCr_planeColor = texture2D(uvMap, fragUV);
    
    float Cb, Cr, Y;
    float R ,G, B;
    Y = Y_planeColor.r * 255.0;
    Cb = CbCr_planeColor.r * 255.0 - 128.0;
    Cr = CbCr_planeColor.a * 255.0 - 128.0;
    
    R = 1.402 * Cr + Y;
    G = -0.344 * Cb - 0.714 * Cr + Y;
    B = 1.772 * Cb + Y;
    
    
    vec4 videoColor = vec4(R / 255.0, G / 255.0, B / 255.0, 1.0);
    gl_FragColor = videoColor;
}

處理并綁定好紋理后宦棺,為了保證不同屏幕尺寸下瓣距,紋理不被非等比拉伸,所以對(duì)viewport進(jìn)行重了新計(jì)算[self setupViewport: CGSizeMake(imageHeight, imageWidth)];代咸。接下來將ARKit計(jì)算出來的攝像機(jī)的變換賦值給self.cameraMatrix蹈丸。注意ARKit捕捉的圖片需要旋轉(zhuǎn)90度后才能正常顯示,所以在設(shè)置Viewport時(shí)特意顛倒了寬和高,并在最后對(duì)攝像機(jī)進(jìn)行了旋轉(zhuǎn)逻杖。

VideoPlane

VideoPlane是為了顯示視頻編寫的幾何體奋岁,它能夠接收兩個(gè)紋理,Y和UV荸百。

@interface VideoPlane : GLObject
@property (assign, nonatomic) GLuint yuv_yTexture;
@property (assign, nonatomic) GLuint yuv_uvTexture;
- (instancetype)initWithGLContext:(GLContext *)context;
- (void)update:(NSTimeInterval)timeSinceLastUpdate;
- (void)draw:(GLContext *)glContext;
@end

...

- (void)draw:(GLContext *)glContext {
    [glContext setUniformMatrix4fv:@"modelMatrix" value:self.modelMatrix];
    bool canInvert;
    GLKMatrix4 normalMatrix = GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert);
    [glContext setUniformMatrix4fv:@"normalMatrix" value:canInvert ? normalMatrix : GLKMatrix4Identity];
    [glContext bindTextureName:self.yuv_yTexture to:GL_TEXTURE0 uniformName:@"yMap"];
    [glContext bindTextureName:self.yuv_uvTexture to:GL_TEXTURE1 uniformName:@"uvMap"];
    [glContext drawTrianglesWithVAO:vao vertexCount:6];
}

其他的功能很簡(jiǎn)單闻伶,就是繪制一個(gè)正方形,最終配合顯示視頻的Shader够话,渲染YUV格式的數(shù)據(jù)蓝翰。

透視投影矩陣

在ARFrame可以獲取渲染需要的紋理和攝像機(jī)矩陣,除了這些女嘲,和真實(shí)攝像頭匹配的透視投影矩陣也是必須的畜份。它能夠讓渲染出來的3D物體透視看起來很自然。

- (void)session:(ARSession *)session cameraDidChangeTrackingState:(ARCamera *)camera {
    matrix_float4x4 projectionMatrix = [camera projectionMatrixWithViewportSize:self.viewport.size orientation:UIInterfaceOrientationPortrait zNear:0.1 zFar:1000];
    GLKMatrix4 newWorldProjectionMatrix = GLKMatrix4Identity;
    for (int col = 0; col < 4; ++col) {
        for (int row = 0; row < 4; ++row) {
           newWorldProjectionMatrix.m[col * 4 + row] = projectionMatrix.columns[col][row];
        }
    }
    self.worldProjectionMatrix = newWorldProjectionMatrix;
}

上面的代碼演示了如何通過ARKit獲取3D透視投影矩陣欣尼,有了透視投影矩陣和攝像機(jī)矩陣爆雹,就可以很方便的利用OpenGL渲染物體了。

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    [super glkView:view drawInRect:rect];
    
    [self.objects enumerateObjectsUsingBlock:^(GLObject *obj, NSUInteger idx, BOOL *stop) {
        [obj.context active];
        [obj.context setUniform1f:@"elapsedTime" value:(GLfloat)self.elapsedTime];
        [obj.context setUniformMatrix4fv:@"projectionMatrix" value:self.worldProjectionMatrix];
        [obj.context setUniformMatrix4fv:@"cameraMatrix" value:self.cameraMatrix];
        
        [obj.context setUniform3fv:@"lightDirection" value:self.lightDirection];
        [obj draw:obj.context];
    }];
}

本文主要介紹了OpenGL ES渲染ARKit的基本思路愕鼓,沒有對(duì)OpenGL ES技術(shù)細(xì)節(jié)描述太多钙态。如果你有興趣,可以直接閱讀示例中的代碼深入了解菇晃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末册倒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子谋旦,更是在濱河造成了極大的恐慌剩失,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件册着,死亡現(xiàn)場(chǎng)離奇詭異拴孤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)甲捏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門演熟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人司顿,你說我怎么就攤上這事芒粹。” “怎么了大溜?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵化漆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我钦奋,道長(zhǎng)座云,這世上最難降的妖魔是什么疙赠? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮朦拖,結(jié)果婚禮上圃阳,老公的妹妹穿的比我還像新娘。我一直安慰自己璧帝,他們只是感情好捍岳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睬隶,像睡著了一般锣夹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苏潜,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天晕城,我揣著相機(jī)與錄音抓于,去河邊找鬼担租。 笑死潦嘶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赃梧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼豌熄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼授嘀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锣险,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蹄皱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后芯肤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巷折,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年崖咨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锻拘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡击蹲,死狀恐怖署拟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歌豺,我是刑警寧澤推穷,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站类咧,受9級(jí)特大地震影響馒铃,放射性物質(zhì)發(fā)生泄漏蟹腾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一骗露、第九天 我趴在偏房一處隱蔽的房頂上張望岭佳。 院中可真熱鬧,春花似錦萧锉、人聲如沸珊随。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叶洞。三九已至,卻和暖如春禀崖,著一層夾襖步出監(jiān)牢的瞬間衩辟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工波附, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艺晴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓掸屡,卻偏偏與公主長(zhǎng)得像封寞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仅财,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容