目次

webgpufundamentals.org

Fix, Fork, Contribute

WebGPU スケール

この記事はGemini Code Assistによって自動翻訳されました。翻訳に問題がある場合は、お手数ですがこちらからPull Requestを送信してください。

この記事は、3D数学について学ぶことを目的とした一連の記事の3番目です。各記事は前のレッスンを基にしているので、順番に読むと最も理解しやすいかもしれません。

  1. 平行移動
  2. 回転
  3. スケーリング ⬅ ここです
  4. 行列演算
  5. 正射影
  6. 透視投影
  7. カメラ
  8. 行列スタック
  9. シーングラフ

スケーリングは、平行移動と同じくらい簡単です。

頂点位置に目的のスケールを乗算します。前の例のシェーダーへの変更は次のとおりです。

  • struct Uniforms {
  • color: vec4f,
  • resolution: vec2f,
  • translation: vec2f,
  • rotation: vec2f,
  • scale: vec2f,
  • };
  •  
  • struct Vertex {
  • @location(0) position: vec2f,
  • };
  •  
  • struct VSOutput {
  • @builtin(position) position: vec4f,
  • };
  •  
  • @group(0) @binding(0) var<uniform> uni: Uniforms;
  •  
  • @vertex fn vs(vert: Vertex) -> VSOutput {
  • var vsOut: VSOutput;
  •  
  • // 位置をスケーリングします
  • let scaledPosition = vert.position * uni.scale;
  •  
  • // 位置を回転させます
  • let rotatedPosition = vec2f(
  • vert.position.x * uni.rotation.y - vert.position.y * uni.rotation.x,
  • vert.position.x * uni.rotation.x + vert.position.y * uni.rotation.y
  • scaledPosition.x * uni.rotation.y - scaledPosition.y * uni.rotation.x,
  • scaledPosition.x * uni.rotation.x + scaledPosition.y * uni.rotation.y
  • );
  •  
  • // 平行移動を追加します
  • let position = rotatedPosition + uni.translation;
  •  
  • // 位置をピクセルから0.0から1.0の値に変換します
  • let zeroToOne = position / uni.resolution;
  •  
  • // 0 <-> 1から0 <-> 2に変換します
  • let zeroToTwo = zeroToOne * 2.0;
  •  
  • // 0 <-> 2から-1 <-> +1(クリップ空間)に変換します
  • let flippedClipSpace = zeroToTwo - 1.0;
  •  
  • // Yを反転させます
  • let clipSpace = flippedClipSpace * vec2f(1, -1);
  •  
  • vsOut.position = vec4f(clipSpace, 0.0, 1.0);
  • return vsOut;
  • }

そして、以前と同様に、スケール値のためのスペースを確保するためにユニフォームバッファを更新する必要があります。

  • // 色、解像度、平行移動、回転、パディング
  • const uniformBufferSize = (4 + 2 + 2 + 2) * 4 + 8;
  • // 色、解像度、平行移動、回転、スケール
  • const uniformBufferSize = (4 + 2 + 2 + 2 + 2) * 4;
  • const uniformBuffer = device.createBuffer({
  • label: 'uniforms',
  • size: uniformBufferSize,
  • usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  • });
  •  
  • const uniformValues = new Float32Array(uniformBufferSize / 4);
  •  
  • // float32インデックスでのさまざまなユニフォーム値へのオフセット
  • const kColorOffset = 0;
  • const kResolutionOffset = 4;
  • const kTranslationOffset = 6;
  • const kRotationOffset = 8;
  • const kScaleOffset = 10;
  •  
  • const colorValue = uniformValues.subarray(kColorOffset, kColorOffset + 4);
  • const resolutionValue = uniformValues.subarray(kResolutionOffset, kResolutionOffset + 2);
  • const translationValue = uniformValues.subarray(kTranslationOffset, kTranslationOffset + 2);
  • const rotationValue = uniformValues.subarray(kRotationOffset, kRotationOffset + 2);
  • const scaleValue = uniformValues.subarray(kScaleOffset, kScaleOffset + 2);

そして、レンダリング時にスケールを更新する必要があります。

  • const settings = {
  • translation: [150, 100],
  • rotation: degToRad(30),
  • scale: [1, 1],
  • };
  •  
  • const radToDegOptions = { min: -360, max: 360, step: 1, converters: GUI.converters.radToDeg };
  •  
  • const gui = new GUI();
  • gui.onChange(render);
  • gui.add(settings.translation, '0', 0, 1000).name('translation.x');
  • gui.add(settings.translation, '1', 0, 1000).name('translation.y');
  • gui.add(settings, 'rotation', radToDegOptions);
  • gui.add(settings.scale, '0', -5, 5).name('scale.x');
  • gui.add(settings.scale, '1', -5, 5).name('scale.y');
  •  
  • function render() {
  • ...
  •  
  • // JavaScript側のFloat32Arrayでユニフォーム値を設定します
  • resolutionValue.set([canvas.width, canvas.height]);
  • translationValue.set(settings.translation);
  • rotationValue.set([
  • Math.cos(settings.rotation),
  • Math.sin(settings.rotation),
  • ]);
  • scaleValue.set(settings.scale);
  •  
  • // ユニフォーム値をユニフォームバッファにアップロードします
  • device.queue.writeBuffer(uniformBuffer, 0, uniformValues);

そして、スケールができました。スライダーをドラッグしてください。

注意すべき点の1つは、負の値でスケーリングするとジオメトリが反転することです。

もう1つ注意すべき点は、0、0からスケーリングすることです。これは、Fの場合は左上隅です。位置をスケールで乗算しているため、0、0から離れて移動するのは理にかなっています。おそらく、それを修正する方法を想像できるでしょう。たとえば、スケーリングする前に別の平行移動、プレスケール平行移動を追加できます。別の解決策は、実際のFの位置データを変更することです。すぐに別の方法について説明します。

これらの最後の3つの投稿が、平行移動回転、およびスケールの理解に役立ったことを願っています。次に、行列の魔法について説明します。これら3つすべてをはるかに単純で、多くの場合より便利な形式に組み合わせます。

問題点/バグ? githubでissueを作成.