HTML5 キャンバスのレイヤー化によるレンダリングの最適化

プラットフォーム上のほとんどのグラフィック要素は、何らかの形の最適化が必要です。この記事で、HTML5 キャンバス要素をレイヤー化することによる最適化手法について学んでください。記事では単純な例に従って、レイヤーを識別する方法を説明し、レイヤーを最適化する場合の独特のレンダリング方法を探ります。キャンバスをレイヤー化して最適化するという戦略は、あらゆるインタラクティブなリアルタイムのシーンに適用することができます。

Adam Ranfelt, Software Developer, The Nerdery

Adam RanfeltAdam Ranfelt は、The Nerdery の特定の技術にとらわれない姿勢を証明する生き証人です。2009年にコンピューター・サイエンスの学位を取得してミネソタ大学を卒業した後、技術の最前線で働くことを楽しんでいます。彼は、HTML、ActionScript、Java、および Objective-C のエキスパートです。2010年に The Nerdery に入社する前は、Curb-Crowser Design で数々のデジタル・メディア・プロジェクトを構築、保守していました。それぞれのジョブに適した正しい技術を使用することを信念とする彼は、10 を超えるプログラミング言語に精通し、現在もその数を増やしています。彼の技術スキルならびにリーダーシップとしての手腕が認められ、2012年には主任ソフトウェア・エンジニアに昇進しました。



2012年 11月 29日

はじめに

HTML5 のキャンバスをレンダリングする際や 2D ゲームでは、最適化を行うために、複数のレイヤーを合成することによって 1 つのシーンが作成されることがよくあります。OpenGL や WebGL などの下位レベルでのレンダリングは、フレームごとにシーンをクリアして描画するという方法で行われます。ゲームは実装後に、レンダリングの処理量を減らしてさまざまなコストを最適化する必要があります。キャンバスは DOM 要素であることから、最適化の手法として、複数のキャンバスをレイヤー化することができます。

頻繁に使用される略語

  • CSS: Cascading Style Sheets
  • DOM: Document Object Model
  • HTML: HyperText Markup Language

この記事では、キャンバスをレイヤー化する論理的根拠を探り、キャンバスをレイヤー化するための DOM のセットアップについて説明します。最適化手法としてレイヤー化を使用する場合、各種の手法が必要になってくるため、記事ではレイヤー化手法を強化する、いくつかの最適化戦略の概念上および技術上の重要なポイントについても探ります。

記事で使用するサンプルのソース・コードは、「ダウンロード」セクションからダウンロードすることができます。


最適化戦略の選択

最善の最適化戦略の選択は、難しい場合があります。シーンをレイヤー化することにした場合、そのシーンをどのように構成するかを検討する必要があります。表示領域が大きい画面のレンダリングは、複数のコンポーネントを再利用することが多く、最適化の対象として調査するのにふさわしい候補です。パララックス (視差) や、エンティティーのアニメーション化などといったエフェクトには、多くの場合、画面スペースの頻繁な変更が必要になります。最善の最適化戦略を探る際には、このような状況を認識しておくことが望ましいです。キャンバスをレイヤー化して最適化する場合には各種の手法が必要になりますが、適切に適用すれば著しい改善がもたらされます。


レイヤーのセットアップ

レイヤー化手法を使用する場合の最初のステップは、DOM にキャンバスをセットアップすることです。一般には、キャンバス要素を定義して、それを DOM に配置するだけのことですが、レイヤーには追加のスタイル設定が必要になります。キャンバスのレイヤー化が功を奏するようにするためには、CSS を使用する際に以下の 2 つの要件を満たす必要があります。

  • キャンバス要素がビューポートの同じ場所に共存すること。
  • それぞれのキャンバスが、別のキャンバスの下に配置されても可視になること。

図 1 に、レイヤーのセットアップの基礎となる一般的なオーバーラップの概念を示します。

図 1. レイヤーの例
レイヤーの一例を示す図

レイヤーをセットアップする手順は以下のとおりです。

  1. キャンバス要素を DOM に追加します。
  2. レイヤー化できるようにキャンバスの位置決めスタイルを追加します。
  3. キャンバス要素の背景を透明にするスタイルを設定します。

キャンバスのオーバーレイ・スタックをセットアップする

オーバーレイ・スタックを CSS で作成するには、多少のスタイルが必要になる場合があります。HTML と CSS を使用してオーバーレイを行う方法は何通りもありますが、この記事の例では単一の <div> タグを使用して、そこに複数のキャンバスを含めます。<div> タグが指定する一意の ID によって、そこに含まれる子 HTML5 キャンバス要素にスタイルを適用するという方法です (リスト 1 を参照)。

リスト 1. キャンバスの位置決めスタイル
#viewport {
    /**
     * Position relative so that canvas elements
     * inside of it will be relative to the parent
     */
    position: relative;
}
 
#viewport canvas {
    /**
     * Position absolute provides canvases to be able
     * to be layered on top of each other
     * Be sure to remember a z-index!
     */
    position: absolute;
}

<div> コンテナーはオーバーレイの要件を満たすために、子要素となるすべてのキャンバス要素に、絶対位置決め方式を使用するスタイルを設定します。ここでは #viewport が相対位置決め方式を使用するように指定することで、子要素のスタイルにどのような絶対レイアウト・スタイルが適用されたとしても、必ず #viewport コンテナーが基準となるようにしています。

これらの HTML5 キャンバス要素の順序は重要です。順序付けは、DOM での要素の出現順で管理することも、キャンバスを表示する順序で z-index スタイルを設定することで管理することもできます。常にそうとは限りませんが、他のスタイルがレンダリングに影響を与える場合もあるので、CSS トランスフォームなどの追加スタイルを導入する際には注意が必要です。

透明の背景

レイヤー化手法の 2 番目のスタイル要件を満たすには、オーバーラップ時の可視性を指定します。ここで示す例では、背景色を DOM 要素に設定するオプションを使用します (リスト 2 を参照)。

リスト 2. 透明の背景を設定するスタイルシートのルール
canvas {
    /**
     * Set transparent to let any other canvases render through
     */
    background-color: transparent;
}

背景を透明にするようにキャンバスのスタイルを設定することで、オーバーラップしたキャンバスを可視にするという 2 番目の要件が満たされます。これでレイヤー化手法の 2 つの要件を満たすマークアップとスタイルの構成が完了したので、レイヤー化されたシーンのセットアップを開始することができます。


レイヤーに関する考慮事項

最適化戦略を選択する際には、その特定の戦略を使用することに伴うあらゆるトレードオフを認識してください。HTML5 キャンバスを使用するシーンのレイヤー化は、実行時の速度を高めるためにランタイム・メモリーに重点を置く戦略です。つまり、ページがブラウザー内で使用するメモリーに重点を置くことで、より高いフレーム・レートが実現されるようにするということです。キャンバスは一般に、グラフィック API が含まれる、ブラウザー上のグラフィック面と見なされます。

Google Chrome 19 でブラウザーのタブのメモリー使用量をテストして記録すると、メモリー使用量に明らかな傾向があることわかります。このテストでは、前のセクションでスタイル設定した <div> を使用し、その <div> に配置される、単色で塗りつぶしたキャンバス要素を生成します。キャンバスのサイズは 1600 x 900 ピクセルに設定し、Chrome1 のタスク・マネージャー・ユーティリティーからデータを収集します。表 1 に、テスト結果の一例を記載します。

