ホームページ  >  記事  >  ウェブフロントエンド  >  three.js_html5 チュートリアル スキルでの needUpdate の適用に関する簡単な説明

three.js_html5 チュートリアル スキルでの needUpdate の適用に関する簡単な説明

WBOY
WBOYオリジナル
2016-05-16 15:50:572638ブラウズ

three.js の多くのオブジェクトには、needsUpdate 属性がありますが、ドキュメントではほとんど言及されていません (ただし、three.js に関するドキュメントはあまりなく、多くの問題は、インターネット上のさまざまなチュートリアルで解決する必要があります)。単純な入門レベルのプログラムではこの属性は使用されないため、これを書くのはあまり得意ではありません。
では、この属性は何に使用されるのでしょうか? 簡単に言うと、このフレームのキャッシュを更新する必要があることをレンダラーに伝えますが、キャッシュが必要な理由を知りたいため、フラグとして使用するのは非常に簡単です。どのキャッシュが更新されるのかを確認する必要があるので、よく理解する必要があります。
needsUpdate が必要な理由
まず、キャッシュが必要な理由を見てみましょう。キャッシュは通常、データ送信の数を減らし、それによってプログラムにかかる時間を短縮するために存在します。データ転送も同様です。 通常、オブジェクト(メッシュ)を画面上に表示するには3回も転送する必要があります。
まず、頂点データとテクスチャデータがすべて揃っています。プログラムを通じてローカルディスクからメモリに読み込みます。
プログラムはメモリ内で適切な処理を行った後、画面に描画する必要があるオブジェクトの頂点データとテクスチャ データをビデオ メモリに転送します。
最後に、各フレームをレンダリングするときに、ビデオ メモリ内の頂点データとテクスチャ データがアセンブリと描画のために GPU にフラッシュされます。
ピラミッド データ送信モデルによれば、最初のステップが WebGL のような環境でネットワーク経由で送信される場合、メモリからビデオへの送信時間はさらに遅くなります。簡単なデータテストは後で行います。
次に、これら 3 つのステップの使用頻度ですが、小さなシーンの場合、最初のステップは 1 回限りの操作です。つまり、プログラムが初期化されるたびにシーンのすべてのデータがメモリにロードされます。大規模なシナリオの場合、非同期読み込みが行われる可能性がありますが、現在検討されていません。 2 番目のステップの頻度は、今回説明する最も重要なことです。まず、この送信ステップによる消費をテストする簡単なプログラムを作成します

コードをコピーします
コードは次のとおりです:

var Canvas = document.createElement('canvas');
var _gl = Canvas.getContext( '実験的 -webgl');
var vertices = [];
for(var i = 0; i vertices.push(i * Math.random() );
}
varbuffer = _gl.createBuffer();
console.profileEnd('buffer_test'); 🎜> 関数 bindingBuffer(){
for(var i = 0; i _gl.bindBuffer(_gl.ARRAY_BUFFER, バッファ);
_gl.ARRAY_BUFFER,新しい Float32Array (頂点)、_gl.STATIC_DRAW);
}
}


まずこのプログラムについて簡単に説明しますと、Vertices は頂点を格納する配列であり、各頂点には x、y、z の 3 つの座標が含まれるため、3000 個のサイズの配列が必要になります。 _gl.createBuffer コマンドは、ビデオ メモリ内のバッファを開いて頂点データを保存し、_gl.bufferData を使用して、生成された頂点データのコピーをメモリからビデオ メモリに転送します。 ここでは、シーン内に 1000 個の頂点を持つ 1000 個のオブジェクトがあると仮定します。各頂点は 32 ビットおよび 4 バイトの 3 つの float データであり、計算するとほぼ 1000 x 1000 x 12 = 11M のデータになります。 15msという時間、ここでは15msがほんのわずかな時間であることがわかるかもしれませんが、リアルタイムプログラムの場合、30fpsのフレームレートを確保したい場合、各フレームに必要な時間を30ms程度に制御する必要があります。つまり、データの送信にかかる時間は半分だけです。レンダリング プロセス全体のすべてのステップは、GPU での描画操作と、CPU でのさまざまな処理で行う必要があることを知っておく必要があります。
したがって、このステップでの送信数は最小限に抑える必要があります。実際、すべての頂点データとテクスチャ データは、ロードされるとすぐにメモリからビデオ メモリに転送されます。これが、three.js の動作です。このとき、まず描画する必要のあるオブジェクト(Geometry)の頂点データを表示メモリに転送し、バッファをgeometry.__webglVertexBufferにキャッシュします。その後、GeometryのverticesNeedUpdate属性が毎回判定されます。更新が必要ない場合は、現在のキャッシュが直接使用され、verticesNeedUpate が true であることがわかると、Geometry の頂点データが geometry.__webglVertexBuffer に再送信されます。静的オブジェクトの場合は、頂点が頻繁に変化するオブジェクトに遭遇した場合は、頂点をパーティクルとして使用するパーティクル システムや、スケルトン アニメーションを使用するメッシュを使用します。これらのオブジェクトはフレームごとに頂点を変更するため、verticesNeedUpdate プロパティを設定する必要があります。すべてのフレームを true に設定して、データを再送信する必要があることをレンダラーに伝えます。
実際、WebGL プログラムでは、頂点シェーダーで頂点の位置を変更してパーティクル エフェクトやスケルトン アニメーションを完成させることがほとんどですが、JavaScript の計算能力の制限により、CPU 側で計算した方が拡張が容易です。 , これらの計算負荷の高い操作のほとんどは GPU 側で実行されます。 この場合、頂点データを再送信する必要がないため、実際のプログラムでは上記のケースはあまり使用されず、テクスチャやマテリアルのキャッシュが更新されることが多くなります。
上記のケースは主に頂点データを送信するシーンを説明していますが、頂点データに加えて、1024*1024 サイズの R8G8B8A8 形式のテクスチャも最大 4M のメモリを占有します。次の例

コードをコピーします
コードは次のとおりです。

var Canvas =ドキュメント。createElement('canvas');
var _gl = Canvas.getContext('experimental-webgl'); img.onload = function(){
console.profile('テクスチャ テスト');
console.profileEnd('テクスチャ テスト'); src = 'test_tex.jpg';
関数bindTexture(){
_gl.bindTexture(_gl.TEXTURE_2D, テクスチャ);
_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA , _gl .UNSIGNED_BYTE, img);
}


ここで 1000 回繰り返す必要はありません。10241024 のテクスチャを 1 回送信するには 30ms かかります。したがって、three.js では、テクスチャは最初に 1 回だけ送信されます。 texture.needsUpdate プロパティが手動で true に設定されていない場合は、ビデオ メモリに転送されたテクスチャが直接使用されます。
どのキャッシュを更新する必要があります
上記では、three.js がそのような needUpdate 属性を追加する必要がある理由を 2 つのケースを通して説明しています。次に、どのような状況で必要かを知るためにいくつかのシナリオをリストします。これらのキャッシュは手動で更新してください。
テクスチャの非同期読み込み
画像を作成した直後に texture.needsUpdate=true を記述すると、three.js にフロントエンド画像が非同期で読み込まれるため、これは小さな落とし穴です。レンダラーは _gl.texImage2D を使用して、このフレームの空のテクスチャ データをビデオ メモリに転送し、その後、画像がロードされるまでビデオ メモリ データは更新されないため、待つ必要があります。 texture.needsUpdate = true
Video Texture
ほとんどのテクスチャは、上記の場合と同様に画像を 1 回ロードして転送するだけですが、 の場合はそうではありません。ビデオ テクスチャ。ビデオはピクチャ ストリームであり、表示されるピクチャはフレームごとに異なるため、グラフィックス カード内のテクスチャ データを更新するには、フレームごとに needUpdate を true に設定する必要があります。
レンダー バッファーを使用する
レンダー バッファーは特別なオブジェクトです。通常、プログラムはシーン全体を画面に直接フラッシュしますが、さらに後処理がある場合や、この画面ベースの xxx (たとえば、画面ベースのアンビエント オクルージョンなど)を使用するには、最初にシーンをレンダー バッファーに描画する必要があります。このバッファーは実際にはテクスチャですが、ディスクからロードするのではなく、前の描画によって生成されます。 three.js には、レンダーバッファを初期化して保存するための特別なテクスチャ オブジェクト WebGLRenderTarget があります。このテクスチャは、各フレームで needUpdate を true に設定する必要もあります。
マテリアルの needUpdate
マテリアルは 3 つあります。実際、マテリアルには送信するデータがありません。なぜ、needsUpdate を作成する必要があるのでしょうか。シェーダーの直訳はシェーダーです。 GPU で提供される頂点とピクセルを処理するためのプログラミングの可能性 ペイントのシェーディング方法を表す用語としてシェーディングがあります。ライティングのシェーディングもプログラムによって計算されます。シェーダーは実行されるので、すべてのプログラムと同様に、WebGL ではシェーダー プログラムを実行時にコンパイルする必要がありますが、これには当然時間がかかります。したがって、プログラムを一度コンパイルして実行するのが最善です。そこでthree.jsでは、マテリアルの初期化時にシェーダプログラムのコンパイルとリンクを行い、コンパイルとリンク後に得られたプログラムオブジェクトをキャッシュします。一般に、マテリアルを調整するには、シェーダ全体を再コンパイルする必要はありません。シェーダの均一パラメータを変更するだけです。ただし、元のフォン シェーダをランバート シェーダに置き換えるなど、マテリアル全体を置き換える場合は、material.needsUpdate を true に設定して再コンパイルする必要があります。ただし、このような状況はまれであり、より一般的なのは以下の状況です。
ライトの追加と削除
これは、three.js を使い始めたばかりの多くの人が、設定後にシーンに動的に追加する可能性があります。しかし、これは、phong や lambert などのthree.jsの組み込みシェーダを使用している場合でした。レンダラでソースコードを見ると、そのことがわかります。 three.js は組み込みシェーダのコードにあり、#define を使用してシーン内のライトの数を設定し、この #define の値はマテリアルが更新されるたびに string splicing シェーダによって取得されます。 コードは次のとおりです。

