WebGPU チュートリアル: Web 上のコンピューティング、頂点、およびフラグメント シェーダー
WebGPU は、最先端の GPU コンピューティング機能を Web にもたらし、共有コード ベースを使用してすべての消費者プラットフォームに利益をもたらすことを約束するグローバル テクノロジーです。
その前身である WebGL は強力ですが、コンピューティング シェーダー機能が著しく不足しており、アプリケーションの範囲が制限されています。
WGSL (WebGPU Shader/Compute Language) は、Rust や GLSL などの分野のベスト プラクティスを活用しています。
WebGPU の使い方を学んでいると、ドキュメントにいくつかのギャップがあることに気づきました。計算シェーダーを使用して頂点シェーダーとフラグメント シェーダーのデータを計算するための簡単な出発点を見つけたいと思っていました。
このチュートリアルのすべてのコードの単一ファイル HTML は、https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb にあります。詳細な内訳については、以下をお読みください。
これは、私のドメインで実行されるこの HTML のシングルクリックのデモです: https://www.php.cn/link/bed827b4857bf056d05980661990ccdc Chrome や Edge などの WebGPU ベースのブラウザhttps://www.php.cn/link/bae00fb8b4115786ba5dbbb67b9b177a)。
詳細設定
これは粒子シミュレーションです。時間の経過とともにタイムステップで発生します。
時間は JS/CPU で追跡され、(float) 均一として GPU に渡されます。
パーティクル データは完全に GPU 上で管理されますが、CPU と対話し続けるため、メモリの割り当てと初期値の設定が可能になります。データを CPU に読み戻すことも可能ですが、このチュートリアルでは省略します。
このセットアップの魅力は、各パーティクルが他のすべてのパーティクルと並行して更新され、ブラウザでの驚異的な計算速度とレンダリング速度が可能になることです (並列化により GPU のコア数が最大化されます。パーティクルの数は次のように割ることができます)。コアごとの更新ステップごとの真のサイクル数を取得するには、コアの数を使用します)。
バインド
WebGPU が CPU と GPU 間のデータ交換に使用するメカニズムはバインドです。WebGPU バッファーを使用して、JS 配列 (Float32Array など) を WGSL のメモリ位置に「バインド」できます。 WGSL メモリの場所は、グループ番号とバインディング番号という 2 つの整数によって識別されます。
私たちのケースでは、コンピューティング シェーダーと頂点シェーダーの両方が、時間とパーティクルの位置という 2 つのデータ バインディングに依存しています。
時間 - 制服
コンピューティング シェーダー (https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb#L43) と頂点シェーダーには均一な定義が存在します。 (https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb#L69) 中 - シェーダー更新位置を計算し、頂点シェーダーは時間に基づいて色を更新します。
コンピューティング シェーダーから始めて、JS と WGSL のバインディング セットアップを見てみましょう。
<code>const computeBindGroup = device.createBindGroup({ /* 参见 computePipeline 定义,网址为 https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb#L102 它允许将 JS 字符串与 WGSL 代码链接到 WebGPU */ layout: computePipeline.getBindGroupLayout(0), // 组号 0 entries: [{ // 时间绑定在绑定号 0 binding: 0, resource: { /* 作为参考,缓冲区声明为: const timeBuffer = device.createBuffer({ size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST}) }) https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb#L129 */ buffer: timeBuffer } }, { // 粒子位置数据在绑定号 1(仍在组 0) binding: 1, resource: { buffer: particleBuffer } }] });</code>
およびコンピューティング シェーダー内の対応する宣言
<code>// 来自计算着色器 - 顶点着色器中也有类似的声明 @group(0) @binding(0) var<uniform> t: f32; @group(0) @binding(1) var<storage read_write=""> particles : array<particle>; </particle></storage></uniform></code>
重要なのは、JS と WGSL のグループ番号とバインディング番号を一致させることによって、JS 側の timeBuffer を WGSL にバインドしていることです。
これにより、JS から変数の値を制御できるようになります:
<code>/* 数组中只需要 1 个元素,因为时间是单个浮点值 */ const timeJs = new Float32Array(1) let t = 5.3 /* 纯 JS,只需设置值 */ timeJs.set([t], 0) /* 将数据从 CPU/JS 传递到 GPU/WGSL */ device.queue.writeBuffer(timeBuffer, 0, timeJs);</code>
パーティクルの位置 - WGSL ストレージ
粒子の位置を GPU からアクセス可能なメモリに直接保存して更新することで、GPU の大規模なマルチコア アーキテクチャを利用して粒子の位置を並行して更新できるようになります。
並列化は、コンピューティング シェーダーで宣言されたワーク グループ サイズを使用して調整されます。
<code>@compute @workgroup_size(64) fn main(@builtin(global_invocation_id) global_id : vec3<u32>) { // ... } </u32></code>
@builtin(global_invocation_id) global_id : vec3
定義により、global_invocation_id = workgroup_id * workgroup_size local_invocation_id - これは、パーティクル インデックスとして使用できることを意味します。
たとえば、10,000 個のパーティクルがあり、workgroup_size が 64 の場合、Math.ceil(10000/64) ワークグループをスケジュールする必要があります。コンピューティング パスが JS からトリガーされるたびに、次の量の作業を実行するように GPU に明示的に指示します:
<code>computePass.dispatchWorkgroups(Math.ceil(PARTICLE_COUNT / WORKGROUP_SIZE));</code>
PARTICLE_COUNT == 10000 および WORKGROUP_SIZE == 64 の場合、157 個のワークグループ (10000/64 = 156.25) が開始され、各ワークグループの local_invocation_id の計算範囲は 0 ~ 63 になります (workgroup_id の範囲は 0 ~ 157 です)。 )。 157 * 64 = 1048 なので、最終的にはワークグループでもう少し多くの計算を行うことになります。冗長な呼び出しを破棄することでオーバーフローを処理します。
これらの要素を考慮したシェーダーの計算の最終結果は次のとおりです:
<code>@compute @workgroup_size(${WORKGROUP_SIZE}) fn main(@builtin(global_invocation_id) global_id : vec3<u32>) { let index = global_id.x; // 由于工作组网格未对齐,因此丢弃额外的计算 if (index >= arrayLength(&particles)) { return; } /* 将整数索引转换为浮点数,以便我们可以根据索引(和时间)计算位置更新 */ let fi = f32(index); particles[index].position = vec2<f32>( /* 公式背后没有宏伟的意图 - 只不过是用时间+索引的例子 */ cos(fi * 0.11) * 0.8 + sin((t + fi)/100)/10, sin(fi * 0.11) * 0.8 + cos((t + fi)/100)/10 ); } </f32></u32></code>
パーティクルはストレージ変数として定義されているため、これらの値は計算パス全体で保持されます。
頂点シェーダーのコンピュートシェーダーでパーティクルの位置を読み取ります
コンピューティング シェーダーのみがストレージに書き込むことができるため、コンピューティング シェーダーから頂点シェーダー内のパーティクルの位置を読み取るには、読み取り専用ビューが必要です。
以下は WGSL からの声明です:
<code>@group(0) @binding(0) var<uniform> t: f32; @group(0) @binding(1) var<storage> particles : array<vec2>>; /* 或等效: @group(0) @binding(1) var<storage read=""> particles : array<vec2>>; */ </vec2></storage></vec2></storage></uniform></code>
コンピューティング シェーダーで同じ read_write スタイルを再利用しようとすると、エラーが発生します:
<code>var with 'storage' address space and 'read_write' access mode cannot be used by vertex pipeline stage</code>
頂点シェーダーのバインディング番号は、コンピューティング シェーダーのバインディング番号と一致する必要はないことに注意してください。頂点シェーダーのバインディング グループ宣言と一致する必要があるだけです。
<code>const renderBindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: timeBuffer } }, { binding: 1, resource: { buffer: particleBuffer } }] });</code>
GitHub サンプル コードで binding:2 を選択しました https://www.php.cn/link/2e5281ee978b78d6f5728aad8f28fedb#L70 - WebGPU によって課される制約の境界を調べるためだけに
シミュレーションを段階的に実行します
すべての設定が完了すると、更新ループとレンダリング ループが JS で調整されます。
<code>/* 从 t = 0 开始模拟 */ let t = 0 function frame() { /* 为简单起见,使用恒定整数时间步 - 无论帧速率如何,都会一致渲染。 */ t += 1 timeJs.set([t], 0) device.queue.writeBuffer(timeBuffer, 0, timeJs); // 计算传递以更新粒子位置 const computePassEncoder = device.createCommandEncoder(); const computePass = computePassEncoder.beginComputePass(); computePass.setPipeline(computePipeline); computePass.setBindGroup(0, computeBindGroup); // 重要的是要调度正确数量的工作组以处理所有粒子 computePass.dispatchWorkgroups(Math.ceil(PARTICLE_COUNT / WORKGROUP_SIZE)); computePass.end(); device.queue.submit([computePassEncoder.finish()]); // 渲染传递 const commandEncoder = device.createCommandEncoder(); const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(), clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, loadOp: 'clear', storeOp: 'store', }] }); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, renderBindGroup); passEncoder.draw(PARTICLE_COUNT); passEncoder.end(); device.queue.submit([commandEncoder.finish()]); requestAnimationFrame(frame); } frame();</code>
結論
WebGPU は、ブラウザ内で大規模並列 GPU コンピューティングのパワーを解放します。
パスで実行されます。各パスには、メモリ バインディング (CPU メモリと GPU メモリのブリッジ) を備えたパイプラインを通じて有効化されたローカル変数があります。
コンピューティングの配信により、ワークグループを通じて並列ワークロードを調整できます。
多少の重いセットアップが必要ですが、ローカル バインディング/状態スタイルは WebGL のグローバル状態モデルに比べて大幅に改善されていると思います。使いやすくなり、同時に GPU コンピューティングのパワーを最終的に Web にもたらします。
以上がWebGPU チュートリアル: Web 上のコンピューティング、頂点、およびフラグメント シェーダーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます
