ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScriptで3Dエンジンを構築します
このチュートリアルの目的は、webglなしでWeb用のシンプルな3Dエンジンをどのように構築できるかを説明することです。まず、3Dシェイプを保存する方法を確認します。次に、これらの形状を2つの異なるビューで表示する方法を確認します。
キーテイクアウト
JavaScriptのみを利用して、WebGLなどの高度なライブラリを必要とせずにシンプルな3Dエンジンを作成でき、キューブなどの基本的な形状をレンダリングするのに適しています。 このエンジン内の3Dモデリングには、ポリヘドロンが不可欠です。このエンジンでは、2Dのポリゴンと同様に、平らな面を使用して複雑な形状が近似されます。
効率的なメモリ管理は、頂点を複製するのではなく、頂点への参照を保存することによって達成されます。これにより、各頂点が複数の面で共有されるため、メモリの使用量が大幅に削減されます。
多面体を保存する方法を推測するには、数学でそのようなことをどのように識別できるかを覚えておく必要があります。あなたは確かにあなたの学年の間にすでにいくつかの基本的な幾何学をしました。たとえば、正方形を識別するには、a、b、c、およびdでabcdと呼び、正方形の各角を構成する頂点を指します。
3Dエンジンの場合、同じになります。まず、形状の各頂点を保存することから始めます。次に、この形状はその顔を一覧表示し、各顔はその頂点をリストします。頂点を表すには、正しい構造が必要です。ここでは、頂点の座標を保存するクラスを作成します。
これで、頂点は他のオブジェクトと同様に作成できます。
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>次に、多面体を表すクラスを作成します。例としてキューブを取りましょう。クラスの定義は以下にあり、その直後の説明があります。
このクラスを使用して、その中心とそのエッジの長さを示すことにより、仮想キューブを作成できます。
<span>var A = new Vertex(10, 20, 0.5); </span>
キューブクラスのコンストラクターは、指定された中心の位置から計算されたキューブの頂点を生成することから始まります。スキーマはより明確になるので、生成する8つの頂点の位置を参照してください。
<span>var <span>Cube</span> = function(center<span>, size</span>) { </span> <span>// Generate the vertices </span> <span>var d = size / 2; </span> <span>this.vertices = [ </span> <span>new Vertex(center.x - d, center.y - d, center.z + d), </span> <span>new Vertex(center.x - d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z + d) </span> <span>]; </span> <span>// Generate the faces </span> <span>this.faces = [ </span> <span>[this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3]], </span> <span>[this.vertices[3], this.vertices[2], this.vertices[5], this.vertices[4]], </span> <span>[this.vertices[4], this.vertices[5], this.vertices[6], this.vertices[7]], </span> <span>[this.vertices[7], this.vertices[6], this.vertices[1], this.vertices[0]], </span> <span>[this.vertices[7], this.vertices[0], this.vertices[3], this.vertices[4]], </span> <span>[this.vertices[1], this.vertices[6], this.vertices[5], this.vertices[2]] </span> <span>]; </span><span>}; </span>
次に、顔をリストします。各顔は正方形なので、各顔に4つの頂点を示す必要があります。ここで私は配列で顔を表現することを選びましたが、必要に応じて、そのための専用クラスを作成することができます。
<span>var cube = new Cube(new Vertex(0, 0, 0), 200); </span>顔を作成するとき、4つの頂点を使用します。それらはthis.vertices [i]オブジェクトに保存されているため、それらの位置を再度示す必要はありません。それは実用的ですが、私たちがそれをしたもう一つの理由があります。
デフォルトでは、JavaScriptは可能な限り最小のメモリの使用を試みます。それを達成するために、関数の引数として渡されたり、アレイに保存されたりするオブジェクトをコピーしません。私たちの場合、それは完璧な動作です。
実際、各頂点には3つの数値(座標)に加えて、追加する必要がある場合はいくつかの方法が含まれています。顔ごとに頂点のコピーを保存すると、多くのメモリを使用しますが、これは役に立ちません。ここで、私たちが持っているのは参照です。座標(およびその他の方法)は一度だけ保存されます。各頂点はコピーではなく参照を保存することにより、3つの異なる面で使用されているため、必要なメモリを3つ(多かれ少なかれ)!
既に3D(たとえばBlenderなどのソフトウェア、またはWebGLのようなライブラリを使用して)を使用している場合、三角形を使用する必要があると聞いたことがあるかもしれません。ここでは、三角形を使用しないことを選択しました
この選択の背後にある理由は、この記事がトピックの紹介であり、キューブのような基本的な形状を表示することです。三角形を使用して正方形を表示することは、私たちの場合の他の何よりも複雑なものです。 ただし、より完全なレンダラーを構築する予定がある場合は、一般に、三角形が望ましいことを知る必要があります。これには2つの主な理由があります:
テクスチャ:顔に画像を表示するには、いくつかの数学的な理由で三角形が必要です;
画像のレンダリング
3Dオブジェクトを保存する方法と行動する方法を知っています。さあ、それらを見る方法を見る時が来ました!しかし、最初に、私たちが何をしようとしているのかを理解するために、理論に小さな背景が必要です。
投影<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>
今、私たちは何をすべきかを知っています:(x、y、z)ベースに座標があり、それらを表示するには、(x、z)ベースでそれらを座標に変換する必要があります:平面であるため、それらを表示することができます。
1つの投影だけではありません。さらに悪いことに、さまざまな投影が無限に存在します!この記事では、実際に最も使用されているものである2つの異なる種類の投影が表示されます。
シーンをレンダリングする方法
関数のコードを見て、それに続いて説明:
この関数は説明に値します。より正確には、このプロジェクト()機能とは何か、そしてこれらのDXおよびDYの引数は何であるかを説明する必要があります。残りは基本的にオブジェクトをリストしてから各顔を描くこと以外は何もありません。
その名前が示すように、プロジェクト()関数は3D座標を2Dの座標に変換するためにここにあります。 3D空間の頂点を受け入れ、2D平面の頂点を返します。
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>座標xとzに名前を付ける代わりに、z座標をyに変更するためにここで選択しました。2Dジオメトリでよく見られる古典的な慣習を維持しますが、必要に応じてzを維持できます。
プロジェクト()の正確なコンテンツは、次のセクションで表示されるものです。選択したプロジェクションのタイプに依存します。しかし、このタイプが何であれ、render()関数は今のように保持できます。
飛行機に座標があると、キャンバスにそれらを表示できます。それが私たちがしていることです...少しのトリックで:プロジェクト()関数によって返された実際の座標を実際に描画しません。
実際、プロジェクト()関数は仮想2D平面で座標を返しますが、3Dスペースで定義したものと同じ起源を持っています。ただし、起源がキャンバスの中心にあることを望んでいるため、座標を翻訳するのはそのためです。頂点(0,0)はキャンバスの中心にありませんが、(0 dx、0 dy)は、賢明にdxとdyを選択してください。 (DX、DY)がキャンバスの中心にあることを望んでいるように、私たちは本当に選択肢がなく、DX = canvas.width / 2およびdy = canvas.height / 2を定義します。
最後に、最後の詳細:なぜyを使用していて、直接yを使用しているのですか?答えは私たちの選択です。Z軸は上部に向けられています。次に、私たちのシーンでは、正のz座標を持つ頂点が上がります。ただし、キャンバスでは、y軸は底に向けられます。正のy座標を持つ頂点が下に移動します。だからこそ、キャンバスのY座標をキャンバス上のZ座標の逆の逆として定義する必要があります。render()関数が明確になったので、project()を見る時が来ました。
正書法のビュー
3つの座標があり、2つしか必要です。そのような場合に最も簡単なことは何ですか?座標の1つを削除します。そして、それが私たちが正書法の見方で行うことです。深さを表す座標を削除します:y座標。
この記事の冒頭以来、書いたすべてのコードをテストできるようになりました。おめでとうございます、あなたはフラット画面に3Dオブジェクトを表示しました!
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>この関数は以下のライブ例に実装されています。ここでは、マウスで回転することでキューブと対話できます。
時々、類似点を維持するという利点があるため、正書法の見解が私たちが望むものです。しかし、それは最も自然な見方ではありません。私たちの目はそのように見えません。そのため、2番目の予測が表示されます。
パースペクティブビューいくつかの計算を行う必要があるため、パースペクティブビューは正書法のものよりも少し複雑です。ただし、これらの計算はそれほど複雑ではなく、1つのことを知る必要があります。インターセプト定理の使用方法。
理由を理解するには、正書法の見解を表すスキーマを見てみましょう。私たちは直交する方法で飛行機にポイントを投影しました。
基本的に、2つのステップがあります
正書法の見解に反して、ここの飛行機の正確な位置が重要です。カメラから遠く離れた飛行機を配置すると、それを近くに置く場合と同じ効果が得られません。ここでは、カメラから距離dに配置します。
3D空間の頂点m(x、y、z)から始まると、平面上の投影m 'の座標(x'、z ')を計算したい。
これらの座標を計算する方法を推測するために、別の視点を取り、上から同じスキーマを見てみましょう。
インターセプト定理で使用される構成を認識できます。上記のスキーマでは、とりわけ、x、y、dなどのいくつかの値がわかります。 x 'を計算したいので、インターセプト定理を適用して、この方程式を取得します:x' = d / y * x。
パースペクティブビューを使用してプロジェクト()関数を書くことができます:
この関数は、以下のライブ例でテストできます。繰り返しになりますが、キューブと対話できます。
codepenのSitePoint(@sitepoint)によるペン3Dパースペクティブビューを参照してください。
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>単語を閉じます
(非常に基本的な)3Dエンジンは、必要な3D形状を表示する準備ができました。それを強化するためにできることがいくつかあります。たとえば、私たちは形のすべての顔、後ろの顔さえも見ます。それらを隠すために、バックフェイスのcullingを実装できます。
また、テクスチャについては話しませんでした。ここで、私たちのすべての形状は同じ色を共有しています。たとえば、オブジェクトにカラープロパティを追加して、それらを描く方法を知ることで変更できます。多くのことを変えることなく、顔ごとに1色を選択することもできます。また、顔に画像を表示することもできます。ただし、そのようなことを行う方法を詳細に説明することはより困難であり、記事全体を撮るでしょう。
他のものを変更できます。カメラをスペースの原点に配置しましたが、移動できます(頂点を投影する前に、基礎の変更が必要になります)。また、カメラの後ろに配置された頂点がここに描かれていますが、それは私たちが望むものではありません。クリッピングプレーンはそれを修正できます(理解しやすく、実装しやすい)。ここで構築した3Dエンジンは、完全になるまで遠くにあり、それは私自身の解釈でもあります。他のクラスに独自のタッチを追加できます。たとえば、Three.jsは専用のクラスを使用してカメラとプロジェクションを管理します。また、基本的な数学を使用して座標を保存しましたが、より複雑なアプリケーションを作成したい場合、たとえばフレーム中に多くの頂点を回転させる必要がある場合は、スムーズな体験がありません。それを最適化するには、より複雑な数学が必要になります:均質座標(射影幾何学)とQuaternions。
エンジンの独自の改善のアイデアがある場合、またはこのコードに基づいてクールなものを構築した場合は、以下のコメントでお知らせください!JavaScriptで3Dエンジンの構築に関するよくある質問(FAQ) JavaScriptで3Dエンジンを構築するための前提条件は何ですか?
JavaScriptで3Dエンジンを構築するには、JavaScriptとその概念を確実に理解する必要があります。 HTMLとCSSに精通していることも有益です。ベクトル、マトリックス、四項を含む3D数学の知識が重要です。パイプライン、シェーダー、テクスチャマッピングのレンダリングなど、コンピューターグラフィックの基本を理解することも役立ちます。 3D数学を学ぶためにオンラインで利用可能ないくつかのリソース。 Khan AcademyのようなWebサイトは、3D数学を理解するための基本的な線形代数とベクター計算に関するコースを提供しています。 「グラフィックスとゲーム開発のための3D Math Primer」などの本も役立ちます。 JavaScriptで3Dエンジンを構築するための最も人気のあるライブラリの2つ。どちらのライブラリもWebGLに高レベルのインターフェイスを提供し、複雑な3Dシーンを簡単に作成できるようにします。また、広範なドキュメントとコミュニティのサポートを提供しています。JavaScript 3Dエンジンを最適化してパフォーマンスを向上させるにはどうすればよいですか? 1つの方法は、インスタンスやバッチなどの手法を使用して、抽選コールの数を最小限に抑えることです。別の方法は、圧縮されたテクスチャとジオメトリを使用してGPUに送信されるデータの量を減らすことです。パフォーマンスを向上させるためにシェーダーを最適化することもできます。 。ブラウザが提供する「AddEventListener」メソッドを使用して、これらのイベントをリッスンできます。その後、イベントデータを使用して、3Dシーンのカメラまたはその他の要素を制御できます。JavaScript 3Dエンジンに照明を追加するには、光源の作成とシェーディングモデルの実装が含まれます。光源は、方向性、ポイント、またはスポットライトにすることができます。表面が光にどのように反応するかを決定するシェーディングモデルは、単純な(ランバートシェーディングなど)または複雑な(物理ベースのレンダリングなど)。
JavaScript 3Dエンジンにテクスチャを追加するには、画像ファイルのロード、テクスチャオブジェクトの作成、ジオメトリへのマッピングが含まれます。ブラウザが提供する「画像」オブジェクトを使用して画像をロードできます。次に、WebGLが提供する「GL.CreatEtexture」メソッドを使用してテクスチャオブジェクトを作成できます。時間の経過に伴うオブジェクトのプロパティ。ブラウザが提供する「requestAnimationFrame」メソッドを使用して、一貫したレートでオブジェクトのプロパティを更新するループを作成できます。その後、補間技術を使用して、異なる状態間でスムーズに移行できます。 。これは、境界ボックス間のオーバーラップをチェックするのと同じくらい簡単です。複雑なジオメトリ間の交差点をチェックするのと同じくらい複雑です。衝突が検出されたら、衝突オブジェクトのプロパティを変更することで応答できます。 3Dグラフィックスの複雑さ。ただし、WebGLインスペクターやChrome Devtoolsなどのツールは非常に役立ちます。これらのツールを使用すると、WebGLコンテキストの状態を検査し、バッファーとテクスチャの内容を表示し、シェーダーを踏むことができます。以上がJavaScriptで3Dエンジンを構築しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。