Google Chrome のタスク・マネージャーでは、ページで使用されたメモリー (RAM) の量を確認することができます。さらに、Chrome は GPU メモリー (GPU によるメモリー使用量) も表示します。一般に GPU メモリーに格納されるのは、コンピューターがキャンバスのデータを画面にレンダリングするために必要なあらゆる類のバッファリング対象データ (例えば、形状や質感などのデータ) の情報です。このメモリー量が少なければ少ないほど、コンピューターにかかる負担は軽くなります。コンピューターが耐え切れなくなる絶対的な値というものはありませんが、プログラムが度を越えてメモリーを使い過ぎていないことを確認するためのテストは、決して怠らないでください。メモリーの使用量があまりにも大きくなりすぎると、メモリー・リソースが不足して、ブラウザーまたはページが強制終了してしまいます。GPU 処理については、果てしのないプログラミングの追求であるため、この記事では説明しません。GPU 処理について基礎から学ぶには、OpenGL について学ぶことや Chrome のドキュメントを調べることから始めてください (「参考文献」を参照)。

表 1. キャンバスのレイヤーのメモリー・オーバーヘッド
レイヤー数メモリーGPU メモリー
030.011.9
137.628.9
137.628.9
249.046.6
352.259.6
858.498.0
1665.0130
32107187

表 1 では、より多くの HTML5 キャンバス要素を導入してページ上で使用するようになるのに伴い、メモリーの使用量が増加しています。通常のメモリーにも線形相関がありますが、1 つのレイヤーを追加するごとに増加する使用量はほんのわずかです。このテストによって、レイヤーがパフォーマンスに与える影響が詳細に説明されることはないにせよ、このテストはキャンバスが GPU メモリーに大きな影響を与えることを明らかに示しています。したがって、ターゲット・プラットフォームでは必ずストレス・テストを行って、アプリケーションを実行不可能にする制約がプラットフォームにないことを確実にしてください。

単一キャンバスのレンダリング・サイクルを、レイヤーを使用したソリューションに変更することにした場合には、パフォーマンスの向上をメモリーのオーバーヘッドに照らして検討する必要があります。メモリーは犠牲になるとは言え、この手法はフレームごとに変更するピクセル数を減らすことによって功を奏します。

次のセクションでは、レイヤー化手法を使用してシーンを編成する方法を説明します。


シーンのレイヤー化: ゲーム

このセクションでは、マルチレイヤー・ソリューションについて学ぶために、プラットフォームがスクロールするランナー・スタイルのゲームに実装された単一のキャンバスによるパララックス効果をリファクタリングします。図 2 に示すように、このゲームの画面は雲、丘、地面、背景、そしていくつかのインタラクティブ・エンティティーで構成されています。

図 2. さまざま要素で構成されるゲームの画面
さまざま要素で構成されるゲームの画面

このゲームでは、雲、丘、地面、背景のすべてを異なる速度で移動させます。基本的に、背景の遠くのほうにある要素の移動速度を前景の要素より遅くすることにより、パララックス効果を実現します。さらに状況を複雑にするために、背景の移動速度は、背景が 0.5 秒間隔で再レンダリングされるだけの速度にまで落とします。

背景は画像であり、絶えず変化することから、一般に、優れたソリューションでは各フレームで画面をクリアして再レンダリングします。けれどもこの例では、背景が変化するのは 1 秒間に 2 回だけなので、すべてのフレームで再レンダリングする必要はありません。

ワークスペースは定義できたので、次は、シーンのどの部分をレイヤーに配置するかを決めます。レイヤーが編成された後、そのレイヤー構成に適用する各種のレンダリング戦略を調べることになります。まずは、単一のキャンバスを使用してソリューションを実装する場合について検討します (リスト 3 を参照)。

リスト 3. 単一キャンバスのレンダリング・ループの擬似コード
/**
 * Render call
 *
 * @param {CanvasRenderingContext2D} context Canvas context
 */
function renderLoop(context)
{
    context.clearRect(0, 0, width, height);
    background.render(context);
    ground.render(context);
    hills.render(context);
    cloud.render(context);
    player.render(context);
}

単一のキャンバスを使用するソリューションでは、リスト 3 のコードのように、renderLoop 関数をゲーム・ループの呼び出しごと、あるいは更新間隔ごとに呼び出すことになります。この例の場合のレンダリングは、メイン・ループの呼び出しと、各要素の位置を更新するための呼び出しからは抽象化されています。