コードをコピーします
コードは次のとおりです:

"#define MAX_DIR_LIGHTS " パラメーター.maxDirLights,
"#define MAX_POINT_LIGHTS "parameters.maxPointLights,
"#define MAX_SPOT_LIGHTS "parameters.maxSpotLights,
"#define MAX_HEMI_LIGHTS "parameters.maxHemiLights,

この書き方により、GPU レジスタの使用を効果的に削減できることは事実です。ライトが 1 つしかない場合、宣言できるのは 1 つのライトに必要なユニフォーム変数のみですが、ライトの数が変わるたびに (特に、ライトの数が変わる場合)。追加 シェーダーを再ステッチ、コンパイル、リンクする必要があります。このとき、すべてのマテリアルのmaterial.needsUpdateをtrueに設定する必要もあります。
テクスチャを変更します。テクスチャデータを更新するという意味ではなく、元のマテリアルがテクスチャを使用していたが使用を中止した、または元のマテリアルがテクスチャを使用していなかったために後から強制的にマテリアルを更新しないと、最終的な効果が変わってしまいます。この問題の原因は次のとおりです。 上記のライトの追加は、テクスチャが使用されるかどうかを決定するマクロがシェーダーに追加されるため、


となります。
コードをコピーしますコードは次のとおりです:
parameters.map? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.bumpMap ? "#define USE_BUMPMAP" : "",
parameters.normalMap ? define USE_NORMALMAP" : "",
parameters.specularMap ? "#define USE_SPECULARMAP" : "",


したがって、map、envMap、または lightMap が真の値を変更するたびに、マテリアルは次のことを行う必要があります。

他の頂点データの変更
実際には、上記のテクスチャの変更も問題を引き起こします。主な理由は、初期化中にテクスチャが存在せず、後で動的に追加されることです。この環境では、material.needsUpdate を true に設定するだけでは不十分です。 geometry.uvsNeedsUpdate を true に設定する必要もあります。なぜこの問題は、three.js によるプログラムの最適化によって発生するのでしょうか。メモリ内のデータには各頂点の UV データが含まれていますが、テクスチャがないと判断された場合、レンダラーでジオメトリとマテリアルが初めて初期化されますが、three.js はこれらのデータをビデオにコピーしません。本来の目的は貴重なビデオ メモリ スペースを節約することですが、テクスチャを追加した後、ジオメトリはテクスチャ使用のために UV データをインテリジェントに再送信しなくなり、更新する時期が来たことを伝えるために uvsNeedsUpdate を手動で設定する必要があります。この問題は最初は長い間私を悩ませていました。 いくつかの頂点データの needUpdate 属性については、この問題を参照してください
https://github.com/mrdoob/three.js/wiki/Updates

最後に
three .js の最適化は良好ですが、さまざまな最適化にはさまざまな落とし穴が伴います。この場合、最善の方法は、ソース コードを確認するか、github 上のファイルの問題を確認することです。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。