原理 この記事で使用されているコードは、https://github.com/SquarePants1991/OpenGLESLearn.git の ARKit ブランチにあります。 iOS11では新しいフレームワークARKitが導入され、ARKitとSceneKitを通じてARアプリを簡単に作成できるようになりました。 Apple は基本的な AR アプリケーション フレームワークも提供しており、そこから直接 AR アプリの開発を開始できます。 ただし、このシリーズの記事では、OpenGL ES を使用して ARKit のレンダリング サポートを提供します。次に、まず ARKit の理論的な知識について学習しましょう。 ARの基本概念 AR の最も基本的な概念は、仮想コンピュータグラフィックスと実際の環境を組み合わせる技術です。このテクノロジーを実装する方法は多数あります。
ワールドトラッキング 現実世界の特徴点を追跡し、実際のカメラの位置を計算し、それを 3D 世界の仮想カメラに適用することが、AR 実装の最も重要な部分です。計算結果の精度はレンダリング結果に直接影響します。 ARKit は、カメラ位置の計算を含む AR 処理フロー全体を ARSession を使用して管理します。 #pragma make - AR コントロール - (void)セットアップAR { (@利用可能(iOS 11.0、*)) { self.arSession = [ARSession新規]; self.arSession.delegate = 自分; } } - (void)runAR { (@利用可能(iOS 11.0、*)) { ARWorldTrackingSessionConfiguration *config = [ARWorldTrackingSessionConfiguration new]; config.planeDetection = ARPlaneDetectionHorizontal; [self.arSession 実行構成:config]; } } - (void)一時停止AR { (@利用可能(iOS 11.0、*)) { [self.arSession 一時停止]; } } ARSession の使い方は非常に簡単です。デリゲートを初期化して設定します。ARSession を開始するには、構成 ARWorldTrackingSessionConfiguration を渡す必要があります。ARWorldTrackingSessionConfiguration は、AR システムが現実世界の特徴点を追跡し、カメラの位置を計算することを意味します。 Apple は将来、トラッキング マーカーを識別するために ARMarkerTrackingSessionConfiguration などの構成をリリースする可能性もあります。 ARSession をオンにすると、カメラが起動し、センサーを通じて携帯電話の位置が感知されます。 WWDCから写真を拝借。 ARSession は、カメラでキャプチャされたビデオ ストリームと位置情報を統合して、一連の連続した ARFrame を生成します。 - (void)セッション:(ARSession *)セッションdidUpdateFrame:(ARFrame *)フレーム{ ... } 各 ARFrame には、カメラで撮影された画像やカメラの位置に関する情報などが含まれます。この方法では、カメラで撮影した画像を描画する必要があります。カメラの位置などの情報に基づいて 3D オブジェクトを描画します。 平面検出 ARKit には、現実世界の平面を検出し、平面の位置、サイズ、方向、その他の情報を記述する ARPlaneAnchor オブジェクトを提供するという、もう 1 つの優れた機能があります。 - (void)runAR { (@利用可能(iOS 11.0、*)) { ARWorldTrackingSessionConfiguration *config = [ARWorldTrackingSessionConfiguration new]; config.planeDetection = ARPlaneDetectionHorizontal; [self.arSession 実行構成:config]; } } 上記の config.planeDetection = ARPlaneDetectionHorizontal; は、検出された平面のタイプを水平に設定します。ただし、これが現在利用できる唯一のオプションです。 ARKit が平面を検出すると、デリゲート メソッド (void)session:(ARSession *)session didAddAnchors:(NSArray *)anchors を通じてデータが提供されます。 ARAnchor が ARPlaneAnchor であるかどうかを判断し、平面が検出されたかどうかを判断できます。 ARAnchor は、実際の環境における 3D オブジェクトの位置を表すために使用されます。 3D オブジェクトと ARAnchor の 3D 変換を同期させるだけで、AR 効果を実現できます。 ヒットテスト ヒット テストを使用すると、検出された平面にオブジェクトを簡単に配置できます。画面をタップすると、Hit Test を使用してタップした場所にある平面を検出し、オブジェクトが配置される場所を設定するための ARAnchor を提供します。 [フレーム hitTest:CGPointMake(0.5, 0.5) タイプ:ARHitTestResultTypeExistingPlane]; ARFrame の hitTest メソッドを使用すると、渡される最初のポイントの範囲は (0,0) から (1,1) までとなり、2 番目のパラメータは検出できるオブジェクトを表します。検出できる物体は以下の通りです。
検出が成功すると、NSArray * が返されます。ARHitTestResult には、検出タイプ、交差点の距離、平面の ARAnchor が含まれます。 ARAnchor は、ARHitTestResultTypeExistingPlane と ARHitTestResultTypeExistingPlaneUsingExtent が検出された場合にのみ使用可能であることに注意してください。これら 4 つのテスト タイプは、ARHitTestResultTypeEstimatedHorizontalPlane | ARHitTestResultTypeExistingPlane のように、| の形式で同時に存在できます。 光量調整 ARKit は、主に 3D モデルの照明強度を環境の照明強度と一致させるために、光の強度を検出する機能も提供します。 ARFrame には lightEstimate 変数があり、光の強度が正常に検出された場合に値が設定されます。値の型は ARLightEstimate で、1 つの変数 (ambientIntensity) のみが含まれます。 3D 照明モデルでは、周囲光に対応し、その値の範囲は 0 ~ 2000 です。 OpenGL でレンダリングする場合、この値を使用して照明モデルの周囲光の強度を調整できます。 ARKit の理論的な知識はこれでほぼ終わりです。次の記事では、OpenGL ES を使用して ARFrame のコンテンツをレンダリングする方法を紹介します。 実装 この記事で使用されているコードは、https://github.com/SquarePants1991/OpenGLESLearn.git の ARKit ブランチにあります。 この記事で使用している OpenGL の基本コードは、ジオメトリやテクスチャのレンダリングなどの基本機能を備えた OpenGL ES シリーズのものです。実装の詳細については繰り返しません。 ARKit を統合するためのキーコードは ARGLBaseViewController にあります。コードを見てみましょう。 ARFrameの処理 - (void)セッション:(ARSession *)セッションdidUpdateFrame:(ARFrame *)フレーム{ // YUV情報をyTextureとuvTextureに同期する CVPixelBufferRef ピクセルバッファ = frame.capturedImage; GLsizei 画像の幅 = (GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); GLsizei イメージの高さ = (GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); void * ベースアドレス = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0); glBindTexture(GL_TEXTURE_2D、self.yTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 画像幅、画像高さ、0, GL_LUMINANCE, GL_UNSIGNED_BYTE, ベースアドレス); glBindTexture(GL_TEXTURE_2D, 0); 画像の幅 = (GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer, 1); 画像の高さ = (GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); void *laAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1); glBindTexture(GL_TEXTURE_2D、self.uvTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 画像の幅、画像の高さ、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 に追加します。 [自己設定ビューポート: CGSizeMake(imageHeight, imageWidth)]; // カメラを同期する matrix_float4x4 cameraMatrix = matrix_invert([frame.camera transform]); GLKMatrix4 の新しいカメラマトリックス = GLKMatrix4Identity; (int col = 0; col < 4; ++col) の場合 { (int row = 0; row < 4; ++row) の場合 { newCameraMatrix.m[列 * 4 + 行] = cameraMatrix.columns[列][行]; } } self.cameraMatrix = 新しいカメラマトリックス; GLKVector3 を forward = GLKVector3Make(-self.cameraMatrix.m13, -self.cameraMatrix.m23, -self.cameraMatrix.m33); GLKMatrix4 回転行列 = GLKMatrix4MakeRotation(M_PI / 2、forward.x、forward.y、forward.z); self.cameraMatrix = GLKMatrix4Multiply(回転マトリックス、新しいカメラマトリックス); } 上記のコードは、ARKit でキャプチャした ARFrame を処理する方法を示しています。ARFrame の capturedImage には、カメラでキャプチャした画像情報が格納されており、その型は CVPixelBufferRef です。デフォルトでは、画像情報の形式は YUV であり、2 つのプレーンに保存され、2 つの画像としても理解されます。 1 つの形式は、明るさの情報を格納する Y (輝度) で、もう 1 つは彩度と濃度を格納する UV (色度、彩度) です。これら 2 つの画像をそれぞれ異なるテクスチャにバインドし、Shader のアルゴリズムを使用して YUV を RGB に変換する必要があります。以下はテクスチャを処理し、色変換の式を使用するフラグメント シェーダーです。 高精度の高浮動小数点数。 変化する vec3 fragNormal; 変化するvec2 fragUV; 均一な float 経過時間; 均一mat4通常行列; 均一サンプラー2D yMap; 均一サンプラー2D uvMap; void main(void) { vec4 Y_planeColor = texture2D(yMap, fragUV); vec4 CbCr_planeColor = texture2D(uvMap, fragUV); Cb、Cr、Yを浮動小数点数化します。 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ビデオカラー = vec4(R / 255.0, G / 255.0, B / 255.0, 1.0); gl_FragColor = ビデオカラー; } テクスチャを処理してバインドした後、異なる画面サイズでテクスチャが不均一に引き伸ばされないようにするために、ビューポートが再計算されます [self setupViewport: CGSizeMake(imageHeight, imageWidth)];。次に、ARKit によって計算されたカメラ変換を self.cameraMatrix に割り当てます。なお、ARKit でキャプチャした画像は正しく表示するには 90 度回転させる必要があるため、Viewport を設定する際に意図的に幅と高さを反転し、最後にカメラを回転させます。 ビデオプレーン VideoPlane はビデオを表示するために書き込まれたジオメトリであり、Y と UV の 2 つのテクスチャを受け取ることができます。 @interface ビデオプレーン : GLObject @property (割り当て、非アトミック) GLuint yuv_yTexture; @property (割り当て、非アトミック) GLuint yuv_uvTexture; - (インスタンスタイプ)initWithGLContext:(GLContext *)コンテキスト; - (void)update:(NSTimeInterval)timeSinceLastUpdate; - (void)draw:(GLContext *)glContext; @終わり ... - (void)draw:(GLContext *)glContext { [glContext setUniformMatrix4fv:@"modelMatrix" 値:self.modelMatrix]; ブール値は反転できます。 GLKMatrix4 の normalMatrix を GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert); [glContext setUniformMatrix4fv:@"normalMatrix" 値:canInvert ? normalMatrix : GLKMatrix4Identity]; [glContext bindTextureName:self.yuv_yTexture to:GL_TEXTURE0 universeName:@"yMap"]; [glContext bindTextureName:self.yuv_uvTexture to:GL_TEXTURE1 universeName:@"uvMap"]; [glContext drawTrianglesWithVAO:vao 頂点数:6]; } 他の機能は非常にシンプルで、正方形を描画し、最後にシェーダーと連携してビデオを表示し、データを YUV 形式でレンダリングします。 透視投影マトリックス ARFrame では、レンダリングに必要なテクスチャとカメラ行列を取得できます。これらに加えて、実際のカメラに合わせた透視投影行列も必要です。レンダリングされた 3D オブジェクトの遠近感が自然に見えるようになります。 - (void)セッション:(ARSession *)セッション cameraDidChangeTrackingState:(ARCamera *)カメラ { matrix_float4x4 projectionMatrix = [カメラ projectionMatrixWithViewportSize:self.viewport.size orientation:UIInterfaceOrientationPortrait zNear:0.1 zFar:1000]; GLKMatrix4 の新しいワールド投影マトリックス = GLKMatrix4Identity; (int col = 0; col < 4; ++col) の場合 { (int row = 0; row < 4; ++row) の場合 { 新しいWorldProjectionMatrix.m[列 * 4 + 行] = projectionMatrix.columns[列][行]; } } self.worldProjectionMatrix = 新しいWorldProjectionMatrix; } 上記のコードは、ARKit を通じて 3D 透視投影行列を取得する方法を示しています。透視投影行列とカメラ行列を使用すると、OpenGL を使用してオブジェクトを簡単にレンダリングできます。 - (void)glkView:(GLKView *)ビューdrawInRect:(CGRect)rect { [スーパー glkView:ビュー drawInRect:rect]; [self.objects enumerateObjectsUsingBlock:^(GLObject *obj, NSUInteger idx, BOOL *stop) { [obj.context アクティブ]; [obj.context setUniform1f:@"elapsedTime" 値:(GLfloat)self.elapsedTime]; [obj.context setUniformMatrix4fv:@"projectionMatrix" 値:self.worldProjectionMatrix]; [obj.context setUniformMatrix4fv:@"cameraMatrix" 値:self.cameraMatrix]; [obj.context setUniform3fv:@"lightDirection" 値:self.lightDirection]; [obj 描画:obj.context]; }]; } この記事では、OpenGL ES の技術的な詳細についてはあまり説明せずに、主に OpenGL ES レンダリング ARKit の基本的な考え方を紹介します。興味がある場合は、Github でコードを直接クローンして詳細を確認できます。 |
<<: 長いテキストの復号化畳み込みニューラルネットワークアーキテクチャ
映画データベース (TMDB) は映画データ用の API を提供し、ユーザーはこのデータベースからデ...
[[311593]] [51CTO.com クイック翻訳] 人工知能は最新の開発トレンドであり、その...
この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...
近年のホットな言葉といえば、「人工知能」が挙げられます。昨年のChatGPTの人気爆発により、「AI...
[[315538]]米国では無人タクシーの試験と導入が進み、SFで描かれた無人運転のシナリオが徐々...
MySQL への接続は、Java 開発において非常に一般的なタスクの 1 つです。次のセクションでは...
機械学習とディープラーニングの違いは何だろうとよく疑問に思う方は、この記事を読んで、その違いを一般の...
金融部門は、個人の購入から大規模な取引まで、莫大な富につながる大量の貴重なデータを定期的に生成してお...
16 年前、ビル・ゲイツはスパムの問題は 2006 年までに解決すると約束しました。 2020 年...
脳コンピューターインターフェースの時代では、毎日新しいものが生まれます。今日、私が皆さんに紹介したい...
マージソートとは、2つ(またはそれ以上)の順序付きリストを新しい順序付きリストにマージすることです。...
このレビュー記事では、著者はマルチインテリジェンス強化学習の理論的基礎を詳細に紹介し、さまざまなマル...