クリアしてからレンダリングするという貪欲な考えに従い、renderLoop の呼び出しではコンテキストをクリアした後、画面の該当するエンティティーごとに render 関数を呼び出します。リスト 3 は、要素をキャンバス上に配置するための極めて典型的な手続き型のパスです。このソリューションは、画面にエンティティーをレンダリングする上で有効ですが、使用するレンダリング・メソッドのいずれについても記述していません。また、何らかのレンダリング最適化を行うこともできません。

エンティティーのレンダリング・メソッドを詳しく記述するには、2 つのタイプのエンティティー・オブジェクトを使用します。リスト 4 に、この例で使用して改良する 2 つのエンティティーを記載します。

リスト 4. レンダリング可能な Entity の擬似コード
var Entity = function() {
    /**
     Initialization and other methods
     **/
     
    /**
      * Render call to draw the entity
      *
      * @param {CanvasRenderingContext2D} context
      */
    this.render = function(context) {
        context.drawImage(this.image, this.x, this.y);
    }
};
 
var PanningEntity = function() {
    /**
     Initialization and other methods
     **/
     
    /**
      * Render call to draw the panned entity
      *
      * @param {CanvasRenderingContext2D} context
     */
    this.render = function(context) {
        context.drawImage(
            this.image,
            this.x - this.width,
            this.y - this.height);
        context.drawImage(
            this.image,
            this.x,
            this.y);
        context.drawImage(
            this.image,
            this.x + this.width,
            this.y + this.height);
    }
};

リスト 4 のオブジェクトには、エンティティーの画像、x、y、幅、高さを対象としたインスタンス変数を格納します。これらのオブジェクトは JavaScript の構文に従っていますが、簡潔にするために、目的とするオブジェクトの擬似コードとしては不完全なものになっています。今の時点でのレンダリング・アルゴリズムは、かなり貪欲に画像をキャンバスにレンダリングしており、ゲーム・ループのその他のニーズを一切考慮していません。

パフォーマンスにとって注意すべき重要な点は、panningEntity レンダリング呼び出しは、必要とされている画像の部分よりも広い範囲を出力することです。この特定の部分の最適化に関しては、この記事では目をつぶりますが、画像が提供するスペースよりも小さいスペースを画像が使用するときには、必要な範囲だけをレンダリングするようにしてください。


レイヤーの決定

単一のキャンバスを使用する場合の実装する方法がわかったところで、ここからは、このようなシーンを改良してレンダリング・ループをより高速に実行する方法を見つけましょう。レイヤー化手法を使用する場合には、レイヤー化で必要とされる HTML5 キャンバスの要素を明らかにする必要があります。それには、エンティティーのレンダリングがオーバーラップする部分を見つけます。

再描画領域

エンティティーのレンダリングがオーバーラップする部分を見つけ出すには、「再描画領域」と呼ばれる不可視の領域について検討しなければなりません。再描画領域とは、エンティティーの画像を描画する前にキャンバスをクリアする必要がある領域のことです。再描画領域について検討することで、シーンをより適切にレンダリングするための最適化手法を見つけることができるので、再描画領域はレンダリングの分析をする上で重要です (図 3 を参照)。

図 3. さまざま要素で構成されるゲームの画面に再描画領域が表示された様子
さまざま要素で構成されるゲームの画面に再描画領域が表示された様子

図 3 ではエフェクトを視覚化するために、シーンの各エンティティーには、幅がビューポートと同じで高さがエンティティーの画像と同じ再描画領域を表すオーバーレイがあります。シーンは、背景、前景、インタラクティブという 3 つのグループに分けることができます。シーンの再描画領域では、色が付けられたオーバーレイによって、異なる領域が以下のように区別されます。

  • 背景 ― 黒
  • 雲 ― 赤
  • 丘 ― 緑
  • 地面 ― 青
  • 赤いボール ― 青
  • 黄色い障害物 ― 青

ボールと障害物を除くすべてのオーバーレイの再描画領域は、ビューポートの幅全体に及んでいます。画面のほぼ全部が、これらのエンティティーの画像によって占められます。これらのエンティティーはそのパンニング要件により、図 4 に示すようにビューポートの幅全体にわたってレンダリングされます。ボールと障害物は、ビューポートをまたがって移動することになるため、それぞれに該当する領域はエンティティーの位置で定義することができます。シーンにレンダリングされた画像を取り除いて再描画領域だけを残すと、個々のレイヤーを容易に確認することができます。

図 4. 再描画領域
再描画領域

互いに重なっている領域に注目すれば、最初にレイヤーにするエンティティーは明らかです。ボールと障害物の領域は丘と地面に重なっているので、この 2 つのエンティティー (ボールと障害物) を 1 つのレイヤーにグループ化してインタラクティブ・レイヤーという名前を付けることができます。ゲームのエンティティーをレンダリングする順序から考えると、インタラクティブ・レイヤーが最上位のレイヤーとなります。

他のレイヤーを見つけるには、エンティティーのレンダリングがオーバーラップしていない領域をすべて集めるという方法があります。ビューポートの幅全体にまたがる、赤、緑、青の領域は重なっていません。これらの領域で、前景レイヤーと名付けた 2 番目のレイヤーを構成します。雲とインタラクティブ・レイヤーのエンティティーの領域は重なっていませんが、ボールが赤の領域まで弾む可能性があるため、雲のエンティティーは別のレイヤーに配置することを検討する必要があります。

黒の領域については、簡単に推測できるはずです。この背景エンティティーが、最後のレイヤーを構成します。このシーンには該当しませんが、背景エンティティーのようなビューポート全体を占める領域は、レイヤー全体を包含する領域であると考える必要があります。3 つのレイヤーを定義できたので、次は、これらのレイヤーをキャンバスに割り当てます (図 5 を参照)。

図 5. レイヤー化されたゲームのビュー
レイヤー化されたゲームのビュー

エンティティーのグループごとにレイヤーが定義されました。これで、キャンバスのクリアを最適化する作業に取り掛かることができます。キャンバスのクリアの最適化で目標とするのは、各ステップでレンダリングする画面表示領域の量を減らして、処理時間を短縮することです。重要な点として、画像ごとに異なるレンダリング戦略を使用して、より適切な最適化を行う場合があることに注意してください。次のセクションでは、各種のエンティティーまたはレイヤーのそれぞれに応じた最適化手法を探ります。


レンダリングの最適化

レイヤー化戦略の中核は、エンティティーを最適化することであり、エンティティーをレイヤー化することで、レンダリング戦略を採用できるようにします。一般に、最適化手法で目指すのは、オーバーヘッドを削減することです。表 1 に示されているように、レイヤーを導入するとメモリーのオーバーヘッドが増加します。このセクションで取り上げる最適化手法では、ゲームの実行速度を向上させるために、プロセッサーが処理しなければならない作業量を減らす手法です。ここでの目標は、レンダリングするスペースの量を減らす手段を見つけること、そして各ステップで行われるレンダリングとクリアの呼び出しを可能な限りなくすことです。

単一エンティティーのクリア

最初の最適化手法では、スペースのクリアをターゲットにしており、エンティティーを構成する画面を部分的にクリアすることのみにより、処理時間を短縮します。まず始めに、再描画領域のそれぞれのエンティティーの周囲にある透明のピクセルと重なる再描画領域の範囲を減らします。この手法を使用するターゲットは明らかに、比較的小さくて、ビューポートでの占有面積が小さいエンティティーです。

最初のターゲットとなるエンティティーは、ボールと障害物です。単一エンティティーをクリアする手法には、エンティティーを新しい場所にレンダリングする前に、前のフレームでそのエンティティーがレンダリングされた領域をクリアする作業が伴います。したがって、各エンティティーのレンダリングにクリア・ステップを組み込んで、エンティティーの画像の境界ボックスを保管しなければなりません。このステップを追加すると、エンティティー・オブジェクトが変更されて、クリア・ステップが組み込まれます (リスト 5 を参照)。

