iOS OpenGL ES プログラミングガイド 目次 OpenGL ESについて 10 はじめに 10 OpenGL ESはiOSにおけるプラットフォームに依存しないAPI 11 GLKitによる描画サーフェスとアニメーションサポートの提供 11 iOSでの代替レンダリング対象のサポート 12 アプリケーションに必要な追加のパフォーマンスチューニング 12 バックグラウンドアプリケーションでOpenGL ESを使用できない可能性について 13 OpenGL ESによるマルチスレッドアプリケーションへのその他の制約事項 13 この文書の使い方 13 必要事項 14 関連項目 14 OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリスト 15 サポートするOpenGL ESバージョンの選択 15 OpenGL ESの機能の検証 16 レンダリング先の選択 17 iOSとの一体化 17 レンダリングエンジンの実装 18 デバッグとプロファイル 18 OpenGL ESコンテキストの設定 19 EAGLとはOpenGL ESレンダリングコンテキストのiOSにおける実装 19 現在のコンテキストはOpenGL ES関数呼び出しのターゲット 19 どのコンテキストもOpenGL ESの特定のバージョンをターゲットにする 20 EAGL sharegroupはコンテキストのOpenGL ESオブジェクトを管理 21 OpenGL ESおよびGLKitによる描画 24 GLKit ViewはOpenGL ESコンテンツをオンデマンドで描画する 24 GLKit Viewの作成と設定 25 GLKit Viewの描画 26 デリゲートオブジェクトを使用したレンダリング 28 GLKit View ControllerがOpenGL ESコンテンツをアニメーション化する 29 アニメーションループについて 29 GLKit View Controllerの使用 30 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 2 目次 GLKitを使用したレンダラの開発 32 ベクトルと行列の数値演算の処理 32 OpenGL ES 1.1の固定機能パイプラインからの移行 32 テクスチャデータの読み込み 33 その他のレンダリング先への描画 34 フレームバッファオブジェクトの生成 34 オフスクリーンフレームバッファオブジェクトの作成 35 フレームバッファオブジェクトを使用したテクスチャへのレンダリング 36 Core Animationレイヤへのレンダリング 37 フレームバッファオブジェクトへの描画 39 オンデマンドまたはアニメーションループでのレンダリング 39 フレームのレンダリング 41 マルチサンプル機能の使用による画像品質の向上 43 マルチタスク、高解像度、iOSのその他の機能 47 マルチタスク対応OpenGL ESアプリケーションの実装 47 バックグラウンドのアプリケーションはグラフィックスハードウェアのコマンドを実行しない場 合がある 47 作成し直したリソースをバックグラウンドに移動する前に簡単に削除 48 高解像度ディスプレイのサポート 49 デバイスの向きに応じた調整 51 外部ディスプレイへのOpenGL ESコンテンツの表示 51 OpenGL ES設計ガイドライン 52 OpenGL ESを視覚化する方法 52 クライアント/サーバアーキテクチャとしてのOpenGL ES 52 グラフィックスパイプラインとしてのOpenGL ES 53 OpenGL ESの版とレンダラのアーキテクチャ 54 OpenGL ES 3.0 54 OpenGL ES 2.0 59 OpenGL ES 1.1 59 高パフォーマンスなOpenGL ESアプリケーションの設計 59 同期とフラッシュの操作の回避 62 glFlushの効果的な使用 62 OpenGL ESの状態照会の回避 63 OpenGL ESを使用したリソースの管理 63 ダブルバッファリングによるリソースの競合の回避 63 OpenGL ESの状態に注意を払う 65 OpenGL ESのオブジェクトで状態をカプセル化する 66 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 3 目次 描画関数の呼び出しを整理して状態変化を最小限に抑える 66 OpenGL ESアプリケーションのチューニング 68 XcodeやInstrumentsで、アプリケーションをデバッグ、プロファイリングする 68 XcodeやInstrumentsでOpenGL ESのエラーを監視する 69 情報提供のデバッグおよびプロファイル用に、OpenGL ESのコードに注釈を付ける 70 パフォーマンスのための一般的な推奨事項 71 シーンのデータが変更された場合のみシーンを再描画する 71 使用しないOpenGL ES機能を無効にする 72 ライティングモデルを簡素化する 72 タイルベースの遅延レンダリングを効率的に使う 72 論理バッファの読み込みや格納を避ける 73 隠面除去を効率的に使う 74 OpenGL ESコマンドをグループ化して効率的にリソースを管理する 75 描画コマンドの数を最小限に抑える 76 インスタンス描画法により描画関数の呼び出し回数を減らす 76 OpenGL ESのメモリ消費を最小限に抑える 79 Core Animation合成のパフォーマンスに注意する 79 頂点データを扱うためのベストプラクティス 81 モデルの簡素化 82 属性の配列に定数を格納するのを避ける 83 属性には可能な限り最小の型を使用する 83 インターリーブされた頂点データを使用する 84 ずれた頂点データを避ける 85 三角形ストリップを使用して頂点データを一括処理する 85 頂点バッファオブジェクトの使用による頂点データのコピーの管理 87 バッファの使用に関するヒント 89 頂点配列オブジェクトの使用による頂点配列状態の変化の整理統合 92 バッファをクライアント側メモリにマップして高速に更新する 94 テクスチャデータを扱うためのベストプラクティス 97 初期化中にテクスチャをロードする 97 GLKitフレームワークを使用したテクスチャデータの読み込み 97 テクスチャのメモリ使用量を削減する 99 テクスチャの圧縮 99 低精度のカラーフォーマットを使用する 99 適切なサイズのテクスチャを使用する 100 複数のテクスチャをテクスチャアトラスにまとめる 100 ミップマップを使用してメモリの帯域幅の使用を削減する 101 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 4 目次 マルチパスの代わりにマルチテクスチャリングを使用する 101 シェーダのベストプラクティス 103 初期化中にシェーダをコンパイルおよびリンクする 103 デバッグ時のシェーダプログラムエラーのチェック 103 コンパイルとリンクを高速化するために別々のシェーダオブジェクトを使用する 104 シェーダに関するハードウェア制限に従う 106 精度ヒントを使用する 106 ベクトル計算の時間のかかる実行 107 シェーダ内での計算の代わりにuniformまたはconstantを使用する 108 分岐命令は注意して使う 108 ループをなくす 109 シェーダにおける配列インデックスの計算を避ける 109 動的なテクスチャ検索に注意する 109 プログラマブルブレンディング用にフレームバッファのデータを取得する 110 フレームバッファを取得する(GLSL ES 1.0の場合) 112 フレームバッファを取得する(GLSL ES 3.0の場合) 113 頂点シェーダで、より大容量のメモリバッファとしてテクスチャを使う 113 並列処理とOpenGL ES 115 並列処理のメリットがあるかどうか判断する 115 OpenGL ESは各コンテキストを単一スレッドに制限 116 OpenGL ESアプリケーションに並列処理を実装する方法 117 マルチスレッド化したOpenGL ES 118 ワーカータスクにおけるOpenGL ES演算の実行 118 複数のOpenGL ESコンテキストの使用 119 OpenGL ESアプリケーションのスレッド化に関するガイドライン 120 OpenGL ES 3.0への移行 121 OpenGL ES 3.0に移行するためのチェックリスト 121 拡張機能のコードを改訂する 122 拡張機能の接尾辞を除去する 122 拡張機能APIの使い方を修正する 122 他の拡張機能も、多くはOpenGL ES 3.0でもそのまま利用できる 124 OpenGL ESシェーディング言語の第3.0版に移行する 124 Xcodeに付属するOpenGL ES用ツールの概要 126 「FPS Debug Gauge」および「GPU Report」の使い方 126 OpenGL ESのフレームを取り込んで分析する 128 OpenGL ES Frame Debuggerひとめぐり 131 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 5 目次 ナビゲータ領域 132 エディタ領域 133 デバッグ領域 139 texturetoolを使用したテクスチャの圧縮 143 texturetoolのパラメータ 143 書類の改訂履歴 149 用語解説 151 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 6 図、リスト OpenGL ESコンテキストの設定 19 図 2-1 リスト 2-1 リスト 2-2 2つのコンテキストによるOpenGL ESオブジェクトの共有 22 同じアプリケーションでの複数バージョンのOpenGL ESのサポート 20 共通のsharegroupを持つ2つのコンテキストの作成 22 OpenGL ESおよびGLKitによる描画 24 図 3-1 図 3-2 リスト 3-1 リスト 3-2 リスト 3-3 リスト 3-4 GLKit ViewでのOpenGL ESコンテンツのレンダリング 25 アニメーションループ 29 GLKit Viewの設定 26 GLKit Viewの描画メソッドの例 26 ハードウェアの機能に基づいたレンダラクラスの選択 28 GLKit ViewおよびView Controllerを使用した、OpenGL ESコンテンツの描画とアニメーショ ン化 30 その他のレンダリング先への描画 34 図 4-1 図 4-2 図 4-3 図 4-4 リスト 4-1 リスト 4-2 リスト 4-3 リスト 4-4 リスト 4-5 色と深度のレンダバッファを持つフレームバッファ 34 Core AnimationとOpenGL ESとのレンダバッファの共有 37 iOSにおけるOpenGLのレンダリング手順 41 マルチサンプル機能の仕組み 44 ディスプレイリンクの作成と開始 40 フレームバッファアタッチメントの消去 41 深度フレームバッファの破棄 43 レンダリングの完了したフレームの表示 43 マルチサンプルバッファの作成 44 OpenGL ES設計ガイドライン 52 図 6-1 図 6-2 図 6-3 図 6-4 図 6-5 図 6-6 図 6-7 図 6-8 OpenGL ESのクライアント/サーバアーキテクチャ 52 OpenGL ESグラフィックスパイプライン 53 フラグメントシェーダが多重レンダターゲットに出力する様子 55 パーティクル系アニメーションの概要 57 変換のフィードバック機能を利用するグラフィックスパイプラインの構成例 58 リソース管理のためのアプリケーションモデル 60 単一のバッファに格納されたテクスチャデータ 64 ダブルバッファリングされたテクスチャデータ 65 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 7 図、リスト リスト 6-1 リスト 6-2 多重レンダターゲットを用意するコード例 55 多重レンダターゲットに出力するフラグメントシェーダのコード例 56 OpenGL ESアプリケーションのチューニング 68 図 7-1 図 7-2 リスト 7-1 リスト 7-2 リスト 7-3 リスト 7-4 リスト 7-5 リスト 7-6 デバッグマーカグループの追加前と追加後のXcode Frame Debugger 70 透明なオブジェクトをトリミングしてフラグメント処理を減らす様子 75 デバッグマーカを使って描画コマンドに注釈をつけるコード例 71 デバッグラベルを利用してOpenGL ESオブジェクトに注釈をつけるコード例 71 インスタンス描画法によらずに、よく似たオブジェクトを多数描画するコード例 77 OpenGL ES 3.0の頂点シェーダが、gl_InstanceIDをもとに、インスタンスによって異な る値を計算するコード例 77 インスタンスごとに異なる情報を頂点属性に格納するコード例 78 インスタンス配列を参照して動作するOpenGL ES 3.0の頂点シェーダ 78 頂点データを扱うためのベストプラクティス 81 図 8-1 図 8-2 図 8-3 図 8-4 図 8-5 図 8-6 図 8-7 リスト 8-1 リスト 8-2 リスト 8-3 リスト 8-4 リスト 8-5 リスト 8-6 リスト 8-7 シェーダ変数への属性データの変換 81 インターリーブされたメモリ構造では、ある頂点に関するすべてのデータがまとめてメ モリに配置されます。 84 一部のデータの使われ方が異なる場合に複数の頂点構造体を使用する 84 追加の処理を避けるために頂点データを揃える 85 三角形ストリップ 85 縮退三角形を使用して三角形ストリップをマージする 86 頂点配列オブジェクトの設定 93 OpenGL ES 3.0でのプリミティブスタートの使用 86 シェーダプログラムへの頂点データの送信 87 頂点バッファオブジェクトを生成するコード例 88 頂点バッファオブジェクトを使って描画するコード例 89 複数の頂点バッファオブジェクトの使用によるモデルの描画 90 頂点配列オブジェクトの設定 93 手動同期により頂点バッファを動的に更新するコード例 95 テクスチャデータを扱うためのベストプラクティス 97 リスト 9-1 ファイルからの2次元テクスチャの読み込み 98 シェーダのベストプラクティス 103 図 10-1 図 10-2 リスト 10-1 リスト 10-2 リスト 10-3 従来型の固定機能ブレンディング 110 プログラマブルブレンディングとフレームバッファのデータ取得 111 開発ビルドでのみシェーダのコンパイル/リンクログを読み込む 103 独立したシェーダオブジェクトをコンパイル、使用するコード例 104 フラグメントの色に低精度を使用する 106 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 8 図、リスト リスト 10-4 リスト 10-5 リスト 10-6 リスト 10-7 リスト 10-8 ベクトル演算をうまく活用していない 107 ベクトル演算の適切な使用 107 書き込みマスクの指定 108 従属テクスチャ読み込み 110 プログラマブルブレンディングをおこなうフラグメントシェーダの、GLSL ES 1.0による 実装例 112 リスト 10-9 色の後処理を施すフラグメントシェーダの、GLSL ES 3.0による実装例 113 リスト 10-10 高さマップからレンダリングする頂点シェーダ 114 Xcodeに付属するOpenGL ES用ツールの概要 126 図 B-1 図 B-2 図 B-3 図 B-4 図 B-5 図 B-6 図 B-7 図 B-8 図 B-9 図 B-10 図 B-11 図 B-12 図 B-13 図 B-14 「FPS Debug Gauge」および「GPU Report」の画面 126 「Debug」バーと「Capture OpenGL ES Frame」ボタン 129 フレームデバッガに描画関数呼び出しとリソースの状況が表示されている様子 131 フレームデバッガにシェーダプログラムの処理性能や分析結果が表示されている様子 132 ナビゲータの「View Frame By」ポップアップメニュー 132 フレームバッファ情報ポップオーバー 134 フレームバッファ設定ポップオーバー 135 GLSLシェーダのソースエディタと更新ボタン 136 アシスタントエディタに配列バッファの内容を表示している様子 137 アシスタントエディタに頂点配列オブジェクトがプレビュー表示されている様子 137 アシスタントエディタにキューブマップテクスチャをプレビュー表示している様子 138 OpenGL ESのデバッグバー 139 デバッグ領域に「GL Context」ビューと「Bound GL Objects」ビューが表示されている様 子 140 デバッグ領域に「Auto」ビューと「Context Info」ビューが現れている様子 141 texturetoolを使用したテクスチャの圧縮 143 リスト C-1 リスト C-2 リスト C-3 エンコードオプション 144 画像をPVRTC圧縮フォーマットにエンコードする。 147 プレビューを作成するとともに画像をPVRTC圧縮フォーマットにエンコードする 147 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 9 OpenGL ESについて Open Graphics Library(OpenGL)は、2Dおよび3Dのデータを視覚化するために使用します。OpenGL は、オープンスタンダードに基づいた多用途のグラフィックスライブラリで、2Dおよび3Dのデジタ ルコンテンツ作成、機械設計および建築設計、仮想プロトタイピング、フライトシミュレーション、 ビデオゲームなどのアプリケーションをサポートします。OpenGLを使用して、3Dグラフィックスパ イプラインを設定し、そのパイプラインにデータを送信します。頂点は、変形およびライティングさ れ、プリミティブとして組み立てられ、ラスタ化されて2D画像が作成されます。OpenGLは関数呼び 出しを、基盤となるグラフィックスハードウェアに送信可能なグラフィックスコマンドに変換するた めに設計されています。この基盤ハードウェアはグラフィックスコマンドの処理専用のため、OpenGL による描画は一般に非常に高速です。 OpenGL for Embedded Systems(OpenGL ES)は、OpenGLの冗長な機能を取り除いた簡略版で、OpenGL よりも覚えやすく、モバイルグラフィックスハードウェアに実装しやすいライブラリを提供します。 はじめに OpenGL ESを使用すると、基盤となるグラフィックスプロセッサの機能をアプリケーションが利用で きます。iOSデバイスのGPUは、洗練された2Dおよび3D描画のほか、最終画像のすべてのピクセルで 複雑なシェーディング計算を実行できます。OpenGL ESを使用すべきなのは、アプリケーションの設 計要件でGPUハードウェアに対して可能な最も直接的かつ包括的なアクセスが必要とされる場合で す。OpenGL ESの一般的なクライアントとして、3Dグラフィックスを表示するビデオゲームやシミュ レーションがあります。 OpenGL ESは、低レベルでハードウェアに焦点を合わせたAPIです。最も高機能で柔軟なグラフィック ス処理ツールではありますが、学習曲線が急で、アプリケーションの設計全体に対して重大な影響が あります。特殊なユーザのために高性能なグラフィックスが必要なアプリケーションに対して、iOS はいくつかのより高レベルなフレームワークを提供します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 10 OpenGL ESについて はじめに ● Sprite Kitフレームワークは、2Dゲームの作成に対して最適化された、ハードウェアで加速するア ニメーションシステムを提供します。(Sprite Kit Programming Guide を参照。) ● Core Imageフレームワークは、静止画と動画に対してリアルタイムのフィルタと分析を行います。 (Core Image Programming Guide を参照。) ● Core Animationは、ハードウェアで加速するグラフィックスレンダリングと、すべてのiOSアプリ ケーションのためのアニメーションインフラストラクチャのほか、洗練されたユーザインター フェイスのアニメーションを簡単に実装できるようにする、簡単な宣言型プログラミングモデル も備えています。(Core Animation Programming Guide を参照。) ● UIKitフレームワークの機能を使用して、Cocoa Touchユーザインターフェイスに対して、アニメー ション、物理ベースのダイナミクス、およびその他の特殊効果を追加できます。 OpenGL ESはiOSにおけるプラットフォームに依存しないAPI OpenGL ESはC言語ベースのAPIであるため、移植性が非常に高く、幅広くサポートされています。 OpenGL ESは、C言語のAPIとして、Objective-CのCocoa Touchアプリケーションとシームレスに統合し ます。OpenGL ESの仕様はウインドウレイヤを定義しておらず、ホストオペレーティングシステムが OpenGL ESレンダリングコンテキスト(コマンド受理)とフレームバッファ(描画コマンドの結果の 書き込み先)を作成する関数を提供しなければなりません。iOS上でOpenGL ESを使用する場合は、iOS クラスを使用して描画サーフェスを設定および表示し、プラットフォームに依存しないAPIを使用し てそのコンテンツをレンダリングします。 関連する章: “OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリス ト” (15 ページ)、“OpenGL ESコンテキストの設定” (19 ページ) GLKitによる描画サーフェスとアニメーションサポートの提供 UIKitフレームワークによって定義された、ViewおよびView Controllerにより、iOS上でビジュアルコン テンツの表示が制御されます。GLKitフレームワークは、これらのクラスのOpenGL ES対応バージョン を提供します。OpenGL ESアプリケーションの開発時に、GLKViewオブジェクトを使用してOpenGL ES コンテンツをレンダリングします。GLKViewControllerオブジェクトを使用して、ビューを管理し たり、そのコンテンツのアニメーション化をサポートしたりすることもできます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 11 OpenGL ESについて はじめに 関連する章: “OpenGL ESおよびGLKitによる描画” (24 ページ) iOSでの代替レンダリング対象のサポート 画面全体またはビュー階層の一部を占める描画コンテンツ以外に、他のレンダリング方法にOpenGL ES フレームバッファオブジェクトも使用できます。iOSは、標準的なOpenGL ESフレームバッファオブ ジェクトを実装します。これは、オフスクリーンバッファやOpenGL ESシーンの他の部分で使用する テクスチャへのレンダリングに使用できます。さらに、iOS上のOpenGL ESは、Core Animationレイヤ (CAEAGLLayerクラス)へのレンダリングをサポートします。このレイヤを他のレイヤと結合して、 アプリケーションのユーザインターフェイスまたはその他の視覚的表示を構築することができます。 関連する章: “その他のレンダリング先への描画” (34 ページ) アプリケーションに必要な追加のパフォーマンスチューニング グラフィックスプロセッサは、グラフィックス操作向けに最適化された並行処理デバイスです。アプ リケーションに優れたパフォーマンスを発揮させるには、グラフィックスハードウェアがアプリケー ションと並行で動作するよう、アプリケーションを注意深く設計してデータとコマンドをOpenGL ES に供給しなければなりません。チューニングが十分でないアプリケーションは、一方がコマンドの処 理を完了するまでCPUまたはGPUのどちらかを待機させることになります。 OpenGL ES APIを効率よく使用してアプリケーションを設計してください。アプリケーションのビルド が完了したら、Instrumentsを使用してアプリケーションのパフォーマンスを微調整します。OpenGL ES内でアプリケーションのボトルネックが発生している場合は、このガイドの情報を使用してアプリ ケーションのパフォーマンスを最適化してください。 Xcode は、OpenGL ESアプリケーションのパフォーマンスを向上する新しいツールを提供します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 12 OpenGL ESについて この文書の使い方 関連する章: “OpenGL ES設計ガイドライン” (52 ページ)、“頂点データを扱うためのベス トプラクティス” (81 ページ)、“テクスチャデータを扱うためのベストプラクティ ス” (97 ページ)、“シェーダのベストプラクティス” (103 ページ)、“OpenGL ESアプリケー ションのチューニング” (68 ページ) バックグラウンドアプリケーションでOpenGL ESを使用できない可能性に ついて バックグラウンドで実行されるアプリケーションはOpenGL ES関数を呼び出さない場合があります。 アプリケーションがバックグラウンド時にグラフィックスプロセッサにアクセスすると、iOSがアプ リケーションを自動的に終了します。これを回避するには、バックグラウンドへの移行前にOpenGL ES に送信された未処理のコマンドをフラッシュし、アプリケーションがフォアグラウンドに戻るまでの 間OpenGL ESを呼び出さないようにする必要があります。 関連する章: “マルチタスク、高解像度、iOSのその他の機能” (47 ページ) OpenGL ESによるマルチスレッドアプリケーションへのその他の制約事項 並列処理を利用するようアプリケーションを設計することは、アプリケーションパフォーマンスの向 上に役立つ可能性があります。OpenGL ESアプリケーションに並列処理を追加する場合は、アプリケー ションが、異なる2つのスレッドから同じコンテキストに同時にアクセスしないようにしなければな りません。 関連する章: “並列処理とOpenGL ES” (115 ページ) この文書の使い方 まず、“OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリスト” (15 ペー ジ)、“OpenGL ESコンテキストの設定” (19 ページ)、“OpenGL ESおよびGLKitによる描画” (24 ペー ジ)の3章を読んでください。ここでは、OpenGL ESがiOSに統合されている様子を概観した後、この ライブラリを活用する初めてのアプリケーションを実装し、iOSデバイス上で実行するために必要な 事項を詳しく解説します。 iOSでOpenGL ESを使うための基本事項を理解したところで、プラットフォームに特有の重要なガイド ラインを述べた、“その他のレンダリング先への描画” (34 ページ)、“マルチタスク、高解像度、iOS のその他の機能” (47 ページ)の2章に目を通してください。iOS 5.0以前でOpenGL ESを使ったことが ある開発者向けに、“OpenGL ESおよびGLKitによる描画” (24 ページ)で、開発効率の向上に資する 新機能を紹介しています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 13 OpenGL ESについて 必要事項 最後に、“OpenGL ES設計ガイドライン” (52 ページ)、“OpenGL ESアプリケーションのチューニン グ” (68 ページ)の2章およびそれ以降の各章で、効率のよいOpenGL ESアプリケーションの設計法 を、深く掘り下げて解説します。 特に断りのない限り、本書のOpenGL ESコード例は、OpenGL ES 3.0を対象としています。他のバージョ ンのOpenGL ESでこのコード例を使用するには、変更を加える必要がある場合があります。 必要事項 OpenGL ESを使用してみる前に、iOSアプリケーション全体のアーキテクチャに慣れておく必要があり ます。詳細については、『Start Developing iOS Apps Today 』を参照してください。 この資料は、各種プラットフォームで動作するOpenGL ESのAPIの、詳細なチュートリアルやリファレ ンスとしては使えません。OpenGL ESについてさらに詳しくは、以下に示す資料を参照してください。 関連項目 OpenGL ESはKhronos Groupによって定義された公開標準です。OpenGL ES標準の詳細については、 http://www.khronos.org/opengles/のWebページを参照してください。 ● ● ● ● ● ● 『OpenGL® ES 3.0 Programming Guide 』(Addison-Wesley刊行)は、OpenGL ESの概念についての総 合的な入門書です。 『OpenGL® Shading Language, Third Edition 』(Addison-Wesley刊行)は、OpenGL ESアプリケーショ ンで使用可能な多くのシェーディングアルゴリズムを提供しています。これらのアルゴリズムの 一部は、モバイルグラフィックスプロセッサ上で効率よく実行するために修正が必要となる場合 があります。 OpenGL ES API Registryは、OpenGL ES仕様、OpenGL ESシェーディング言語仕様、およびOpenGL ES 拡張機能に関する文書の公式リポジトリです。 『OpenGLESFrameworkReference 』は、OpenGL ESをiOSに統合するためにAppleが提供する、プラッ トフォーム固有の関数とクラスを解説しています。 『iOS Device Compatibility Reference 』では、アプリケーションで利用可能なハードウェアおよびソ フトウェア機能についての詳細情報が記されています。 『GLKit Framework Reference 』は、OpenGL ES 2.0および3.0アプリケーションの開発を容易にする ためにAppleが提供しているフレームワークを解説しています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 14 OpenGL ESを組み込んでiOSアプリケーションを 構築するためのチェックリスト OpenGL ESの仕様により、GPUハードウェアを使用してグラフィックスをレンダリングするための、 プラットフォームに依存しないAPIが定義されます。OpenGL ESを実装するプラットフォームは、 OpenGL ESコマンドを実行するためのレンダリングコンテキスト、レンダリング結果を保持するフレー ムバッファ、およびフレームバッファのコンテンツを表示する1つ以上のレンダリング先を提供しま す。iOSでは、EAGLContextクラスがレンダリングコンテキストを実装します。iOSが提供するフレー ムバッファはOpenGL ESフレームバッファオブジェクトの1種類のみで、GLKViewおよびCAEAGLLayer クラスがレンダリング先を実装します。 iOSでOpenGL ESアプリケーションを構築する場合は、いくつかの考慮事項があります。OpenGL ESプ ログラミングに汎用的なものもあれば、iOSに固有のものもあります。以下のチェックリストとその 下の詳細な節に従って始めましょう。 1. アプリケーションに対して適切な機能セットを持つOpenGL ESのバージョンを判断し、OpenGL ES コンテキストを作成します。 2. 使用するOpenGL ESの機能がデバイスでサポートされていることを実行時に確認します。 3. OpenGL ESコンテンツのレンダリング先を選択します。 4. iOSでアプリケーションが正しく実行されていることを確認します。 5. レンダリングエンジンを実装します。 6. Use XcodeおよびInstrumentsを使用してOpenGL ESアプリケーションをデバッグし、最適なパフォー マンスが実現されるように調整します。 サポートするOpenGL ESバージョンの選択 OpenGL ES 3.0、OpenGL ES 2.0、OpenGL ES 1.1のどれをサポートするか、または複数のバージョンをサ ポートするか、を判断します。 ● OpenGL ES 3.0はiOS 7で新たに導入された版です。高い処理性能を実現し、さまざまな目的にGPU 計算技術を駆使するばかりでなく、従来はデスクトップ機やゲームコンソールでしか実現できな かった、より複雑な視覚効果を生み出す数々の新機能を備えています。 ● OpenGL ES 2.0はiOSデバイス用のベースラインプロファイルで、プログラマブルシェイダを基盤と する、適合性の高いグラフィックスパイプラインを提供します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 15 OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリスト OpenGL ESの機能の検証 OpenGL ES 1.1はごく基本的な固定機能グラフィックスパイプラインを提供するだけであり、iOSに 今でも搭載されているのは、後方互換性の維持だけが目的です。 ● アプリケーションに最も関係のある機能やデバイスをサポートするOpenGL ESのバージョンを対象に する必要があります。iOSデバイスにおけるOpenGL ESの能力については、『iOS Device Compatibility Reference 』を参照してください。 サポートする予定のOpenGL ESのバージョンのコンテキストの作成については、“OpenGL ESコンテキ ストの設定” (19 ページ)を参照してください。また、OpenGL ESのバージョンによって、利用でき るレンダリングアルゴリズムにどのような違いがあるか、“OpenGL ESの版とレンダラのアーキテク チャ” (54 ページ)で解説します。 OpenGL ESの機能の検証 iOS Device Compatibility Reference では、iOSデバイスの出荷時に用意されている機能と拡張機能の概要 を示します。ただし、アプリケーションをできるだけ多くのデバイスとiOSバージョン上で実行でき るようにするために、アプリケーションは実行時の機能について必ずOpenGL ESの実装を照会する必 要があります。 最大テクスチャサイズや頂点属性の最大数など、実装固有の制限を確認するには、そのデータ型に適 したglGet関数を使用して、対応するトークンの値を調べます(gl.hヘッダにあるMAX_TEXTURE_SIZE やMAX_VERTEX_ATTRIBSなど)。 OpenGL ES 3.0拡張機能の有無を確認するには、次のコード例のように、glGetIntegervおよび glGetStringi関数を使用します。 BOOL CheckForExtension(NSString *searchName) { // Create a set containing all extension names. // (For better performance, create the set only once and cache it for future use.) int max = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &max); NSMutableSet *extensions = [NSMutableSet set]; for (int i = 0; i < max; i++) { [extensions addObject: @( (char *)glGetStringi(GL_EXTENSIONS, i) )]; } return [extensions containsObject: searchName]; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 16 OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリスト レンダリング先の選択 } OpenGL ES 1.1および2.0の拡張機能の有無を確認するには、glGetString(GL_EXTENSIONS)を呼び出 して、すべての拡張名のスペース区切りの一覧を取得します。 レンダリング先の選択 In iOSでは、フレームバッファオブジェクトに描画コマンドの結果が格納されます。(iOSにはウイン ドウシステム提供のフレームバッファは実装されません。)フレームバッファオブジェクトのコンテ ンツは次のような複数の方法で使用できます。 ● ● GLKitフレームワークは、OpenGL ESコンテンツを描画し、独自のフレームバッファオブジェクト を管理するビュー、およびOpenGL ESコンテンツのアニメーションをサポートするView Controller を提供します。これらのクラスを使用して、全画面表示を作成するか、OpenGL ESコンテンツを UIKit View階層に収めます。これらのクラスについては、“OpenGL ESおよびGLKitによる描 画” (24 ページ)を参照してください。 CAEAGLLayerクラスは、OpenGL ESコンテンツをCore Animationレイヤ合成の一部として描画する 方法を提供します。このクラスの使用時に独自のフレームバッファオブジェクトを作成する必要 があります。 ● 他のOpenGL ES実装と同様に、オフスクリーングラフィックス処理や、グラフィックスパイプラ インの他の部分で使用するテクスチャへのレンダリングにもフレームバッファを使用できます。 OpenGL ES 3.0で、オフスクリーンバッファは複数のレンダリング対象を利用するレンダリングア ルゴリズムで使用できます。 オフスクリーンバッファ、テクスチャ、またはCore Animationレイヤへのレンダリングについては、 “その他のレンダリング先への描画” (34 ページ)を参照してください。 iOSとの一体化 iOSアプリケーションはデフォルトではマルチタスクをサポートしていますが、OpenGL ESアプリケー ションでこの機能を正しく扱うには、さらに考慮が必要です。OpenGL ESを適切に使用しないと、ア プリケーションがバックグラウンドに移動したときにシステムによって強制終了される可能性があり ます。 多くのiOSデバイスには高解像度ディスプレイが含まれるため、アプリケーションは複数の表示サイ ズと解像度をサポートする必要があります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 17 OpenGL ESを組み込んでiOSアプリケーションを構築するためのチェックリスト レンダリングエンジンの実装 これらの機能やiOSのその他の機能のサポートについては、“マルチタスク、高解像度、iOSのその他の 機能” (47 ページ)を参照してください。 レンダリングエンジンの実装 OpenGL ESの描画コードの設計には可能な方法が多数あり、その詳細はこのマニュアルの範囲を超え ています。レンダリングエンジンの設計の多くの側面は、OpenGLおよびOpenGL ESのすべての実装に 汎用的です。 iOSの開発者にとって重要な設計上の考慮事項については、“OpenGL ES設計ガイドライン” (52 ペー ジ)および“並列処理とOpenGL ES” (115 ページ)を参照してください。 デバッグとプロファイル XcodeおよびInstrumentsは、アプリケーションにおいて、レンダリングの問題を特定し、OpenGL ESの パフォーマンスを分析する多数のツールを備えています。 OpenGL ESアプリケーションでの問題の解決とパフォーマンスの向上については、“OpenGL ESアプリ ケーションのチューニング” (68 ページ)を参照してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 18 OpenGL ESコンテキストの設定 OpenGL ESのどの実装も、OpenGL ES仕様で求められる状態を管理するためのレンダリングコンテキス トを作成する手段を提供します。この状態をコンテキスト内に配置することにより、複数のアプリ ケーションが、ほかのアプリケーションの状態に干渉することなく、グラフィックスハードウェアを 容易に共有できます。 この章では、iOS上でコンテキストを作成し、設定する方法を詳しく説明します。 EAGLとはOpenGL ESレンダリングコンテキストのiOSにおける 実装 アプリケーションがOpenGL ES関数を呼び出せるようにするには、アプリケーションがEAGLContext オブジェクトを初期化する必要があります。EAGLContextクラスも、OpenGL ESコンテンツをCore Animationに統合するために使用する手段を提供します。 現在のコンテキストはOpenGL ES関数呼び出しのターゲット iOSアプリケーションのすべてのスレッドには、現在のコンテキストがあります。OpenGL ES関数を呼 び出すと、これは状態が呼び出しによって変化するコンテキストとなります。 スレッドの現在のコンテキストを設定するには、そのスレッドで実行するときにEAGLContextクラス のsetCurrentContext:メソッドを呼び出します。 [EAGLContext setCurrentContext: myContext]; スレッドの現在のコンテキストを取得するには、EAGLContextクラスのcurrentContextメソッドを 呼び出します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 19 OpenGL ESコンテキストの設定 どのコンテキストもOpenGL ESの特定のバージョンをターゲットにする 注意: アプリケーションが同一スレッド上の2つ以上のコンテキストをアクティブに切り替 える場合、新しいコンテキストを現在のコンテキストに設定する前に、glFlush関数を呼び 出します。これにより、事前に送信されていたコマンドは、適切なタイミングでグラフィッ クスハードウェアに配信されます。 OpenGL ESは、現在のコンテキストに対応する、EAGLContextオブジェクトの強い参照を保持します (独自に参照カウントを実装している場合でも、OpenGL ESはこのオブジェクトを保持)。 setCurrentContext:メソッドで現在のコンテキストを変更すると、OpenGL ESは前のコンテキスト を参照できなくなります(独自に参照カウントを実装している場合、OpenGL ESはEAGLContextオブ ジェクトを解放)。現在のコンテキストでなくなってもEAGLContextオブジェクトが割り当て解除さ れないようにしたければ、当該オブジェクトに対する強い参照を保持しなければなりません。 どのコンテキストもOpenGL ESの特定のバージョンをターゲッ トにする EAGLContextオブジェクトでは、1つのバージョンのOpenGL ESのみがサポートされます。たとえば、 OpenGL ES 1.1用に記述されたコードは、OpenGL ES 2.0または3.0のコンテキストと互換性がありませ ん。OpenGL ES 2.0のコア機能を使用したコードは、OpenGL ES 3.0のコンテキストと互換性があり、 OpenGL ES 2.0拡張機能用に設計されたコードは、多くの場合、細かな変更を行うだけでOpenGL ES 3.0 のコンテキストで使用できます。OpenGL ES 3.0の多くの新機能や強化されたハードウェア機能では、 OpenGL ES 3.0のコンテキストが必要です。 アプリケーションは、EAGLContextオブジェクトを作成し、初期化するときに、どのバージョンの OpenGL ESをサポートするかを決定します。要求されたバージョンのOpenGL ESをデバイスがサポート していない場合、initWithAPI:メソッドはnilを返します。アプリケーションは、コンテキストが正 常に初期化されたかを確認するためコンテキストを使用する前にテストする必要があります。 アプリケーションので複数バージョンのOpenGL ESをレンダリングオプションとしてサポートするに は、まず、対象とする最新バージョンのレンダリングコンテキストの初期化を試行する必要がありま す。返されたオブジェクトがnilの場合は、古いバージョンのコンテキストを初期化します。リスト 2-1で、この方法を紹介しています。 リスト 2-1 同じアプリケーションでの複数バージョンのOpenGL ESのサポート EAGLContext* CreateBestEAGLContext() { EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 20 OpenGL ESコンテキストの設定 EAGL sharegroupはコンテキストのOpenGL ESオブジェクトを管理 if (context == nil) { context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; } return context; } コンテキストのAPIプロパティは、コンテキストがサポートするOpenGL ESのバージョンを示します。 アプリケーションは、コンテキストのAPIプロパティをテストし、そのプロパティを使用して正しい レンダリングパスを選びます。これを実装する一般的なパターンは、各レンダリングパスについてク ラスを作成するというものです。アプリケーションは、初期化時にコンテキストをテストし、一度レ ンダラを作成します。 EAGL sharegroupはコンテキストのOpenGL ESオブジェクトを管 理 コンテキストはOpenGL ESの状態を保持しますが、OpenGL ESオブジェクトを直接管理するわけではあ りません。OpenGL ESオブジェクトは、EAGLSharegroupオブジェクトによって作成され、維持され ます。どのコンテキストにもオブジェクトの作成をデリゲートするEAGLSharegroupオブジェクトが 含まれます。 sharegroupの利点は、2つ以上のコンテキストが同じsharegroupを参照する場合に明らかになってきま す(図 2-1を参照)。複数のコンテキストが共通のsharegroupに接続されている場合、何らかのコン テキストで作成したOpenGL ESオブジェクトはすべてのコンテキスト上で利用できます。オブジェク トを作成したコンテキストではなく、別のコンテキスト上で同じオブジェクト識別子にバインドする と、同じ OpenGL ESオブジェクトを参照します。リソースは、モバイルデバイスでは希少な場合が少 なくありません。複数のコンテキストにおいて同じコンテンツのコピーを複数作成するのは非効率的 です。共通のリソースを共有することで、デバイス上のグラフィックスリソースの空きをより効果的 に利用できます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 21 OpenGL ESコンテキストの設定 EAGL sharegroupはコンテキストのOpenGL ESオブジェクトを管理 sharegroupは不透明なオブジェクトです。呼び出すことができるメソッドやプロパティはありませ ん。sharegroupオブジェクトが使用されるコンテキストでは、それに対する強参照が保持されます。 図 2-1 2つのコンテキストによるOpenGL ESオブジェクトの共有 sharegroupは、次に示す2つの具体的シナリオで最も有用です。 ● ● コンテキスト間で共有されているリソースのほとんどが変更されない場合。 レンダラのメインスレッド以外のスレッド上に、アプリケーションが新たなOpenGL ESオブジェ クトを作成できるようにしたい場合。この場合、2つ目のコンテキストは独立したスレッド上で 実行され、データのフェッチとリソースの作成に充てられます。リソースがロードされた後、最 初のコンテキストはオブジェクトにバインドしてすぐに使用できます。GLKTextureLoaderクラ スは、このパターンを使用して、テクスチャの非同期読み込みを実行します。 同じsharegroupを参照する複数のコンテキストを作成するには、最初のコンテキストをinitWithAPI: を呼び出して初期化します。sharegroupはそのコンテキストに対して自動的に作成されます。2つ目 以降のコンテキストは、initWithAPI:sharegroup:メソッドを呼び出すことにより、最初のコンテ キストのsharegroupを使用するよう初期化されます。リスト 2-2では、この仕組みについて説明して います最初のコンテキストは、リスト 2-1 (20 ページ)で定義される、簡易関数を使用して作成さ れます。2つ目のコンテキストは、最初のコンテキストからAPIのバージョンとsharegroupを抽出して 作成されます。 Important: 同じsharegroupに関連付けられたすべてのコンテキストは、初期コンテキストと同じ バージョンのOpenGL ES APIを使用する必要があります。 リスト 2-2 共通のsharegroupを持つ2つのコンテキストの作成 EAGLContext* firstContext = CreateBestEAGLContext(); EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]]; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 22 OpenGL ESコンテキストの設定 EAGL sharegroupはコンテキストのOpenGL ESオブジェクトを管理 sharegroupが複数のコンテキストによって共有されている場合、OpenGL ESオブジェクトに生じる状 態の変化の管理はアプリケーションの役割です。変化の管理に関する規則は次のとおりです。 ● ● ● オブジェクトの修正中ではない場合、複数のコンテキストにわたって同時にそのオブジェクトに アクセスすることが可能。 コンテキストに送信されたコマンドによってオブジェクトが修正されている間、オブジェクトを 読み取ったり、ほかのコンテキスト上でオブジェクトを修正したりしてはならない。 オブジェクトが修正された後、すべてのコンテキストがオブジェクトを再バインドして変更を認 識する必要がある。オブジェクトのコンテンツは、コンテキストがバインドする前にオブジェク トを参照している場合は未定義である。 OpenGL ESオブジェクトを更新するためにアプリケーションが従う手順は次のとおりです。 1. オブジェクトを使用する可能性のあるコンテキストごとにglFlushを呼び出す。 2. オブジェクトを修正するコンテキスト上で、1つ以上のOpenGL ES関数を呼び出してオブジェクト に変更を加える。 3. 状態変更コマンドを受信するコンテキスト上でglFlushを呼び出す。 4. そのほかの各コンテキスト上で、オブジェクト識別子を再バインドする。 注意: オブジェクトを共有するもう1つの方法は、単一のレンダリングコンテキストではな く、複数のレンダリング先フレームバッファを使用することです。レンダリング時におい て、アプリケーションは適切なフレームバッファをバインドし、必要に応じてフレームをレ ンダリングします。すべてのOpenGL ESオブジェクトは単一のコンテキストから参照される ため、OpenGL ESオブジェクトは同一のOpenGL ESデータを認識しています。この方法はリ ソースはあまり使用しませんが、コンテキストの状態を細かく制御可能な単一スレッドのア プリケーションでしか有用ではありません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 23 OpenGL ESおよびGLKitによる描画 GLKitフレームワークは、OpenGL ESコンテンツの描画やアニメーションに必要な設定および保守コー ドが不要なViewおよびView Controllerクラスを提供します。GLKViewクラスは、描画コードの場所を提 供するOpenGL ESインフラストラクチャを管理し、GLKViewControllerクラスはGLKit ViewでのOpenGL ES コンテンツの滑らかなアニメーションのレンダリングループを提供します。これらのクラスにより、 ビューコンテンツの描画とビュープレゼンテーションの管理のための標準的なUIKit設計パターンが拡 張されます。結果として、OpenGL ESレンダリングコードに集中的に取り組み、短期間でアプリケー ションを稼働させることができます。GLKitフレームワークは、OpenGL ES 2.0および3.0の開発を容易 にするその他の機能も備えています。 GLKit ViewはOpenGL ESコンテンツをオンデマンドで描画する GLKViewクラスは、標準的なUIView描画サイクルのOpenGL ESベースの等価物を提供します。UIView インスタンスは、drawRect:実装がQuartz 2D描画コマンドのみを実行する必要があるようにグラフィッ クコンテキストを自動的に設定し、GLKViewインスタンスは、描画メソッドがOpenGL ES描画コマン ドのみを実行する必要があるようにそれ自体を自動的に設定します。GLKViewクラスは、OpenGL ES 描画コマンドの結果を保持し、描画メソッドが返されると結果をCore Animationに自動的に表示する フレームバッファオブジェクトを管理することによって、この機能を提供します。 標準的なUIKit Viewと同様に、GLKit Viewはそのコンテンツをオンデマンドでレンダリングします。 ビューは最初に表示されたときに描画メソッドを呼び出します。Core Animationはレンダリング出力 をキャッシュし、ビューが表示されるたびにそれを表示します。ビューのコンテンツを変更するとき は、そのsetNeedsDisplayメソッドを呼び出すと、ビューは再び描画メソッドを呼び出し、結果の画 像をキャッシュし、それを画面に表示します。この方法は、画像のレンダリングに使用するデータが 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 24 OpenGL ESおよびGLKitによる描画 GLKit ViewはOpenGL ESコンテンツをオンデマンドで描画する あまり頻繁に変化しない場合や、ユーザのアクションに応答してのみ変化する場合に有用です。必要 なときだけ新しいビューコンテンツをレンダリングすることによって、デバイスのバッテリーが節約 され、デバイスがほかのアクションを実行する時間が長くなります。 図 3-1 GLKit ViewでのOpenGL ESコンテンツのレンダリング GLKit Viewの作成と設定 GLKViewオブジェクトは、プログラムまたはInterface Builderを使用して作成および設定できます。描 画に使用する前に、EAGLContextオブジェクトと関連付ける必要があります(“OpenGL ESコンテキス トの設定” (19 ページ)を参照)。 ● ● ビューをプログラムで作成するときは、最初にコンテキストを作成し、それをビューの initWithFrame:context:メソッドに渡します。 ストーリーボードからビューを読み込んだ後、コンテキストを作成し、ビューのcontextプロパ ティ値として設定します。 GLKit Viewは、その独自のOpenGL ESフレームバッファオブジェクトとレンダバッファを自動的に作成 および設定します。リスト 3-1に示すように、ビューの描画可能プロパティを使用してこれらのオブ ジェクトの属性を制御します。GLKit Viewのサイズ、倍率、描画可能プロパティを変更すると、次回 のコンテンツ描画時に適切なフレームバッファオブジェクトとレンダバッファが自動的に削除および 再作成されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 25 OpenGL ESおよびGLKitによる描画 GLKit ViewはOpenGL ESコンテンツをオンデマンドで描画する リスト 3-1 GLKit Viewの設定 - (void)viewDidLoad { [super viewDidLoad]; // Create an OpenGL ES context and assign it to the view loaded from storyboard GLKView *view = (GLKView *)self.view; view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // Configure renderbuffers created by the view view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; view.drawableDepthFormat = GLKViewDrawableDepthFormat24; view.drawableStencilFormat = GLKViewDrawableStencilFormat8; // Enable multisampling view.drawableMultisample = GLKViewDrawableMultisample4X; } GLKViewインスタンスのマルチサンプル機能をそのdrawableMultisampleプロパティを使用して有効 にできます。マルチサンプル機能は、ギザギザのエッジを滑らかするアンチエイリアスの一種で、よ り多くのメモリとフラグメント処理時間を使用して3Dアプリケーションでの画質を向上させます。マ ルチサンプル機能を有効にする場合は、パフォーマンスが許容できる水準を維持できるよう、アプリ ケーションのパフォーマンスを必ずテストしてください。 GLKit Viewの描画 図 3-1 (25 ページ)では、OpenGL ESコンテンツを描画するための3つのステップ(OpenGL ESインフ ラストラクチャの準備、描画コマンドの発行、Core Animationへのレンダリングコンテンツの表示) の概要を説明します。GLKViewクラスは、最初のステップと3番目のステップを実装します。2番目の ステップについては、リスト 3-2の中の例のような描画メソッドを実装します。 リスト 3-2 GLKit Viewの描画メソッドの例 - (void)drawRect:(CGRect)rect { // Clear the framebuffer 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 26 OpenGL ESおよびGLKitによる描画 GLKit ViewはOpenGL ESコンテンツをオンデマンドで描画する glClearColor(0.0f, 0.0f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw using previously configured texture, shader, uniforms, and vertex array glBindTexture(GL_TEXTURE_2D, _planetTexture); glUseProgram(_diffuseShading); glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m); glBindVertexArrayOES(_planetMesh); glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT); } 注意: glClear関数は、既存のフレームバッファコンテンツを破棄できるというヒントを OpenGL ESに伝達し、それまでのコンテンツをメモリに読み込むという負担のかかるメモリ 操作を回避します。最適なパフォーマンスを確保するために、描画の前には必ずこの関数を 呼び出す必要があります。 GLKViewクラスは、OpenGL ESのレンダリングプロセスの標準的な部分を管理するため、OpenGL ESの 描画のための簡単なインターフェイスを提供します。 ● 描画メソッドを呼び出す前に、ビューは次のことを行います。 ● ● ● EAGLContextオブジェクトを現在のコンテキストにする フレームバッファオブジェクトとレンダバッファオブジェクトを、その現在のサイズ、倍率、 および描画可能プロパティ(必要に応じて)に基づいて作成する ● フレームバッファオブジェクトを描画コマンドの現在の発行先としてバインドする ● フレームバッファサイズに一致するようにOpenGL ESビューポートを設定する 描画メソッドが返された後、ビューは次のことを行います。 ● マルチサンプル機能のバッファを解決する(マルチサンプル機能が有効になっている場合) ● コンテンツが不要になったレンダバッファを破棄する ● レンダリングバッファのコンテンツをキャッシュと表示のためにCore Animationに表示する 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 27 OpenGL ESおよびGLKitによる描画 デリゲートオブジェクトを使用したレンダリング デリゲートオブジェクトを使用したレンダリング 多くのOpenGL ESアプリケーションでは、カスタムクラスにレンダリングコードが実装されています。 この方法の利点は、それぞれに対して異なるレンダリングクラス複数のレンダリングアルゴリズムを 簡単にサポートできる点です。一般的な機能を共有するレンダリングアルゴリズムは、スーパークラ スからその機能を継承できます。たとえば、OpenGL ES 2.0と3.0の両方をサポートするために、異な るレンダラクラスを使用することができます(“OpenGL ESコンテキストの設定” (19 ページ)を参 照)。または、それらを使用して、より高性能のハードウェアのデバイス上で画質を向上させるため にレンダリングをカスタマイズすることができます。 GLKitはこの方法に大変適しています。レンダラオブジェクトを標準的なGLKViewインスタンスのデリ ゲートにすることができます。GLKViewのサブクラスを定義し、drawRect:メソッドを実装する代わ りに、レンダラクラスはGLKViewDelegateプロトコルを使用し、glkView:drawInRect:メソッドを 実装します。リスト 3-3は、アプリケーションの起動時にハードウェアに機能に基づいてレンダラク ラスを選択する方法を示します。 リスト 3-3 ハードウェアの機能に基づいたレンダラクラスの選択 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Create a context so we can test for features EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; [EAGLContext setCurrentContext:context]; // Choose a rendering class based on device features GLint maxTextureSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); if (maxTextureSize > 2048) self.renderer = [[MyBigTextureRenderer alloc] initWithContext:context]; else self.renderer = [[MyRenderer alloc] initWithContext:context]; // Make the renderer the delegate for the view loaded from the main storyboard GLKView *view = (GLKView *)self.window.rootViewController.view; view.delegate = self.renderer; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 28 OpenGL ESおよびGLKitによる描画 GLKit View ControllerがOpenGL ESコンテンツをアニメーション化する // Give the OpenGL ES context to the view so it can draw view.context = context; return YES; } GLKit View ControllerがOpenGL ESコンテンツをアニメーション 化する GLKViewオブジェクトは、デフォルトでは、オンデマンドでそのコンテンツをレンダリングします。 とはいえ、OpenGL ESでの描画の重要な利点は、複雑なシーンの連続的なアニメーション、すなわち 静止画像を表示することがほとんどないゲームやシミュレーションなどのアプリケーションに、グラ フィックス処理ハードウェアを使用できることです。このような場合に、GLKitフレームワークは、管 理するGLKViewオブジェクトのアニメーションループを管理するView Controllerクラスを提供します。 このループは、更新と表示の2つのフェーズで、ゲームやシミュレーションで一般的な設計パターン に従います。図 3-2に、アニメーションループの簡単な例を示します。 図 3-2 アニメーションループ アニメーションループについて 更新フェーズでは、View Controllerがその独自のupdateメソッド(またはそのデリゲートの glkViewControllerUpdate:メソッド)を呼び出します。このメソッドでは、次のフレームを描画す る準備をする必要があります。たとえば、ゲームでは、このメソッドを使用して、最後のフレーム以 降に受信された入力イベントに基づいて、ユーザや敵のキャラクターの位置を決定することができま す。サイエンティフィックビジュアライゼーションでは、このメソッドを使用してそのシミュレー 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 29 OpenGL ESおよびGLKitによる描画 GLKit View ControllerがOpenGL ESコンテンツをアニメーション化する ションのステップを実行することができます。次のフレームのアプリケーションの状態を判断するた めにタイミング情報が必要な場合は、timeSinceLastUpdateプロパティなど、View Controllerのいず れかのタイミングプロパティを使用します。図 3-2では、更新フェーズでangle変数が増え、それを 使用して変換行列が計算されています。 表示フェーズでは、View Controllerがそのビューのdisplayメソッドを呼び出し、今度はそれが描画メ ソッドを呼び出します。描画メソッドでは、OpenGL ES描画コマンドをGPUに送信して、コンテンツ をレンダリングします。最適なパフォーマンスのために、アプリケーションは、新しいフレームのレ ンダリング開始時にOpenGL ESオブジェクトを修正し、その後、描画コマンドを送信する必要があり ます。図 3-2では、表示フェーズでシェーダプログラムのuniform変数が更新フェーズで計算された行 列に設定され、描画コマンドを送信して新しいコンテンツがレンダリングされています。 アニメーションループでは、これらの2つのフェーズがView ControllerのframesPerSecondプロパティ によって指定された速度で繰り返されます。preferredFramesPerSecondプロパティを使用して、希 望のフレームレートを設定できます。View Controllerでは、現在の表示ハードウェアのパフォーマン スを最適化するために、希望の値に近い、最適なフレームレートが自動的に選択されます。 Important: 最良の結果を得るためには、アプリケーションが一貫して達成可能なフレームレート を選びます。スムーズで一貫したフレームレートは、一貫性のないフレームレートの場合よりも より快適なユーザ体験を提供します。 GLKit View Controllerの使用 リスト 3-4では、GLKViewControllerサブクラスとGLKViewインスタンスを使用して、アニメーショ ン化されたOpenGL ESコンテンツをレンダリングするための一般的な方法を示します。 リスト 3-4 GLKit ViewおよびView Controllerを使用した、OpenGL ESコンテンツの描画とアニメーション化 @implementation PlanetViewController // subclass of GLKViewController - (void)viewDidLoad { [super viewDidLoad]; // Create an OpenGL ES context and assign it to the view loaded from storyboard GLKView *view = (GLKView *)self.view; view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 30 OpenGL ESおよびGLKitによる描画 GLKit View ControllerがOpenGL ESコンテンツをアニメーション化する // Set animation frame rate self.preferredFramesPerSecond = 60; // Not shown: load shaders, textures and vertex arrays, set up projection matrix [self setupGL]; } - (void)update { _rotation += self.timeSinceLastUpdate * M_PI_2; // one quarter rotation per second // Set up transform matrices for the rotating planet GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(_rotation, 0.0f, 1.0f, 0.0f); _normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL); _modelViewProjectionMatrix = GLKMatrix4Multiply(_projectionMatrix, modelViewMatrix); } - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { // Clear the framebuffer glClearColor(0.0f, 0.0f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set shader uniforms to values calculated in -update glUseProgram(_diffuseShading); glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m); glUniformMatrix3fv(_uniformNormalMatrix, 1, 0, _normalMatrix.m); // Draw using previously configured texture and vertex array glBindTexture(GL_TEXTURE_2D, _planetTexture); glBindVertexArrayOES(_planetMesh); glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 31 OpenGL ESおよびGLKitによる描画 GLKitを使用したレンダラの開発 } @end この例では、PlanetViewControllerクラス(カスタムGLKViewControllerサブクラス)のインスタ ンスが、標準的なGLKViewインスタンスとその描画可能プロパティとともに、ストーリーボードから 読み込まれます。viewDidLoadメソッドでは、OpenGL ESコンテキストが作成され、それがビューに 指定され、アニメーションループのフレームレートも設定されます。 View Controllerは自動的にそのビューのデリゲートとなるため、アニメーションループの更新フェー ズと表示フェーズの両方を実装します。updateメソッドでは、回転惑星の表示に必要な変換行列を 計算します。glkView:drawInRect:メソッドでは、シェーダプログラムにそれらの行列を指定し、 描画コマンドを送信して惑星ジオメトリをレンダリングします。 GLKitを使用したレンダラの開発 GLKitフレームワークは、ViewおよびView Controllerインフラストラクチャに加えて、iOSでのOpenGL ES開発を容易にするための他のいくつかの機能を提供します。 ベクトルと行列の数値演算の処理 OpenGL ES 2.0以降では、変換行列を作成または指定するための組み込み関数が用意されていません。 代わりに、プログラム可能なシェーダには頂点変換が用意されており、一般的なuniform変数を使用 してシェーダへの入力を指定します。GLKitフレームワークには、iOSハードウェアの高いパフォーマ ンスのために最適化された、ベクトルおよび行列タイプと関数の包括的なライブラリが含まれていま す。(GLKit Framework Reference を参照。) OpenGL ES 1.1の固定機能パイプラインからの移行 OpenGL ES 2.0以降では、OpenGL ES 1.1の固定機能グラフィックスパイプラインに関連する機能がすべ て削除されています。GLKBaseEffectクラスは、OpenGL ES 1.1パイプラインの変換、ライティング、 シェーディング段階にObjective-Cアナログを提供し、GLKSkyboxEffectおよび GLKReflectionMapEffectクラスは一般的な視覚効果のサポートを追加します。詳細については、こ れらのクラスのリファレンスマニュアルを参照してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 32 OpenGL ESおよびGLKitによる描画 GLKitを使用したレンダラの開発 テクスチャデータの読み込み GLKTextureLoaderクラスには、iOSでサポートされている任意の画像形式からOpenGL ESコンテキス トにテクスチャデータを同期的または非同期的に簡単に読み込む方法が用意されています。(“GLKit フレームワークを使用したテクスチャデータの読み込み” (97 ページ)を参照してください。) 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 33 その他のレンダリング先への描画 フレームバッファオブジェクトは、レンダリングコマンドの送信先です。フレームバッファオブジェ クトを作成すると、色、深度、および、ステンシルデータを正確に制御できます。このストレージを 提供するには、画像をフレームバッファにアタッチします(図 4-1を参照)。最も一般的な画像アタッ チメントはレンダバッファオブジェクトです。OpenGL ESテクスチャをフレームバッファの色アタッ チメントポイントにアタッチすることもできます。これは、描画コマンドがテクスチャにレンダリン グされることを意味します。画像がレンダリングされたテクスチャは、その後使用されるレンダリン グコマンドの入力の役割を果たすことができます。単一のレンダリングコンテキストに複数のフレー ムバッファオブジェクトを作成することもできます。これによって、同じレンダリングパイプライン とOpenGL ESリソースを複数のフレームバッファ間で共有することができます。 図 4-1 色と深度のレンダバッファを持つフレームバッファ これらのすべての方法において、OpenGL ESコンテキストのレンダリング結果を格納するフレームバッ ファオブジェクトとレンダバッファオブジェクトを手動で作成する必要があるほか、コンテンツを画 面に表示し、(必要に応じて)アニメーションループを実行するための追加コードを記述する必要が あります。 フレームバッファオブジェクトの生成 アプリケーションは、実行しようとするタスクに応じて異なるオブジェクトを設定し、フレームバッ ファオブジェクトにアタッチします。ほとんどの場合、フレームバッファの設定における違いは、フ レームバッファオブジェクトの色アタッチメントポイントにどのオブジェクトがアタッチされるかと いうことです。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 34 その他のレンダリング先への描画 フレームバッファオブジェクトの生成 ● ● ● オフスクリーンの画像処理にフレームバッファを使用するには、レンダバッファをアタッチしま す。詳細については、“オフスクリーンフレームバッファオブジェクトの作成” (35 ページ)を 参照してください。 フレームバッファの画像を後のレンダリング手順の入力として使用するには、テクスチャをア タッチします。詳細については、“フレームバッファオブジェクトを使用したテクスチャへのレン ダリング” (36 ページ)を参照してください。 Core Animationレイヤ合成でフレームバッファを使用するには、特別なCore Animation対応レンダ バッファを使用してください。詳細については、“Core Animationレイヤへのレンダリング” (37 ペー ジ)を参照してください。 オフスクリーンフレームバッファオブジェクトの作成 オフスクリーンレンダリングのためのフレームバッファは、そのアタッチメントのすべてをOpenGL ES レンダバッファとして割り当てます。次のコードは、色アタッチメントおよび深度アタッチメントを 持つフレームバッファオブジェクトを割り当てます。 1. フレームバッファを作成し、それをバインドします。 GLuint framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); 2. 色レンダバッファを作成し、それにストレージを割り当て、フレームバッファの色アタッチメン トポイントにストレージをアタッチします。 GLuint colorRenderbuffer; glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); 3. 深度または深度/ステンシルのレンダバッファを作成し、それにストレージを割り当て、フレー ムバッファの深度アタッチメントポイントにストレージをアタッチします。 GLuint depthRenderbuffer; glGenRenderbuffers(1, &depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 35 その他のレンダリング先への描画 フレームバッファオブジェクトの生成 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); 4. フレームバッファの完全性をテストします。このテストは、フレームバッファの設定が変更され た場合のみ実行する必要があります。 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER) ; if(status != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"failed to make complete framebuffer object %x", status); } オフスクリーンレンダバッファへの描画後、glReadPixels関数を使用してさらに処理するために、 そのコンテンツをCPUに戻すことができます。 フレームバッファオブジェクトを使用したテクスチャへのレンダリング このフレームバッファを作成するコードは、オフスクリーンの例のものとほとんど同じですが、テク スチャが割り当てられて色アタッチメントポイントにアタッチされる点が異なります。 1. (“オフスクリーンフレームバッファオブジェクトの作成” (35 ページ)と同じ手順で)フレー ムバッファオブジェクトを作成します。 2. レンダリング先のテクスチャを作成し、そのテクスチャをフレームバッファの色アタッチメント ポイントにアタッチします。 // create the texture GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, GL_UNSIGNED_BYTE, NULL); width, height, 0, GL_RGBA, glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 3. 深度バッファを割り当ててアタッチします(前述のとおり)。 4. フレームバッファの完全性をテストします(前述のとおり)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 36 その他のレンダリング先への描画 フレームバッファオブジェクトの生成 この例は色テクスチャへのレンダリングを前提としていますが、ほかの方法をとることができます。 たとえば、OES_depth_texture拡張機能を使用することで、テクスチャを深度アタッチメントポイ ントにアタッチし、シーンから得た深度情報をテクスチャに格納することができます。この深度情報 を使用して、最終的にレンダリングされるシーンの影を計算することができます。 Core Animationレイヤへのレンダリング Core Animationは、iOSでのグラフィクスレンダリングとアニメーションの中心的なインフラストラク チャです。UIKit、Quartz 2D、OpenGL ESなどのさまざまなiOSサブシステムを使用してレンダリングさ れたコンテツをホストするレイヤを使用して、アプリケーションのユーザインターフェイスまたはそ の他の視覚的表示を合成できます。OpenGL ESは、CAEAGLLayerクラス、コンテンツがOpenGL ESレン ダバッファに由来する特殊な種類のCore Animationレイヤを介して、Core Animationに接続します。 Core Animationは、レンダバッファのコンテンツと他のレイヤのコンテンツとを合成し、結果の画像 を画面に表示します。 図 4-2 Core AnimationとOpenGL ESとのレンダバッファの共有 CAEAGLLayerは、2つの重要な機能を提供することによって、このサポートをOpenGL ESに提供しま す。1つは、レンダバッファ用の共有ストレージを割り当てることです。2つ目は、Core Animationに レンダバッファを表示し、レイヤのそれ以前のコンテンツをレンダバッファのデータで置き換えま す。このモデルの利点は、Core Animationレイヤのコンテンツを、フレームごとに描画する必要がな く、レンダリングされた画像が変化する場合だけレンダリングすればよいということです。 注意: GLKViewクラスは以下の手順を自動化するため、ビューのコンテンツレイヤでOpenGL ES を使用して描画するときにこのクラスを使用する必要があります。 OpenGL ESレンダリングでCore Animationレイヤを使用するには 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 37 その他のレンダリング先への描画 フレームバッファオブジェクトの生成 1. CAEAGLLayerオブジェクトを作成し、そのプロパティを設定します。 パフォーマンスを最適化するために、レイヤのopaqueプロパティの値をYESに設定します。詳細 については、“Core Animation合成のパフォーマンスに注意する” (79 ページ)を参照してくださ い。 必要な場合は、値の新しいディクショナリをCAEAGLLayerオブジェクトのdrawableProperties プロパティに割り当てて、レンダリングサーフェスのサーフェスプロパティを設定します。レン ダバッファのピクセルフォーマットを指定したり、Core Animationに送信した後にレンダバッファ のコンテンツを破棄するかどうかを指定したりできます。設定可能なキーの一覧については、 『EAGLDrawable Protocol Reference 』を参照してください。 2. OpenGL ESコンテキストを割り当てて、そのコンテキストを現在のコンテキストに設定します。 詳細については、“OpenGL ESコンテキストの設定” (19 ページ)を参照してください。 3. (上記の“オフスクリーンフレームバッファオブジェクトの作成” (35 ページ)と同様に)フレー ムバッファオブジェクトを作成します。 4. 色レンダバッファを作成し、コンテキストのrenderbufferStorage:fromDrawable:メソッドを 呼び出してストレージを割り当て、レイヤオブジェクトをパラメータとして渡します。幅、高 さ、ピクセルフォーマットはレイヤから取得し、レンダバッファにストレージを割り当てるため に使用します。 GLuint colorRenderbuffer; glGenRenderbuffers(1, &colorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:myEAGLLayer]; glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer); 注意: Core Animationレイヤの境界矩形またはプロパティが変化した場合、アプリケー ションはレンダバッファのストレージを再割り当てする必要があります。レンダバッ ファを再割り当てしないと、レンダバッファのサイズがレイヤのサイズと合致しませ ん。この場合、Core Animationは画像のコンテンツがレイヤにきちんと収まるよう拡大 縮小することがあります。 5. 色レンダバッファの高さと幅を取得します。 GLint width; GLint height; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 38 その他のレンダリング先への描画 フレームバッファオブジェクトへの描画 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); 前出の各例では、バッファのストレージを割り当てるため、レンダバッファの幅と高さが明示的 に指定されていました。この例では、ストレージが割り当てられた後、コードによって色レンダ バッファから幅と高さを取得しています。アプリケーションがこのようにするのは、色レンダ バッファの実際の寸法がレイヤの境界矩形と倍率に基づいて計算されるためです。フレームバッ ファにアタッチされるほかのレンダバッファも同じ寸法でなければなりません。深度バッファを 割り当てるために高さと幅を使用するほか、OpenGL ESビューポートの割り当てるためと、アプ リケーションのテクスチャとモデルで必要な詳細レベルの決定に役立てるために幅と高さを使用 します。詳細については、“高解像度ディスプレイのサポート” (49 ページ)を参照してくださ い。 6. 深度バッファを割り当ててアタッチします(前述のとおり)。 7. フレームバッファの完全性をテストします(前述のとおり)。 8. CAEAGLLayerオブジェクトを表示レイヤのaddSublayer:メソッドに渡すことによって、Core Animationレイヤ階層に追加します。 フレームバッファオブジェクトへの描画 フレームバッファオブジェクトを作成したら、次にそのオブジェクトを埋める必要があります。この 節では、新しいフレームをレンダリングしてユーザに表示するために必要な手順を説明します。テク スチャまたはオフスクリーンのフレームバッファへのレンダリングは同じように行われますが、アプ リケーションが最終フレームを使用する方法だけが異なります。 オンデマンドまたはアニメーションループでのレンダリング GLKit ViewおよびView Controllerで描画するときのように、Core Animationレイヤをレンダリングする ときにOpenGL ESコンテンツを描画するタイミングを選択する必要があります。オフスクリーンフレー ムバッファまたはテクスチャにレンダリングする場合、それらの種類のフレームバッファを使用する 状況に適しているときは常に描画します。 オンデマンドの描画では、独自の描画メソッドを実装してレンダバッファを表示し、新しいコンテン ツを表示するたびにそのメソッドを呼び出します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 39 その他のレンダリング先への描画 フレームバッファオブジェクトへの描画 アニメーションループを使用して描画するには、CADisplayLinkオブジェクトを使用します。ディス プレイリンクはCore Animationによって提供される一種のタイマで、描画と画面のリフレッシュレー トを同期化できます。リスト 4-1 (40 ページ)では、ビューを表示されている画面を取得し、その 画面を使用して新たなディスプレイリンクオブジェクトを作成し、そのディスプレイリンクオブジェ クトを実行ループに追加する方法を示しています。 注意: GLKViewControllerクラスは、GLKViewコンテンツをアニメーションするために、 CADisplayLinkオブジェクトの使用を自動化します。CADisplayLinkクラスを直接使用す るのは、GLKitフレームワークの提供内容を超えて動作する必要がある場合のみです。 リスト 4-1 ディスプレイリンクの作成と開始 displayLink = [myView.window.screen displayLinkWithTarget:self selector:@selector(drawFrame)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; drawFrameメソッドの実装内部で、ディスプレイリンクのtimestampプロパティを読み取って、レン ダリングする次のフレームのタイムスタンプを取得します。アプリケーションはその値を使用して、 次のフレームのオブジェクトの位置を計算できます。 通常、ディスプレイリンクオブジェクトは、画面がリフレッシュするごとに作動します。リフレッ シュの値は通常 60Hzですが、デバイスによって異なる可能性があります。ほとんどのアプリケーショ ンは、画面を1秒間に60回更新する必要はありません。ディスプレイリンクのframeIntervalプロパ ティは、メソッドが呼び出される前に経過する実際のフレーム数に設定することができます。たとえ ば、フレームの間隔が3に設定される場合、アプリケーションは3フレームごとに呼び出され、1秒あ たりおよそ20フレームが表示されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 40 その他のレンダリング先への描画 フレームバッファオブジェクトへの描画 Important: 最良の結果を得るためには、アプリケーションが一貫して達成可能なフレームレート を選びます。スムーズで一貫したフレームレートは、一貫性のないフレームレートの場合よりも より快適なユーザ体験を提供します。 フレームのレンダリング 図 4-3は、フレームをレンダリングし、表示するためにiOS上でOpenGL ESアプリケーションがとるべ き手順を示します。この手順には、アプリケーションのパフォーマンスを向上させるヒントが多く含 まれています。 図 4-3 iOSにおけるOpenGLのレンダリング手順 バッファの消去 各フレームの始まりに、次のフレームの描画には必要のない、前のフレームのコンテンツが含まれる すべてのフレームバッファアタッチメントのコンテンツを消去します。glClear関数を呼び出して、 消去するすべてのバッファについてビットマスクを渡します(リスト 4-2を参照)。 リスト 4-2 フレームバッファアタッチメントの消去 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); レンダバッファまたはテクスチャの既存のコンテンツは破棄できるというOpenGL ESに対するglClear のヒントを使用して、以前のコンテンツをメモリに読み込むという負担のかかる操作を回避します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 41 その他のレンダリング先への描画 フレームバッファオブジェクトへの描画 リソースの準備と描画コマンドの実行 この2つのステップは、アプリケーションのアーキテクチャ設計時における、重要な判断に関係しま す。まず、画面に何を表示するか決め、対応するOpenGL ESオブジェクト(頂点バッファオブジェク ト、テクスチャ、シェイダプログラム、入力変数など)をGPUにアップロードするための設定をしま す。次に、リソースをどのように使ってフレームをレンダリングするか、GPUに指示する描画コマン ドを実行します。 レンダラの設計について詳しくは、“OpenGL ES設計ガイドライン” (52 ページ)を参照してくださ い。今のところ、パフォーマンス最適化における最も重要な点は、新しいフレームのレンダリング開 始時にpenGL ESオブジェクトを修正する場合だけ、アプリケーションの実行が高速化するということ です。アプリケーションは、オブジェクトの修正と描画コマンドの実行を交互に行う(図 4-3 (41 ペー ジ)に点線で示した部分)ことができますが、アプリケーションは各手順を1度だけ実行する場合に、 より高速に実行されます。 描画コマンドの実行 この手順は、前の手順で作成されたオブジェクトを取得し、これらオブジェクトを使用する描画コマ ンドを送信します。レンダリングコードのこの部分が効率よく実行されるよう設計する方法は、 “OpenGL ES設計ガイドライン” (52 ページ)で詳しく説明しています。今のところ、パフォーマンス 最適化における最も重要な点は、新しいフレームのレンダリング開始時にpenGL ESオブジェクトを修 正する場合だけ、アプリケーションの実行が高速化するということです。アプリケーションは、オブ ジェクトの修正と描画コマンドの送信を交互に行う(点線で示した部分)ことができますが、アプリ ケーションは各手順を1度だけ実行する場合に、より高速に実行されます。 マルチサンプル機能のリゾルブ アプリケーションは、マルチサンプル機能を使用して画像品質を改善する場合、ユーザに表示される 前にピクセルをリゾルブしなければなりません。マルチサンプル機能については、“マルチサンプル 機能の使用による画像品質の向上” (43 ページ)で詳しく説明しています。 不要なレンダバッファの破棄 破棄 操作は、1つ以上のレンダバッファのコンテンツが不要になったことをOpenGL ESに伝達するパ フォーマンス上のヒントです。レンダバッファのコンテンツが不要になったというというヒントを OpenGL ESに伝達することにより、バッファ内のデータを破棄することができたり、バッファのコン テンツを更新し続けるという負担のかかるタスクを回避できたりします。 レンダリングループのこの段階では、アプリケーションは、フレームに対するすべての描画コマンド の送信が完了しています。アプリケーションが、画面に表示するための色レンダバッファを必要とし ている間は、深度バッファのコンテンツを必要としていない可能性があります。リスト 4-3は、深度 バッファのコンテンツを破棄します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 42 その他のレンダリング先への描画 マルチサンプル機能の使用による画像品質の向上 リスト 4-3 深度フレームバッファの破棄 const GLenum discards[] = {GL_DEPTH_ATTACHMENT}; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glDiscardFramebufferEXT(GL_FRAMEBUFFER,1,discards); 注意: glDiscardFramebufferEXT関数は、OpenGL ES 1.1および2.0のEXT_discard_framebuffer拡張機能によって提供されます。OpenGL ES 3.0のコンテキストでは、 glInvalidateFramebuffer関数を代わりに使用します。 Core Animationへの結果の表示 この手順の段階では、色レンダバッファがレンダリングの完了したフレームを保持しています。その ため、必要なことはフレームをユーザに表示することだけです。リスト 4-4 (43 ページ)は、レン ダバッファをコンテキストにバインドし、レンダバッファを表示します。これにより、レンダリング の完了したフレームをCore Animationに渡すことができます。 リスト 4-4 レンダリングの完了したフレームの表示 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; デフォルトでは、レンダバッファのコンテンツは、アプリケーションがレンダバッファを表示した後 は破棄 されることを前提としなければなりません。これは、アプリケーションがフレームを表示する たびに、フレームのコンテンツを完全に作成し直して新しいフレームをレンダリングしなければなら ないことを意味します。こうした理由から、上記のコードは常に色バッファを消去します。 アプリケーションがフレーム間の色レンダバッファのコンテンツを維持する必要のある場合は、 kEAGLDrawablePropertyRetainedBackingキーをCAEAGLLayerオブジェクトのdrawableProperties プロパティに格納されるディクショナリに追加し、前のglClear関数呼び出しから GL_COLOR_BUFFER_BIT定数を削除します。保持されているバッキングは、バッファのコンテンツを 維持するため追加のメモリの割り当てをiOSに求める場合があり、これによりアプリケーションのパ フォーマンスが低下する可能性があります。 マルチサンプル機能の使用による画像品質の向上 マルチサンプル機能は、アンチエイリアスの1つの形式で、ほとんどの3Dアプリケーションでギザギ ザのエッジを滑らかにしたり、画質を向上させたりします。OpenGL ES 3.0は、中核仕様の一部として マルチサンプル機能を備えています。OpenGL ES 1.1および2.0では、iOSは、APPLE_framebuffer_mul- 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 43 その他のレンダリング先への描画 マルチサンプル機能の使用による画像品質の向上 tisample拡張機能によってこれを提供します。マルチサンプル機能では、使用されるメモリ量が多 く、画層をレンダリングするフラグメント処理時間もかかりますが、他の方法よりも低いパフォーマ ンス上の負担で画質を向上させることができる可能性があります。 図 4-4に、マルチサンプル機能が働く仕組みを示します。アプリケーションはフレームバッファオブ ジェクトを、1個ではなく2個作成します。マルチサンプル機能バッファには、コンテンツのレンダリ ングに必要なすべてのアタッチメント(一般的には色バッファと深度バッファ)が含まれています。 リゾルブバッファには、ユーザにレンダリング画像を表示するために必要なアタッチメント(一般的 には色レンダバッファですが、テクスチャの可能性もあります)のみが含まれ、“フレームバッファ オブジェクトの生成” (34 ページ)に示す適切な手順で作成されます。マルチサンプルレンダバッ ファは、リゾルブフレームバッファと同じ寸法を使用して割り当てられますが、それぞれのマルチサ ンプルレンダバッファには、各ピクセルについて格納するサンプル数を指定する追加のパラメータが 含まれます。アプリケーションは、マルチサンプルバッファへのレンダリングすべてを実行した後、 バッファのサンプルをリゾルブバッファへリゾルブ することによって、アンチエイリアス化された最 終画像を生成します。 図 4-4 マルチサンプル機能の仕組み リスト 4-5は、マルチサンプルバッファを作成するコードを示しています。このコードは、以前に作 成したバッファの幅と高さを使用し、glRenderbufferStorageMultisampleAPPLE関数を呼び出し て、レンダバッファ用にマルチサンプル化されたストレージを作成します。 リスト 4-5 マルチサンプルバッファの作成 glGenFramebuffers(1, &sampleFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer); glGenRenderbuffers(1, &sampleColorRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, sampleColorRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_RGBA8_OES, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, sampleColorRenderbuffer); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 44 その他のレンダリング先への描画 マルチサンプル機能の使用による画像品質の向上 glGenRenderbuffers(1, &sampleDepthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, sampleDepthRenderbuffer); glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, sampleDepthRenderbuffer); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); マルチサンプル機能をサポートするレンダリングコードの修正手順を次に示します。 1. バッファ消去の手順では、マルチサンプルフレームバッファのコンテンツを消去します。 glBindFramebuffer(GL_FRAMEBUFFER, sampleFramebuffer); glViewport(0, 0, framebufferWidth, framebufferHeight); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 2. 描画コマンドを送信した後、マルチサンプルバッファのコンテンツをリゾルブバッファへとリゾ ルブします。各ピクセルについて格納されたサンプルは、リゾルブバッファにおいて1つのサン プルに結合されます。 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, resolveFrameBuffer); glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, sampleFramebuffer); glResolveMultisampleFramebufferAPPLE(); 3. 破棄の手順では、マルチサンプルフレームバッファにアタッチされている両方のレンダバッファ を破棄できます。これは、表示しようとするコンテンツがリゾルブバッファに格納されているた めです。 const GLenum discards[] = {GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT}; glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE,2,discards); 4. 結果表示の手順では、リゾルブフレームバッファにアタッチされている色レンダバッファを表示 します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 45 その他のレンダリング先への描画 マルチサンプル機能の使用による画像品質の向上 glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER]; マルチサンプル機能は負担を伴わないわけではありません。追加のサンプルを格納するには追加のメ モリが必要となり、リゾルブフレームバッファへのサンプルのリゾルブには時間がかかります。マル チサンプル機能をアプリケーションに追加する場合は、パフォーマンスが許容できる水準を維持でき るよう、アプリケーションのパフォーマンスを必ずテストしてください。 注意: 上記のコードは、OpenGL ES 1.1または2.0のコンテキストを前提としています。マル チサンプル機能はOpenGL ES 3.0のコアAPIの一部ですが、関数はさまざまです。詳細につい ては、仕様を参照してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 46 マルチタスク、高解像度、iOSのその他の機能 OpenGL ESでの作業の多くの側面はプラットフォームに依存しませんが、iOS 上でのOpenGL ESの使用 の細部には特別な配慮が必要です。特に、OpenGL ESを使用するiOSアプリケーションは、マルチタス クを正しく処理したり、バックグラウンドに移動すると終了するリスクに対処したりする必要があり ます。iOSデバイス向けのOpenGL ESコンテンツを開発するときは、表示解像度やその他のデバイス機 能も考慮する必要があります。 マルチタスク対応OpenGL ESアプリケーションの実装 アプリケーションは、ユーザが別のアプリケーションに切り替えても実行し続けることができます。 iOSにおけるマルチタスクの概要については、“App States and Multitasking”を参照してください。 OpenGL ESアプリケーションは、バックグラウンドに移動したときに追加の作業を実行しなければな りません。アプリケーションがこれらの作業を適切に処理しない場合、代わりにiOSがそのアプリケー ションを終了する可能性があります。さらに、アプリケーションは、OpenGL ESリソースを解放して、 それらのリソースをフォアグラウンドのアプリケーションが利用できるようにすることもできます。 バックグラウンドのアプリケーションはグラフィックスハードウェアのコ マンドを実行しない場合がある OpenGL ESアプリケーションは、グラフィックスハードウェアでOpenGL ESコマンドを実行しようとす ると終了します。tiOSはバックグラウンドアプリケーションがグラフィックスプロセッサにアクセ スするのを阻止するため、Frontmostアプリケーションはユーザにとって常に使いやすいものになり ます。アプリケーションは、バックグラウンドでOpenGL ESを呼び出したときだけでなく、すでに送 信されたコマンドがバックグラウンドでGPUにフラッシュされた場合にも終了する可能性がありま す。アプリケーションは、バックグラウンドに移動する前に、すでに送信されたすべてのコマンドが 実行を完了しているようにしなければなりません。 GLKit ViewとView Controllerを使用し、描画メソッドでOpenGL ESコマンドを送信するだけの場合、ア プリケーションはバックグラウンドに移動すると、自動的に正しく動作します。GLKViewController クラスは、デフォルトでは、アプリケーションがアクティブでなくなったときにアニメーションタイ マを一時停止します。これにより、描画メソッドが呼び出されなくなります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 47 マルチタスク、高解像度、iOSのその他の機能 マルチタスク対応OpenGL ESアプリケーションの実装 GLKit ViewまたはView Controllerを使用しない場合やGLKView描画メソッド以外でOpenGL ESコマンドを 送信する場合は、次の手順を行ってアプリケーションがバックグラウンドで終了しないようにしま す。 1. アプリケーションデリゲートのapplicationWillResignActive:メソッドにおいて、アプリケー ションがアニメーションタイマーを停止し(タイマーがある場合)、アプリケーションを基地の 適切な状態に置いた後、glFinish関数を呼び出します。 2. アプリケーションデリゲートのapplicationDidEnterBackground:メソッドにおいて、OpenGL ES オブジェクトの一部を削除することで、メモリとリソースをフォアグラウンドアプリケーション が利用できるようにすることもできます。glFinish関数を呼び出して、リソースがただちに削除 されるようにします。 3. アプリケーションがapplicationDidEnterBackground:メソッドを終了した後は、新たなOpenGL ES呼び出しを行ってはなりません。アプリケーションがOpenGL ES呼び出しを行った場合は、iOS によってアプリケーションが終了されます。 4. アプリケーションのapplicationWillEnterForeground:メソッドでは、オブジェクトを作成し 直して、アニメーションタイマーを再始動します。 要約すると、アプリケーションは、すでに送信されたコマンドすべてがコマンドバッファから取り出 され、OpenGL ESによって実行されるようにするために、glFinish関数を呼び出す必要があるという ことです。アプリケーションがバックグラウンドに移動した後、フォアグラウンドに戻るまでの間 は、OpenGL ESの使用を一切回避しなければなりません。 作成し直したリソースをバックグラウンドに移動する前に簡単に削除 アプリケーションは、バックグラウンドに移動するとき、OpenGL ESオブジェクトを解放する必要は まったくありません。通常、アプリケーションはコンテンツの破棄を避ける必要があります。次の2 つのシナリオについて考えてみます。 ● ● ユーザがゲームをプレイしていて、少しの間ゲームを抜けて、カレンダーをチェックします。 ユーザがゲームに戻ると、ゲームのリソースはメモリ内に存在したままで、ゲームをすぐに再開 できます。 ユーザが別のOpenGL ESアプリケーションを起動して、OpenGL ESアプリケーションがバックグラ ウンドの状態にあります。ユーザが後から起動したOpenGL ESアプリケーションが、デバイス上 の空きメモリより多くのメモリを必要とする場合、バックグラウンドのアプリケーションは、追 加の作業の実行を要求されることなく、音を出さず自動的に終了します。 正常に動作するようにアプリケーションを設計することを目標とすべきです。つまり、フォアグラウ ンドへの移動にかかる時間をできるだけ短くしながら、バックグラウンドに移動している間はメモリ 占有量を減らす必要があります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 48 マルチタスク、高解像度、iOSのその他の機能 高解像度ディスプレイのサポート 次は、上記の2つのシナリオに対する対処方法です。 ● ● アプリケーションは、テクスチャやモデルなどのアセットをメモリに保持します。再作成に時間 のかかるリソースは、アプリケーションがバックグラウンドへ移動するときに破棄しないように します。 アプリケーションは、すばやく簡単に再作成できるオブジェクトを破棄しなければなりません。 大量のメモリを消費するオブジェクトを探します。 破棄しやすいターゲットは、レンダリング結果を保持するためにアプリケーションが割り当てるフ レームバッファです。アプリケーションがバックグラウンドにある場合、そのアプリケーションは ユーザからは見えず、OpenGL ESを使用して新しいコンテンツをレンダリングする可能性はありませ ん。これは、アプリケーションのフレームバッファが消費するメモリが割り当てられているのに、役 に立っていないことを意味します。さらに、フレームバッファのコンテンツは一時的 なものです。ほ とんどのアプリケーションは、新しいフレームをレンダリングするごとにフレームバッファのコンテ ンツを作成し直します。このため、レンダバッファはメモリを大量に消費する、簡単に再作成可能な リソースとなり、アプリケーションがバックグラウンドに移動するときに破棄できるオブジェクトの ちょうどよい候補となります。 GLKit ViewとView Controllerを使用する場合、アプリケーションがバックグラウンドに移動すると、 GLKViewControllerクラスはその関連するビューのフレームバッファを自動的に破棄します。ほか で使用するためにフレームバッファを手動で作成した場合は、アプリケーションがバックグラウンド に移動したときに、それらを破棄する必要があります。いずれの場合も、そのときにアプリケーショ ンが破棄できる他の一時的なリソースも考慮する必要があります。 高解像度ディスプレイのサポート デフォルトでは、GLKit ViewのcontentScaleFactorプロパティはそれが含まれる画面の倍率に一致 するため、それに関連するフレームバッファは最大解像度の表示でのレンダリング用に設定されま す。UIKitで高解像度ディスプレイをサポートする方法の詳細については、“Supporting High-Resolution Screens In Views”を参照してください。 Core Animationを使用してOpenGL ESコンテンツを表示する場合、倍率はデフォルトでは1.0に設定さ れます。Retinaディスプレイの最大解像度で描画するには、画面の倍率に合わせてCAEAGLLayerオブ ジェクトの倍率を変更する必要があります。 高解像度ディスプレイを備えたハードウェアをサポートするときは、それに応じてアプリケーション のモデルおよびテクスチャアセットも調整する必要があります。高解像度デバイスで実行するとき は、より詳細なモデルやテクスチャを選んで、より高画質な画像をレンダリングすることができま す。逆に、標準解像度デバイス上では、小さなモデルとテクスチャを使用できます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 49 マルチタスク、高解像度、iOSのその他の機能 高解像度ディスプレイのサポート Important: 多くのOpenGL ES APIは、画面ピクセルの明確な寸法を呼び出します。1.0よりも大き い倍率を使用する場合は、glScissor、glBlitFramebuffer、glLineWidth、glPointSize関数 またはgl_PointSizeシェーダ変数を使用するとき、それに応じて寸法を調整する必要がありま す。 高解像度ディスプレイをサポートする方法を決める際の重要な要素として、パフォーマンスがありま す。Retinaディスプレイの倍率を2倍にすると、ピクセル数は4倍になり、GPUは4倍のフラグメントを 処理するようになります。アプリケーションがフラグメント単位の計算を多く実行している場合、ピ クセル数の増加によりフレームレートが下がる可能性があります。高い倍率ではアプリケーションの 実行が極端に遅くなるようであれば、次の選択肢を検討してください。 ● ● ● ● この文書のパフォーマンスチューニングガイドラインを使用して、フラグメントシェーダのパ フォーマンスを最適化する。 よりシンプルなアルゴリズムをフラグメントシェーダに実装する。これにより、個々のピクセル の品質を下げて、画像全体をより高い解像度でレンダリングします。 1.0と画面の倍率の間の倍率を設定する。1.5の倍率は、1.0の倍率よりも品質は高くなりますが、 2.0に拡大された画像よりも塗りつぶしに必要なピクセル数は少なくて済みます。 GLKViewオブジェクトのdrawableColorFormatおよびdrawableDepthFormatプロパティに対し て精度の低い形式を使用する。これによって、基盤となるレンダバッファ上で操作するために必 要なメモリ帯域幅を減らします。 ● 低い倍率を使用し、マルチサンプル機能を有効にする。さらなる利点として、マルチサンプル機 能は高解像度ディスプレイをサポートしていないデバイス上で、より高い品質がもたらされるこ とが挙げられます。 GLKViewオブジェクトのマルチサンプル機能を有効にするには、そのdrawableMultisampleプロ パティの値を変更します。GLKit Viewへのレンダリングを行わない場合は、マルチサンプル機能 バッファを手動で設定し、最終画像の表示前にそれらを解決する必要があります(“マルチサンプ ル機能の使用による画像品質の向上” (43 ページ)を参照)。 マルチサンプル機能は負担を伴わないわけではありません。追加のサンプルを格納するには追加 のメモリが必要となり、リゾルブフレームバッファへのサンプルのリゾルブには時間がかかりま す。マルチサンプル機能をアプリケーションに追加する場合は、パフォーマンスが許容できる水 準を維持できるよう、アプリケーションのパフォーマンスを必ずテストしてください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 50 マルチタスク、高解像度、iOSのその他の機能 デバイスの向きに応じた調整 デバイスの向きに応じた調整 他のアプリケーションと同様に、OpenGL ESアプリケーションがそのコンテンツに適切なデバイスの 向きをサポートする必要があります。supportedInterfaceOrientationsメソッドを使用して、ア プリケーションの情報プロパティリスト、またはOpenGL ESコンテンツをホストしているView Controller で、アプリケーションに対してサポートされているデバイスの向きを宣言します。(詳細について は、『View Controller Programming Guide for iOS 』を参照。) デフォルトでは、GLKViewControllerおよびGLKViewクラスは向きの変更を自動的に処理します。 ユーザがサポートされている向きにデバイスを回転すると、向きの変更がアニメーション化され、 View Controllerのビューのサイズが変更されます。サイズが変わると、GLKViewオブジェクトはそれに 応じてそのフレームバッファとビューポートのサイズを調整します。この変更に対応する必要がある 場合は、GLKViewControllerサブクラスのviewWillLayoutSubviewsまたはviewDidLayoutSubviews メソッドを実装するか、カスタムGLKViewサブクラスを使用している場合は、layoutSubviewsメソッ ドを実装します。 Core Animationレイヤを使用してOpenGL ESコンテンツを描画する場合は、ユーザデバイスの向きを管 理するためにアプリケーションにView Controllerを含める必要があります。 外部ディスプレイへのOpenGL ESコンテンツの表示 iOSデバイスは、外部ディスプレイにアタッチできます。外部ディスプレイの解像度とコンテンツの 倍率は、メイン画面とは異なる可能性があります。フレームをレンダリングするコードで適合するよ う調整する必要があります。 外部ディスプレイへの描画手順は、メイン画面に描画する場合とほとんど同じです。 1. Multiple Display Programming Guide for iOS の手順に従って、外部ディスプレイにウインドウを作成 します。 2. レンダリング方法に適したビューまたはView Controllerオブジェクトをウインドウに追加します。 ● ● GLKitでレンダリングする場合は、GLKViewControllerおよびGLKView(またはカスタムサブ クラス)のインスタンスを設定し、そのrootViewControllerプロパティを使用してそれら をウインドウに追加します。 Core Animationレイヤへのレンダリングを行う場合、レイヤがウインドウのサブビューとして 含まれるビューを追加します。レンダリングにアニメーションループを使用する場合は、ウ インドウのscreenプロパティを取得し、displayLinkWithTarget:selector:メソッドを呼 び出して、外部ディスプレイに対して最適化されたディスプレイリンクオブジェクトを作成 します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 51 OpenGL ES設計ガイドライン ここまで、iOSアプリケーションにOpenGL ESを組み込むための基本事項を説明したので、この章で は、高い処理性能を発揮するレンダリングエンジンの設計に役立つ事項を解説します。レンダラの設 計に関する重要な概念をこの章で取り上げ、以降の章ではさらに、よりよい設計技法や、性能改善の ための技術を紹介します。 OpenGL ESを視覚化する方法 この節では、OpenGL ESの設計を思い描けるよう、クライアント/サーバアーキテクチャとパイプライ ンという、2つの概念を取り上げます。いずれも、アプリケーションのアーキテクチャを計画、評価 する上で有用な概念です。 クライアント/サーバアーキテクチャとしてのOpenGL ES 図 6-1に、OpenGL ESをクライアント/サーバアーキテクチャとして表した図を示します。アプリケー ションは、状態変化、テクスチャや頂点のデータ、レンダリングコマンドを、OpenGL ESクライアン トとの間でやり取りします。クライアントはこれを、グラフィックスハードウェアが認識できる形式 に変換し、GPUに転送します。この処理はアプリケーションの描画性能に対するオーバーヘッドにな ります。 図 6-1 OpenGL ESのクライアント/サーバアーキテクチャ 処理性能を改善するためには、このオーバーヘッドをうまく管理しなければなりません。適切に設計 されたアプリケーションは、OpenGL ESに対する呼び出しの頻度を落とし、ハードウェアにとって適 切なデータ形式を採用して変換の負荷を抑え、OpenGL ESとの間でやり取りするデータの流れを注意 深く管理します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 52 OpenGL ES設計ガイドライン OpenGL ESを視覚化する方法 グラフィックスパイプラインとしてのOpenGL ES 図 6-2に、OpenGL ESをグラフィックスパイプラインとして表した図を示します。アプリケーションは グラフィックスパイプラインを構成し、描画コマンドを実行して、頂点データをパイプラインに送り 込みます。パイプラインとして連続している各ステージでは、頂点シェーダが頂点データを処理し、 これをプリミティブとしてまとめ、ラスタライズしてフラグメントにし、フラグメントシェーダが各 フラグメントの色や深度を計算し、これをフレームバッファにブレンドして、最終的な表示に到りま す。 図 6-2 OpenGL ESグラフィックスパイプライン アプリケーションがどのようにしてフレームを生成しているのか、思い浮かべるためのモデルとして パイプラインを使うとよいでしょう。レンダラの設計も、これに応じていくつかに分けておこないま す。すなわち、シェーダプログラム(パイプラインの頂点ステージやフラグメントステージを処理) を記述し、このプログラムに送り込む頂点データやテクスチャデータを編成し、パイプラインの固定 機能ステージを駆動するようOpenGL ESの状態機械を設定する、という分割です。 グラフィックスパイプラインの各ステージは、それぞれの計算処理を同時に実行できます。たとえ ば、アプリケーションが新しいプリミティブを用意している間に、グラフィックスハードウェアの独 立した各部分が、それまでに送り込まれたジオメトリデータをもとに、頂点やフラグメントの計算を 進めるのです。もっとも、後のステージの処理は、前のステージの出力に依存します。パイプライン のいずれかの段階の作業量が多すぎる場合、または作業の実行に時間がかかりすぎている場合、ほか のパイプラインは、最も時間のかかっている段階が作業を完了するまでの間、アイドル状態となりま す。グラフィックスハードウェアの能力に応じ、パイプラインの各ステージの処理量が均衡していれ ば、よい設計ということになります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 53 OpenGL ES設計ガイドライン OpenGL ESの版とレンダラのアーキテクチャ Important: アプリケーションのパフォーマンスをチューニングする場合、最初の手順は通常、ア プリケーションのボトルネックとなっている段階とその理由を特定することです。 OpenGL ESの版とレンダラのアーキテクチャ iOSには3つの版のOpenGL ESが搭載されています。新しい版ほど適合性が高く、高画質の視覚効果を 実現するレンダリングアルゴリズムを、処理性能を損なうことなく実装できるようになっています。 OpenGL ES 3.0 OpenGL ES 3.0はiOS 7で新たに導入された版です。その機能を駆使して、従来はデスクトップ機やゲー ムコンソールでしか実現できなかった、高度なグラフィックスプログラミング技法を実装し、高いグ ラフィック性能と卓越した視覚効果を生み出すことができます。 OpenGL ES 3.0の目玉となる機能をいくつか取り上げて紹介します。そのほかの機能については『OpenGL ES 3.0 Specification 』(OpenGL ES API Registryで公開)を参照してください。 OpenGL ESのシェーディング言語(第3.0版) GLSL ES 3.0には、ユニフォームブロック、32ビット整数、追加の整数演算などの新機能が加わってお り、頂点シェーダやフラグメントシェーダで、より汎用的な計算処理を実行できるようになりまし た。新しい言語でシェーダプログラムを記述するためには、ソースコードの先頭に「#version 330 es」という記述子が必要です。OpenGL ES 3.0のコンテキストは、OpenGL ES 2.0用に記述したシェーダ と互換性があります。 詳しくは、“OpenGL ESシェーディング言語の第3.0版に移行する” (124 ページ)、および『OpenGL ES Shading Language 3.0 Specification 』(OpenGL ES API Registryで公開)を参照してください。 多重レンダターゲット 多重レンダターゲットを有効にすると、複数のフレームバッファアタッチメントに、同時に書き込む フラグメントシェーダを実装できます。 この機能を利用して、遅延シェーディングのような高度なレンダリングアルゴリズムを実装できま す。一連のテクスチャをレンダリングしてジオメトリデータを保存しておき、このテクスチャを読み 込んだ後、光源を計算して最終的な画像を出力する、シェーディングパスをいくつか実行するという アルゴリズムです。この方式では、入力を事前に計算した上で光源を計算するため、多数の光源が 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 54 OpenGL ES設計ガイドライン OpenGL ESの版とレンダラのアーキテクチャ あっても計算負荷はそれほど増えません。遅延シェーディングのアルゴリズムを実装し、充分な処理 性能を引き出すためには、多重レンダターゲットの機能が不可欠です(図 6-3を参照)。これがなけ れば、多数のテクスチャをレンダリングするため、それぞれ独立した描画パスが必要になります。 図 6-3 フラグメントシェーダが多重レンダターゲットに出力する様子 “フレームバッファオブジェクトの生成” (34 ページ)で説明した処理に加え、多重レンダターゲッ トを用意します。フレームバッファ用の色アタッチメントを、1つでなく必要な数だけ生成します。 次にglDrawBuffers関数で、どのフレームバッファアタッチメントをレンダリングに使うか指定しま す(リスト 6-1を参照)。 リスト 6-1 多重レンダターゲットを用意するコード例 // Attach (previously created) textures to the framebuffer. glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _colorTexture, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _positionTexture, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, _normalTexture, 0); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 55 OpenGL ES設計ガイドライン OpenGL ESの版とレンダラのアーキテクチャ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0); // Specify the framebuffer attachments for rendering. GLenum targets[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2}; glDrawBuffers(3, targets); アプリケーションが描画コマンドを発行すると、フラグメントシェーダは、各レンダターゲットの各 ピクセルに、どの色(または色以外のデータ)を出力するか判断します。リスト 6-2に、多重ターゲッ トにレンダリングする、基本的なフラグメントシェーダを示します。場所がリスト 6-1の設定に合致 するフラグメント出力変数に代入する、という形で処理します。 リスト 6-2 多重レンダターゲットに出力するフラグメントシェーダのコード例 #version 300 es uniform lowp sampler2D myTexture; in mediump vec2 texCoord; in mediump vec4 position; in mediump vec3 normal; layout(location = 0) out lowp vec4 colorData; layout(location = 1) out mediump vec4 positionData; layout(location = 2) out mediump vec4 normalData; void main() { colorData = texture(myTexture, texCoord); positionData = position; normalData = vec4(normalize(normal), 1.0); } 多重レンダターゲットは、実時間リフレクション、SSAO(Screen-Space Ambient Occlusion、画面空間 のアンビエントオクルージョン)、体積光など、他の高度なグラフィックス技法にも有用です。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 56 OpenGL ES設計ガイドライン OpenGL ESの版とレンダラのアーキテクチャ 変換のフィードバック グラフィックスハードウェアは、ベクトル処理用に最適化した、高度に並列化したアーキテクチャを 使います。このハードウェアに新たに導入された、変換のフィードバック機能も活用するとよいで しょう。頂点シェーダの出力を取り込んで、GPUメモリのバッファオブジェクトに送り込む機能で す。あるレンダリングパスからデータを取り込んで別のパスで使う、あるいは、グラフィックスパイ プラインの一部を無効にし、他の一般的な目的に変換のフィードバックを利用することが可能です。 この機能が有用な技術として、アニメーションパーティクル効果があります。パーティクル系をレン ダリングする一般的なアーキテクチャを図 6-4に示します。アプリケーションはまず、パーティクル シミュレーションの初期状態を設定します。次に、レンダリングする各フレームについてシミュレー ションのステップを実行して、各対象パーティクルの位置や向き、速度を更新した後、パーティクル の現在の状態を表す視覚アセットを描画します。 図 6-4 パーティクル系アニメーションの概要 従来、パーティクル系を実装するアプリケーションは、CPU側でシミュレーションを実行して結果を 頂点バッファに格納し、パーティクルアートのレンダリングに使っていました。しかし、頂点バッ ファの内容をGPUメモリに転送する処理には時間がかかります。変換のフィードバック機能を利用す れば、現代的なGPUハードウェアが備える並列処理アーキテクチャを駆使して、より効率的に問題を 解くことができます。 この方針にもとづき、実際にレンダリングエンジンを設計してみましょう。図 6-5に、OpenGL ESのグ ラフィックスパイプラインを利用して、パーティクル系アニメーションを実装する方法の概要を示し ます。OpenGL ESは個々のパーティクルやその状態を頂点として表現するので、GPUの頂点シェーダ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 57 OpenGL ES設計ガイドライン OpenGL ESの版とレンダラのアーキテクチャ ステージでは、いくつかのパーティクルに対して同時にシミュレーションを実行できます。パーティ クルの状態データを格納する頂点バッファはフレーム間で共用できるので、データをGPUメモリに転 送するという負荷の高い処理は、初期化の際に一度起こるだけです。 図 6-5 変換のフィードバック機能を利用するグラフィックスパイプラインの構成例 1. 初期化の際に頂点バッファを生成し、シミュレーション対象のパーティクルすべてについて、初 期状態データを格納します。 2. GLSL頂点シェーダプログラムでパーティクルのシミュレーションをおこない、フレームごとに、 パーティクルの位置データを格納した頂点バッファの内容を描画します。 ● ● 変換のフィードバック機能を有効にした状態でレンダリングするため、 glBeginTransformFeedback関数を実行します(その後、glEndTransformFeedback()を呼 び出してから、通常の描画処理を実行)。 glTransformFeedbackVaryings関数で、変換のフィードバック機能がどのシェーダ出力を 取り込むかを指定します。また、glBindBufferBase関数またはglBindBufferRange関数と、 GL_TRANSFORM_FEEDBACK_BUFFERというバッファ型を使って、取り込み先のバッファを指定 します。 ● ラスタ化(およびパイプラインの後続ステージ)を無効にするため、 glEnable(GL_RASTERIZER_DISCARD)を実行します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 58 OpenGL ES設計ガイドライン 高パフォーマンスなOpenGL ESアプリケーションの設計 3. シミュレーション結果を反映して実際にレンダリングするため、パーティクルの位置を格納した 頂点バッファを第2の描画パスに送り込みます。ラスタ化(およびパイプラインの後続ステージ) を再び有効にし、アプリケーションの視覚コンテンツのレンダリングに適した頂点シェーダやフ ラグメントシェーダを使います。 4. 次のフレームのシミュレーションでは、直前のフレーム用に計算した頂点バッファ出力を入力と して使います。 変換のフィードバック機能を有効に利用できる他のグラフィックスプログラミング技法として、スケ ルタルアニメーション(あるいはスキニング)やレイマーチングがあります。 OpenGL ES 2.0 OpenGL ES 2.0には、プログラマブルシェーダを備えた適応性の高いグラフィックスパイプラインがあ り、現行のどのiOSデバイスでも動作します。iOSデバイスでは、OpenGL ES 3.0の仕様で正式に導入さ れた機能の多くが、OpenGL ES 2.0でも拡張機能として使えるようになっています。したがって、多く のデバイスとの互換性を保ちながら、高度なグラフィックスプログラミング技術を実装できます。 OpenGL ES 1.1 OpenGL ES 1.1は、基本的な固定機能グラフィックスパイプラインのみを提供します。iOSは主として 後方互換性を維持するためにOpenGL ES 1.1を搭載しています。OpenGL ES 1.1用のアプリケーションを 保守している場合、新しい版のOpenGL ESに合わせて更新することも検討してください。 GLKitフレームワークには、OpenGL ES 1.1の固定機能パイプラインを、新しい版に移行するための支援 機能があります。詳しくは“GLKitを使用したレンダラの開発” (32 ページ)を参照してください。 高パフォーマンスなOpenGL ESアプリケーションの設計 簡潔に言うと、しっかりした設計のOpenGL ESアプリケーションは、以下を行う必要があります。 ● OpenGL ESパイプラインで並行処理を活用する。 ● アプリケーションとグラフィックスハードウェアとの間のデータの流れを管理する。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 59 OpenGL ES設計ガイドライン 高パフォーマンスなOpenGL ESアプリケーションの設計 図 6-6は、画面に表示するアニメーションをOpenGL ESを使用して実行するアプリケーションにおける プロセスの流れを示しています。 図 6-6 リソース管理のためのアプリケーションモデル アプリケーションが起動して最初に実行するのは、アプリケーションの実行中に変更するつもりのな いリソースの初期化です。こうしたリソースは、アプリケーションがOpenGL ESオブジェクトにカプ セル化することが理想です。目標は、アプリケーションの実行中(または、ゲーム内のあるレベルの 期間など、アプリケーション実行における一部の期間)に変更しないままにできるオブジェクトを作 成し、初期化にかかる時間の増加と引き替えにレンダリングパフォーマンスを向上させることです。 複雑なコマンドまたは状態の変更は、単一の関数呼び出しで使用可能なOpenGL ESオブジェクトに置 き換える必要があります。たとえば、固定機能パイプラインの設定には、多くの関数呼び出しを要す る可能性があります。その代わりに、初期化時にグラフィックスシェーダをコンパイルし、単一の関 数呼び出しの実行時にグラフィックスシェーダに切り替えます。作成や修正に負荷が生じるOpenGL ES オブジェクトは、ほとんどの場合、静的オブジェクトとして作成します。 レンダリングループは、OpenGL ESコンテキストにレンダリングするすべての項目を処理し、その結 果を画面に表示します。アニメーション化されたシーンでは、一部のデータがフレームごとに更新さ れます。図 6-6に示す内側のレンダリングループでは、アプリケーションは、レンダリングリソース の更新(プロセス内のOpenGL ESオブジェクトの作成または修正)と、レンダリングリソースを使用 する描画コマンドの送信とを交互に実行します。この内側のループの目標は、CPUとGPUが並行して 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 60 OpenGL ES設計ガイドライン 高パフォーマンスなOpenGL ESアプリケーションの設計 処理を行い、アプリケーションとOpenGL ESが同時に同じリソースにアクセスすることのないよう、 作業負荷のバランスをとることです。iOSでは、OpenGL ES オブジェクトの修正は、修正がフレーム の最初と最後に実行されない場合、負荷が高くなる可能性があります。 この内側のループの重要な目標は、OpenGL ESからアプリケーションに再びデータがコピーされるの を避けることです。GPUからCPUへの結果のコピーは非常に時間がかかる可能性があります。中央の レンダリングループに示すように、コピーされたデータが、現在のフレームのレンダリングプロセス の一部として後で使用される場合、アプリケーションは、それまでに送信されたすべての描画コマン ドが完了するまでブロックされます。 アプリケーションは、フレームで必要なすべての描画コマンドを送信した後、結果を画面に表示しま す。インタラクティブではないアプリケーションは、その後の処理のために最終画像をアプリケー ションが保有するメモリにコピーします。 最後に、アプリケーションが終了する準備ができているとき、またはアプリケーションが主要なタス クを完了したとき、アプリケーションはOpenGL ESオブジェクトを解放し、そのアプリケーション自 身またはほかのアプリケーションのために、さらにリソースが利用できるようにします。 この設計の特性の重要な点をまとめると、次のようになります。 ● ● ● 実用的な場合は必ず静的リソースを作成する。 内側のレンダリングループは、動的リソースの修正とレンダリングコマンドの送信を交互に実行 する。フレームの最初と最後を除き、動的リソースの修正を避けるようにする。 中間のレンダリング結果がアプリケーションに読み取りに戻らないようにする。 この章の残りでは、このレンダリングループ機能の実装に役立つ、OpenGL ESのプログラミングテク ニックを紹介します。この後の章では、次に示す汎用テクニックをOpenGL ESプログラミングの特定 の領域に適用する方法について説明します。 ● “同期とフラッシュの操作の回避” (62 ページ) ● “OpenGL ESの状態照会の回避” (63 ページ) ● “OpenGL ESを使用したリソースの管理” (63 ページ) ● “ダブルバッファリングによるリソースの競合の回避” (63 ページ) ● “OpenGL ESの状態に注意を払う” (65 ページ) ● “OpenGL ESのオブジェクトで状態をカプセル化する” (66 ページ) 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 61 OpenGL ES設計ガイドライン 同期とフラッシュの操作の回避 同期とフラッシュの操作の回避 OpenGL ESの仕様では、すぐにコマンドを実行するための実装は不要です。多くの場合、コマンドは コマンドバッファのキューに追加され、後でハードウェアが実行します。通常、OpenGL ESはアプリ ケーションがハードウェアにコマンドを送信する前に多くのコマンドをキューに追加するまで待ちま す。通常はバッチ処理を使用するとより効率的です。ただし、一部のOpenGL ES関数は、コマンドバッ ファをすぐにフラッシュしなければなりません。それ以外の関数は、コマンドバッファをフラッシュ するだけでなく、アプリケーションの制御を返す前に、それまでに送信されたコマンドが完了するま でブロックされます。フラッシュコマンドと同期コマンドを使用するのは、その挙動が必要な場合の みです。フラッシュコマンドや同期コマンドを使いすぎると、ハードウェアによるレンダリングが完 了するのを待っている間にアプリケーションが停止する可能性があります。 次に示す状況では、OpenGL ESは、実行に備えコマンドバッファをハードウェアに送信する必要があ ります。 ● glFlush関数が、コマンドバッファをグラフィックスハードウェアに送信するとき。この関数は、 コマンドがハードウェアに送信されるまでブロックされますが、コマンドの実行完了までは待ち ません。 ● glFinish関数がコマンドバッファをフラッシュし、それまでに送信されたすべてのコマンドがグ ラフィックスハードウェアで実行し終わるのを待っているとき。 ● ● フレームバッファコンテンツを取得する関数(glReadPixelsなど)も送信されたコマンドが完 了するのを待っているとき。 コマンドバッファが一杯になっているとき。 glFlushの効果的な使用 いくつかのデスクトップOpenGL実装では、CPUとGPUの作業のバランスを効率的にとるために、 glFlush関数を定期的に呼び出すと役に立つ場合がありますが、これはiOSには当てはまりません。 iOSのグラフィックスハードウェアによって実装されるタイルベースの遅延レンダリングアルゴリズ ムは、シーン内のすべての頂点データの即時バッファリングに依存するため、非表示のサーフェスの 削除に対して最適に処理されます。通常、OpenGL ESアプリケーションがglFlushまたはglFinish関 数を呼び出す必要がある状況は2つだけです。 ● ● アプリケーションがバックグラウンドに移動するときは、コマンドバッファをフラッシュする必 要があります。アプリケーションがバックグラウンドにあるときにGPU上でOpenGL ESコマンドを 実行すると、iOSがアプリケーションを終了させるからです。(“マルチタスク対応OpenGL ESアプ リケーションの実装” (47 ページ)を参照。) 複数のコンテキスト間でアプリケーションがOpenGL ESオブジェクト(頂点バッファやテクスチャ など)を共有する場合は、glFlush関数を呼び出してこれらのリソースへのアクセスを同期させ る必要があります。たとえば、頂点データのコンテンツが別のコンテンツによって取得される準 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 62 OpenGL ES設計ガイドライン OpenGL ESを使用したリソースの管理 備ができているようにするには、その頂点データを1つのコンテキストにロードした後、glFlush 関数を呼び出す必要があります。このアドバイスは、OpenGL ESオブジェクトをCore Imageなどの 他のiOS APIと共有するときにも該当します。 OpenGL ESの状態照会の回避 glGet*()(glGetError()を含む)を呼び出すと、OpenGL ESでは、状態変数を取得する前にそれ以 前のコマンドが実行されなければならない可能性があります。このような同期化のために、グラフィッ クスハードウェアとCPUが間を置かずに実行されなければならないため、並行処理の機会が減ります。 これを避けるには、照会する必要のある状態のコピーを保持し、OpenGL ESを呼び出すのではなく、 コピーに直接アクセスします。 エラーが発生すると、OpenGL ESはエラーフラグを設定します。これらのエラーやその他のエラーは、 XcodeのOpenGL ES Frame DebuggerまたはInstrumentsのOpenGL ES Analyzerに表示されます。glGetError 関数は呼び出し頻度が高い場合にはパフォーマンスを低下させるため、代わりにこれらのツールを使 用する必要があります。glCheckFramebufferStatus()、glGetProgramInfoLog()、 glValidateProgram()などの他の照会が役立つのも、一般には開発中やデバッグ中のみです。アプ リケーションのリリースビルドではこれらの関数の呼び出しを省くべきです。 OpenGL ESを使用したリソースの管理 多くのOpenGLデータは、OpenGL ESレンダリングコンテキストとそれに関連付けられた共有グループ オブジェクト内に直接格納できます。OpenGL ESの実装は、グラフィックスハードウェアに最適な形 式にデータを自由に変換できます。これにより、更新頻度が低いデータの場合は特に、パフォーマン スが大幅に向上する可能性があります。アプリケーションは、データがどのように使用されるのかに ついて、OpenGL ESに対するヒントも提供します。OpenGL ESの実装では、これらのヒントを使用し て、データをより効率的に処理できます。たとえば、静的データは、グラフィックス専用のメモリも 含め、グラフィックスプロセッサが簡単にフェッチできるメモリに配置できます。 ダブルバッファリングによるリソースの競合の回避 リソースの競合は、アプリケーションとOpenGL ESが、あるOpenGL ESオブジェクトに同時にアクセス するときに発生します。一方の当事者(アプリケーションまたはOpenGL ES)が使用中のOpenGL ESオ ブジェクトをもう一方の当事者が修正しようとすると、両者はそのオブジェクトが使用できなくなる までブロックされる可能性があります。オブジェクトの修正が開始されると、もう一方の当事者は修 正が完了するまでそのオブジェクトにアクセスできません。あるいは、OpenGL ESが暗黙的にオブジェ クトを複製して、両方の当事者がコマンドの実行を継続できるようにする場合があります。どちらの 方法も安全ですが、どちらもアプリケーションのボトルネックとなるおそれがあります。図 6-7はこ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 63 OpenGL ES設計ガイドライン ダブルバッファリングによるリソースの競合の回避 の問題を示しています。この例では、単一のテクスチャオブジェクトがあり、そのオブジェクトを OpenGL ESとアプリケーションの両方が使用しようとしています。アプリケーションがテクスチャを 変更しようとすると、そのアプリケーションは、それまで送信された描画コマンドが完了するまで、 つまりCPUとGPUが同期するまで待たなければなりません。 図 6-7 単一のバッファに格納されたテクスチャデータ この問題を解決するため、アプリケーションは、オブジェクトの変更とオブジェクトに関連する描画 との間で追加の作業を実行することができます。しかし、実行可能な追加の作業がアプリケーション にない場合、アプリケーションは同じサイズの2つのオブジェクトを明示的に作成します。これによ り、一方の当事者がオブジェクトを読み取っている間、もう一方が残りのオブジェクトを修正できま す。図 6-8に、ダブルバッファによるアプローチを示します。GPUがあるテクスチャを処理している 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 64 OpenGL ES設計ガイドライン OpenGL ESの状態に注意を払う 間、CPUが別のテクスチャを修正するという方法です。最初に起動した後は、CPUもGPUもアイドル 状態です。テクスチャを例にとりましたが、この方法はほとんどの種類のOpenGL ESオブジェクトで 機能します。 図 6-8 ダブルバッファリングされたテクスチャデータ ダブルバッファリングはほとんどのアプリケーションで有効ですが、両方の当事者がほぼ同時にコマ ンドの処理を完了する必要があります。ブロック状態を回避するため、さらにバッファを追加するこ とができます。この場合は、従来のプロデューサ/コンシューマモデルが実装されます。コンシュー マがコマンド処理を完了する前にプロデューサが処理を完了した場合、プロデューサはアイドルバッ ファを利用してコマンドの処理を継続します。この状況では、プロデューサは、コンシューマの処理 が著しく遅れる場合のみアイドル状態になります。 ダブルバッファリングやトリプルバッファリングには、パイプラインが停止するのを防げる代わり に、メモリの消費量が増えるというトレードオフがあります。メモリの使用量が増えると、アプリ ケーションのほかの部分に負担がかかる可能性があります。iOSデバイスでは、メモリは貴重なリソー スです。設計において、メモリの使用量を増やすことと、ほかのアプリケーションの最適化とのバラ ンスをとる必要があります。 OpenGL ESの状態に注意を払う OpenGL ESの実装では、多種多様な状態データ群を管理します。glEnable関数やglDisable関数によ るスイッチの状態、現在のシェーダプログラムおよびそのユニフォーム変数、現在バインドしている テクスチャユニット、現在バインドしている頂点バッファおよびその有効な頂点属性などです。ハー 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 65 OpenGL ES設計ガイドライン OpenGL ESの状態に注意を払う ドウェアも現在の状態を1つ保持しており、これを集約して遅延キャッシュするようになっています。 状態の切り替えは負荷がかかるため、状態の切り替えを最小限に抑えたアプリケーションを設計する ことが最も推奨されます。 すでに設定済みの状態は設定しないでください。ある機能を一度有効化したら、再び有効化する必要 はありません。たとえば、同じ引数を指定してglUniform関数を複数回呼び出した場合、同じユニ フォーム状態が設定済みかどうか、OpenGL ESが確認するとは限りません。OpenGL ESは、現在の値と 同じ値の場合は、単にその状態値で更新するだけです。 状態を設定する呼び出しを描画ループ内に配置するのではなく、専用のセットアップルーチンまたは シャットダウンルーチンを使用することによって、不要な状態設定を避けてください。セットアップ ルーチンおよびシャットダウンルーチンは、特別な視覚効果を達成する機能のオンとオフの切り替え にも有用です。たとえば、テクスチャ化されたポリゴンのワイヤフレームの輪郭を描画する場合に便 利です。 OpenGL ESのオブジェクトで状態をカプセル化する 状態変化の頻度を抑えるため、OpenGL ESの状態変化を単一のオブジェクトに集約し、1回の関数呼び 出しでまとめてバインドすることも考えられます。たとえば、頂点配列オブジェクトでは、複数の頂 点属性の設定が1つのオブジェクトに格納されます。詳細については、“頂点配列オブジェクトの使用 による頂点配列状態の変化の整理統合” (92 ページ)を参照してください。 描画関数の呼び出しを整理して状態変化を最小限に抑える OpenGL ESの状態を変更しても、すぐには効果が現れません。描画コマンドを発行してはじめて、新 しい状態にもとづき、OpenGL ESが必要な描画処理を実行するようになっています。状態の変更を最 小限に抑えることにより、グラフィックスパイプラインの再設定に要するCPU時間を削減できます。 たとえば、状態ベクトルをアプリケーション側で管理し、対応するOpenGL ESの状態は、次の描画関 数呼び出しまでの間に状態が変化した場合にのみ設定し直すようにするとよいでしょう。ほかに有用 なアルゴリズムとして、状態の整列があります。必要な描画操作と、それぞれのために必要な状態変 更の量を追跡し、同じ状態で実行する描画操作は連続しておこなうよう並べ換える、というもので す。 iOSにおけるOpenGL ESの実装では、設定データの一部をキャッシュすることにより状態を効率よく切 り替えることも可能ですが、個々の状態集合を最初に設定する処理には、より時間を要します。一定 の処理性能が得られるよう、初期設定の段階で、使う予定の状態集合をそれぞれ「暖機運転」してお くことも考えられます。 1. 使う予定の状態設定やシェーダを有効にしておきます。 2. その状態設定で、少数の頂点を描画します(暖機運転)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 66 OpenGL ES設計ガイドライン OpenGL ESの状態に注意を払う 3. OpenGL ESのコンテキストをフラッシュして、この間に描画した内容が表示されないようにしま す。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 67 OpenGL ESアプリケーションのチューニング iOSにおけるOpenGL ESアプリケーションのパフォーマンスは、OS Xやその他のデスクトップオペレー ティングシステムにおけるOpenGLのパフォーマンスとは異なります。iOSベースのデバイスは強力な 計算デバイスですが、デスクトップコンピュータやラップトップコンピュータが持つメモリやCPUパ ワーは持ち合わせていません。組み込みのGPUは典型的なデスクトップやラップトップのGPUとは異 なるアルゴリズムを使用しており、メモリと電力の使用を抑えるように最適化されています。グラ フィックスデータのレンダリングが非効率的であると、フレームレートが低下したり、iOSベースの デバイスのバッテリー持続時間が著しく減少したりする可能性があります。 後の章では、アプリケーションのパフォーマンスを向上させる数多くのテクニックを取り上げていま す。この章では全体的な戦略について説明します。特に注記しない限り、この章に挙げる事項はどの 版のOpenGL ESにも適用できます。 XcodeやInstrumentsで、アプリケーションをデバッグ、プロ ファイリングする アプリケーションは、さまざまなデバイス上で、さまざまなシナリオに基づいてパフォーマンスをテ ストするまでは最適化しないでください。XcodeやInstrumentsには、性能を見極め、問題点を修正す るためのツールが付属しています。 ● Xcodeのデバッグゲージで性能を概観できます。Xcode上でアプリケーションを実行すれば、この ゲージは常に表示されるので、開発の過程で性能に変化が生じればすぐに気がつくでしょう。 ● Instrumentsに付属する、OpenGL ES AnalysisおよびOpenGL ES Driverというツールで、実行時の性 能をさらに詳しく調べることができます。アプリケーションがリソースをどのように使っている か、OpenGL ESのベストプラクティスに適合しているか、といった詳しい情報が得られます。ま た、グラフィックスパイプラインの一部を選択的に無効にすることにより、顕著なボトルネック になっている箇所を判断できます。詳細については、『Instruments User Guide 』を参照してくだ さい。 ● Xcodeに付属する、OpenGL ES Frame DebuggerおよびPerformance Analyzerというツールで、性能 やレンダリングに関する問題の箇所を特定し、対処できます。あるフレームのレンダリングおよ び表示に使われたOpenGL ESのコマンドをすべて取り込み、OpenGL ESの状態、バインドされたリ ソース、出力フレームバッファに対して、各コマンドがどのような効果を及ぼしているか確認し ます。また、シェーダのソースコードを表示、編集し、変更を施したとき出力画像にどんな変化 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 68 OpenGL ESアプリケーションのチューニング XcodeやInstrumentsで、アプリケーションをデバッグ、プロファイリングする が現れるか調べることも可能です。OpenGL ES 3.0対応のデバイスであれば、Frame Debugger上 で、レンダリング時間の多くを占めている描画関数やシェーダ命令も分かります。ツールについ て詳しくは、“Xcodeに付属するOpenGL ES用ツールの概要” (126 ページ)を参照してください。 XcodeやInstrumentsでOpenGL ESのエラーを監視する APIの使い方が誤っていると、OpenGL ESがエラーになります(基盤となるハードウェアが実行できな い演算を要求するなど)。コンテンツが正しくレンダリングされた場合でも、これらのエラーはパ フォーマンス上の問題を示すことがあります。OpenGL ESのエラーをチェックする従来の方法は glGetError関数を呼び出すことですが、この関数を繰り返し呼び出すと、パフォーマンスが大幅に 低下する可能性があります。代わりに上記のツールで、次のように調べてください。 ● Instrumentsでアプリケーションをプロファイルすると、ツール「OpenGL ES Analyzer」の詳細ペイ ンが表示され、記録中にレポートされたOpenGL ESのエラーが表示されます。 ● Xコードでアプリケーションをデバッグしているときに、フレームを取り込み、そのフレームの 生成に使用された描画コマンドのほか、それらのコマンドの実行中に発生したエラーを確認する ことができます。 OpenGL ESエラーの発生時にプログラムの実行を停止するようにXcodeを設定することもできます。 (「OpenGL ESのエラーブレークポイントの追加」を参照。) 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 69 OpenGL ESアプリケーションのチューニング XcodeやInstrumentsで、アプリケーションをデバッグ、プロファイリングする 情報提供のデバッグおよびプロファイル用に、OpenGL ESのコードに注釈 を付ける OpenGL ESコマンドを論理的なグループにまとめ、OpenGL ESオブジェクトに意味のあるラベルを追加 することによって、より効率的にデバッグとプロファイルを行うことができます。これらのグループ とラベルは、XcodeのOpenGL ES Frame Debugger(図 7-1を参照)と、InstrumentsのOpenGL ES Analyzer に表示されます。グループとラベルを追加するには、EXT_debug_markerおよびEXT_debug_label拡 張機能を使用します。 図 7-1 デバッグマーカグループの追加前と追加後のXcode Frame Debugger 1つの意味のある演算を表す一連の描画コマンド(たとえば、ゲームのキャラクタの描画など)があ るときは、マーカを使用してデバッグ用にそれらをグループ化することができます。リスト 7-1に、 これらの関数を使用して、テクスチャ、プログラム、頂点配列をグループ化し、シーンの1つの要素 に対する呼び出しを描画するコード例を示します。まずglPushGroupMarkerEXT関数で意味のある名 前を与えた後、OpenGL ESのコマンド群を実行します。最後にglPopGroupMarkerEXT関数で、一連の コマンドが終了したことを示します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 70 OpenGL ESアプリケーションのチューニング パフォーマンスのための一般的な推奨事項 リスト 7-1 デバッグマーカを使って描画コマンドに注釈をつけるコード例 glPushGroupMarkerEXT(0, "Draw Spaceship"); glBindTexture(GL_TEXTURE_2D, _spaceshipTexture); glUseProgram(_diffuseShading); glBindVertexArrayOES(_spaceshipMesh); glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0); glPopGroupMarkerEXT(); 複数の入れ子になったマーカを使用して、複雑なシーンに意味のあるグループの階層を作成すること ができます。GLKViewクラスを使用してOpenGL ESコンテンツを描画すると、すべてのコマンドが描 画メソッドに含まれる「Rendering」グループが自動的に作成されます。作成するすべてのマーカが このグループ内で入れ子になります。 ラベルを使用して、テクスチャ、シェーダプログラム、頂点配列オブジェクトなどのOpenGL ESオブ ジェクトに意味のある名前を付けることができます。glLabelObjectEXT関数でオブジェクトに名前 を与え、デバッグやプロファイルの際に表示されるようにします。リスト 7-2に、この関数で頂点配 列オブジェクトにラベル付けをするコード例を示します。GLKTextureLoaderクラスを使用してテク スチャデータを読み込むと、それらのファイル名で作成されたOpenGL ESテクスチャオブジェクトに 自動的にラベルが付けられます。 リスト 7-2 デバッグラベルを利用してOpenGL ESオブジェクトに注釈をつけるコード例 glGenVertexArraysOES(1, &_spaceshipMesh); glBindVertexArrayOES(_spaceshipMesh); glLabelObjectEXT(GL_VERTEX_ARRAY_OBJECT_EXT, _spaceshipMesh, 0, "Spaceship"); パフォーマンスのための一般的な推奨事項 常識に従って、パフォーマンスチューニングの作業を進めます。たとえば、アプリケーションがフ レームごとに数十個程度の三角形を描く場合、頂点データの送信方法を変更するとパフォーマンスが 向上する可能性が低くなります。作業のパフォーマンスを最も向上させる最適化を探します。 シーンのデータが変更された場合のみシーンを再描画する アプリケーションは、シーン内に何らかの変更があるまで新しいフレームのレンダリングを待機する 必要があります。Core Animationはユーザに表示された最後の画像をキャッシュし、新しいフレーム が表示されるまでその画像を表示し続けます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 71 OpenGL ESアプリケーションのチューニング タイルベースの遅延レンダリングを効率的に使う データが変化した場合でも、必ずしもハードウェアがコマンドを処理するのと同じ速度でフレームを レンダリングする必要はありません。一般に、高速でもフレームレートが変動する場合と比べると、 より低速で固定のフレームレートのほうがユーザには滑らかに見えます。ほとんどのアニメーション では30フレーム/秒の固定フレームレートで十分であり、電力消費の削減に役立ちます。 使用しないOpenGL ES機能を無効にする 最高の処理性能が得られるのは、アプリケーションが何も計算せずに済む場合です。たとえば、事前 に計算を行って、結果をモデルのデータに格納できる場合は、アプリケーション実行時にその計算の 実行を回避することができます。 OpenGL ES 2.0以降向けにアプリケーションを記述する場合は、アプリケーションがシーンのレンダリ ングに必要とする各タスクを実行するための、多数のスイッチと条件が設定された単一のシェーダを 作成しないでください。代わりに、各シェーダが的を絞った特定のタスクを実行する、複数のシェー ダプログラムをコンパイルします。 アプリケーションでOpenGL ES 1.1を使用する場合は、シーンのレンダリングに不要な固定機能操作を 無効にします。たとえば、アプリケーションでライティングやブレンディングが不要な場合は、それ らの機能を無効にしてください。同様に、2Dモデルのみを描画する場合はフォグと深度テストを無効 にします。 ライティングモデルを簡素化する これは、OpenGL ES 1.1の固定機能ライティングと、OpenGL ES 2.0以降のカスタムシェーダで使用する シェーダベースのライティング演算の、どちらにも適用できるガイダンスです。 ● ● ライティングの使用はできるだけ控え、アプリケーションにとって最もシンプルなタイプのライ ティングを使用します。より多くの演算が必要となるスポットライティングの代わりに、指向性 ライトの使用を検討します。シェーダは、ライティング演算をモデル空間で行う必要がありま す。より複雑なライティングアルゴリズムではなく、よりシンプルなライティング方程式の使用 を検討してください。 ライティングを事前に計算し、フラグメント処理によってサンプリング可能なテクスチャにカ ラー値を保存します。 タイルベースの遅延レンダリングを効率的に使う iOSデバイスのGPUはいずれも、タイルベースの遅延レンダリング(TBDR、Tile-Based Deferred Rendering)を実装しています。レンダリングコマンドをハードウェアに送信するためOpenGL ES関数 を呼び出すと、これらのコマンドは、一定量のコマンドが蓄積されるまでバッファに格納されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 72 OpenGL ESアプリケーションのチューニング タイルベースの遅延レンダリングを効率的に使う ハードウェアが実際に頂点を処理し、ピクセルのシェーディングを始めるのは、レンダバッファを表 示するか、コマンドバッファをフラッシュしてからです。コマンド群を単一の演算としてレンダリン グするため、フレームバッファをタイルに分割し、タイルごとに1度だけ描画コマンドを実行します。 このとき各タイルでは、その中に現れるプリミティブのみを描画します(GLKViewクラスは描画メ ソッド終了後のレンダバッファを表します。CAEAGLLayerクラスで、表示用に独自のレンダバッファ を用意した場合、その表示はOpenGL ESコンテキストのpresentRenderbuffer:メソッドでおこない ます。glFlush関数やglFinish関数は、コマンドバッファの内容をフラッシュします)。 タイルメモリはGPUハードウェアの一部なので、レンダリング処理の一部(深度のテスト、ブレンド など)は、処理時間の面でも消費電力の面でも、従来のストリームベースのGPIアーキテクチャに比 べ、効率よく動作します。このアーキテクチャでは、シーン全体の頂点を一括処理するので、フラグ メントの処理に先だって隠面除去が可能です。表示されないピクセルは、テクスチャのサンプリング やフラグメント処理を実行せずに破棄されます。したがって、GPUがタイルをレンダリングするため に実行しなければならない計算量が大幅に削減されます。 従来型のストリームベースのレンダラにとって有用なレンダリング戦略の中には、iOSグラフィック スハードウェアに対する負荷が高いものがあります。以下のガイドラインに従えば、TBDRハードウェ アで実行したとき、処理性能の改善に効果があるでしょう。 論理バッファの読み込みや格納を避ける TBDRグラフィックスプロセッサは、タイルのレンダリングを始めると、まずフレームバッファの該当 部分の内容を、共有メモリから、GPUのタイルメモリに転送しなければなりません。この処理を論理 バッファの読み込みと言いますが、相当の時間と電力を消費します。多くの場合、フレームバッファ の内容は、次のフレームを描画する際には不要になります。前回のバッファを読み込むことのないよ う、新規フレームのレンダリングを始める際にはglClear関数を実行してください。 同様に、GPUがタイルのレンダリングを終えると、そのピクセルデータを共有メモリに書き戻す必要 があります。これを論理バッファの格納と言いますが、やはり負荷の高い処理です。描画するフレー ムごとに少なくとも1回はこの転送が必要です。画面に表示する色レンダバッファを共有メモリに転 送して、Core Animationが実際に表示できるようにするためです。レンダリングアルゴリズムで使う 他のフレームバッファアタッチメント(深度、ステンシル、マルチサンプリングなどのバッファ) は、次のフレームを描画する際にまた生成されるので、保存しておく必要はありません。明示的に無 効にしていない限り、OpenGL ESは自動的にこういったバッファの内容も共有メモリに書き戻すよう になっており、これも性能向上を妨げる要因です。バッファを無効にするためには、OpenGL ES 3.0で あればglInvalidateFramebufferコマンド、1.1や2.0であればglDiscardFramebufferEXTコマンド を使います(詳しくは“不要なレンダバッファの破棄” (42 ページ)を参照)。GLKViewクラスが提 供する基本的な描画サイクルを使えば、深度、ステンシル、マルチサンプリングのバッファは自動的 に無効になります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 73 OpenGL ESアプリケーションのチューニング タイルベースの遅延レンダリングを効率的に使う 論理バッファの格納や読み込みは、レンダリング先を切り替えたときにも起こります。レンダリング 先をテクスチャからビューのフレームバッファに切り替えた後、再び同じテクスチャに切り替える と、テクスチャの内容を共有メモリとGPUの間で転送する処理が繰り返し発生します。描画演算をま とめ、同じレンダリング先への描画は一括しておこなうようにしてください。(glBindFramebuffer 関数、glFramebufferTexture2D関数、bindDrawableメソッドで)フレームバッファを切り替える 際には、不要なフレームバッファアタッチメントを無効にして、論理バッファの格納が起こらないよ うにしなければなりません。 隠面除去を効率的に使う TBDRグラフィックスプロセッサは自動的に深度バッファを使って、シーン全体の隠面除去をおこな い、ピクセルごとにフラグメントシェーダが1回しか適用されないようにしています。フラグメント 処理を削減する従来の技法は不要になりました。たとえば、オブジェクトやプリミティブを深度順に (手前から奥へ)整列する処理は、GPUの処理と実質的に重複し、CPU時間を無駄に消費するだけで す。 ブレンディングやアルファテストが有効になっていれば、GPUは隠面除去を実行できません。フラグ メントシェーダがdiscard命令を使っていたり、gl_FragDepth出力変数に書き込んでいたりする場 合も同様です。このような場合、深度バッファを調べるだけではフラグメントが隠れているかどうか 判断できないので、各ピクセルを覆うプリミティブすべてについてフラグメントシェーダを実行する 必要があり、フレームのレンダリングに要する時間や消費電力が大幅に増してしまいます。これを避 けるため、ブレンディング、discard命令、深度の書き込みは、最小限に抑えるようにしてくださ い。 ブレンディングやアルファテスト、discard命令が避けられない場合は、できるだけ性能を損なわずに 済むよう、次の戦略を検討するとよいでしょう。 ● オブジェクトを不透明度の順に整列する方法。最初に不透明なオブジェクトを描画します。次 に、シェーダがdiscard命令(OpenGL ES 1.1の場合はアルファテストも)を要するオブジェクト を描画します。最後に、アルファブレンドされたオブジェクトを描画します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 74 OpenGL ESアプリケーションのチューニング タイルベースの遅延レンダリングを効率的に使う ● ブレンディングやdiscard命令を要するオブジェクトをトリミングして、処理するフラグメント の数を減らす方法。たとえば、余白が多い2Dスプライトテクスチャの場合、矩形ではなく、画像 にほぼ外接する多角形を描画します(図 7-2を参照)。頂点の処理が若干増えますが、結果が使 われることのないフラグメントシェーダを実行することに比べれば、無視しても構いません。 図 7-2 ● 透明なオブジェクトをトリミングしてフラグメント処理を減らす様子 discard命令はフラグメントシェーダの最初の方に配置して、結果が使われることのない計算を 避けます。 ● ● アルファテストやdiscard命令でピクセルを破棄する代わりに、0を設定したアルファとのアル ファブレンディングをおこなう方法があります。色フレームバッファは修正されませんが、グラ フィックスハードウェアは実行するZバッファの最適化を引き続き使用できます。深度バッファ に格納されている値は変更されないため、透過プリミティブの背面から前面への並べ替えが必要 となる可能性があります。 discard命令が不可欠であるため性能改善に限界がある場合は、「Z-Prepass」レンダリング技法を 検討してください。破棄ロジックのみを含む単純なフラグメントシェーダ(コストのかかるライ ティング演算を避ける)を使用してシーンを1回レンダリングして、深度バッファを埋めます。 次に、GL_EQUAL深度テスト関数とライティングシェーダを使用してシーンをもう一度レンダリン グします。マルチパスレンダリングは通常はパフォーマンスの低下を招きますが、この方法では 多数の破棄操作を伴うシングルパスレンダリングよりもパフォーマンスが向上する可能性があり ます。 OpenGL ESコマンドをグループ化して効率的にリソースを管理する 先に説明したメモリ帯域幅と計算量の節約は、大きなシーンを処理する場合に最も効果があります。 しかし、小さいシーンのレンダリングを要求するOpenGL ESコマンドをハードウェアが受け取ると、 レンダラの効率は大幅に低下します。たとえば、テクスチャを使ってひとまとまりの三角形をレンダ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 75 OpenGL ESアプリケーションのチューニング 描画コマンドの数を最小限に抑える リングした後、そのテクスチャを修正する場合、OpenGL ESの実装では、それらのコマンドをただち にフラッシュするか、テクスチャを複製しなければなりませんが、どちらもハードウェアを効率よく 使っているとは言えません。同様に、フレームバッファからピクセルデータを読み込もうとすると、 その前のコマンドがフレームバッファを変更する場合はそれらが実行されている必要があります。 このような性能低下を防ぐため、OpenGL ESのコマンド呼び出しを整理して、同じレンダターゲット に対する描画コマンドは一括して実行できるようにしてください。 描画コマンドの数を最小限に抑える アプリケーションがOpenGL ESで処理するプリミティブを送信するたびに、CPUはグラフィックスハー ドウェア用のコマンドを準備します。シーンをレンダリングするためにglDrawArraysや glDrawElementsを何度も呼び出していれば、GPUを充分に活用できず、CPUリソースが性能のボトル ネックになってしまいます。 このオーバーヘッドを削減するため、レンダリング処理を少数の描画呼び出しにまとめられないか検 討してください。次のような戦略が考えられます。 ● ● ● 複数のプリミティブを、単一の三角形ストリップにマージする方法(“三角形ストリップを使用し て頂点データを一括処理する” (85 ページ)を参照)。最良の結果を得るには、近接して描画さ れるプリミティブを整理統合します。広範囲にわたるモデルの場合、フレームに表示されていな い部分をアプリケーションが効率よく間引くのは難しくなります。 テクスチャアトラスを作成し、プリミティブごとに、同じテクスチャ画像の異なる部分を使って 描画する方法(“複数のテクスチャをテクスチャアトラスにまとめる” (100 ページ)を参照)。 インスタンス描画法(下記参照)により、よく似た多数のオブジェクトをレンダリングする方 法。 インスタンス描画法により描画関数の呼び出し回数を減らす インスタンス描画(instanced drawing)コマンドを使えば、1回の描画関数呼び出しで、同じ頂点デー タを何度でも描画できます。位置オフセット、変換行列、色やテクスチャの座標などに、少しずつ違 いがあるメッシュのインスタンスを(CPU時間を消費して)個々に用意し、それぞれについて描画コ マンドを実行する代わりに、GPU側のシェーダコードで各インスタンスを処理するのです。 頂点データを何度も使い回しする状況はインスタンス描画法に向いています。たとえばリスト 7-3の コードは、シーン内の何箇所にも同じオブジェクトを描画しています。しかし、glUniformや glDrawArraysを何度も実行するとCPUに対するオーバーヘッドとなり、性能低下の原因になります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 76 OpenGL ESアプリケーションのチューニング 描画コマンドの数を最小限に抑える リスト 7-3 インスタンス描画法によらずに、よく似たオブジェクトを多数描画するコード例 for (x = 0; x < 10; x++) { for (y = 0; y < 10; y++) { glUniform4fv(uniformPositionOffset, 1, positionOffsets[x][y]); glDrawArrays(GL_TRIANGLES, 0, numVertices); } } インスタンス描画法を適用するためには2つの手順が必要です。まず、先のコード例のようなループ を、glDrawArraysInstancedやglDrawElementsInstancedを一度呼び出すだけの記述に変更しま す。この関数はglDrawArraysやglDrawElementsとほとんど同じですが、描画するインスタンスの数 を表す引数が追加されています(リスト 7-3の例では100)。次に、インスタンスごとに異なる情報を 頂点シェーダに渡す方法が2通りあるので、いずれかを選んで実装します。 シェーダインスタンスIDを使う方式の場合、インスタンスごとの情報は、頂点シェーダが計算あるい は検索して求めます。組み込み変数gl_InstanceIDには、頂点シェーダを実行する都度、描画対象イ ンスタンスを識別する番号が入ります。位置オフセット、色など、インスタンスによって異なる値 は、シェーダ側のコードで、この番号をもとに計算し、あるいはユニフォーム配列などの大容量スト レージから検索します。リスト 7-4に、この技法で10×10の格子上に100個のインスタンスを描画する 例を示します。 リスト 7-4 OpenGL ES 3.0の頂点シェーダが、gl_InstanceIDをもとに、インスタンスによって異なる値を計 算するコード例 #version 300 es in vec4 position; uniform mat4 modelViewProjectionMatrix; void main() { float xOffset = float(gl_InstanceID % 10) * 0.5 - 2.5; float yOffset = float(gl_InstanceID / 10) * 0.5 - 2.5; vec4 offset = vec4(xOffset, yOffset, 0, 0); gl_Position = modelViewProjectionMatrix * (position + offset); } 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 77 OpenGL ESアプリケーションのチューニング 描画コマンドの数を最小限に抑える インスタンス配列を使う方式では、インスタンスによって異なる情報を、頂点配列属性に格納しま す。頂点シェーダはこの属性にアクセスして、インスタンスごとの情報を取得します。 glVertexAttribDivisor関数で、OpenGL ESが各インスタンスを描画するごとに、当該属性がどのよ うに変化するかを指定します。リスト 7-5にインスタンス描画法で使う頂点配列を用意するコード例、 リスト 7-6に対応するシェーダのコード例を示します。 リスト 7-5 インスタンスごとに異なる情報を頂点属性に格納するコード例 #define kMyInstanceDataAttrib 5 glGenBuffers(1, &_instBuffer); glBindBuffer(GL_ARRAY_BUFFER, _instBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(instData), instData, GL_STATIC_DRAW); glEnableVertexAttribArray(kMyInstanceDataAttrib); glVertexAttribPointer(kMyInstanceDataAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribDivisor(kMyInstanceDataAttrib, 1); リスト 7-6 インスタンス配列を参照して動作するOpenGL ES 3.0の頂点シェーダ #version 300 es layout(location = 0) in vec4 position; layout(location = 5) in vec2 inOffset; uniform mat4 modelViewProjectionMatrix; void main() { vec4 offset = vec4(inOffset, 0.0, 0.0) gl_Position = modelViewProjectionMatrix * (position + offset); } インスタンス描画法はOpenGL ES 3.0のコアAPIに組み込まれています。OpenGL ES 2.0の場合は、 EXT_draw_instancedおよびEXT_instanced_arraysという拡張機能として実装されています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 78 OpenGL ESアプリケーションのチューニング OpenGL ESのメモリ消費を最小限に抑える OpenGL ESのメモリ消費を最小限に抑える iOSアプリケーションは、システムおよびほかのiOSアプリケーションとメインメモリを共有します。 OpenGL ES用にメモリを割り当てると、アプリケーションのほかの部分で使用できるメモリが減りま す。そのことを念頭において、必要なメモリだけを割り当てて、割り当てたメモリが不要になったら できるだけ早くそのメモリの割り当てを解除します。メモリを節約する方法をいくつか紹介します。 ● 画像をOpenGL ESテクスチャにロードした後、元の画像を解放する。 ● アプリケーションが必要としているときだけ深度バッファを割り当てる。 ● アプリケーションがすべてのリソースを一度に必要としない場合は、項目のサブセットだけを ロードします。たとえば、1つのゲームを複数のレベルに分割できます。各レベルは、より厳し いリソース制限に収まる、リソース全体のサブセットをロードします。 iOSの仮想メモリシステムは、スワップファイルを使用しません。メモリ不足の状態が検出されると、 仮想メモリは揮発性のページをディスクに書き込む代わりに、不揮発性メモリを解放して実行中のア プリケーションに必要なメモリを提供します。アプリケーションはメモリの使用をできる限り少なく する努力をしなければなりません。また、アプリケーションにとって必須ではないオブジェクトを破 棄できるように備えておかなければなりません。メモリ不足状態への対応については、『iOS App Programming Guide 』で詳しく説明しています。 Core Animation合成のパフォーマンスに注意する Core Animationは、レンダバッファのコンテンツをビュー階層に含まれるほかのレイヤと合成します。 合成されるレイヤは、OpenGL ES、Quartz、またはそのほかのグラフィックスライブラリのいずれで 描画されたかは関係ありません。これは便利です。なぜなら、OpenGL ESがCore Animationにとって最 も優遇できるグラフィックスライブラリであることを意味するからです。ただし、OpenGL ESコンテ ンツをほかのコンテンツと混在させるには時間がかかります。適切に使用しなければ、アプリケー ションの実行速度が低下し、やり取りを行うフレームレートに到達するのに時間がかかる可能性があ ります。 最良のパフォーマンスを確実に実現するには、アプリケーションにおいて、OpenGL ESだけを使用し てコンテンツをレンダリングする必要があります。OpenGL ESコンテンツを保持するビューのサイズ を画面に一致させ、画面のopaqueプロパティをYES(GLKViewオブジェクトのデフォルト)に設定 し、ほかのビューやCore Animationレイヤが見えないようにビューのサイズを設定します。 レイヤの中で一番上に合成されるCore Animationレイヤをレンダリングする場合、CAEAGLLayerオブ ジェクトを不透明にすることでパフォーマンス上の負担は軽減できますが、なくすことはできませ ん。CAEAGLLayerオブジェクトが、その下にあるレイヤ階層の一番上のレイヤで合成される場合、レ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 79 OpenGL ESアプリケーションのチューニング Core Animation合成のパフォーマンスに注意する ンダバッファの色データは、Core Animationが正しく合成するよう、事前乗算済みのアルファ形式に なっていなければなりません。OpenGL ESコンテンツをほかのコンテンツの上で合成することは、パ フォーマンスの著しい低下を伴います。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 80 頂点データを扱うためのベストプラクティス OpenGL ESを使用するフレームをレンダリングするには、アプリケーションにおいてグラフィックス パイプラインを設定し、描画するグラフィックスプリミティブを送信します。すべてのプリミティブ を同じパイプライン設定を使用して描画するアプリケーションもあれば、フレームを構成する異なる 要素を別々のテクニックを使用して描画するアプリケーションもあります。しかし、アプリケーショ ンがどのプリミティブを使用するか、またはパイプラインがどのように設定されているかに関係な く、アプリケーションはOpenGL ESに頂点を提供します。この章では、頂点データに関する有用な情 報を提供し、頂点データを効率的に処理する方法について、対象を絞ったアドバイスを紹介します。 頂点は、位置、色、法線、テクスチャの座標など、1つ以上の属性で構成されています。OpenGL ES 2.0または3.0アプリケーションは、独自の属性を自由に定義できます。頂点データの各属性は、頂点 シェーダへの入力の働きをする、属性変数と対応しています。OpenGL 1.1アプリケーションは、固定 機能パイプラインによって定義される属性を使用します。 アプリケーションは、1~4個のコンポーネントで構成されるベクトルとして属性を定義します。属性 内のすべてのコンポーネントが、共通のデータ型を共有します。たとえば、色は4個のGLubyteコン ポーネント(赤、緑、青、アルファ)として定義される場合があります。属性がシェーダ変数にロー ドされるとき、アプリケーションデータ内で提供されていないコンポーネントは、OpenGL ESのデフォ ルト値で埋められます。図 8-1に示すように、最後のコンポーネントは1で埋められ、未指定のほかの コンポーネントは0で埋められます。 図 8-1 シェーダ変数への属性データの変換 アプリケーションが属性を定数 として設定する可能性があるということは、描画コマンドの一部とし て送信されるすべての頂点について同じ値が使用されることを意味し、配列 として設定する可能性が あるということは、各頂点がその属性の値であることを意味します。アプリケーションがOpenGL ES の関数を呼び出して一連の頂点を描画する場合、頂点データはアプリケーションからグラフィックス 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 81 頂点データを扱うためのベストプラクティス モデルの簡素化 ハードウェアにコピーされます。そして、グラフィックスハードウェアは頂点データを扱い、シェー ダで各頂点を処理し、プリミティブを組み立て、プリミティブをラスタ化してフレームバッファに送 ります。OpenGL ESの利点の1つは、頂点データをOpenGL ESに送信する関数の単一のセットを標準と して採用していて、OpenGLが提供していた古くて効率の悪い仕組みは削除されていることです。 フレームをレンダリングするため多数のプリミティブを送信しなければならないアプリケーション は、頂点データおよび頂点データのOpenGL ESへの提供方法を注意深く管理する必要があります。こ の章で説明する方法は、次に示すいくつかの基本原則に要約することができます。 ● ● 頂点データのサイズを減らす。 OpenGL ESがグラフィックスハードウェアに頂点データを転送する前に必ず発生する前処理を減 らす。 ● グラフィックスハードウェアに頂点データをコピーするのにかかる時間を減らす。 ● 各頂点について実行される演算を減らす。 モデルの簡素化 iOSベースのデバイスのグラフィックスハードウェアは非常に高性能ですが、デバイスの表示する画 像は非常に小さいことがよくあります。iOS上に魅力的なグラフィックスを表示するのに極端に複雑 なモデルは必要ありません。モデルの描画に使用する頂点の数を減らすことは、頂点データのサイズ および頂点データについて実行される演算を減らすことに直接つながります。 モデルの複雑さは、次に示すテクニックを使用することで低減できます。 ● ● ● 異なる詳細レベルのモデルを複数バージョン用意し、カメラからのオブジェクトの距離とディス プレイの寸法に基づいて、適切なモデルを実行時に選ぶ。 テクスチャを使用して、一定の頂点情報を不要にする。たとえば、バンプマップは、頂点データ をさらに追加することなくモデルにディテールを追加するのに使用できます。 モデルによっては頂点を追加して、ライティングのディテールやレンダリングの質を向上させ る。これは通常、値を各頂点について計算し、ラスタ化の段階で三角形を対象に値を補間する場 合に行われます。たとえば、スポットライトを三角形の中心に向ける場合、スポットライトの最 も明るい部分は頂点に向けられていないため、スポットライトの効果は気付かれない可能性があ ります。頂点を追加することで、頂点データのサイズやモデルについて実行される演算が増加す るという犠牲を払って、追加の補間ポイントが提供されます。追加の頂点を加える代わりに、パ イプラインのフラグメントの段階に演算を移動することを検討します。 ● アプリケーションでOpenGL ES 2.0以降を使用する場合、アプリケーションは頂点シェーダの 演算を実行し、varying変数に結果を割り当てます。varying値は、グラフィックスハードウェ アによって補間され、入力としてフラグメントシェーダに渡されます。代わりに、演算の入 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 82 頂点データを扱うためのベストプラクティス 属性の配列に定数を格納するのを避ける 力をvarying変数に割り当てて、フラグメントシェーダで演算を実行します。これにより、そ の演算を実行するコストが頂点単位のコストからフラグメント単位のコストに変わり、パイ プラインにおける頂点の段階の負荷と、それよりも大きいフラグメントの段階の負荷が低減 されます。これは、頂点処理においてアプリケーションがブロックされる場合、計算の負荷 が大きくない場合、および変更によって頂点の数が大幅に減少する可能性のある場合に実行 します。 ● アプリケーションでOpenGL ES 1.1を使用する場合は、DOT3ライティングを使用してフラグメ ント単位のライティングを実行できます。これは、具体的には、法線情報を保持するバンプ マップテクスチャの追加、およびGL_DOT3_RGBモードでのテクスチャ合成操作を使用したバ ンプマップの適用によって実行します。 属性の配列に定数を格納するのを避ける アプリケーションのモデルにモデル全体において固定されたままのデータを使用する属性が含まれる 場合は、各頂点についてそのデータを複製しないでください。OpenGL ES 2.0および3.0アプリケーショ ンは、固定的な頂点属性を設定することも、その代わりに値を保持する一定のシェーダ値を使用する こともできます。OpenGL ES 1.1アプリケーションでは、glColor4ubやglTexCoord2fなどの、頂点単 位の属性関数を使用します。 属性には可能な限り最小の型を使用する 属性のコンポーネントそれぞれのサイズを指定するときは、必要な結果が得られる最小のデータ型を 選びます。次に、いくつかのガイドラインを示します。 ● ● ● ● 頂点の色は、4個の符号なしバイトコンポーネント(GL_UNSIGNED_BYTE)を使用して指定する。 2個または4個の符号なしバイト値(GL_UNSIGNED_BYTE)、または符号なしshort値 (GL_UNSIGNED_SHORT)を使用してテクスチャの座標を指定する。テクスチャ座標の複数のセッ トを単一の属性に詰め込まないようにします。 OpenGL ESのGL_FIXEDデータ型の使用を避ける。このデータ型は、GL_FLOATと同じ量のメモリを 必要としますが、値の範囲はより狭くなります。すべてのiOSデバイスがハードウェアの浮動小数 点ユニットをサポートしており、浮動小数点の値をよりすばやく処理することができます。 OpenGL ES 3.0のコンテキストでは、GL_HALF_FLOATやGL_INT_2_10_10_10_REVなど、小さなデー タ型のより広い値がサポートされます。これらは多くの場合、GL_FLOATよりも小さな専有量で、 法線などの属性に十分な精度を提供します。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 83 頂点データを扱うためのベストプラクティス インターリーブされた頂点データを使用する 比較的小さいコンポーネントを指定する場合は、頂点データのずれを避けるために頂点フォーマット を必ず再編成してください。詳細については、“ずれた頂点データを避ける” (85 ページ)を参照し てください。 インターリーブされた頂点データを使用する 頂点データを、連続する配列(配列の構造体 )または各要素が複数の属性を含む配列(構造体の配 列 )として指定することができます。iOSに適した形式は、インターリーブされた単一の頂点フォー マットを使用する、構造体の配列です。インターリーブされたデータを使用すると、各頂点につい て、より優れたメモリ配置が可能となります。 図 8-2 インターリーブされたメモリ構造では、ある頂点に関するすべてのデータがまとめてメモリに配 置されます。 このルールの例外は、一部の頂点データをそれ以外の頂点データとは異なるレートで更新する必要の ある場合、または一部のデータを2つ以上のモデルで共有可能な場合です。どちらの場合も、属性デー タを2つ以上の構造体に分けることは可能です。 図 8-3 一部のデータの使われ方が異なる場合に複数の頂点構造体を使用する 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 84 頂点データを扱うためのベストプラクティス ずれた頂点データを避ける ずれた頂点データを避ける 頂点構造体を設計するときは、各属性の先頭をオフセット(コンポーネントのサイズの倍数または4 バイトのどちらか大きい方)に揃えます。属性がずれている場合、iOSはグラフィックスハードウェ アにデータを渡す前に追加の処理を実行しなければなりません。 図 8-4 (85 ページ)では、位置と法線のデータがそれぞれ3つのshort整数として、計6バイトで定義 されています。法線データは、オフセット6から開始しています。これはネイティブサイズ(2バイ ト)の倍数ですが、4バイトの倍数ではありません。この頂点データがiOSに送信されたとすると、iOS は、グラフィックスハードウェアにデータを渡す前に、そのデータをコピーして揃えるための時間を さらに費やす必要があります。これを修正するため、各属性の後に2バイトのパディングバイトを明 示的に追加します。 図 8-4 追加の処理を避けるために頂点データを揃える 三角形ストリップを使用して頂点データを一括処理する 三角形ストリップを使用することで、OpenGL ES がモデルに対して実行しなければならない頂点計算 の数が大幅に減少します。図 8-5の左側では、合計9個の頂点を使用して3個の三角形が指定されてい ます。C、E、Gは実際には同じ頂点を指定しています。データを三角形ストリップとして指定するこ とにより、頂点の数を9個から5個に減らすことができます。 図 8-5 三角形ストリップ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 85 頂点データを扱うためのベストプラクティス 三角形ストリップを使用して頂点データを一括処理する 場合によっては、複数の三角形ストリップを、より大きな単一の三角形ストリップとして統合するこ とができます。すべての三角形ストリップは、レンダリング要件が同じでなければなりません。これ は次のことを意味します。 ● ● ● 同じシェーダを使用してすべての三角形ストリップを描画しなければなりません。 OpenGLの状態を一切変えずにすべての三角形ストリップをレンダリングできなければなりませ ん。 三角形ストリップは、頂点の属性が同じでなければなりません。 2つの三角形ストリップをマージするには、図 8-6に示すように、1つ目のストリップの最後の頂点と、 2つ目のストリップの最初の頂点を二重にします。このストリップをOpenGL ESに送信すると、三角形 DEE、三角形EEF、三角形EFF、三角形FFGは縮退したとみなされて、処理もラスタ化もされません。 図 8-6 縮退三角形を使用して三角形ストリップをマージする 最高のパフォーマンスを得るため、モデルはインデックスのある単一の三角形ストリップとして送信 する必要があります。頂点バッファで同じ頂点のデータを複数回指定するのを避けるために、別個の インデックスバッファを使用し、glDrawElements関数(適宜、glDrawElementsInstancedまたは glDrawRangeElements関数)を使用して三角形ストリップを描画します。 OpenGL ES 3.0では、縮退三角形を使用しないで三角形ストリップをマージするプリミティブリスター ト機能を使用できます。この機能を有効にすると、OpenGL ESは、インデックスバッファにおいて可 能な最大値を、ある三角形ストリップを終了して別の三角形ストリップを開始するコマンドとして扱 います。リスト 8-1に、この方法を示します。 リスト 8-1 OpenGL ES 3.0でのプリミティブスタートの使用 // Prepare index buffer data (not shown: vertex buffer data, loading vertex and index buffers) GLushort indexData[11] = { 0, 1, 2, 3, 4, 0xFFFF, 5, 6, 7, 8, 9, // triangle strip ABCDE // primitive restart index (largest possible GLushort value) // triangle strip FGHIJ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 86 頂点データを扱うためのベストプラクティス 頂点バッファオブジェクトの使用による頂点データのコピーの管理 }; // Draw triangle strips glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); glDrawElements(GL_TRIANGLE_STRIP, 11, GL_UNSIGNED_SHORT, 0); 可能な場合は、同じ頂点を共有する三角形が三角形ストリップ内でお互い適度に近い位置で描画され るよう、頂点とインデックスのデータを並べ替えます。グラフィックスハードウェアは、最近の頂点 演算をキャッシュして、頂点の再演算を避けます。 頂点バッファオブジェクトの使用による頂点データのコピー の管理 リスト 8-2では、シンプルなアプリケーションが頂点シェーダに位置と色のデータを提供するために 使用できる関数を示しています。この関数では2つの属性が有効化され、それぞれの属性がインター リーブされた頂点構造体を参照するよう設定されています。最後に、glDrawElements関数を呼び出 して、モデルを単一の三角形ストリップとしてレンダリングします。 リスト 8-2 シェーダプログラムへの頂点データの送信 typedef struct _vertexStruct { GLfloat position[2]; GLubyte color[4]; } vertexStruct; void DrawModel() { const vertexStruct vertices[] = {...}; const GLubyte indices[] = {...}; glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), &vertices[0].position); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), &vertices[0].color); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 87 頂点データを扱うためのベストプラクティス 頂点バッファオブジェクトの使用による頂点データのコピーの管理 glEnableVertexAttribArray(GLKVertexAttribColor); glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices); } このコードは動作はしますが、非効率的です。DrawModelが呼び出されるごとにインデックスデータ と頂点データがOpenGL ESにコピーされ、グラフィックスハードウェアに転送されます。呼び出しと 呼び出しの間で頂点データが変化しなければ、データの不要なコピーがパフォーマンスに影響を及ぼ すおそれがあります。不必要なコピーを避けるために、アプリケーションは頂点データを頂点バッ ファオブジェクト(VBO)に格納する必要があります。OpenGL ESは頂点バッファオブジェクトのメ モリを保有しているため、グラフィックスハードウェアがよりアクセスしやすいメモリにバッファを 格納することも、データがグラフィックスハードウェアに適したフォーマットになるよう事前に処理 することもできます。 注意: OpenGL ES 3.0で頂点配列オブジェクトを使用するときは、頂点バッファオブジェク トも使用する必要があります。 リスト 8-3のコードは、頂点バッファオブジェクトの組を生成しています。一方は頂点データ、もう 一方はストリップのインデックスを保持します。どちらのオブジェクトの場合も、コードは新しいオ ブジェクトを生成し、そのオブジェクトを現在のバッファにバインドし、バッファを埋めます。 CreateVertexBuffersは、アプリケーションの初期化時に呼び出されます。 リスト 8-3 頂点バッファオブジェクトを生成するコード例 GLuint vertexBuffer; GLuint indexBuffer; void CreateVertexBuffers() { glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glGenBuffers(1, &indexBuffer); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 88 頂点データを扱うためのベストプラクティス 頂点バッファオブジェクトの使用による頂点データのコピーの管理 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); } リスト 8-4は、リスト 8-2 (87 ページ)を修正し、頂点バッファオブジェクトを使うようにしたもの です。リスト 8-4の主な違いは、glVertexAttribPointer関数のパラメータが、頂点配列を指すもの でなくなった点です。その代わり、各パラメータは、頂点バッファオブジェクトへのオフセットとな ります。 リスト 8-4 頂点バッファオブジェクトを使って描画するコード例 void DrawModelUsingVertexBuffers() { glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), (void *)offsetof(vertexStruct, position)); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), (void *)offsetof(vertexStruct, color)); glEnableVertexAttribArray(GLKVertexAttribColor); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0); } バッファの使用に関するヒント これまでの例では、頂点バッファを初期化したら、その後でそのコンテンツを決して変更しませんで した。頂点バッファのコンテンツは変更できます。頂点バッファオブジェクトの設計の重要な部分 は、バッファに格納されているデータの使いかたをアプリケーションがOpenGL ESに通知できるとい うことです。OpenGL ESの実装は、このヒントを使って、頂点データの格納に使用する方法を変更で きます。リスト 8-3 (88 ページ)では、glBufferData関数の各呼び出しの最後のパラメータに用途 のヒントが指定されています。GL_STATIC_DRAWをglBufferDataに渡すことにより、両方のバッファ の内容が変更される予定はまったくないことがOpenGL ESに伝えられ、それによってOpenGL ESはデー タを格納する方法と場所を最適化するための機会がさらに与えられます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 89 頂点データを扱うためのベストプラクティス 頂点バッファオブジェクトの使用による頂点データのコピーの管理 OpenGL ESの仕様には、次の使用事例が定義されています。 GL_STATIC_DRAWは、何度もレンダリングされ、コンテンツが一度指定されると変更されない頂 ● 点バッファ向けです。 GL_DYNAMIC_DRAWは、何度もレンダリングされ、レンダリングループ中にコンテンツが変更され ● る頂点バッファ向けです。 GL_STREAM_DRAWは、レンダリングの回数が少なくレンダリング後に破棄される頂点バッファ向 ● けです。 In iOSで、GL_DYNAMIC_DRAWとGL_STREAM_DRAWは同等です。glBufferSubData関数を使用してバッ ファコンテンツを更新することができますが、それはパフォーマンスを低下を招きます。コマンド バッファをフラッシュし、すべてのコマンドの完了を待機するからです。このパフォーマンス上の負 担は、バッファリングを二重化または三重化することによって、若干軽減されます。(詳細について は、“ダブルバッファリングによるリソースの競合の回避” (63 ページ)を参照してください。)パ フォーマンスを向上させるために、OpenGL ES 3.0ではglMapBufferRange関数を使用し、OpenGL ES 2.0または1.1ではEXT_map_buffer_range機能拡張によって提供される対応する関数を使用してくだ さい。 頂点フォーマット内のさまざまな属性が異なる使用パターンを必要とする場合は、頂点データを複数 の構造体に分割し、使用方法の特徴が共通している属性のコレクションごとに独立した頂点バッファ オブジェクトを割り当てます。リスト 8-5は、独立したバッファを使用して色データを保持するよう、 前の例を修正したものです。GL_DYNAMIC_DRAWというヒントを使用して色バッファを割り当てること により、OpenGL ESは、アプリケーションが適切なパフォーマンスを維持するようそのバッファを割 り当てることができます。 リスト 8-5 複数の頂点バッファオブジェクトの使用によるモデルの描画 typedef struct _vertexStatic { GLfloat position[2]; } vertexStatic; typedef struct _vertexDynamic { GLubyte color[4]; } vertexDynamic; // Separate buffers for static and dynamic data. 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 90 頂点データを扱うためのベストプラクティス 頂点バッファオブジェクトの使用による頂点データのコピーの管理 GLuint staticBuffer; GLuint dynamicBuffer; GLuint indexBuffer; const vertexStatic staticVertexData[] = {...}; vertexDynamic dynamicVertexData[] = {...}; const GLubyte indices[] = {...}; void CreateBuffers() { // Static position data glGenBuffers(1, &staticBuffer); glBindBuffer(GL_ARRAY_BUFFER, staticBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(staticVertexData), staticVertexData, GL_STATIC_DRAW); // Dynamic color data // While not shown here, the expectation is that the data in this buffer changes between frames. glGenBuffers(1, &dynamicBuffer); glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(dynamicVertexData), dynamicVertexData, GL_DYNAMIC_DRAW); // Static index data glGenBuffers(1, &indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); } void DrawModelUsingMultipleVertexBuffers() { glBindBuffer(GL_ARRAY_BUFFER, staticBuffer); glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), (void *)offsetof(vertexStruct, position)); glEnableVertexAttribArray(GLKVertexAttribPosition); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 91 頂点データを扱うためのベストプラクティス 頂点配列オブジェクトの使用による頂点配列状態の変化の整理統合 glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer); glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), (void *)offsetof(vertexStruct, color)); glEnableVertexAttribArray(GLKVertexAttribColor); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0); } 頂点配列オブジェクトの使用による頂点配列状態の変化の整 理統合 DrawModelUsingMultipleVertexBuffers「複数の頂点バッファオブジェクトの使用によるモデルの 描画」リスト 8-5 (90 ページ)関数を詳しく見てみます。この関数により、多くの属性が使用可能 になり、複数の頂点バッファオブジェクトがバインドされ、バッファを参照するよう属性が設定され ます。その初期化コードのすべてが基本的に静的であり、フレームからフレームの間で変化するパラ メータはありません。この関数がアプリケーションがフレームをレンダリングするごとに呼び出され る場合は、グラフィックスパイプラインを設定し直すという不要なオーバーヘッドが多数存在してい ます。アプリケーションが数多くの異なる種類のモデルを描画する場合、パイプラインの再設定はボ トルネックとなる可能性があります。代わりに、頂点配列オブジェクトを使用して、すべての属性設 定を格納します。頂点配列オブジェクトはOpenGL ES 3.0コア仕様の一部であり、OpenGL ES 2.0および 1.1ではOES_vertex_array_object拡張機能を使用して利用できます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 92 頂点データを扱うためのベストプラクティス 頂点配列オブジェクトの使用による頂点配列状態の変化の整理統合 図 8-7は、2つの頂点配列オブジェクトを使用した設定例です。各設定は他の設定に依存せず、各頂点 配列オブジェクトは、頂点属性の別のセットを参照できます。この別のセットは、同じ頂点バッファ オブジェクトに格納したり、複数の頂点バッファオブジェクトに分割したりすることができます。 図 8-7 頂点配列オブジェクトの設定 リスト 8-6は、上記の1つ目の頂点配列オブジェクトの設定に使用するコードを示しています。この コードは新しい頂点配列オブジェクトの識別子を生成し、さらに頂点配列オブジェクトをコンテキス トにバインドします。この後、コードが頂点配列オブジェクトを使用しなかった場合と同じ呼び出し を行って頂点属性を設定します。設定は、コンテキストではなく、バインドされた頂点配列オブジェ クトに格納されます。 リスト 8-6 頂点配列オブジェクトの設定 void ConfigureVertexArrayObject() { // Create and bind the vertex array object. glGenVertexArrays(1,&vao1); glBindVertexArray(vao1); // Configure the attributes in the VAO. glBindBuffer(GL_ARRAY_BUFFER, vbo1); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(staticFmt), (void*)offsetof(staticFmt,position)); glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(staticFmt), (void*)offsetof(staticFmt,texcoord)); glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 93 頂点データを扱うためのベストプラクティス バッファをクライアント側メモリにマップして高速に更新する sizeof(staticFmt), (void*)offsetof(staticFmt,normal)); glEnableVertexAttribArray(GLKVertexAttribNormal); glBindBuffer(GL_ARRAY_BUFFER, vbo2); glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(dynamicFmt), (void*)offsetof(dynamicFmt,color)); glEnableVertexAttribArray(GLKVertexAttribColor); // Bind back to the default state. glBindBuffer(GL_ARRAY_BUFFER,0); glBindVertexArray(0); } 描画するには、コードによって頂点配列オブジェクトをバインドし、これまでと同じように描画コマ ンドを送信します。 注意: OpenGL ES 3.0では、頂点配列データをクライアント側ストレージに置くことはでき なくなりました。頂点配列オブジェクトは頂点バッファオブジェクトに格納しなければなり ません。 最高のパフォーマンスを得るため、アプリケーションが各頂点配列オブジェクトを設定したら、実行 時には決して変更しないようにします。その代わり、頂点配列オブジェクトをフレームごとに変更す る必要のある場合は、複数の頂点配列オブジェクトを作成します。たとえば、バッファの二重化を使 用するアプリケーションは、頂点配列オブジェクトの1つ目のセットの奇数フレーム用に、そして2つ 目のセットを偶数フレーム用に設定することができます。頂点配列オブジェクトの各セットは、その セットのフレームのレンダリングに使用する頂点バッファオブジェクトを参照します。頂点配列オブ ジェクトの設定が変わらない場合、OpenGL ESは頂点フォーマットに関する情報をキャッシュして、 頂点属性を処理する方法を改善することができます。 バッファをクライアント側メモリにマップして高速に更新す る OpenGL ESアプリケーションの設計では、動的リソースの取り扱いも難しい問題です。特に頂点デー タがフレームごとに変わる場合が困難です。CPUとGPUが均衡を保って動作するためには、アプリケー ション側メモリ空間と、OpenGL ESのメモリとのデータ転送を、慎重に管理しなければなりません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 94 頂点データを扱うためのベストプラクティス バッファをクライアント側メモリにマップして高速に更新する glBufferSubData関数を使うなど従来型の技法では、性能が損なわれる可能性があります。データ転 送中はGPUに待機するよう強制するからです。同じバッファの別の場所にあるデータをもとにレンダ リングすることもできません。 たとえば、高フレームレートのレンダリングループにおける各パスで、頂点バッファを修正すると同 時に、その内容を描画できれば、性能向上に役立つかも知れません。しかし、CPUがバッファメモリ にアクセスして次のフレームの描画準備をしている間に、レンダリングが済んだ最終フレームの描画 コマンドが、なおGPUを使っている可能性があります。そのためバッファ更新関数は、GPUの処理終 了まで、CPUでの処理をブロックします。このような状況では、CPUとGPUのバッファアクセスを手 動で同期させることにより、性能を改善できます。 glMapBufferRange関数を使えば、より効率よく、頂点バッファを動的に更新できます(この関数 は、OpenGL ES 3.0ではコアAPIの一部になっています。OpenGL ES 1.1および2.0では EXT_map_buffer_range拡張機能として提供されています)。この関数で、OpenGL ESメモリのある 領域を指すポインタを取得し、これを使って新規データを書き出せます。glMapBufferRange関数 は、バッファのデータストレージ中のある部分領域を、クライアント側メモリにマップすることがで きるのです。さらに、OpenGLの同期オブジェクトと併用することを考慮して、非同期にバッファを 修正するためのヒントにも対応しています(リスト 8-7を参照)。 リスト 8-7 手動同期により頂点バッファを動的に更新するコード例 GLsync fence; GLboolean UpdateAndDraw(GLuint vbo, GLuint offset, GLuint length, void *data) { GLboolean success; // Bind and map buffer. glBindBuffer(GL_ARRAY_BUFFER, vbo); void *old_data = glMapBufferRange(GL_ARRAY_BUFFER, offset, length, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT ); // Wait for fence (set below) before modifying buffer. glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); // Modify buffer, flush, and unmap. memcpy(old_data, data, length); glFlushMappedBufferRange(GL_ARRAY_BUFFER, offset, length); success = glUnmapBuffer(GL_ARRAY_BUFFER); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 95 頂点データを扱うためのベストプラクティス バッファをクライアント側メモリにマップして高速に更新する // Issue other OpenGL ES commands that use other ranges of the VBO's data. // Issue draw commands that use this range of the VBO's data. DrawMyVBO(vbo); // Create a fence that the next frame will wait for. fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); return success; } この例のUpdateAndDraw関数は、特定のバッファオブジェクトを使う描画コマンドを実行した直後 に、glFenceSync関数で同期ポイント(フェンス)を確立しています。(レンダリングループの次の パスでは)glClientWaitSync関数で同期ポイントを確認した上で、バッファオブジェクトを修正しま す。レンダリングループを一回りして戻ってくる前に、GPU上で描画コマンドの実行が終了した場 合、CPU側で実行がブロックされることはないので、UpdateAndDraw関数はそのままバッファを修正 し、次のフレームを描画します。一方、描画コマンドの実行が終わっていなければ、glClientWaitSync 関数はGPUがフェンスに到達するまで、さらにCPU側の実行をブロックし続けます。リソースが衝突 しうる箇所のまわりにのみ、手動で同期ポイントを置くことにより、CPUがGPUの処理終了を待つ時 間を最小限に抑えることができます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 96 テクスチャデータを扱うためのベストプラク ティス テクスチャデータは、アプリケーションがフレームのレンダリングに使用するデータのうち最も大き い部分を占めることが少なくありません。それは、テクスチャが美しい画像をユーザに表示するため に必要なディテールを提供してくれるためです。アプリケーションからできるだけ最高のパフォーマ ンスを引き出すため、アプリケーションのテクスチャを注意深く管理します。テクスチャの管理に関 するガイドラインは次のとおりです。 ● アプリケーションの初期化時にテクスチャを作成し、レンダリングループの間はテクスチャを決 して変更しない。 ● テクスチャの使用するメモリの量を減らす。 ● 比較的小さいテクスチャをより大きいテクスチャアトラスにまとめる。 ● ミップマップを使用して、テクスチャデータをフェッチするのに必要な帯域幅を減らす。 ● マルチテクスチャリングを使用してテクスチャリング演算を実行する。 初期化中にテクスチャをロードする テクスチャの作成とロードはコストのかかる操作です。最良のパフォーマンスを得るにはアプリケー ションの実行中に新しいテクスチャを作成することは避けます。代わりに、初期化中にテクスチャ データの作成とロードを行います。 テクスチャを作成したら、フレームの最初または最後以外でのテクスチャの変更は避けます。現時点 では、すべてのiOSデバイスはタイルベースの遅延レンダラを使用しています。このレンダラは、 glTexSubImage関数とglCopyTexSubImage関数の呼び出しの際に特にコストがかかります。詳細に ついては、“Tile-Based Deferred Rendering” in OpenGL ES Hardware Platform Guide for iOS を参照してくだ さい。 GLKitフレームワークを使用したテクスチャデータの読み込み テクスチャデータの読み込みは、明確にするために重要な基本的演算です。GLKitフレームワークを使 用して、GLKTextureLoaderクラスは新しいテクスチャの作成と読み込みを容易にします。 GLKTextureLoaderクラスは、ファイル、URL、メモリ保存形式、CGImagesなど、さまざまなソース からテクスチャデータを読み込むことができます。入力ソースに関係なく、GLKTextureLoaderクラ スは新しいテクスチャを作成したりデータから読み込んだりし、テクスチャ情報をGLKTextureInfo 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 97 テクスチャデータを扱うためのベストプラクティス 初期化中にテクスチャをロードする オブジェクトとして返します。GLKTextureInfoオブジェクトのプロパティにアクセスし、テクスチャ をコンテキストにバインドしたり、描画のために有効にするなど、さまざまなタスクを実行すること ができます。 注意: GLKTextureInfoオブジェクトは、それが記述するOpenGL ESテクスチャオブジェク トを所有しません。テクスチャオブジェクトを使用し終わったら、glDeleteTextures関数 を呼び出して破棄する必要があります。 リスト 9-1に、ファイルから新しいテクスチャを読み込んだり、後で使用するためにテクスチャを有 効にしたりするための一般的な方法を示します。 リスト 9-1 ファイルからの2次元テクスチャの読み込み GLKTextureInfo *spriteTexture; NSError *theError; NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Sprite" ofType:@"png"]; // 1 spriteTexture = [GLKTextureLoader textureWithContentsOfFile:filePath options:nil error:&theError]; // 2 glBindTexture(spriteTexture.target, spriteTexture.name); // 3 glEnable(spriteTexture.target); // 4 一覧内で番号が付けられた手順に対応して、コードが行うことを次に示します。 1. テキスチャデータを含む画像へのパスを作成します。このパスは、GLKTextureLoaderクラスの textureWithContentsOfFile:options:error:メソッドにパラメータとして渡されます。 2. 画像ファイルから新しいテクスチャを読み込み、テクスチャ情報をGLKTextureInfoオブジェク トに格納します。さまざまなテクスチャ読み込みオプションが用意されています。詳細について は、『GLKTextureLoader Class Reference 』を参照してください。 3. GLKTextureInfoオブジェクトの該当するプロパティをパラメータとして使用して、テクスチャ をコンテキストにバインドします。 4. GLKTextureInfoオブジェクトの該当するプロパティをパラメータとして使用して、描画のため にテクスチャの使用を有効にします。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 98 テクスチャデータを扱うためのベストプラクティス テクスチャのメモリ使用量を削減する GLKTextureLoaderクラスは、最も一般的な画像形式のキューブマップテクスチャを読み込むことも できます。そして、アプリケーションは、実行中に新しいテクスチャを読み込んだり作成したりする 必要がある場合、GLKTextureLoaderクラスはテクスチャの非同期読み込みのメソッドも提供しま す。詳細については、『GLKTextureLoader Class Reference 』を参照してください。 テクスチャのメモリ使用量を削減する iOSアプリケーションが使用するメモリ量を減らすことは、常にアプリケーションのチューニングに おける重要な部分です。とはいえ、OpenGL ESアプリケーションも、テクスチャを読み込むために使 用できる総メモリ量が制限されています。アプリケーションがテクスチャデータの保持に使用するメ モリ量をできるだけ抑えるようにします。テクスチャが使用するメモリを減らすことは、必ずといっ ていいほど画像の品質を犠牲にします。そのため、アプリケーションがテクスチャに加える変更と、 最終的にレンダリングされたフレームの品質レベルとのバランスをとらなければなりません。最良の 結果を得るため、以下に示す別のテクニックを試し、許容できる品質レベルで最もメモリが節約され る方法を選びます。 テクスチャの圧縮 テクスチャの圧縮は通常、メモリの節約と品質のバランスが最もよくとれる方法です。iOS OpenGL ES では、複数の圧縮テクスチャフォーマットがサポートされています。 すべてのiOSデバイスで、GL_IMG_texture_compression_pvrtc拡張機能を実装することによって、PowerVR Texture Compression(PVRTC)フォーマットがサポートされています。PVRTC圧縮には、4ビット/ピク セルと2ビット/ピクセルの2つのレベルがあり、圧縮なしの32ビットテクスチャフォーマットと比較 して圧縮率はそれぞれ8:1と16:1です。圧縮されたPVRTCテクスチャでも、特に4ビットレベルの場合 は、相当レベルの品質を提供できます。テクスチャをPVRTCフォーマットに圧縮する方法については、 “texturetoolを使用したテクスチャの圧縮” (143 ページ)を参照してください。 OpenGL ES 3.0はETC2およびEACという圧縮テクスチャフォーマットも扱えますが、iOSデバイスでは PVRTCテクスチャを使うよう推奨します。 低精度のカラーフォーマットを使用する アプリケーションで圧縮テクスチャを使用できない場合、より低精度のピクセルフォーマットの使用 を検討します。フォーマットがRGB565、RGBA5551、またはRGBA4444のテクスチャは、RGBA8888フォー マットのテクスチャの半分のメモリ使用で済みます。RGBA8888を使用するのは、アプリケーション でそのレベルの品質が必要な場合のみにします。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 99 テクスチャデータを扱うためのベストプラクティス 複数のテクスチャをテクスチャアトラスにまとめる 適切なサイズのテクスチャを使用する iOSベースのデバイスが表示する画像は非常に小さいものです。アプリケーションでは、画面に対し て適切な画像を表示するために大きなテクスチャを提供する必要はありません。テクスチャの縦横両 方の寸法を半分にすれば、そのテクスチャに必要なメモリ量は元のテクスチャの4分の1に削減できま す。 テクスチャを縮小する前に、まずテクスチャの圧縮や低精度のカラーフォーマットの使用を試してみ るべきです。一般に、PVRTCフォーマットによるテクスチャの圧縮を使用した方がテクスチャを縮小 するよりも画像の品質は高く、しかもメモリの使用量も少なくて済みます。 複数のテクスチャをテクスチャアトラスにまとめる テクスチャへのバインドでは、OpenGL ESによる処理に時間がかかります。OpenGL ESの状態を変更す る回数が少ないアプリケーションのほうがパフォーマンスが高くなります。テクスチャに関して、新 たなテクスチャへのバインドを避ける方法の1つは、複数の小さなテクスチャを結合して、1つの大き なテクスチャ(テクスチャアトラスと呼ぶ)にすることです。テクスチャアトラスを使用すると、単 一のテクスチャをバインドし、テクスチャを使用する複数の描画呼び出しを行ったり、さらに複数の 描画呼び出しを単一の描画呼び出しにしたりすることもできます。頂点データで提供されるテクス チャの座標は、テクスチャアトラス内のテクスチャのより小さい部分を選ぶよう修正されます。 テクスチャアトラスには次のような制限があります。 ● GL_REPEATテクスチャラップパラメータを使用している場合は、テクスチャアトラスを使用でき ません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 100 テクスチャデータを扱うためのベストプラクティス ミップマップを使用してメモリの帯域幅の使用を削減する ● ● フィルタリングすると想定の範囲を超えたテクセルが取得される場合があります。テクスチャア トラス内でこのようなテクスチャを使用するには、テクスチャアトラスを構成するテクスチャの 間にパディングを配置する必要があります。 テクスチャアトラスもまた1つのテクスチャであるため、他のテクスチャ属性と同様に、OpenGL ES 実装の最大テクスチャサイズの影響を受けます。 Xcode 5は、画像のコレクションからテキスチャアトラスを自動的に構築できます。テクスチャアト ラスの作成の詳細については、テクスチャアトラスのヘルプを参照してください。この機能は、Sprite Kitフレームワークを使用する開発者に対して主に提供されますが、アプリケーションはそれが生成す るテキスチャアトラスファイルを使用できます。Xcodeは、プロジェクトの.atlasフォルダごとに、 アプリケーションバンドルに.atlascフォルダを作成します。これには、1つ以上のコンパイルされ たアトラス画像とプロパティリスト(.plist)ファイルが含まれます。プロパティリストファイルは、 アトラスとアトラス画像内でのそれらの場所を構成する個々の画像を記述します。この情報を使用し て、OpenGL ESの描画での使用に適したテクスチャ座標を計算することができます。 ミップマップを使用してメモリの帯域幅の使用を削減する 2Dの拡大縮小なしの画像を描画する場合を除いて、アプリケーションはすべてのテクスチャにミップ マップを提供するべきです。ミップマップは余分なメモリを使用しますが、テクスチャリングによる アーティファクトを抑制し画像の品質を向上させます。さらに重要なことは、サンプリングされた ミップマップが小さければ小さいほど、テクスチャメモリから取得されるテクセルも少なくなり、グ ラフィックスハードウェアが必要とするメモリの帯域幅が削減され、パフォーマンスが向上します。 GL_LINEAR_MIPMAP_LINEARフィルタモードはテクスチャリングの際に最良の品質を提供しますが、 メモリから余分にテクセルを取得する必要があります。GL_LINEAR_MIPMAP_NEARESTフィルタモード を指定すると、画像品質を落とす代わりにパフォーマンスを向上させることができます。 ミップマップとテクスチャアトラスを組み合わせる場合、OpenGL ES 3.0ではTEXTURE_MAX_LEVELパ ラメータを使って、テクスチャのフィルタリング方法を制御します(この機能はOpenGL ES 1.1や2.0 でも、APPLE_texture_max_level拡張機能として適用可)。 マルチパスの代わりにマルチテクスチャリングを使用する 多くのアプリケーションが、パスごとにグラフィックスパイプラインの設定を変えながら、複数のパ スを実行してモデルを描画します。これは、グラフィックスパイプラインを設定し直す回数が増える だけでなく、パスごとに頂点情報を再処理したり、後半のパスではピクセルデータをフレームバッ ファから読み直したりする必要が生じます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 101 テクスチャデータを扱うためのベストプラクティス マルチパスの代わりにマルチテクスチャリングを使用する iOSにおけるOpenGL ESの実装は、少なくとも2つのテクスチャユニットをサポートし、ほとんどのデ バイスが少なくとも8個のテクスチャユニットをサポートします。アプリケーションはこれらのテク スチャユニットを使用し、各パスのアルゴリズムにおいてできるだけ多くの手順を実行する必要があ ります。glGetIntegerv関数を呼び出し、パラメータとしてGL_MAX_TEXTURE_UNITSを渡すことによ り、アプリケーションで利用可能なテクスチャユニットの数を取得できます。 1つのオブジェクトをレンダリングするためにマルチパスが必要な場合は、次の点に注意してくださ い。 ● ● パスのたびに位置データが変化しないことを保証すること。 2つ目以降のパスでは、GL_EQUALをパラメータとしてglDepthFunc関数を呼び出して、モデルの 表面上のピクセルをテストすること。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 102 シェーダのベストプラクティス シェーダは高い柔軟性をもたらしますが、多くの計算を実行しすぎたり、計算の効率が悪かったりす ると大きなボトルネックとなるおそれがあります。 初期化中にシェーダをコンパイルおよびリンクする シェーダプログラムの作成は、OpenGL ESのその他の状態変更に比べて、コストのかかる操作です。 アプリケーションを初期化するときに、プログラムのコンパイル、リンク、および検証を行います。 すべてのシェーダを作成したら、アプリケーションではglUseProgramを呼び出すことによって効率 的にそれらを切り替えることができます。 デバッグ時のシェーダプログラムエラーのチェック シェーダプログラムのコンパイルまたはリンク後に診断情報に読み込むことはアプリケーションのリ リースビルドでは不要であり、パフォーマンスが低下する可能性があります。OpenGL ES関数を使用 して、シェーダコンパイルを読み込むか、アプリケーションの開発ビルドでのみログをリンクします (リスト 10-1を参照)。 リスト 10-1 開発ビルドでのみシェーダのコンパイル/リンクログを読み込む // After calling glCompileShader, glLinkProgram, or similar #ifdef DEBUG // Check the status of the compile/link glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLen); if(logLen > 0) { // Show any errors as appropriate glGetProgramInfoLog(prog, logLen, &logLen, log); fprintf(stderr, "Prog Info Log: %s\n", log); } #endif 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 103 シェーダのベストプラクティス 初期化中にシェーダをコンパイルおよびリンクする 同様に、glValidateProgram関数も開発ビルドでのみ呼び出す必要があります。この関数を使用し て、シェーダプログラムで必要なすべてのテクスチャユニットをバインドできないなどの開発エラー を検出することができます。ただし、検証プログラムでは、OpenGL ESのコンテキスト状態全体に対 して開発エラーがチェックされるため、これはコストのかかる操作です。プログラム検証の結果に意 味があるのは開発中だけであるため、アプリケーションのリリースビルドではこの関数を呼び出さな いでください。 コンパイルとリンクを高速化するために別々のシェーダオブジェクトを使 用する 多くのOpenGL ESアプリケーションでは、いくつかの頂点シェーダとフラグメントシェーダが使用さ れ、頂点シェーダが異なる同じフラグメントシェーダまたはフラグメントシェーダが異なる同じ頂点 シェーダを再利用すると便利なことがよくあります。OpenGL ESコア仕様では、頂点シェーダとフラ グメントシェーダを1つのシェーダプログラムでリンクさせる必要があるため、多数のプログラムで シェーダの結果を組み合わせたり照合したりすると、アプリケーションの初期化時にシェーダのコン パイルとリンクの合計時間が増えます。 iOSでのOpenGL ES 2.0および3.0のコンテキストでは、EXT_separate_shader_objects拡張機能がサ ポートされます。この拡張機能によって提供される関数を使用して、頂点シェーダとフラグメント シェーダを別々にコンパイルしたり、プログラムパイプラインオブジェクトを使用してレンダリング 時に事前にコンパイルされたシェーダ段階を組み合わせたり照合したりすることができます。さら に、この拡張機能により、リスト 10-2に示すように、シェーダをコンパイルしたり使用したりするた めの簡単なインターフェイスが提供されます。 リスト 10-2 独立したシェーダオブジェクトをコンパイル、使用するコード例 - (void)loadShaders { const GLchar *vertexSourceText = " ... vertex shader GLSL source code ... "; const GLchar *fragmentSourceText = " ... fragment shader GLSL source code ... "; // Compile and link the separate vertex shader program, then read its uniform variable locations _vertexProgram = glCreateShaderProgramvEXT(GL_VERTEX_SHADER, 1, &vertexSourceText); _uniformModelViewProjectionMatrix = glGetUniformLocation(_vertexProgram, "modelViewProjectionMatrix"); _uniformNormalMatrix = glGetUniformLocation(_vertexProgram, "normalMatrix"); 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 104 シェーダのベストプラクティス 初期化中にシェーダをコンパイルおよびリンクする // Compile and link the separate fragment shader program (which uses no uniform variables) _fragmentProgram = &fragmentSourceText); glCreateShaderProgramvEXT(GL_FRAGMENT_SHADER, 1, // Construct a program pipeline object and configure it to use the shaders glGenProgramPipelinesEXT(1, &_ppo); glBindProgramPipelineEXT(_ppo); glUseProgramStagesEXT(_ppo, GL_VERTEX_SHADER_BIT_EXT, _vertexProgram); glUseProgramStagesEXT(_ppo, GL_FRAGMENT_SHADER_BIT_EXT, _fragmentProgram); } - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { // Clear the framebuffer glClearColor(0.65f, 0.65f, 0.65f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Use the previously constructed program pipeline and set uniform contents in shader programs glBindProgramPipelineEXT(_ppo); glProgramUniformMatrix4fvEXT(_vertexProgram, _uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m); glProgramUniformMatrix3fvEXT(_vertexProgram, _uniformNormalMatrix, 1, 0, _normalMatrix.m); // Bind a VAO and render its contents glBindVertexArrayOES(_vertexArray); glDrawElements(GL_TRIANGLE_STRIP, _indexCount, GL_UNSIGNED_SHORT, 0); } 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 105 シェーダのベストプラクティス シェーダに関するハードウェア制限に従う シェーダに関するハードウェア制限に従う OpenGL ESでは、頂点シェーダまたはフラグメントシェーダで使用できる各変数型の数が制限されて います。OpenGL ES仕様では、これらの制限を超えた場合のソフトウェアフォールバックを提供する ための実装は不要です。代わりに、シェーダはコンパイルまたはリンクに失敗します。アプリケー ションの開発時には、シェーダのコンパイル中にエラーが発生しないことを確認する必要があります (リスト 10-1を参照)。 精度ヒントを使用する 組み込みデバイスの厳しいハードウェア制限に相応しいコンパクトなシェーダ変数のニーズに対応す るために、GLSL ESの言語仕様に精度ヒントが追加されました。各シェーダはデフォルトの精度を定 義しなければなりません。個々のシェーダ変数はこの精度をオーバーライドして、その変数のアプリ ケーションにおける使用方法についてコンパイラにヒントを提供する場合があります。OpenGL ESの 実装では、ヒント情報を使用する必要はありませんが、より効率の良いシェーダを生成するためにヒ ント情報を使用することは可能です。GLSL ES仕様には、各ヒントの範囲と精度の一覧が示されてい ます。 Important: 精度ヒントで定義されている範囲制限は強制ではありません。そのため、データがこ の範囲に収まっていると想定することはできません。 以下にそのガイドラインを示します。 ● 判断がつかない場合は、高精度をデフォルトにする。 ● 0.0~1.0の範囲の色は通常、低精度の変数を使用して表現できる。 ● 位置データは通常、高精度で格納する。 ● ライティングの計算で使用される法線とベクトルは通常、中精度で格納できる。 ● 精度を下げた後は、アプリケーションを再テストして、結果が期待通りのものであることを確認 する。 リスト 10-3は高精度の変数がデフォルトですが、高精度は不要なため低精度の変数を使用して色出力 を計算します。 リスト 10-3 フラグメントの色に低精度を使用する precision highp float; // Defines precision for float and float-derived (vector/matrix) types. uniform lowp sampler2D sampler; // Texture2D() result is lowp. 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 106 シェーダのベストプラクティス ベクトル計算の時間のかかる実行 varying lowp vec4 color; varying vec2 texCoord; // Uses default highp precision. void main() { gl_FragColor = color * texture2D(sampler, texCoord); } シェーダ変数の実際の精度はIOSデバイスによって異なることがあります。各精度レベルでの処理性 能も同様です。デバイスに応じて考慮するべき事項については『iOSDeviceCompatibilityReference 』を 参照してください。 ベクトル計算の時間のかかる実行 すべてのグラフィックスプロセッサにベクトルプロセッサが含まれているわけではなく、スカラープ ロセッサ上でベクトル計算を実行するグラフィックスプロセッサもあります。シェーダで計算を実行 する場合は、スカラープロセッサ上計算が実行される場合であっても効率的に実行されるよう、演算 の順序を考慮します。 リスト 10-4のコードがベクトルプロセッサ上で実行される場合、各乗算は4個のベクトルのコンポー ネントすべてにわたって並行して実行されます。ただし、括弧の位置が理由で、3つのパラメータの うち2つがスカラー値であっても、プロセッサ上の同じ演算で8回の乗算を要することになります。 リスト 10-4 ベクトル演算をうまく活用していない highp float f0, f1; highp vec4 v0, v1; v0 = (v1 * f0) * f1; リスト 10-5に示すように、括弧を移動することによって同じ計算をさらに効率的に実行できます。こ の例では、スカラー値を最初に乗じて、その結果をベクトルパラメータで乗じます。全体の演算は、 5つの乗算で計算できます。 リスト 10-5 ベクトル演算の適切な使用 highp float f0, f1; highp vec4 v0, v1; 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 107 シェーダのベストプラクティス シェーダ内での計算の代わりにuniformまたはconstantを使用する v0 = v1 * (f0 * f1); 同様に、アプリケーションで、結果のコンポーネントすべてを使用するわけではない場合は、必ずベ クトル演算に書き込みマスクを指定してください。スカラープロセッサ上では、マスクで指定されて いないコンポーネントの計算はスキップできます。リスト 10-6は、2つのコンポーネントだけを必要 と指定しているため、スカラープロセッサ上では2倍の速度で実行されます。 リスト 10-6 書き込みマスクの指定 highp vec4 v0; highp vec4 v1; highp vec4 v2; v2.xz = v0 * v1; シェーダ内での計算の代わりにuniformまたはconstantを使用 する シェーダの外部で値を計算できる場合は、値をuniformまたはconstantとしてシェーダに渡します。動 的な値の再計算は、シェーダでは非常に負荷が大きくなる可能性があります。 分岐命令は注意して使う シェーダ内での分岐は推奨されません。3Dグラフィックスプロセッサ上で並列に演算を実行する能力 が低下する可能性があるためです(OpenGL ES 3.0対応デバイスでは、それほど深刻ではありません が)。 まったく分岐命令を使わなければ、アプリケーションは高い性能を発揮できるでしょう。たとえば、 条件に応じた多くの部分から成る大規模なシェーダよりも、特定のレンダリング処理に特化した、小 規模なものにしてください。シェーダ内の分岐の数を減らすか、作成するシェーダの数を増やすかは トレードオフです。さまざまなオプションをテストし、最も高速なものを選びます。 シェーダで分岐を使用しなければならない場合は、次の推奨事項に従ってください。 ● ベストプラクティス: シェーダのコンパイル時にわかっているconstantに対して分岐する。 ● 許容: uniform変数に対して分岐する。 ● 速度低下の可能性:シェーダ内で計算された値に対して分岐する。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 108 シェーダのベストプラクティス シェーダ内での計算の代わりにuniformまたはconstantを使用する ループをなくす ループを展開するか、ベクトルを使用して演算を実行することによって、多くのループをなくすこと ができます。たとえば、次のコードは非常に非効率的です。 int i; float f; vec4 v; for(i = 0; i < 4; i++) v[i] += f; 同じ演算を、コンポーネントレベルの加算を直接使用して実行できます。 float f; vec4 v; v += f; ループを排除できない場合は、ループに定数の上限を設けて動的な分岐を避けるのが良いでしょう。 シェーダにおける配列インデックスの計算を避ける シェーダ内で計算されるインデックスを使用すると、constantまたはuniformの配列インデックスを使 用するよりもコストが高くつきます。通常uniform配列へのアクセスは一時配列へのアクセスと比べ てコストはかかりません。 動的なテクスチャ検索に注意する 動的なテクスチャ検索は、従属テクスチャ読み込み とも呼ばれ、フラグメントシェーダが、シェー ダに渡された、修正されていないテクスチャ座標を使用するのではなく、テクスチャ座標を計算する 場合に発生します。従属テクスチャリードはOpenGL ES 3.0対応のハードウェアではパフォーマンス上 の負担なしでサポートされます。他のデバイス上の従属リードでは、テクセルデータの読み込みに遅 延が生じ、パフォーマンスが低下する可能性があります。シェーダが従属テクスチャ読み込みを行わ ない場合は、グラフィックスハードウェアがシェーダの実行前にテクセルデータを事前に取得し、メ モリアクセスの待ち時間を感じさせない可能性があります。 リスト 10-7は、新しいテクスチャ座標を計算するフラグメントシェーダを示しています。この例の計 算は、頂点シェーダでは簡単に実行できます。計算を頂点シェーダに移動し、頂点シェーダが計算し たテクスチャ座標を直接使用することで、従属テクスチャ読み込みを避けることができます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 109 シェーダのベストプラクティス プログラマブルブレンディング用にフレームバッファのデータを取得する 注意: わかりにくいかもしれませんが、テクスチャ座標に関する計算はどれも、従属テクス チャ読み込みとしてカウントされます。たとえば、テクスチャ座標の複数のセットを1つの varyingパラメータに包み、スウィズルコマンドを使用してテクスチャ座標を抽出しても、 従属テクスチャ読み込みが発生します。 リスト 10-7 従属テクスチャ読み込み varying vec2 vTexCoord; uniform sampler2D textureSampler; void main() { vec2 modifiedTexCoord = vec2(1.0 - vTexCoord.x, 1.0 - vTexCoord.y); gl_FragColor = texture2D(textureSampler, modifiedTexCoord); } プログラマブルブレンディング用にフレームバッファのデー タを取得する OpenGLやOpenGL ESの従来の実装には、固定機能ブレンディングステージがありました(図 10-1を参 照)。描画関数を呼び出す前に、ブレンディング操作を、取りうるパラメータとして固定された集合 の中から指定します。フラグメントシェーダがピクセルの色データを出力した後、OpenGL ESのブレ ンディングステージは、デスティネーションフレームバッファの対応するピクセルに当たる色データ を取得し、指定されたブレンディング操作を施して2つを混ぜ合わせ、最終的な色として出力します。 図 10-1 従来型の固定機能ブレンディング 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 110 シェーダのベストプラクティス プログラマブルブレンディング用にフレームバッファのデータを取得する iOS 6.0以降、EXT_shader_framebuffer_fetch拡張機能を使って、プログラマブルブレンディングそ の他の効果を実装できるようになりました。元の色をOpenGLに供給してブレンドするのではなく、 フラグメントシェーダ側で、処理するべきフラグメントに対応するデスティネーションフレームバッ ファの内容を読み込みます。したがってフラグメントシェーダは、どのようなアルゴリズムでも自由 に適用して、最終的な色を決めることができるのです(図 10-2を参照)。 図 10-2 プログラマブルブレンディングとフレームバッファのデータ取得 この拡張機能により、各種の高度なレンダリング技法が使えるようになります。 ● ● ● 追加のブレンディングモード。色を混合する独自のGLSL ES関数を定義することにより、OpenGL ES固定機能ブレンディングステージでは不可能だったブレンディングモードを実装できます。リ スト 10-8 (112 ページ)に、OverlayおよびDifferenceという、各種グラフィックスソフトウェアに よくあるブレンディングモードの実装例を示します。 効果の後処理。シーンのレンダリング後、フラグメントシェーダで、現在のフラグメント色を取 得し、変換を施して新しい色を出力する、という方法で全画面を占める四角形を描画できます。 リスト 10-9 (113 ページ)に示すシェーダは、この方式により、シーンをグレイスケールに変換 します。 色以外のフラグメントに対する操作。フレームバッファには色以外のデータを収容することもあ ります。たとえば遅延シェーディングアルゴリズムでは、複数のレンダターゲットを使って、深 度や法線の情報を格納します。フラグメントシェーダは、あるレンダターゲットからこのような データを取得し、別のレンダターゲットに色を出力できます。 こういった効果は、フレームバッファを取得する拡張機能がなくても実現できます。たとえばグレー スケール変換は、シーンをテクスチャにレンダリングし、このテクスチャと、テクセル色をグレース ケールに変換するフラグメントシェーダを使って、全画面を占める四角形を描画すればよいのです。 しかしこの拡張機能を使えば、多くの場合、性能を改善できます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 111 シェーダのベストプラクティス プログラマブルブレンディング用にフレームバッファのデータを取得する この機能を有効にするためには、フラグメントシェーダに、EXT_shader_framebuffer_fetch拡張 機能を使う旨の宣言が必要です(リスト 10-8、リスト 10-9を参照)。実装コードの書き方は、OpenGL ES Shading Language(GLSL ES)の版によって異なります。 フレームバッファを取得する(GLSL ES 1.0の場合) OpenGL ES 2.0やOpenGL ES 3.0であっても、「#version 300 es」という記述子を使わない場合は、組 み込み変数gl_FragColorおよびgl_LastFragDataを、フラグメントシェーダの出力用、フレーム バッファの読み取り用としてそれぞれ使います(リスト 10-8を参照)。 リスト 10-8 プログラマブルブレンディングをおこなうフラグメントシェーダの、GLSL ES 1.0による実装例 #extension GL_EXT_shader_framebuffer_fetch : require #define kBlendModeDifference 1 #define kBlendModeOverlay 2 #define BlendOverlay(a, b) ( (b<0.5) ? (2.0*b*a) : (1.0-2.0*(1.0-a)*(1.0-b)) ) uniform int blendMode; varying lowp vec4 sourceColor; void main() { lowp vec4 destColor = gl_LastFragData[0]; if (blendMode == kBlendModeDifference) { gl_FragColor = abs( destColor - sourceColor ); } else if (blendMode == kBlendModeOverlay) { gl_FragColor.r = BlendOverlay(sourceColor.r, destColor.r); gl_FragColor.g = BlendOverlay(sourceColor.g, destColor.g); gl_FragColor.b = BlendOverlay(sourceColor.b, destColor.b); gl_FragColor.a = sourceColor.a; } else { // normal blending gl_FragColor = sourceColor; } } 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 112 シェーダのベストプラクティス 頂点シェーダで、より大容量のメモリバッファとしてテクスチャを使う フレームバッファを取得する(GLSL ES 3.0の場合) GLSL ES 3.0では、修飾子outをつけて宣言したユーザ定義変数を、フラグメントシェーダの出力用と して使えます。修飾子inoutをつければ、フラグメントシェーダの実行時に、フレームバッファの データが収容されるようになります。リスト 10-9に、inout変数を使って、グレースケール後処理技 法を実装した例を示します。 リスト 10-9 色の後処理を施すフラグメントシェーダの、GLSL ES 3.0による実装例 #version 300 es #extension GL_EXT_shader_framebuffer_fetch : require layout(location = 0) inout lowp vec4 destColor; void main() { lowp float luminance = dot(vec3(0.3, 0.59, 0.11), destColor.rgb); destColor.rgb = vec3(luminance); } 頂点シェーダで、より大容量のメモリバッファとしてテクス チャを使う iOS 7.0以降、頂点シェーダは、現在バインドされているテクスチャユニットを読み込めるようになり ました。この技法により、頂点処理の際、より大容量のメモリバッファにアクセスできるので、高度 なレンダリング技法を高性能で実行できます。適用可能な技法として、たとえば次のようなものがあ ります。 ● ● 変位マッピング。既定の頂点位置でメッシュを描画した後、頂点シェーダのテクスチャから各頂 点を読み込んで位置を変えます。リスト 10-10に、この技法を使って、グレースケールの高さマッ プテクスチャから3次元ジオメトリを生成する例を示します。 インスタンス描画法。“インスタンス描画法により描画関数の呼び出し回数を減らす” (76 ペー ジ)で説明したように、よく似たオブジェクトが多数並んだシーンをレンダリングする際、CPU のオーバーヘッドを大幅に削減できます。しかし、インスタンスごとに異なる情報を頂点シェー ダに供給する方法は、判断が難しいかも知れません。テクスチャには多くのインスタンスに関す るさまざまな情報を格納できます。たとえば広大な都市景観を、単なる立方体を記述するだけの 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 113 シェーダのベストプラクティス 頂点シェーダで、より大容量のメモリバッファとしてテクスチャを使う 頂点データから、数百ものインスタンスを描画する、という方法でレンダリングできます。頂点 シェーダはgl_InstanceID変数を使い、テクスチャからサンプリングして、それぞれのインスタ ンス(建物)に適用する変換行列、色、テクスチャ座標オフセット、高さなどを求めます。 リスト 10-10 高さマップからレンダリングする頂点シェーダ attribute vec2 xzPos; uniform mat4 modelViewProjectionMatrix; uniform sampler2D heightMap; void main() { // Use the vertex X and Z values to look up a Y value in the texture. vec4 position = texture2D(heightMap, xzPos); // Put the X and Z values into their places in the position vector. position.xz = xzPos; // Transform the position vector from model to clip space. gl_Position = modelViewProjectionMatrix * position; } (OpenGL ES 3.0で導入された)ユニフォーム配列やユニフォームバッファオブジェクトを使って、大 量のデータを頂点シェーダに供給することもできますが、頂点テクスチャを使う方法にも状況によっ ては長所があります。テクスチャには、ユニフォーム配列やユニフォームバッファオブジェクトより も多くのデータを格納でき、また、ラッピングやフィルタリングの機能により、データを補間するこ とも可能です。さらに、テクスチャにレンダリングすることにより、この後の頂点処理ステージで使 うデータをGPUで生成できる、という利点があります。 デバイス上で、頂点テクスチャのサンプリングが可能かどうか(可能な場合は頂点シェーダが使える テクスチャユニット数)、実行時にMAX_VERTEX_TEXTURE_IMAGE_UNITSの制限値を確認してくださ い(詳しくは“OpenGL ESの機能の検証” (16 ページ)を参照)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 114 並列処理とOpenGL ES 並列処理とは、コンピューティングにおいては通常、複数のプロセッサ上で同時にタスクを実行する ことを指します。作業を並行して実行することで、タスクがより短時間で完了し、ユーザから見たア プリケーションの応答性が高まります。しっかりした設計のOpenGL ESアプリケーションには、具体 的な形の並列処理、すなわちCPU上でのアプリケーションの処理とGPU上でのOpenGL ESの処理の並列 処理が見られます。“OpenGL ES設計ガイドライン” (52 ページ)で紹介する技法の多くは、特に、 CPUとGPUの間での優れた並列処理を実現するOpenGLアプリケーションの作成を狙いとしています。 並列処理アプリケーションを設計するということは、作業をサブタスクに分解し、並行して安全に実 行できるタスクと、順番に実行しなければならないタスクを特定することを意味します。つまり、ほ かのタスクが使用するリソース、またはほかのタスクから返された結果のどちらかに依存するタスク を特定することを意味します。 iOSにおける各プロセスは、1つ以上のスレッドで構成されています。スレッドとは、プロセスのため のコードを実行する、実行の流れのことです。Appleは、従来のスレッドと、Grand Central Dispatch (GCD)と呼ばれる機能の両方を提供します。Grand Central Dispatchを使用すると、スレッドを手動 で管理せずにタスクをサブタスクに分解することができます。Grand Central Dispatchは、デバイス上 で利用可能なコア数に基づいてスレッドを割り当て、それらのスレッドに対して自動的にタスクのス ケジューリングを行います。 より高いレベルでは、Cocoa TouchがNSOperationとNSOperationQueueを提供して、作業単位の作成 とスケジューリングのためのObjective-C抽象化を可能にしています。 この章ではこれらのテクノロジーは詳しく取り上げません。OpenGL ESアプリケーションに並列処理 を追加する方法を検討する前に、『Concurrency Programming Guide 』をお読みください。スレッドを 手動で管理する場合は、『Threading Programming Guide 』もお読みください。使用するテクニックに 関係なく、マルチスレッドシステム上でOpenGL ESを呼び出す場合に追加の制限があります。この章 は、マルチスレッドによってOpenGL ESアプリケーションのパフォーマンスが向上する場合、OpenGL ES がマルチスレッドアプリケーション課す制限、そしてOpenGL ESアプリケーションに並列処理を実装 するために使用できる一般的な設計方法を理解するのに役立ちます。 並列処理のメリットがあるかどうか判断する マルチスレッドアプリケーションの作成では、アプリケーションの設計、実装、テストにおいてかな りの労力が必要です。スレッドもアプリケーションに複雑さとオーバーヘッドを追加する要素です。 アプリケーションはワーカースレッドに渡すことができるようデータをコピーする必要のある場合が 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 115 並列処理とOpenGL ES OpenGL ESは各コンテキストを単一スレッドに制限 あり、複数のスレッドは同一リソースへのアクセスを同期する必要の生じる場合があります。OpenGL ESアプリケーションに並列処理を実装しようとする前に、“OpenGL ES設計ガイドライン” (52 ペー ジ)で説明するテクニックを使用して、まず単一スレッド環境でOpenGL ESコードを最適化します。 最初は、CPUとGPUの効率的な並列処理の達成に焦点を合わせ、その後で並列プログラミングによっ てパフォーマンスを向上できるかどうかについて評価します。 並列処理に適した候補には、次に示す特性のどちらか、またはその両方が備わっています。 ● ● アプリケーションが、OpenGL ESのレンダリングに依存しない多くのタスクをCPU上で実行する。 たとえば、ゲームは、ゲームの世界をシミュレートし、コンピュータに操られる敵の人工知能を 計算し、サウンドを再生します。このシナリオでは、タスクの多くがOpenGL ESの描画コードに 依存していないため、並列処理を活用できます。 アプリケーションのプロファイルを調べた結果、OpenGL ESのレンダリングコードがCPUで多くの 時間を費やしていることがわかった。このシナリオでは、アプリケーションがGPUに十分な速さ でコマンドを送信できていないため、GPUはアイドル状態です。CPUに結び付けられているコー ドがすでに最適化されている場合は、並行して実行されるタスクへと作業内容を分割することに より、アプリケーションのパフォーマンスを向上できる可能性があります。 アプリケーションがGPUを待機してブロックされている場合で、OpenGL ESによる描画と並行して実 行可能な作業がない場合、そのアプリケーションは並列処理に適した候補ではありません。CPUとGPU の両方がアイドル状態の場合、OpenGL ESのニーズはおそらく、それ以上のチューニングの必要がな いほどシンプルです。 OpenGL ESは各コンテキストを単一スレッドに制限 iOSにおけるスレッドにはそれぞれ、単一で現在のOpenGL ESレンダリングコンテキストがあります。 アプリケーションがOpenGL ES関数を呼び出すごとに、OpenGL ESは現在のスレッドに関連付けられて いるコンテキストを暗黙的に参照し、そのコンテキストに関連付けられている状態またはオブジェク トを修正します。 OpenGL ESは再入可能ではありません。複数のスレッドに関連付けられている同じコンテキストを同 時に修正する場合、結果は予測できません。アプリケーションがクラッシュする可能性もあれば、レ ンダリングが正常に行われない可能性もあります。何らかの理由で複数のスレッドが同じコンテキス トをターゲットとするよう設定することにした場合は、そのコンテキストに対するすべてのOpenGL ES 呼び出しにミューテックスを配置して、スレッドを同期させなければなりません。ブロック状態にな るOpenGL ESコマンド(glFinishなど)は、スレッドを同期しません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 116 並列処理とOpenGL ES OpenGL ESアプリケーションに並列処理を実装する方法 Grand Central DispatchおよびNSOperationQueueオブジェクトは、選択したスレッド上でタスクを実 行することができます。これらは、そのタスク専用のスレッドを作成する場合もあれば、既存のス レッドを再利用する場合もあります。しかし、どちらの場合でも、どのスレッドがタスクを実行する か保証できません。これは、OpenGL ESアプリケーションにとっては、次の意味を持ちます。 ● 各タスクは、OpenGL ESコマンドを実行する前にコンテキストを設定しなければならない。 ● 同じコンテキストを評価する2つのタスクを自動に実行することはできない。 ● 各タスクは、終了する前にスレッドのコンテキストを消去する必要がある。 OpenGL ESアプリケーションに並列処理を実装する方法 並列処理が可能なOpenGL ESアプリケーションは、OpenGL ESがより多くの作業をGPUに提供できるよ う、CPUの並列処理に焦点を合わせるべきです。OpenGL ESアプリケーションに並列処理を実装する ときの推奨事項を次に示します。 ● ● ● ● アプリケーションを、同時に実行可能なOpenGL ESのタスクとOpenGL ES以外のタスクに分解す る。OpenGL ESの描画コードは単一のタスクとして実行されます。そのため、OpenGL ESの描画 コードは単一のスレッドで実行されます。この方法は、アプリケーションに大量のCPU処理を必 要とするほかのタスクがある場合に最も有効です。 処理性能のプロファイリングにより、OpenGL内でかなりのCPU時間を費やしていることが分かっ た場合は、OpenGL ESコンテキストのマルチスレッド処理を有効にして、処理の一部を別のスレッ ドに移動してください。この方法には簡単であるという利点があります。マルチスレッドを有効 にするコードは1行だけで済みます。“マルチスレッド化したOpenGL ES” (118 ページ)を参照し てください。 アプリケーションがOpenGL ESに送るデータの準備に大量のCPU時間を費やす場合は、レンダリン グデータを準備するタスクと、レンダリングコマンドをOpenGL ESに送信するタスクとに作業を 分割することができます。“ワーカータスクにおけるOpenGL ES演算の実行” (118 ページ)を参照 してください。 同時にレンダリング可能な複数のシーンがある場合、または複数のコンテキストで実行可能な作 業がある場合、アプリケーションは複数のタスクを作成し、1つのタスクに1つのOpenGL ESコン テキストを設定します。複数のコンテキストが同じアートアセットにアクセスする場合は、 sharegroupを使用して、コンテキスト間でOpenGL ESオブジェクトを共有します。“複数のOpenGL ES コンテキストの使用” (119 ページ)を参照してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 117 並列処理とOpenGL ES マルチスレッド化したOpenGL ES マルチスレッド化したOpenGL ES アプリケーションがOpenGL ESの関数を呼び出すと、OpenGL ESは渡されたパラメータを、ハードウェ アが理解できる形式に変換します。このコマンド処理に要する時間は、入力がはじめからハードウェ アに合った形式か否かによって異なりますが、いずれにしてもオーバーヘッドはあります。 OpenGL ES内の計算処理に多くの時間を費やしている場合、適切なデータ形式を既に採用しているの であれば、OpenGL ESのコンテキストをマルチスレッド化することにより、相応の利益が得られるで しょう。マルチスレッド化したOpenGL ESコンテキストは、自動的にワーカースレッドを生成し、計 算の一部をこのスレッドに委ねます。マルチコアデバイスの場合、マルチスレッド化すると、CPU側 で実行するOpenGL ES内部の計算はアプリケーションと並列におこなわれ、性能向上につながります。 ただし、同期型の関数はこれまでどおり、呼び出し元スレッドをブロックします。 OpenGL ESをマルチスレッド化するためには、EAGLContextオブジェクトのmultiThreadedプロパティ をYESと設定します。 注意: マルチスレッド実行の有効/無効を切り替えると、OpenGL ESはそれまでのコマンドを フラッシュしてしまうほか、追加のスレッドを用意するためのオーバーヘッドも発生しま す。切り替えはレンダリングループ内ではなく、初期化関数でおこなうようにしてくださ い。 マルチスレッド化した場合、OpenGL ESはパラメータを複製し、ワーカースレッドに転送しなければ なりません。このようなオーバーヘッドがあるので、マルチスレッド化の有無により、どちらが高速 に処理できるか必ずテストするようにしてください。このオーバーヘッドは、OpenGL ESの使い方を 工夫してアプリケーションに実装することにより、最小限に抑えることができます。以下、これにつ いて詳しく説明します。 ワーカータスクにおけるOpenGL ES演算の実行 一部のアプリケーションは、OpenGL ESにデータを渡す前に、そのデータについて大量の演算を実行 します。たとえば、新たなジオメトリを作成したり、既存のジオメトリをアニメーション化したりす るアプリケーションが該当します。可能な場合は、そのような演算はOpenGL ES内部で実行します。 これは、GPU内部で利用できるすぐれた並列処理を活かしており、アプリケーションとOpenGL ESの 間における結果のコピーに伴うオーバーヘッドを削減します。 図 6-6 (60 ページ)に示す手法は、OpenGL ESオブジェクトの更新と、それらオブジェクトを使用す るレンダリングコマンドの実行を交互に実行するものです。OpenGL ESはGPU上でレンダリングを実 行し、並行してCPU上ではアプリケーションの更新を実行します。CPU上で実行される演算が、GPU 上の演算よりも処理時間がかかってしまうと、それだけGPUのアイドル時間が長くなります。このよ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 118 並列処理とOpenGL ES 複数のOpenGL ESコンテキストの使用 うな状況では、複数CPUを搭載するシステムの並列処理を利用できる場合があります。OpenGL ESレ ンダリングコードを別の演算や処理タスクに分割して、それらを並行して実行します。1つ目のタス クは、2つ目のタスクが使用し、OpenGLに送信するデータを生成します。 最高のパフォーマンスを得るため、タスク間におけるデータのコピーを避けます。1つのタスクでデー タを計算し、計算結果をほかのタスクの頂点バッファオブジェクトにコピーする代わりに、セット アップコードで頂点バッファオブジェクトをマッピングし、ポインタを直接ワーカータスクに渡しま す。 修正タスクをサブタスクへとさらに分解できる場合は、さらにメリットの得られる可能性がありま す。たとえば、2つ以上の頂点バッファオブジェクトがあると仮定し、描画コマンドを送信する前に それぞれのオブジェクトを更新する必要があるとします。各オブジェクトは、ほかのオブジェクトに 関係なく計算し直すことが可能です。このシナリオでは、各バッファに対する修正が演算となり、 NSOperationQueueオブジェクトを使用して作業を管理します。 1. 現在のコンテキストを設定します。 2. 最初のバッファをマッピングします。 3. そのバッファを埋めることがタスクとなるNSOperationオブジェクトを作成します。 4. その演算を演算キューに追加します。 5. ほかのバッファについても手順2から手順4を実行します。 6. 演算キューに対してwaitUntilAllOperationsAreFinishedを呼び出します。 7. バッファのマッピングを解除します。 8. レンダリングコマンドを実行します。 複数のOpenGL ESコンテキストの使用 複数のコンテキストを使用する場合の一般的な手法とは、各コンテキストが別々のスレッドで実行さ れている状態で、ほかのコンテキストがOpenGL ESオブジェクトを使用している間にOpenGL ESオブ ジェクトを更新する1つのコンテキストを持つことです。各コンテキストは別々のスレッド上で実行 されるため、コンテキストのアクションがほかのコンテキストにブロックされることはめったにあり ません。これを実装するには、アプリケーションにおいて2つのコンテキストと2つのスレッドを作成 し、各スレッドが1つのコンテキストを制御するようにします。さらに、2つ目のスレッド上でアプリ ケーションが更新しようとするOpenGL ESオブジェクトは、二重にバッファに格納しなければなりま せん。これは、オブジェクトを使用する側のスレッドが、もう一方のスレッドがOpenGL ESオブジェ クトを修正している間は、OpenGL ESオブジェクトにアクセスできない可能性があるからです。コン テキスト間の変更を同期するプロセスは、“EAGL sharegroupはコンテキストのOpenGL ESオブジェクト を管理” (21 ページ)で詳しく説明しています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 119 並列処理とOpenGL ES OpenGL ESアプリケーションのスレッド化に関するガイドライン GLKTextureLoaderクラスは、この方法を実装して、テクスチャデータの非同期読み込みを実行しま す。(“GLKitフレームワークを使用したテクスチャデータの読み込み” (97 ページ)を参照してくだ さい。) OpenGL ESアプリケーションのスレッド化に関するガイドライ ン OpenGL ESを使用するアプリケーションでのスレッド化を成功させるには、次のガイドラインに従っ てください。 ● 1つのコンテキストにつき1つのスレッドだけを使用する。特定のコンテキストのためのOpenGL ES コマンドはスレッドセーフではありません。決して、複数のスレッドが単一のコンテキストに同 時にアクセスしないようにしてください。 ● Grand Central Dispatchを使用するときは、専用のシリアルキューを使用してOpenGL ESにコマンド をディスパッチする。このキューは、従来のミューテックスパターンを置き換えるために使用で きます。 ● 現在のコンテキストを追跡する。スレッドを切り替えるとき、何も考えずにコンテキストを切り 替えることは簡単ですが、それによりグラフィックスコマンドの実行に予期しない影響が生じま す。新たに作成されたスレッドに切り替えるときは現在のコンテキストを設定し、元のスレッド を離れる前に現在のコンテキストを消去しておかなければなりません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 120 OpenGL ES 3.0への移行 OpenGL ES 3.0はOpenGL ES 2.0の仕様を包含しているので、比較的容易に移行できます。OpenGL ES 2.0 用のコードのままでも、デバイスがOpenGL ES 3.0に対応していれば、リソースの制限が緩和されま す。また、アプリケーションの設計によっては、OpenGL ES 3.0で正式に採用された機能も、追加機能 として利用できます。 OpenGL ES 3.0に移行するためのチェックリスト OpenGL ES 3.0を利用するために、アプリケーションに施す修正事項を示します。 1. OpenGL ESコンテキストを生成し(“OpenGL ESコンテキストの設定” (19 ページ)を参照)、API の版を表す定数として、OpenGL ES 3.0に対応するものを指定します。 EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; OpenGL ES 3.0に未対応のデバイスでも動作するようにしたい場合は、リスト 2-1 (20 ページ)に 従い、必要ならばOpenGL ES 2.0に「降格」して動作するようにしてください。 2. OpenGL ES 3.0のAPIを使うソースファイルには、対応するヘッダファイルをインクルードあるいは インポートする旨の記述を追加します。 #import <OpenGLES/ES3/gl.h> #import <OpenGLES/ES3/glext.h> 3. OpenGL ES 2.0の拡張機能のうち、OpenGL ES 3.0の仕様に取り込まれ、あるいは変更されたものを 使っているコードは、以下の“拡張機能のコードを改訂する”に従って改訂してください。 4. (必要な場合のみ)シェーダプログラムはそのままで、OpenGL ES 2.0と3.0のどちらでも使えま す。ただし、新機能を利用するため、GLSL ES 3.0で記述し直す場合は、“OpenGL ESシェーディン グ言語の第3.0版に移行する” (124 ページ)に示した注意事項を頭に入れておいてください。 5. OpenGL ES 3.0に対応したデバイス上で、アプリケーションが正しく動作するかどうか確認しま す。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 121 OpenGL ES 3.0への移行 拡張機能のコードを改訂する 拡張機能のコードを改訂する OpenGL ES 3.0はOpenGL ES 2.0の仕様を包含しているので、OpenGL ES 2.0のコア機能しか使っていない アプリケーションは、そのままOpenGL ES 3.0のコンテキストでも動作します。しかし、OpenGL ES 2.0 の拡張機能を使っているアプリケーションもあります。OpenGL ES 3.0でも同じ機能が使えますが、多 少のコード変更を要するかも知れません。 拡張機能の接尾辞を除去する 以下に挙げるOpenGL ES 2.0の拡張機能のAPIは、OpenGL ES 3.0のコア仕様に組み込まれています。た だし、OpenGL ES 3.0のコンテキストで利用するためには、関数や定数の名前から、拡張機能であるこ とを表す接尾辞を除去する必要があります。たとえば関数glMapBufferRangeEXTの名前は glMapBufferRangeに、定数DEPTH_COMPONENT24_OES(glRenderbufferStorage関数の internalformat引数として使用)の名前はDEPTH_COMPONENT24となります。 ● OES_depth24 ● OES_element_index_uint ● OES_fbo_render_mipmap ● OES_rgb8_rgba8 ● OES_texture_half_float_linear ● OES_vertex_array_object ● EXT_blend_minmax ● EXT_draw_instanced ● EXT_instanced_arrays ● EXT_map_buffer_range ● EXT_occlusion_query_boolean ● EXT_texture_storage ● APPLE_sync ● APPLE_texture_max_level 拡張機能APIの使い方を修正する OpenGL ES 2.0の拡張機能の中には、OpenGL ES 3.0のコア仕様に取り込まれたとき、APIの定義が変わっ ているものがあります。その場合は以下のように修正してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 122 OpenGL ES 3.0への移行 拡張機能のコードを改訂する テクスチャの形式を処理する OES_depth_texture、OES_packed_depth_stencil、OES_texture_float、OES_texture_half_float、EXT_texture_rg、EXT_sRGBの各拡張機能には、glTexImage系の関数の引数 internalformatおよびtypeに指定する定数が定義されています。ここで定義されている拡張機能 は、OpenGL ES 3.0ではコアAPIになっていますが、いくつか注意点があります。 ● glTexImage関数は、大きさを明示しないinternalformat定数が使えません。次のように、大き さを明示した定数を使ってください。 // Replace this OpenGL ES 2.0 code: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_HALF_FLOAT_OES, data); // With this OpenGL ES 3.0 code: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, data); ● ● ● OpenGL ES 3.0には、LUMINANCEデータやLUMINANCE_ALPHAデータに用いる、浮動小数点型または 半精度浮動小数点型の形式が定義されていません。代わりに対応するRED形式またはRG形式を使っ てください。 深度や深度/ステンシルのテクスチャサンプラが返すベクトルは、OpenGL ES 3.0では、先頭3つの 要素にある深度値を繰り返さないようになりました。このようなテクスチャからサンプリングす るシェーダコードでは、最初の(.r)要素のみ使うようにしてください。 sRGB形式を指定できるのは引数internalformatだけになりました。sRGBテクスチャの形式を表 す引数には、GL_RGBまたはGL_RGBAを指定してください。 あるいは、glTexImage系の関数を、すべて対応するglTexStorage系の関数に置き換える、という方 法もあります。テクスチャストレージ関数は、OpenGL ES 3.0ではコアAPI、OpenGL ES 1.1および2.0で はEXT_texture_storage拡張機能として提供されています。この関数族にはほかにも、glTexStorage 関数を1度呼び出すだけで、状態不変のテクスチャオブジェクトを完全に指定できる、という利点が あります。整合性の検査やメモリ割り当てを真っ先におこなうので、ミップマップレベルの欠落や、 一貫しないキューブマップフェイスにより、不完全なテクスチャオブジェクトが生じることはありま せん。 バッファオブジェクトをクライアント側のメモリにマップする OES_mapbuffer拡張機能には、バッファオブジェクトのデータストレージ全体をクライアント側のメ モリにマップする、glMapBuffer関数が定義されています。OpenGL ES 3.0にはこれに代わる glMapBufferRange関数があり、機能が強化されています。バッファオブジェクトのデータストレー 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 123 OpenGL ES 3.0への移行 OpenGL ESシェーディング言語の第3.0版に移行する ジの一部のみをマップすることができ、さらに非同期にマッピングすることも可能です。 glMapBufferRange関数は、EXT_map_buffer_range拡張機能を組み込めば、OpenGL ES 1.1および2.0の コンテキストでも使えます。 フレームバッファを破棄する OpenGL ES 3.0のglInvalidateFramebuffer関数は、EXT_discard_framebuffer機能拡張の glDiscardFramebufferEXT関数に代わるものです。引数や動作はどちらも同じです。 マルチサンプル機能を利用する OpenGL ES 3.0にはAPPLE_framebuffer_multisampleの拡張機能がすべて組み込まれていますが、 glResolveMultisampleFramebufferAPPLE関数は例外です。これに代わるglBlitFramebuffer関数 は、フレームバッファの複製機能を追加して強化したものです。マルチサンプリングバッファをリゾ ルブするには、読み込み用と描画用のフレームバッファを設定(“マルチサンプル機能の使用による 画像品質の向上” (43 ページ))した上で、glBlitFramebuffer関数で、読み込みフレームバッファ 全体を描画フレームバッファに複製してください。 glBlitFramebuffer(0,0,w,h, 0,0,w,h, GL_COLOR_BUFFER_BIT, GL_NEAREST); 他の拡張機能も、多くはOpenGL ES 3.0でもそのまま利用できる iOSデバイスのグラフィックスハードウェアに搭載された重要な機能の中には、OpenGL ES 3.0のコア 仕様に入っていないものもありますが、拡張機能として使うことはなお可能です。実際に利用する際 には、“OpenGL ESの機能の検証” (16 ページ)に示した手順で、拡張機能の有無を確認してください (どのデバイスでどの機能が使えるか、については『iOS Device Compatibility Reference 』も参照)。 OpenGL ES 2.0の拡張機能として開発されたコードのうち、OpenGL ES 3.0の拡張機能としても提供され ているものは、OpenGL ES 3.0のコンテキストでもそのまま動作します。ただし、頂点/フラグメント シェーダ言語に改変を施す拡張機能には、ほかにも注意事項があります(次の節を参照)。 OpenGL ESシェーディング言語の第3.0版に移行する OpenGL ES 3.0には新しい版のOpenGL ESシェーディング言語(GLSL ES)が付属しています。OpenGL ES 3.0のコンテキストでは、GLSL ESの第1.0版、第3.0版のどちらで記述したシェーダプログラムも動作 します。ただし、ユニフォームブロック、32ビット整数、追加の整数演算などの新機能を使う場合 は、第3.0版のシェーダ(ソースコードに「#version 300 es」という記述子があるもの)が必要で す。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 124 OpenGL ES 3.0への移行 OpenGL ESシェーディング言語の第3.0版に移行する GLSL ESの第1.0版と第3.0版では、言語規約がいくつか変わっています。その結果、OpenGL ES 3.0とデ スクトップ向けOpenGL ES 3.3以降で、シェーダのソースコードの可搬性が向上していますが、既存の コードをGLSL ES 3.0に移植するためには多少の変更が必要です。 ● ● ● ● ● 修飾子attributeおよびvaryingが、GLSL ES 3.0ではinとoutに変わりました。頂点シェーダで は、頂点属性にin修飾子、可変の出力にout修飾子を指定します。フラグメントシェーダでは、 可変の入力にin修飾子を指定します。 GLSL ES 3.0では、組み込みのフラグメント出力変数であるgl_FragDataとgl_FragColorがなくな りました。代わりに独自のフラグメント出力変数を、out修飾子つきで宣言してください。 GLSL ES 3.0ではテクスチャサンプリング関数の名前が変わりました。サンプラの種類によらず、 同じテクスチャ関数名になります。たとえば新たに加わったtexture関数は、sampler2D引数で もsamplerCube引数でも使えます(GLSL ES 1.0ではそれぞれ、texture2D、textureCubeという関数 でした)。 GLSL ES 1.0の拡張機能であるEXT_shader_texture_lod、EXT_shadow_samplers、OES_standard_derivativesは、GLSL ESのコア仕様の一部になりました。こういった機能を使っている シェーダをGLSL ES 3.0に移植する場合、対応するGLSL ES 3.0の関数を使ってください。 EXT_shader_framebuffer_fetch拡張機能の動作が変わっています。GLSL ES 3.0では、組み込み フラグメント出力変数であるgl_FragDataとgl_FragColorがなくなったので、シェーダ側でフ ラグメント出力変数を宣言しなければなりません。これに対応して、組み込み変数 gl_LastFragDataも、GLSL ES 3.0のフラグメントシェーダにはありません。代わりに、フラグメ ント出力変数をinout修飾子つきで宣言すれば、シェーダを実行したとき、前回のフラグメント データが格納されます。詳しくは“プログラマブルブレンディング用にフレームバッファのデータ を取得する” (110 ページ)を参照してください。 GLSL ES 3.0の全面的な概要は、『OpenGL ES Shading Language 3.0 Specification 』(OpenGL ES API Registry で入手可能)を参照してください。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 125 Xcodeに付属するOpenGL ES用ツールの概要 XcodeにはOpenGL ESアプリケーションのデバッグ、分析、調整に用いる各種ツールが付属しており、 開発のあらゆる段階で有用です。「FPS Debug Gauge」および「GPU Report」は、Xcode上でアプリ ケーションを実行すれば、常にGPUの動作状況を要約表示するようになっているので、レンダラを設 計、構築する際、性能上の問題があればすぐに分かります。問題点が分かれば、フレームを取り込 み、OpenGL ES Frame Debuggerの画面でレンダリングの問題箇所をしぼり込んだ上で、性能を損なう 原因を取り除くことができます。 Xcode OpenGL ESの機能を有効に活用するためには、Xcodeのデバッグ画面に多少慣れておく必要があ るでしょう。その背景知識については『Xcode Overview 』を参照してください。 「FPS Debug Gauge」および「GPU Report」の使い方 「FPS Debug Gauge」および付随する「GPU Report」(図 B-1を参照)は、アプリケーション実行中、 OpenGL ESの大まかな処理状況を示します。開発中いつもこの表示を気にしていれば、性能上の問題 が生じたときすぐに気がついて、どこに着目して調整すればよいか検討できるでしょう。 図 B-1 「FPS Debug Gauge」および「GPU Report」の画面 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 126 Xcodeに付属するOpenGL ES用ツールの概要 「FPS Debug Gauge」および「GPU Report」の使い方 注意: 「FPS Debug Gauge」や「GPU Report」の機能の中には、表示リンクタイマーに依存 するものがあります。CADisplayLinkクラスやGLKViewControllerクラスを使わずにOpenGL ESのアニメーション表示を実装している場合、ターゲットのフレームレートを基準とした性 能や、正確なCPUフレーム時間情報を表示することはできません。 このツールは次のような表示から成ります。 ● FPS Gauge。アニメーション表示のフレームレート(単位:FPS、frames per second)を、直近の 履歴とともに表示します。このゲージをクリックすると、Xcodeの主エディタにGPUレポートが現 れます。 ● Frames Per Second。アプリケーションが設定したターゲットのフレームレート(多くの場合30 FPSまたは60 FPS)を基準とした、現在のフレームレートを表示します。青の扇型は、直近の読み 取り度数の範囲を表します。 ● Utilization。GPUの処理リソースを3つに分け、アプリケーションがそれぞれをどの位使っている か、3本の棒グラフで示します。グラフィックスハードウェアのうち、性能上のボトルネックに なっている可能性が高い箇所が分かります。 「Tiler」バーはジオメトリ処理リソースの利用率を表します。この値が高ければ、OpenGL ESのパ イプラインのうち、頂点やプリミティブの処理ステージが、性能上のボトルネックになっている 可能性があります。頂点シェーダの処理効率が悪い、各フレームに描画する頂点やプリミティブ の数が多すぎる、などの原因が考えられます。 「Renderer」バーはピクセル処理リソースの利用率を表します。この値が高ければ、OpenGL ESパ イプラインのうち、フラグメントやピクセルの処理ステージが、性能上のボトルネックになって いる可能性があります。フラグメントシェーダの処理効率が悪い、色をブレンドするために処理 するフレームごとのフラグメント数が多すぎる、などの原因が考えられます。 「Device」バーは上記2つを含むGPU全体の利用率を表します。 ● Frame Time。各フレームの処理に要した時間を、CPUとGPUについて示します。CPUとGPUが均衡 を保って並列処理しているかどうかが分かります。 CPUの処理時間が多ければ、処理の一部をGPU側に移すことにより、性能を改善できるかも知れ ません。たとえば各フレームで、よく似たパラメータでglDrawArraysやglDrawElementsを何度 も実行しているならば、インスタンス描画法を導入することにより、CPUのオーバーヘッドを減 らせるかも知れません(詳しくは“インスタンス描画法により描画関数の呼び出し回数を減ら す” (76 ページ)を参照)。 GPU処理時間が多ければ、処理の一部をCPU側に移すことにより、性能を改善できるかも知れま せん。たとえばある描画関数の実行時に、どの頂点やフラグメントでも同じ結果になる計算を シェーダが実行している場合、CPU側で1度だけ計算し、ユニフォーム変数を使ってシェーダに渡 すとよいでしょう(“シェーダ内での計算の代わりにuniformまたはconstantを使用する” (108 ペー ジ)を参照)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 127 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ESのフレームを取り込んで分析する ● Program Performance。フレームを取り込んだ後にのみ現れます(以下の“OpenGL ESのフレーム を取り込んで分析する” (128 ページ)を参照)。取り込んだフレームをレンダリングする際、各 シェーダプログラムが要した時間を、ミリ秒単位と、レンダリングに要した総時間に対する比率 の両方で示します。プログラムのリストを展開表示にすると、描画関数の呼び出し状況と、それ ぞれに応じたレンダリング時間も分かります。リストからプログラムを選択すると、アシスタン トエディタに、対応するシェーダのソースコードが表示されます。また、描画関数に添えられた 矢印アイコンをクリックすると、フレームナビゲータ(以下の“ナビゲータ領域” (132 ページ) を参照)で、該当する関数の呼び出し位置が選択状態になります。 注意: 「Program Performance」ビューが現れるのは、OpenGL ES 3.0に対応したデバイス 上でデバッグしているときに限ります(アプリケーションがOpenGL ES 3.0または2.0の、 どちらのコンテキストを使っていても可)。 アプリケーションの性能調整時には、このグラフが最適化の役に立つでしょう。たとえば、フ レームのレンダリング時間の50%を占めているプログラムは、10%しか占めていないプログラム に比べ、最適化による性能改善効果が高いと考えられます。このビューはシェーダプログラムが 費やすフレーム時間を整理して表示しますが、そのアルゴリズムを工夫することだけが性能改善 の方法というわけではありません。たとえば、負荷の高いシェーダプログラムを使う描画関数の 呼び出し回数を減らしたり、遅いフラグメントシェーダが処理するフラグメントの数を減らした りしても、相応の効果があります。 ● Problems & Solutions。Xcodeがフレームを取り込んで分析した後にのみ現れる領域で(“OpenGL ESのフレームを取り込んで分析する” (128 ページ)を参照)、分析中に見つかった潜在的な問題 と、性能向上のための推奨事項を示します。 取り込んだフレームで、GLSLシェーダプログラムに変更を施す(以下の“シェーダプログラムを編集 する” (135 ページ)を参照)と、「Frame Time」および「Program Performance」のグラフが拡張し、 取り込んだ元のフレームのベースラインレンダリング時間と、編集後のシェーダによる現在のレンダ リング時間の、両方を表示するようになります。 OpenGL ESのフレームを取り込んで分析する アプリケーションがOpenGL ESをどのように使っているか詳しく調べるために、アニメーションのあ るフレームをレンダリングするために使う、OpenGL ESの一連のコマンドを取り込みます。Xcodeには フレームを取り込む手段がいくつか組み込まれています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 128 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ESのフレームを取り込んで分析する ● 手動の取り込み。Xcode上でアプリケーションを実行し、デバッグバーの「カメラ」アイコン(図 B-2を参照)をクリックするか、「Debug」メニュー以下の「Capture OpenGL ES Frame」を実行す る方法です。 図 B-2 「Debug」バーと「Capture OpenGL ES Frame」ボタン 注意: 「Capture OpenGL ES Frame」ボタンは、OpenGL ESフレームワークまたはSprite Kit フレームワークをリンクしたプロジェクトに限り、自動的に現れます。そうでないプロ ジェクトの場合は、アクティブスキームを編集することにより選択できます(「About the Scheme Editing Dialog」を参照)。 ● ● ブレークポイントに到達したときのアクション。ブレークポイントに到達したときのアクション として、「Capture OpenGL ES Frame」を選択する、という方法です。このブレークポイントにデ バッガが到達すると、Xcodeは自動的にフレームを取り込みます(「Setting Breakpoint Actions and Options」を参照)。「OpenGL ES Error」ブレークポイントにこのアクションを設定すれば (「Adding an OpenGL ES Error Breakpoint」を参照)、OpenGL ES Frame Debuggerで、OpenGL ESエ ラーの原因を調査できます。 OpenGL ESイベントマーカ。OpenGL ESのコマンドストリームにイベントマーカを挿入することに より、プログラム的にフレーム取り込みを起動する方法です。マーカの挿入は次のコマンドでお こないます。 glInsertEventMarkerEXT(0, "com.apple.GPUTools.event.debug-frame") OpenGL ESクライアントがこのマーカに到達すると、フレームのレンダリングが終了し、Xcodeは 自動的に、このフレームのレンダリングに使ったコマンド列全体を取り込みます。 Xcodeはフレームを取り込んだ後、OpenGL ES Frame Debuggerの画面を表示します。ここで、フレー ムのレンダリングに用いたOpenGL ESのコマンド列や、OpenGL ESのリソースを調べることができます (“OpenGL ES Frame Debuggerひとめぐり” (131 ページ)を参照)。 さらにXcodeは、アプリケーションがOpenGL ESをどのように使っているか自動的に分析し、レンダラ やシェーダのどの部分を最適化すれば効果が高いか調べます。この機能は「GPU Report」画面の上部 にある「Analyze」ボタンで起動します(図 B-1 (126 ページ)の右上)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 129 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ESのフレームを取り込んで分析する 「Analyze」ボタンを押すと、(まだであれば)Xcodeはフレームを取り込みます。すると、条件をさ まざまに変えて、iOSデバイス上で実験的にレンダリングコードを実行できるようになります。たと えば、レンダリングが遅い原因としてテクスチャの大きさが疑われる場合、アプリケーションがGPU に送り込んだテクスチャデータと、大きさを減らしたテクスチャデータの両方について、取り込んだ OpenGL ESのコマンド列を実行してみるのです。分析が終わると「GPU Report」の「Problems & Solutions」領域には、見つかった問題と、性能向上のための推奨事項が表示されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 130 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり OpenGL ES Frame Debuggerひとめぐり Xcodeはフレームを取り込むと自動的に、OpenGL ESのデバッグに適した形に画面を編成し直します。 「OpenGL ES Frame Debugger」画面は、Xcodeのワークスペースウインドウにあるいくつかの領域に 手を加えたもので、レンダリング処理に関する情報を表示します(以下の図 B-3と図 B-4を参照)。な お、フレームデバッガはインスペクタペインやライブラリペインを使わないので、デバッグ中はXcode のユーティリティ領域を非表示にしても構いません。 図 B-3 フレームデバッガに描画関数呼び出しとリソースの状況が表示されている様子 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 131 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり 図 B-4 フレームデバッガにシェーダプログラムの処理性能や分析結果が表示されている様子 ナビゲータ領域 OpenGL ES Frame Debuggerの画面では、デバッグナビゲータが、OpenGL ESフレームナビゲータに置 き換わります。ここには、取り込んだフレームのレンダリングに用いたOpenGL ESのコマンドが、実 行順、あるいは対応するシェーダプログラムごとに列挙されます。フレームナビゲータの上部にある 「Frame View Options」ポップアップメニューで、表示形式を切り替えることができます。 図 B-5 ナビゲータの「View Frame By」ポップアップメニュー View Frame By Call 取り込んだフレームを、OpenGL ESのコマンド呼び出し順に表示します。どのコマンドにエラーがあ るか特定し、レンダリングの問題点を診断し、処理性能のボトルネックを特定するために有用です。 このモードではコマンドを、アプリケーションが呼び出した順に並べます。エラーの原因になった、 あるいは処理性能の面で問題が起こりうるコマンドの隣に、「エラー」や「警告」のアイコンが現れ ます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 132 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり この一覧に構造を取り入れることもできます。glPushGroupMarkerEXT関数、glPopGroupMarkerEXT 関数で、OpenGL ESコマンドのグループに注釈を加えると、そのグループは「フォルダ」の形で表示 され、必要に応じて展開したり、折りたたんだりできるようになります(詳しくは“情報提供のデバッ グおよびプロファイル用に、OpenGL ESのコードに注釈を付ける” (70 ページ)を参照)。さらに、 OpenGL ESのコマンドを展開して、アプリケーションコードのどの位置でコマンドが発行されたか、 スタックトレースの形で表示することも可能です。 コンテキストメニューで、コマンド名を略称表示するか、どのコマンド/グループ/警告を表示するか、 の選択が可能です。ナビゲータの下部にある「旗」アイコンで、OpenGL ESのコマンドをすべて表示 するか、あるフレームバッファに描画したコマンドのみ表示するか、を切り替えることができます。 リスト中のOpenGL ESコマンドをクリックすると、コマンド並び中の該当する位置に表示が切り替わ り、フレームデバッガの他の領域も表示内容が変わります。接続したデバイスの表示は、その位置ま でコマンドを実行した状態になります。 View Frame By Program 取り込んだフレームをプログラム別に表示すれば、シェーダプログラムや描画コマンドそれぞれが費 やしたGPU時間を分析できます。 プログラムが一覧表示されている中からいずれかを展開すると、当該プログラムが呼び出している シェーダや描画関数それぞれが費やした時間が分かります。また、描画関数の呼び出しが一覧表示さ れている中からいずれかを展開すると、アプリケーションコードのどこで発行したコマンドか、ス タックトレースの形で表示されます。 コンテキストメニューで、消費時間順に並べ替える、総レンダリング時間に対する比率で時間を表示 するなど、表示方法を変更できます。 プログラムやシェーダをクリックすると、対応するGLSLソースコードが主エディタに表示されます。 また、OpenGL ESコマンドをクリックすると、取り込んだフレームの描画コマンド列中、該当する箇 所に移動できます。 注意: 「View Frame By Program」に切り替え可能なのは、OpenGL ES 3.0に対応したデバイス 上でデバッグしているときに限ります(アプリケーションがOpenGL ES 3.0または2.0の、ど ちらのコンテキストを使っていても可)。そうでないデバイスの場合、「Frame View Options」 ポップアップメニューは開けません。 エディタ領域 フレームを取り込んでデバッグする際には、レンダリング対象のフレームバッファを主エディタにプ レビュー表示し、アシスタントエディタ側で、OpenGL ESリソースの状況を調べ、GLSLシェーダプロ グラムを編集することになります。アシスタントエディタは通常、OpenGL ESコンテキストが現在管 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 133 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり 理しているリソースをすべて、グラフィック表示するようになっています(図 B-3 (131 ページ)を 参照)。アシスタントエディタのジャンプバーで、フレームナビゲータで選択したコマンド呼び出し に対応するリソースのみ表示するよう切り替え、あるいは特定のリソースを選択してさらに詳しく調 べることができます。さらに、概要画面に現れているリソースをダブルクリックし、内容を詳しく調 べることも可能です。リソースを選択すると、アシスタントエディタの表示が、その種類に応じた形 式に変わります。 フレームバッファの内容をプレビュー表示する 主エディタには、フレームバッファの内容が、フレームナビゲータで選択している描画コマンドを実 行した状態で表示されます(フレームナビゲータで選択したOpenGL ESコマンドが、状態を設定する glUseProgramなど、描画コマンド以外のものであった場合、直近の描画コマンドを実行した状態に なります)。また、主エディタの上部にあるジャンプバーで、OpenGL ESコマンド列の中を自由にナ ビゲートできます。 図 B-6 フレームバッファ情報ポップオーバー エディタには、描画処理にバインドされている各フレームバッファアタッチメントの内容がプレビュー 表示されます。たとえば3Dレンダリングでは、多くの場合、フレームバッファとともに、色および深 度のアタッチメントを使います(図を参照)。エディタの左下にあるコントロール部品で、表示する フレームバッファアタッチメントを切り替えることができます。フレームバッファアタッチメント名 の左側にある「情報」ボタンを押すと、そのプロパティを表示するポップオーバーが現れます(図 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 134 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり B-6を参照)。また、右側にある「設定」ボタンを押すと現れるポップオーバーで、プレビュー画像 の表示を制御できます。たとえば、深度バッファのZ値がある範囲内の部分をグレースケールでプレ ビュー表示する、といったことが可能です(図 B-7を参照)。 図 B-7 フレームバッファ設定ポップオーバー 各フレームバッファアタッチメントのプレビューでは、現在実行している描画コマンドの効果を、緑 のワイヤーフレームで強調して示すことも可能です(図 B-3 (131 ページ)を参照)。プレビュー画 像のコンテキストメニューで、強調部分をプレビューに表示するか、接続したデバイスに表示する か、切り替えることができます。 シェーダプログラムを編集する アシスタントエディタのジャンプバーやリソース概要画面で、シェーダプログラムを選択すると、当 該プログラムのフラグメントシェーダのGLSLソースコードが表示されます(図 B-8を参照)。フレー ムナビゲータでプログラムを選択すると(“View Frame By Program” (133 ページ)を参照)、主エディ 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 135 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり タにはプログラムのフラグメントシェーダ、アシスタントエディタには頂点シェーダが現れます。フ ラグメントシェーダを表示するエディタはいずれも、ジャンプバーを使って、対応する頂点シェーダ の表示と相互に切り替えることができます。 図 B-8 GLSLシェーダのソースエディタと更新ボタン シェーダのソースコードの各行には、レンダリング時間に占める割合に応じて、右余白に棒が表示さ れます。どの部分に最適化を施せば効果があるか、これで見極めることができます。ある特定の数行 が大部分の時間を占めているようであれば、そこを高速化する手立てを考えてください(シェーダの 性能向上に関するヒントが“シェーダのベストプラクティス” (103 ページ)に載っています)。 エディタ上でシェーダのソースコードに変更を施すことができます。その後、エディタ下部の 「Update」ボタンを押すと(図 B-8 (136 ページ)を参照)、プログラムが再コンパイルされ、取り 込んだフレームでその効果を確認できます。GLSLコンパイラがエラーや警告のメッセージを出した場 合、Xcodeは該当するソースコードに注釈を施します。再コンパイルしたシェーダプログラムはデバ イス上にそのまま残るので、アプリケーションの実行を再開できます。デバッグバーの「Continue」 ボタンを押すと、動作している状態でシェーダの変化が確認可能です。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 136 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり 頂点データを調査する 配列バッファの内容を調査するためには、アシスタントエディタを使うのが便利でしょう(図 B-9を 参照)。OpenGL ESのメモリ上のバッファには形式が定義されていないので、エディタ下部のポップ アップメニューで、表示方法(32ビット整数や浮動小数点数をそのまま表示する、16ビット整数や半 精度浮動小数点数が2つ並んだものとして表示する、など)や、データを列に並べる数を選択できま す。 図 B-9 アシスタントエディタに配列バッファの内容を表示している様子 頂点配列オブジェクト(VAO、Vertex Array Object)には、OpenGL ESのメモリ上にあるデータバッファ と、そこからシェーダプログラムに頂点データを供給するための属性バインディングがカプセル化さ れています(VAOの使い方について詳しくは“頂点配列オブジェクトの使用による頂点配列状態の変化 の整理統合” (92 ページ)を参照)。VAOバインディングにはバッファ内容の形式に関する情報があ るので、VAOを調べることにより、OpenGL ESがその内容をどのように解釈したかが分かります(図 B-10を参照)。 図 B-10 アシスタントエディタに頂点配列オブジェクトがプレビュー表示されている様子 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 137 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり テクスチャやレンダバッファを表示する テクスチャやレンダバッファを調べるためには、アシスタントエディタで、その内容の画像プレビュー を表示するとよいでしょう。主エディタと同じコントロール部品を使って、テクスチャオブジェクト やレンダバッファに関する詳細を調べ、画像プレビューを補正できます。テクスチャについては、ア シスタントエディタの左下隅にコントロール部品が追加され、各ミップマップレベルと、(もしあれ ば)キューブマップテクスチャの各面をプレビュー表示できます(図 B-11を参照)。 図 B-11 アシスタントエディタにキューブマップテクスチャをプレビュー表示している様子 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 138 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり デバッグ領域 デバッグバーには、取り込んだOpenGL ESのコマンド列をナビゲートするためのコントロール部品が いくつかあります(図 B-12を参照)。これを使って、フレームナビゲータに表示されている階層に 沿ってコマンドを選択し、あるいは矢印やスライダでコマンド列を前後に行き来することができま す。「Continue」ボタンを押すとフレームのデバッグが終了し、アプリケーションの実行に戻りま す。 図 B-12 OpenGL ESのデバッグバー フレームデバッガにはデバッグコンソールがありません。代わりにXcodeには変数ビューがいくつか あり、OpenGL ESにおけるレンダリング処理の状態について、それぞれ異なる視点から概要を表示す るようになっています。変数ビューはポップアップメニューで切り替え可能です。個々の変数ビュー について、以下に解説します。 「All GL Objects」ビュー 「All GL Objects」ビューは、「Bound GL Objects」ビュー(図 B-13 (140 ページ)の右側)と同様に、 アシスタントエディタに示したグラフィカル概要と同じ、OpenGL ESのリソースを列挙します。しか しグラフィック概要と違って、三角形アイコンをクリックすることにより展開表示に切り替えると、 リソースに関するより詳しい情報が得られます。たとえばフレームバッファやバッファオブジェクト のリストを展開表示すると、glGetBufferParameterやglGetFramebufferAttachmentParameterな ど、OpenGL ESの問い合わせ関数を使ってしか調べられない情報も得られます。シェーダプログラム のリストを展開表示にすると、状態、属性バインディング、ユニフォーム変数それぞれにバインドさ れている値が分かります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 139 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり 「Bound GL Objects」ビュー 「Bound GL Objects」ビュー(図 B-13の右側)は、「All GL Objects 」ビューとほぼ同じ動作ですが、フ レームナビゲータで選択したOpenGL ESコマンドで使われているリソースのみを一覧表示します。 図 B-13 デバッグ領域に「GL Context」ビューと「Bound GL Objects」ビューが表示されている様子 「GL Context」ビュー 「GL Context」ビュー(図 B-13の左側)には、OpenGL ESレンダラの状態ベクトルがすべて、機能グ ループごとに整理して表示されます。フレームナビゲータで、OpenGL ESの状態を変更する関数呼び 出しを選択すると、変更後の値が強調表示されます。たとえばglCullFace関数やglFrontFace関数 を呼び出すと、状態が列挙されているうち「Culling」セクションの値が変わり、強調表示になりま す。同様に、glEnable(GL_BLEND)関数でブレンディングを有効にし、あるいはglBlendFunc関数で そのパラメータを変更すると、「Blending」セクションの値が変わり、強調表示になります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 140 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり 「Context Info」ビュー 「Context Info」ビュー(図 B-14の右側)には、使用中のOpenGL ESレンダラに関する静的な情報、す なわち、名前、版、能力、拡張機能などのデータが表示されます。レンダラ属性 (GL_MAX_TEXTURE_IMAGE_UNITS、GL_EXTENSIONSなど)を問い合わせるコードを記述しなくても、 必要なデータを得ることができます。 図 B-14 デバッグ領域に「Auto」ビューと「Context Info」ビューが現れている様子 「Auto」ビュー 「Auto」ビュー(図 B-14の左側)には、他の変数ビューに通常表示される項目や、フレームナビゲー タで選択した関数呼び出しに関係する他の情報の一部が、自動的に表示されます。たとえば次のよう な内容です。 ● ● ● ● 選択した関数を実行した結果、OpenGL ESのエラーが生じた、あるいは性能上の問題点をXcodeが 特定した場合、エラー/警告とその修正方法が表示されます。 選択した関数が、OpenGL ESのコンテキスト状態の一部を変更し、あるいはその動作がコンテキ スト状態に依存する場合、「GL Context 」ビューから自動的に関連情報を選んで表示するように なっています。 選択した関数がリソースをバインドし、あるいはバインドしたリソース(頂点配列オブジェク ト、プログラム、テクスチャなど)を使っている場合、「BoundGLObjects 」ビューから自動的に 関連情報を選んで表示するようになっています。 描画関数を選択すると、プログラムの処理性能に関する情報(描画中に各シェーダが費やした総 時間を含む)が表示されます。また、フレームを取り込んだ後にシェーダを変更、再コンパイル した場合は、各シェーダについて、変更前の基準時間との差も表示されます(プログラムの処理 性能に関する情報が得られるのは、OpenGL ES 3.0対応デバイスでデバッグしている場合に限りま す)。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 141 Xcodeに付属するOpenGL ES用ツールの概要 OpenGL ES Frame Debuggerひとめぐり このビューにはさらに、フレームのレンダリング性能に関する累積統計情報(描画関数の呼び出し 数、フレームレートなど)も表示されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 142 texturetoolを使用したテクスチャの圧縮 iOS SDKには、テクスチャをPVRTCまたはASTCフォーマットに圧縮するtexturetoolという名前のツー ルが含まれています。iOS 7.0 SDKと一緒にXcodeをインストールした場合、texturetoolは次の場所 にありま す。/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/texturetool texturetoolには、画像の品質とサイズの間のトレードオフに対応するさまざまな圧縮オプションが あります。どの設定が最適な妥協点かを判断するには、テクスチャごとに実験する必要があります。 注意: texturetoolで利用可能なエンコーダ、フォーマット、およびオプションは変更され る可能性があります。この文書では、iOS 7現在で利用可能なオプションについて説明しま す。 texturetoolのパラメータ この節では、texturetoolに渡すことができるパラメータについて説明します。 user$ texturetool -h Usage: texturetool [-hl] texturetool -c <reference_image> <input_image> texturetool [-ms] [-e <encoder>] [-p <preview_file>] -o <output> [-f <format>] <input_image> first form: -h -l Display this help menu. List available encoders, individual encoder options, and file formats. second form: -c Compare <input_image> to <reference_image> and report differences. third form: 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 143 texturetoolを使用したテクスチャの圧縮 texturetoolのパラメータ -m Generate a complete mipmap chain from the input image. -s encoded image. -e <encoder> Report numerical differences between <input_image> and the Encode texture levels with <encoder>. -p <preview_file> Output a PNG preview of the encoded output to <preview_file>. Requires -e option -o <output> Write processed image to <output>. -f <format> Set file <format> for <output> image. 注意: -pオプションを指定する場合は-e オプションも指定する必要があります。また、-o オプションも必要です。 リスト C-1 エンコードオプション user$ texturetool -l Encoders: PVRTC --channel-weighting-linear --channel-weighting-perceptual --bits-per-pixel-2 --bits-per-pixel-4 --alpha-is-independent --alpha-is-opacity --punchthrough-unused --punchthrough-allowed --punchthrough-forced ASTC --block-width-4 --block-width-5 --block-width-6 --block-width-8 --block-width-10 --block-width-12 --compression-mode-veryfast 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 144 texturetoolを使用したテクスチャの圧縮 texturetoolのパラメータ --compression-mode-fast --compression-mode-medium --compression-mode-thorough --compression-mode-exhaustive --srgb-yes --srgb-no --block-height-4 --block-height-5 --block-height-6 --block-height-8 --block-height-10 --block-height-12 Formats: Raw PVR KTX 何もオプションが指定されない場合、texturetoolはデフォルトの--bits-per-pixel-4、 --channel-weighting-linear、および-f Rawで実行されます。 --bits-per-pixel-2オプションと--bits-per-pixel-4オプションは、元のピクセルを2ビット/ピ クセルまたは4ビット/ピクセルにエンコードするPVRTCデータを作成します。これらのオプションは、 圧縮なしの32ビットRGBA画像データと比べて、それぞれ16:1と8:1の圧縮率になります。32バイトが 最小データサイズです。圧縮処理によってこれより小さいファイルは作成されません。したがって、 圧縮されたテクスチャデータをアップロードするときは少なくともこのバイト数はアップロードされ ます。 --channel-weighting-linearを指定して圧縮した場合は、すべてのチャンネルに均等に圧縮誤差が 拡散されます。対照的に、--channel-weighting-perceptualを指定すると、リニアオプションの 場合と比べて緑チャネルの誤差を減らすことができます。一般に、PVRTC圧縮は線画よりも写真画像 に効果的です。 -mオプションを利用すると元の画像に対するミップマップレベルが自動的に生成されます。これらの レベルは、作成されるアーカイブ内に追加画像データとして提供されます。Raw画像フォーマットを 使用した場合は、各レベルの画像データがアーカイブの最後に順番に追加されます。PVRアーカイブ フォーマットを使用した場合は、各ミップマップは独立した画像としてアーカイブに提供されます。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 145 texturetoolを使用したテクスチャの圧縮 texturetoolのパラメータ (-f)パラメータは、その出力ファイルの形式を制御します。デフォルトのフォーマットはRawで す。このフォーマットは1つのテクスチャレベル(-mオプションなし)、または各テクスチャレベル を連結した(-mオプションあり)未加工の圧縮テクスチャデータです。ファイルに格納された各テク スチャレベルのサイズは少なくとも32バイトあり、これがそのままGPUにアップロードされなければ なりません。PVRフォーマットは、Imagination TechnologiesのPowerVR SDKに含まれているPVRTexTool で使われるフォーマットと同じです。PVR圧縮テキスチャを読み込むには、GLKTextureLoaderクラ スを使います。 -sおよび-cオプションは、エンコード中にエラーメトリックを印刷します。-sオプションは入力(圧 縮なし)画像を出力(エンコード済み)画像と比較し、-cオプションは任意の2つの画像を比較しま す。比較結果として、二乗平均平方根エラー(rms)、聴覚特性を考慮したpRms、最悪の場合のテク セルエラー(max)、および各統計の合成ベースバージョン(rmsC、pRmsC、およびmaxC)がありま す。合成ベースエラーは、画像のアルファチャネルが不透明度に使用され、各テクセルの色が最悪の 場合のレンダリング先の色と混合されるようにします。 -sおよび-cおよびオプション、ならびに圧縮画像を処理するときにエンコーダによって使用されるエ ラーメトリックは、画像のアルファチャネルをデフォルトでは(または--alpha-is-independentオ プションの使用時には)独立したチャネルとして扱います。--alpha-is-opacityオプションは、エ ラーメトリックを、glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA )を呼び出すことに よって実装される標準混合操作に基づいたメトリックに変更します。 PVRテクスチャ圧縮では、4x4ブロック単位で有効にできる、特別なパンチスルーモードがサポートさ れています。このモードでは、ブロック内で使用できる色のグラデーションが制限されますが、ピク セルのアルファ値を0に強制するオプションが導入されています。これは、PVRTCの滑らかな色の補間 を無効にし、ブロック境界のアーティファクトを生じさせるため、注意して使用する必要がありま す。パンチスルーオプションは次の3つです。 ● --punchthrough-unused — パンチスルーなし(デフォルトのオプション)。 ● --punchthrough-allowed — エンコーダは、画質を最適化するときに、ブロック単位でパンチ スルーを有効にできます。このオプションは、圧縮アルゴリズムによって使用される客観的な(1 ピクセルあたり)のエラーメトリックを一般に向上させますが、主観的なアーティファクトを生 じさせる可能性があります。 ● --punchthrough-forced — パンチスルーはすべてのブロックで有効になり、色のグラデーショ ンを制限しますが、ブロック内の任意のピクセルのアルファが0になるようにすることができま す。このオプションは、主に完全性のために提供されますが、結果を他のオプションと視覚的に 比較できるときにも役立つ場合があります。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 146 texturetoolを使用したテクスチャの圧縮 texturetoolのパラメータ Important: エンコーダ用のソース画像は次の要件を満たしている必要があります。 ● 高さと幅が少なくとも8はある。 ● 高さと幅が2のべき乗である。 ● 正方形である(高さ==幅)。 ● ソース画像は、OS X上でImage IOが受理するフォーマットである。最良の結果を得るには、元 のテクスチャは圧縮なしのデータフォーマットであるべきです。 Important: PVRTexToolを使用してテクスチャを圧縮している場合は、2のべき乗の長さを持つ正方 形のテクスチャを作成しなければなりません。アプリケーションが正方形以外のテクスチャや2の べき乗以外のテクスチャをiOSにロードしようとすると、エラーが返されます。 リスト C-2 画像をPVRTC圧縮フォーマットにエンコードする。 Encode Image.png into PVRTC using linear weights and 4 bpp, and saving as ImageL4.pvrtc user$ texturetool -e PVRTC --channel-weighting-linear --bits-per-pixel-4 -o ImageL4.pvrtc Image.png Encode Image.png into PVRTC using perceptual weights and 4 bpp, and saving as ImageP4.pvrtc user$ texturetool -e PVRTC --channel-weighting-perceptual --bits-per-pixel-4 -o ImageP4.pvrtc Image.png Encode Image.png into PVRTC using linear weights and 2 bpp, and saving as ImageL2.pvrtc user$ texturetool -e PVRTC --channel-weighting-linear --bits-per-pixel-2 -o ImageL2.pvrtc Image.png Encode Image.png into PVRTC using perceptual weights and 2 bpp, and saving as ImageP2.pvrtc user$ texturetool -e PVRTC --channel-weighting-perceptual --bits-per-pixel-2 -o ImageP2.pvrtc Image.png リスト C-3 プレビューを作成するとともに画像をPVRTC圧縮フォーマットにエンコードする Encode Image.png into PVRTC using linear weights and 4 bpp, and saving the output as ImageL4.pvrtc and a PNG preview as ImageL4.png user$ texturetool -e PVRTC --channel-weighting-linear --bits-per-pixel-4 -o ImageL4.pvrtc -p ImageL4.png Image.png Encode Image.png into PVRTC using perceptual weights and 4 bpp, and saving the output as ImageP4.pvrtc and a PNG preview as ImageP4.png user$ texturetool -e PVRTC --channel-weighting-perceptual --bits-per-pixel-4 -o ImageP4.pvrtc -p ImageP4.png Image.png 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 147 texturetoolを使用したテクスチャの圧縮 texturetoolのパラメータ Encode Image.png into PVRTC using linear weights and 2 bpp, and saving the output as ImageL2.pvrtc and a PNG preview as ImageL2.png user$ texturetool -e PVRTC --channel-weighting-linear --bits-per-pixel-2 -o ImageL2.pvrtc -p ImageL2.png Image.png Encode Image.png into PVRTC using perceptual weights and 2 bpp, and saving the output as ImageP2.pvrtc and a PNG preview as ImageP2.png user$ texturetool -e PVRTC --channel-weighting-perceptual --bits-per-pixel-2 -o ImageP2.pvrtc -p ImageP2.png Image.png 注意: -oパラメータと有効な出力ファイルを指定しないと、プレビューは作成できません。 プレビュー画像は常にPNG形式になります。 PVR圧縮テキスチャを読み込むには、GLKTextureLoaderクラスを使います。 PVR圧縮したデータを直接処理する例が『PVRTextureLoader 』に載っています。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 148 書類の改訂履歴 この表は「iOS用OpenGL ESプログラミングガイド 」の改訂履歴です。 日付 メモ 2015-10-21 ASTCテクスチャおよびKTXフォーマットを参照するようにtexturetool リストを更新しました。 2014-07-15 レンダリングの手順やメモリ管理に関する説明を改訂しました。 2014-03-10 OpenGL ES 3.0およびその重要な拡張機能、iOS 7.1で新たに組み込ま れたマルチスレッド機能、XcodeのOpenGL ES用ツール群、GPUの アーキテクチャや性能に関する問題について、解説を追加あるいは 補足しました。 2013-09-18 OpenGL ES 3.0、GLKit、およびXcodeデバッガに関する詳細情報を含 めるために更新しました。 2013-04-23 「プラットフォームによる違い」を『OpenGL ES Hardware Platform Guide for iOS』に移動しました。 「プラットフォームによる違い」の章を削除し、その情報をそれ自 体のマニュアル、『OpenGL ES Hardware Platform Guide for iOS 』に 移動しました。 2011-02-24 Xcode 4で提供された新しいOpenGL ESツールについての情報を追加 しました。コンテンツ共有はすべてのコンテンツが同じバージョン のOpenGL ES APIを共有する場合にのみ使用できることを明記しまし た。 2010-11-15 文書内のすべての情報を大幅に改訂、拡充しました。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 149 書類の改訂履歴 日付 メモ よく使用されるグラフィックス関連およびOpenGL ES関連用語の解 説を追加しました。 iOS 4で追加された機能強化(レンダバッファの破棄)も含め、レン ダリングループの詳細な説明を追加しました。 2010-09-01 リンクを訂正しました。パフォーマンスに関するいくつかのガイド ラインを明確にしました。iOS 4で追加された新しい拡張機能へのリ ンクを追加しました。 2010-07-09 『iPhone OS OpenGL ESプログラミングガイド』から文書名を変更し ました。 2010-06-14 iOS 4で公開された新しい拡張機能を追加しました。 2010-01-20 画面への描画を行うフレームバッファオブジェクトを作成するため のコードを修正しました。 2009-11-17 細かな改訂と編集を行いました。 2009-09-02 わかりやすくするために表現を編集しました。拡張機能リストを、 現在利用可能な機能を反映するように更新しました。頂点処理能力 を最高にする三角形ストリップの使いかたを明確にしました。プ ラットフォームの章に、PowerVR SGX上でのテクスチャの処理能力 に関する注を追記しました。 2009-06-11 iPhoneアプリケーション内に高いパフォーマンスのグラフィックス を作成するためにOpenGL ES 1.1および2.0のプログラミングインター フェイスを使用する方法について説明した文書の初版。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 150 用語解説 この用語解説では、AppleによるOpenGL ESの実 装で特に使用されている用語と、OpenGL ESグ ラフィックスプログラミングにおける一般的な 用語を取り上げています。 クリップ座標(clip coordinates) ビューボ リュームのクリッピングに使用される座標系。 クリップ座標は、投影行列を適用した後、透視 除算よりも前に適用されます。 エイリアス化(aliased) グラフィックスのエッ ジがギザギザになっている様子を指す。アンチ エイリアス化の操作によって改善可能。 完全性(completeness) フレームバッファオ ブジェクトが描画のためのすべての要件を満た しているかどうかを表している状態。 アンチエイリアス化(antialiasing) グラフィッ クスにおいて、テキストや線画、画像などのグ ラフィカルオブジェクトを描画したときに現れ ることのある、ギザギザになった(エイリアス 化した)エッジを滑らかにしたり、ぼやかした りするテクニック。 コンテキスト(context) 一連のOpenGL ES状 態変数。コンテキストにアタッチされている描 画可能なオブジェクトに対して、どのように描 画を行うかに影響します。レンダリングコンテ キスト とも呼びます。 間引き(culling) オブザーバが見ることので きない、シーンの一部分を取り除くこと。 アタッチ(attach) 2つの既存のオブジェクト を結び付けること。バインド(bind)も参照の こと。 現在のコンテキスト(current context) アプリ ケーションが発行したコマンドをOpenGL ESが 送る先のレンダリングコンテキスト。 バインド(bind) 新しいオブジェクトを作成 し、そのオブジェクトとレンダリングコンテキ ストを結び付けること。アタッチ(attach)も 参照のこと。 現在の行列(current matrix) ある座標系から 別の座標系へと座標を変換するためにOpenGL ES が使用する行列。モデルビュー行列、透視行 列、テクスチャ行列などがあります。GLSL ESで は、代わりにユーザ定義の行列が使用されま す。 ビットマップ(bitmap) ビットで構成された 矩形状の配列。 バッファ(buffer) 頂点属性や色データ、イン デックスなど、特定の種類のデータを格納する ためだけに確保されたメモリのブロック。 OpenGL ESが管理。 深度(depth) OpenGLにおいて、z 座標のこと を指し、オブザーバからピクセルがどれくらい 離れているかを指定。 深度バッファ(depth buffer) 各ピクセルの深 度値を格納するため使用する、メモリのブロッ ク。深度バッファは、オブザーバからピクセル が見えるかどうかを決めるために使用します。 OpenGL ESがラスタ化したフラグメントはすべ クリッピング(clipping) 描画する領域を特定 する操作。クリッピング領域にないものは描画 されません。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 151 用語解説 て、深度バッファに格納されている値と受信し た深度値とを比較する深度テストを通過しなけ ればなりません。 フィルタ(filtering) ピクセルまたはテクセル を組み合わせることで画像を修正するプロセ ス。 ダブルバッファリング(double buffering) 2 つのバッファを使用して、グラフィックサブシ ステムの異なる2つの部分の間でリソースが競 合するのを避けること。フロントバッファを一 方の当事者が使用している間に、もう一方の当 事者がバックバッファを修正します。スワップ が発生すると、前面バッファと背面バッファが 入れ替わります。 フォグ(fog) オブザーバからの距離に基づ き、背景色へと色をフェードすることによって 達成される効果。フォグは、オブザーバに深度 の手がかりを与えます。 フラグメント(fragment) プリミティブをラ スタ化するときに計算された色と深度の値。各 フラグメントは、フレームバッファに格納され ているピクセルとブレンドする前に一連のテス トを通過しなければなりません。 描画可能オブジェクト(drawable object) OpenGL ESの外部で割り当てられたオブジェク ト。OpenGL ESフレームバッファオブジェクト の一部として使用できます。iOSでは、この種類 の描画可能オブジェクトのみが、OpenGL ESレ ンダリングをCore Animationに統合する CAEAGLLayerクラスです。 システムフレームバッファ(system framebuffer) オペレーティングシステムによっ て提供されるフレームバッファ。この種類のフ レームバッファでは、オペレーティングシステ ムのウインドウシステムへのOpenGL ESの統合 がサポートされています。iOSではシステムフ レームバッファは使用されません。代わりに、 Core Animationレイヤと関連付けられたフレーム バッファオブジェクトがあります。 拡張機能(extension) OpenGL ESのコアAPIの 一部ではなく、そのためOpenGL ESのすべての 実装でのサポートが保証されていない、 OpenGL ESの機能。拡張機能に使用される命名 規則は、その拡張機能がどれくらい幅広く受け 入れられているかを示しています。特定の会社 だけがサポートする拡張機能の名前には、会社 名の省略形が含まれます。複数の会社が拡張機 能を採用している場合、その拡張機能の名前は 会社名の省略形の代わりにEXTが含まれるよう 変更されます。Khronos OpenGL Working Group が拡張機能を承認している場合、拡張機能の名 前は、EXTまたは会社名の省略形の代わりに、 OESが含まれるよう変更されます。 フレームバッファにアタッチ可能な画像 (framebuffer attachable image) フレームバッ ファオブジェクトのレンダリング先。 フレームバッファオブジェクト(framebuffer object) OpenGL ESによって全体が管理される フレームバッファ。フレームバッファオブジェ クト(framebuffer object)は、OpenGL ESのフ レームバッファに関する状態情報、およびレン ダバッファ と呼ばれる画像のセットを格納しま す。OpenGL ES 2.0以降に構築されるフレーム バッファオブジェクト、およびiOSにおける OpenGL ES 1.1実装のすべてが、(拡張機能 OES_framebuffer_objectを通じて)フレーム バッファオブジェクトをサポートします。 眼点座標(eye coordinates) 原点のオブザー バが使用する座標系。眼点座標はモデルビュー 行列によって生成され、投影行列に渡されま す。 フラスタム(frustum) オブザーバから見え る、透視除算によって歪んだ空間領域。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 152 用語解説 画像(image) ピクセルで構成された矩形状の 配列。 内のすべてのビットプレーンにおける、「x ,y 」 の位置にあるすべてのビットで構成されていま す。 インターリーブされたデータ(interleaved data) 頂点データやテクスチャ座標など、異 なるデータで構成され、グループとしてまとめ られた配列。インターリーブにより、データの 取得を高速化できます。 ピクセル深度(pixel depth) ピクセルの画像 における、1ピクセルあたりのビット数。 ピクセルフォーマット(pixel format) ピクセ ルデータをメモリに格納するために使用する フォーマット。フォーマットは、ピクセルのコ ンポーネント(赤、緑、青、アルファ)、コン ポーネントの数と順序、およびその他の関連情 報(ピクセルにステンシル値や深度値が含まれ るかどうかなど)を記述するものです。 ミップマップ(mipmaps) さまざまな解像度 で提供される、テクスチャマップのセット。オ ンスクリーンの解像度がソースのテクスチャ マップに適合していないジオメトリのプリミ ティブに対して、テクスチャが適用されるとき に発生する可能性のあるアーティファクトを最 小限に抑えることが目的です。ミップマップと はラテン語のmultuminparvo に由来する言葉で、 「狭い場所に多くのものがある」ことを意味し ます。 アルファ値乗算済み(premultiplied alpha) ほ かのコンポーネントがすでにアルファ値で乗じ てあるピクセル。たとえば、RGBA値が(1.0, 0.5, 0.0, 0.5)のピクセルの場合、乗算済みの値は(0.5, 0.25, 0.0, 0.5)となります。 モデルビュー行列(modelview matrix) 点、 線、多角形の変換、およびオブジェクトの座標 から眼点座標への位置の変換のためにOpenGLが 使用する、4×4の行列。 プリミティブ(primitives) OpenGLで最も単純 な要素。点、線、多角形、ビットマップ、画像 のこと。 投影行列(projection matrix) 点、線、多角 形、および眼点座標からクリップ座標への位置 を変換するためにOpenGLが使用する行列。 マルチサンプル機能(multisampling) ピクセ ルを対象に複数のサンプルを取得し、サンプル をカバレッジ値と組み合わせて最終フラグメン トに到達するためのテクニック。 ラスタ化(rasterization) 頂点データおよびピ クセルデータをフラグメントに変換するプロセ ス。各フラグメントは、フレームバッファ内の ピクセルに対応します。 ミューテックス(mutex) マルチスレッドア プリケーションにおける相互排他オブジェク ト。 レンダバッファ(renderbuffer) 2Dピクセル画 像のレンダリング先。OES_framebuffer_object 拡張機能についてOpenGL仕様で定義されている ように、汎用化されたオフスクリーンレンダリ ングについて使用されます。 パッキング(packing) ピクセルの色コンポー ネントを、バッファからアプリケーションが要 求するフォーマットに変換すること。 ピクセル(pixel) ピクチャの要素。グラフィッ クスハードウェアが画面上に表示できる最も小 さい要素です。ピクセルは、フレームバッファ レンダラ(renderer) ビューおよびモデルか ら画像を作成するためにOpenGL ESが使用する ハードウェアとソフトウェアの組み合わせ。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 153 用語解説 レンダリングコンテキスト(rendering context) 状態情報のコンテナ。 テクスチャ(texture) ラスタ化されたフラグ メントの色を修正するために使用する画像デー タ。データの種類は、1次元、2次元、3次元、 またはキューブマップ。 レンダリングパイプライン(rendering pipeline) OpenGL ESがピクセルデータと頂点 データをフレームバッファの画像に変換するた めに使用する操作の順序。 テクスチャマッピング(texture mapping) プ リミティブにテクスチャを適用するプロセス。 render-to-texture テクスチャターゲットに直接 コンテンツを描画する操作。 テクスチャ行列(texture matrix) テクスチャ 座標を、補間処理やテクスチャ検索に使用する 座標に変換するためにOpenGL ES 1.1が使用す る、4×4の行列。 RGBA 赤(Red)、緑(green)、青(blue)、 アルファ(alpha)の色コンポーネント。 テクスチャオブジェクト(texture object) テ クスチャに関連するすべてのデータを格納する ために使用する、不透明なデータ構造体。テク スチャオブジェクトには、画像やミップマッ プ、テクスチャパラメータ(幅、高さ、内部 フォーマット、解像度、ラップモードなど)と いったものを含めることができます。 シェーダ(shader) サーフェスプロパティを 計算するプログラム。 シェーディング言語(shading language) Cに おいてアクセス可能な高水準言語。高度な画像 効果を生み出すために使用します。 ステンシルバッファ(stencil buffer) ステンシ ルテスト専用のメモリ。ステンシルテストは通 常、マスク領域の識別、覆う必要のある無地の ジオメトリの識別、および半透明の多角形の オーバーラップに使用します。 頂点(vertex) 三次元の先端。頂点のセット は、形状のジオメトリを指定します。頂点は、 色やテクスチャ座標など、多数の追加属性を持 つことができます。詳細については、頂点配列 (vertex array)を参照。 ティアリング(tearing) 現在のフレームが画 面上に完全にレンダリングされる前に、現在の フレームの一部がフレームバッファ内の前のフ レームのデータを上書きしてしまう場合に発生 する、視覚的異常。iOSでは、表示可能な OpenGL ESコンテンツすべてをCore Animationを 通じて処理することにより、ティアリングを回 避します。 頂点配列(vertex array) 頂点座標、テクスチャ 座標、サーフェス法線、RGBA色、色インデック ス、エッジフラグなどを指定する、データのブ ロックを格納するデータ構造体。 頂点配列オブジェクト(vertex array object) アクティブな頂点属性のリスト、各属性が格納 されているフォーマット、および頂点と属性を 記述するデータの場所を記録するOpenGL ESオ ブジェクト。頂点配列オブジェクトは、グラ フィックスパイプラインを設定し直す手間を軽 減します。 テッセレーション(tessellation) サーフェス を多角形のメッシュに変形したり、曲線を連続 する直線に変形したりする操作。 テクセル(texel) フラグメントに適用する色 を指定するために使用する、テクスチャの要 素。 2015-10-21 | Copyright © 2015 Apple Inc. All Rights Reserved. 154 Apple Inc. Copyright © 2015 Apple Inc. All rights reserved. 本書の一部あるいは全部を Apple Inc. から書 面による事前の許諾を得ることなく複写複製 (コピー)することを禁じます。また、製品 に付属のソフトウェアは同梱のソフトウェア 使用許諾契約書に記載の条件のもとでお使い ください。書類を個人で使用する場合に限り 1 台のコンピュータに保管すること、またそ の書類にアップルの著作権表示が含まれる限 り、個人的な利用を目的に書類を複製するこ とを認めます。 Apple ロゴは、米国その他の国で登録された Apple Inc. の商標です。 キーボードから入力可能な Apple ロゴについ ても、これを Apple Inc. からの書面による事 前の許諾なしに商業的な目的で使用すると、 連邦および州の商標法および不正競争防止法 違反となる場合があります。 本書に記載されているテクノロジーに関して は、明示または黙示を問わず、使用を許諾し ません。 本書に記載されているテクノロジー に関するすべての知的財産権は、Apple Inc. が保有しています。 本書は、Apple ブランド のコンピュータ用のアプリケーション開発に 使用を限定します。 本書には正確な情報を記載するように努めま した。 ただし、誤植や制作上の誤記がないこ とを保証するものではありません。 Apple Inc. 1 Infinite Loop Cupertino, CA 95014 U.S.A. Apple Japan 〒106-6140 東京都港区六本木 6 丁目10番1号 六本木ヒルズ http://www.apple.com/jp Offline copy. Trademarks go here. Apple Inc. は本書の内容を確認しておりますが、本 書に関して、明示的であるか黙示的であるかを問わ ず、その品質、正確さ、市場性、または特定の目的 に対する適合性に関して何らかの保証または表明を 行うものではありません。その結果、本書は「現状 有姿のまま」提供され、本書の品質または正確さに 関連して発生するすべての損害は、購入者であるお 客様が負うものとします。 いかなる場合も、Apple Inc. は、本書の内容に含ま れる瑕疵または不正確さによって生じる直接的、間 接的、特殊的、偶発的、または結果的損害に対する 賠償請求には一切応じません。そのような損害の可 能性があらかじめ指摘されている場合においても同 様です。 上記の損害に対する保証および救済は、口頭や書面 によるか、または明示的や黙示的であるかを問わ ず、唯一のものであり、その他一切の保証にかわる ものです。 Apple Inc. の販売店、代理店、または従 業員には、この保証に関する規定に何らかの変更、 拡張、または追加を加える権限は与えられていませ ん。 一部の国や地域では、黙示あるいは偶発的または結 果的損害に対する賠償の免責または制限が認められ ていないため、上記の制限や免責がお客様に適用さ れない場合があります。 この保証はお客様に特定 の法的権利を与え、地域によってはその他の権利が お客様に与えられる場合もあります。
© Copyright 2024 Paperzz