リスト 5. 単一ボックスのクリアが組み込まれたエンティティー
var Entity = function() {
    /**
     Initialization and other methods
     **/
     
    /**
     * Render call to draw the entity
     *
     * @param {CanvasRenderingContext2D} context
     */
    this.render = function(context) {
        context.clearRect(
            this.prevX,
            this.prevY,
            this.width,
            this.height);
        context.drawImage(this.image, this.x, this.y);
        this.prevX = this.x;
        this.prevY = this.y;
    }
};

上記では、render 関数を更新して、通常の drawImage の前に clearRect が呼び出されるようにしています。このステップのために、オブジェクトは前の位置を保管する必要があります。図 6 に、この前の位置の情報を利用してオブジェクトが採る clearRect のステップと、drawImage のステップを示します。

図 6. 矩形のクリア
矩形のクリア

このレンダリング・ソリューションを実装するには、エンティティーごとに clear メソッドを作成し、更新ステップの前にこのメソッドを呼び出すという方法があります (ただし、この記事では clear メソッドを使用しません)。あるいは、このクリア戦略を PanningEntity に組み込んで、地面と雲のエンティティーにクリアを追加することもできます (リスト 6 を参照)。

リスト 6. 単一ボックスのクリアが組み込まれた PanningEntity
var PanningEntity = function() {
    /**
     Initialization and other methods
     **/
     
    /**
     * Render call to draw the panned entity
     *
     * @param {CanvasRenderingContext2D} context
     */
    this.render = function(context) {
        context.clearRect(
            this.x,
            this.y,
            context.canvas.width,
            this.height);
        context.drawImage(
            this.image,
            this.x - this.width,
            this.y - this.height);
        context.drawImage(
            this.image,
            this.x,
            this.y);
        context.drawImage(
            this.image,
            this.x + this.width,
            this.y + this.height);
    }
};

PanningEntity はビューポートの幅全体にまたがるので、クリアする矩形のサイズとしてキャンバスの幅を指定することができます。このクリア戦略を使用した後、雲、丘、地面の各エンティティーに対して定義した再描画領域が指定されます。

雲のエンティティーをさらに最適化するために、雲を個々のエンティティーに分離して、それぞれに固有の再描画領域を持たせることもできます。こうすることで、クリアする雲の再描画領域の画面スペースの量が大幅に削減されるようになります。図 7 に、雲の新しい再描画領域を示します。

図 7. 個々に再描画領域を持つ雲
個々に再描画領域を持つ雲

単一エンティティーのクリア戦略は、この例のようにレイヤー化したキャンバスによるゲームでの問題のほとんどを解決するソリューションになりますが、それでもまだ、最適化できることは残っています。このレンダリング戦略のエッジ・ケースを見つけるための例として、ボールが三角形の障害物と衝突するとします。2 つのエンティティーが衝突すると、エンティティーの再描画領域がオーバーラップして、望ましくないレンダリング成果物が作成される場合が考えられます。そこで、衝突する可能性のあるエンティティーに適したもう 1 つのクリア最適化も、レイヤー化手法にとって有用になります。

ダーティー矩形のクリア

単一エンティティーのクリア戦略が使えない場合、ダーティー矩形のクリア戦略が強力な代替手段となる可能性があります。このクリア戦略は、高密度の粒子系や、小惑星を使用したスペース・ゲームなど、再描画領域を持つエンティティーの数が多い場合に使用されます。

概念としては、このアルゴリズムは管理対象とするすべてのエンティティーの再描画領域を収集し、1 回のクリア呼び出しで領域全体をクリアします。さらなる最適化として、このクリア戦略では、個々のエンティティーによって繰り返し行われるクリア呼び出しも取り除かれます (リスト 7 を参照)。

リスト 7. DirtyRectManager
var DirtyRectManager = function() {
    // Set the left and top edge to the max possible
    // (the canvas width) amd right and bottom to least-most
    
    // Left and top will shrink as more entities are added
    this.left   = canvas.width;
    this.top    = canvas.height;
    
    // Right and bottom will grow as more entities are added
    this.right  = 0;
    this.bottom = 0;
    
    // Dirty check to avoid clearing if no entities were added
    this.isDirty = false;
    
    // Other Initialization Code
    
    /**
     * Other utility methods
     */
    
    /**
     * Adds the dirty rect parameters and marks the area as dirty
     * 
     * @param {number} x
     * @param {number} y
     * @param {number} width
     * @param {number} height
     */
    this.addDirtyRect = function(x, y, width, height) {
        // Calculate out the rectangle edges
        var left   = x;
        var right  = x + width;
        var top    = y;
        var bottom = y + height;
        
        // Min of left and entity left
        this.left   = left < this.left     ? left   : this.left;
        // Max of right and entity right
        this.right  = right > this.right   ? right  : this.right;
        // Min of top and entity top
        this.top    = top < this.top       ? top    : this.top;
        // Max of bottom and entity bottom
        this.bottom = bottom > this.bottom ? bottom : this.bottom;
        
        this.isDirty = true;
    };
    
    /**
     * Clears the rectangle area if the manager is dirty
     *
     * @param {CanvasRenderingContext2D} context
     */
    this.clearRect = function(context) {
        if (!this.isDirty) {
            return;
        }
        
        // Clear the calculated rectangle
        context.clearRect(
            this.left,
            this.top,
            this.right - this.left,
            this.bottom - this.top);
        
        // Reset base values
        this.left   = canvas.width;
        this.top    = canvas.height;
        this.right  = 0;
        this.bottom = 0;
        this.isDirty = false;
    }
};

ダーティー矩形アルゴリズムをレンダリング・ループに統合するには、レンダリングの呼び出しをする前に、リスト 7 のマネージャーを呼び出す必要があります。エンティティーをマネージャーに追加すれば、マネージャーはクリア時に、クリアする矩形のサイズを計算することができます。マネージャーは、ゲーム・ループに応じて望ましい最適化を行うと同時に、そのゲーム・ループを対象とした最適化にも対応することができます (図 8 を参照)。

図 8. インタラクティブ・レイヤーの再描画領域
インタラクティブ・レイヤーの再描画領域
  • フレーム 1 ― エンティティーが衝突しており、オーバーラップしかけています。
  • フレーム 2 ― エンティティーの再描画領域はオーバーラップしています。
  • フレーム 3 ― オーバーラップしている再描画領域が、1 つのダーティー矩形に収集されます。
  • フレーム 4 ― ダーティー矩形がクリアされます。

図 8 には、このアルゴリズムによって計算されたインタラクティブ・レイヤーのエンティティーの再描画領域が示されています。このゲームでのインタラクションが行われるのはこのレイヤーなので、オーバーラップしているインタラクティブ・レイヤーの再描画領域の問題は、ダーティー矩形戦略で十分解決することができます。


クリアとしての上書き

固定された再描画領域内でアニメーション化される、完全に不透明なエンティティーには、上書きを最適化手法として使用することができます。不透明なビットマップを領域にレンダリングすると (これはデフォルトの合成操作です)、その領域で行われた元のレンダリングとは関係なく、その場所にピクセルが配置されます。この最適化ではレンダリングが元の領域をカバーするため、レンダリング呼び出しの前にクリアを呼び出す必要がなくなります。

上書きでは、前に行ったレンダリングの上に画像を再レンダリングすることにより、地面のエンティティーの処理時間が短縮されます。最大のレイヤーである背景の処理時間も、同じようにして短縮されます。

各レイヤーの再描画領域を減らすことで、実質的に各レイヤーとそれぞれのレイヤーに含まれるエンティティーに対する最適化戦略が見つかりました。


まとめ

キャンバスをレイヤー化して最適化するという戦略は、あらゆるインタラクティブなリアルタイムのシーンに適用することができます。最適化のためのレイヤー化には、シーンの再描画領域を分析することで、シーン自体がどのように重なっているのかを検討することが必要です。再描画領域の集合がオーバーラップされてレイヤーが定義されるシーンは、キャンバスをレイヤー化してレンダリングする対象として有力な候補です。粒子系や、互いに衝突する多数の物理オブジェクトを扱う場合には常に、キャンバスをレイヤー化することが最適化に適した選択となるでしょう。


ダウンロード

内容ファイル名サイズ
Article source codeHTML5CanvasRenderingSource.zip3KB

参考文献

学ぶために

  • Google Chrome のタスク・マネージャー: タスク・マネージャーを使用して、Google Chrome で実行中の特定のプロセスに関する詳細情報を入手したり、動作がおかしいタブやアプリケーションを強制終了したりする方法を学んでください。
  • GPU Accelerated Compositing in Chrome」: Chrome にハードウェア・アクセラレーションによる合成処理が実装された背景、そしてその詳細を調べてください。
  • パララックス (視差): Wikipedia でパララックス (視差) 効果についての詳細を読んでください。
  • HTML5 の Canvas を使って素晴らしいグラフィックスを作成する」(developerWorks、2011年2月): Canvas を使用して Web ページを改善する方法を学んでください。Canvas は HTML5 の単純な要素ですが、強力な機能が満載されています。
  • HTML5 Canvas: Canvas API の使用法に焦点を当てたこのデモを見て、極めて単純なアニメーションに色を設定する方法を学んでください。
  • HTML5 の基礎: 第 4 回 最後の仕上げ: Canvas」(developerWorks、2011年7月): HTML5 の canvas 要素を紹介するこの記事に、この要素の機能を説明する数々のサンプル・コードが記載されています。
  • Canvas Pixel Manipulation: Safari Dev Center に用意されているこのデモは、Canvas を使って効果的なビジュアル・アセットを開発する好例です。
  • WHATWG: W3C と協力して HTML5 の微調整に取り組んでいるこの開発者コミュニティーについて調べてください。
  • Canvas チュートリアル: Mozilla の開発者たちによるチュートリアルとデモを調べてください。
  • HTML5 Canvas リファレンス: W3Schools.com で提供している、Canvas の知識を深めるのに役立つ演習を試してみてください。
  • jQuery Events API: ユーザーがブラウザーと対話するときに適用する振る舞いを登録するためのメソッドについて学んでください。
  • developerWorks Web architecture ゾーン: さまざまな Web ベースのソリューションを話題にした記事を調べてください。広範な技術に関する記事とヒント、チュートリアル、標準、そして IBM Redbooks については、Web development の技術文書一覧を参照してください。
  • developerWorks の Technical events and webcasts: これらのセッションで最新情報を入手してください。
  • developerWorks オンデマンド・デモ: 初心者向けの製品のインストールおよびセットアップから熟練開発者向けの高度な機能に至るまで、さまざまに揃ったデモを見てください。
  • Twitter での developerWorks: 今すぐ登録して developerWorks のツイートをフォローしてください。

製品や技術を入手するために

  • OpenGL: 最新のドライバーを入手してください。
  • jQuery: このよく使われている JavaScript ライブラリーをダウンロードしてください。HTML 文書のトラバース、イベント処理、アニメーション化、そして Ajax によるやりとりを単純化するこのライブラリーは、迅速な Web 開発に役立ちます。
  • Modernizr: HTML5 および CSS3 を使用した次世代の Web サイトを構築するのに役立つ、オープンソースの JavaScript ライブラリーを入手してください。
  • Kibo: 特定のブラウザーに依存しない高速キーボード・イベント処理を目的に作成された、この評判の高いライブラリーをダウンロードしてください。
  • IBM 製品の評価版: DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

  • developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、ウィキを調べることができます。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。プロフィールで選択した情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=847109
ArticleTitle=HTML5 キャンバスのレイヤー化によるレンダリングの最適化
publish-date=11292012