Refactor cloth component and shaders
Improve readability, typing and structure for the cloth simulation component and WGSL shaders. Changes include: formatted imports, added file/header JSDoc and inline comments, made renderConfig and lifecycle methods public with explicit types, renamed component selector to 'app-cloth', converted several functions to typed helpers (e.g. createAndPopulateBuffer, addConstraint), consolidated buffer creation, and cleaned up compute shader binding mappings. Shader file receives file header and minor comment clarifications and a bounds check comment; overall changes are stylistic and organizational to increase maintainability and clarity without altering core algorithm behavior.
This commit is contained in:
@@ -1,12 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* File: cloth.component.ts
|
||||||
|
* Description: Component for cloth simulation using WebGPU compute shaders.
|
||||||
|
*/
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
|
import { MatCard, MatCardContent, MatCardHeader, MatCardTitle } from '@angular/material/card';
|
||||||
import {TranslatePipe} from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import {BabylonCanvas, RenderConfig, SceneEventData} from '../../../shared/rendering/canvas/babylon-canvas.component';
|
import { BabylonCanvas, RenderConfig, SceneEventData } from '../../../shared/rendering/canvas/babylon-canvas.component';
|
||||||
import {ComputeShader, StorageBuffer, MeshBuilder, ShaderMaterial, ShaderLanguage, ArcRotateCamera} from '@babylonjs/core';
|
import { ComputeShader, StorageBuffer, MeshBuilder, ShaderMaterial, ShaderLanguage, ArcRotateCamera } from '@babylonjs/core';
|
||||||
import {CLOTH_FRAGMENT_SHADER_WGSL, CLOTH_INTEGRATE_COMPUTE_WGSL, CLOTH_SOLVE_COMPUTE_WGSL, CLOTH_VELOCITY_COMPUTE_WGSL, CLOTH_VERTEX_SHADER_WGSL} from './cloth.shader';
|
import {
|
||||||
|
CLOTH_FRAGMENT_SHADER_WGSL,
|
||||||
|
CLOTH_INTEGRATE_COMPUTE_WGSL,
|
||||||
|
CLOTH_SOLVE_COMPUTE_WGSL,
|
||||||
|
CLOTH_VELOCITY_COMPUTE_WGSL,
|
||||||
|
CLOTH_VERTEX_SHADER_WGSL
|
||||||
|
} from './cloth.shader';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-cloth.component',
|
selector: 'app-cloth',
|
||||||
imports: [
|
imports: [
|
||||||
MatCard,
|
MatCard,
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
@@ -21,55 +32,60 @@ import {CLOTH_FRAGMENT_SHADER_WGSL, CLOTH_INTEGRATE_COMPUTE_WGSL, CLOTH_SOLVE_CO
|
|||||||
export class ClothComponent {
|
export class ClothComponent {
|
||||||
private currentSceneData: SceneEventData | null = null;
|
private currentSceneData: SceneEventData | null = null;
|
||||||
|
|
||||||
renderConfig: RenderConfig = {
|
public renderConfig: RenderConfig = {
|
||||||
mode: '3D',
|
mode: '3D',
|
||||||
initialViewSize: 20,
|
initialViewSize: 20,
|
||||||
shaderLanguage: ShaderLanguage.WGSL
|
shaderLanguage: ShaderLanguage.WGSL
|
||||||
};
|
};
|
||||||
|
|
||||||
onSceneReady(event: SceneEventData) {
|
/**
|
||||||
|
* Called when the Babylon scene is ready.
|
||||||
|
* @param event The scene event data.
|
||||||
|
*/
|
||||||
|
public onSceneReady(event: SceneEventData): void {
|
||||||
this.currentSceneData = event;
|
this.currentSceneData = event;
|
||||||
this.createSimulation();
|
this.createSimulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private createSimulation() {
|
/**
|
||||||
if (!this.currentSceneData){
|
* Initializes and starts the cloth simulation.
|
||||||
|
*/
|
||||||
|
private createSimulation(): void {
|
||||||
|
if (!this.currentSceneData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {engine, scene} = this.currentSceneData;
|
|
||||||
|
const { engine, scene } = this.currentSceneData;
|
||||||
|
|
||||||
// --- 1. CONFIGURE CLOTH GRID ---
|
// --- 1. CONFIGURE CLOTH GRID ---
|
||||||
const gridWidth = 50; // 50x50 = 2500 Vertices (Increase this later!)
|
const gridWidth = 50;
|
||||||
const gridHeight = 50;
|
const gridHeight = 50;
|
||||||
const numVertices = gridWidth * gridHeight;
|
const numVertices = gridWidth * gridHeight;
|
||||||
const spacing = 0.1; // Distance between points
|
const spacing = 0.1;
|
||||||
|
|
||||||
// Calculate approximate constraints (horizontal + vertical edges)
|
|
||||||
const numConstraints = (gridWidth - 1) * gridHeight + gridWidth * (gridHeight - 1);
|
|
||||||
|
|
||||||
const positionsData = new Float32Array(numVertices * 4);
|
const positionsData = new Float32Array(numVertices * 4);
|
||||||
const prevPositionsData = new Float32Array(numVertices * 4);
|
const prevPositionsData = new Float32Array(numVertices * 4);
|
||||||
const velocitiesData = new Float32Array(numVertices * 4);
|
const velocitiesData = new Float32Array(numVertices * 4);
|
||||||
|
|
||||||
// Arrays für unsere 4 Phasen (dynamische Größe, da wir pushen)
|
// Arrays for our 4 phases (dynamic size as we push)
|
||||||
const constraintsP0: number[] = [];
|
const constraintsP0: number[] = [];
|
||||||
const constraintsP1: number[] = [];
|
const constraintsP1: number[] = [];
|
||||||
const constraintsP2: number[] = [];
|
const constraintsP2: number[] = [];
|
||||||
const constraintsP3: number[] = [];
|
const constraintsP3: number[] = [];
|
||||||
|
|
||||||
// Hilfsfunktion zum sauberen Hinzufügen (vec4-Struktur)
|
// Helper function for clean adding (vec4 structure)
|
||||||
const addConstraint = (arr: number[], a: number, b: number) => {
|
const addConstraint = (arr: number[], a: number, b: number): void => {
|
||||||
arr.push(a, b, spacing, 1.0);
|
arr.push(a, b, spacing, 1.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Positionen füllen (bleibt wie vorher)
|
// Fill positions and pin the top edge
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let y = 0; y < gridHeight; y++) {
|
||||||
for (let x = 0; x < gridWidth; x++) {
|
for (let x = 0; x < gridWidth; x++) {
|
||||||
const idx = (y * gridWidth + x) * 4;
|
const idx = (y * gridWidth + x) * 4;
|
||||||
positionsData[idx + 0] = (x - gridWidth / 2) * spacing;
|
positionsData[idx + 0] = (x - gridWidth / 2) * spacing;
|
||||||
positionsData[idx + 1] = 5.0 - (y * spacing);
|
positionsData[idx + 1] = 5.0 - (y * spacing);
|
||||||
positionsData[idx + 2] = 0.0;
|
positionsData[idx + 2] = 0.0;
|
||||||
positionsData[idx + 3] = (y === 0) ? 0.0 : 1.0; // Oben festpinnen
|
positionsData[idx + 3] = (y === 0) ? 0.0 : 1.0;
|
||||||
|
|
||||||
prevPositionsData[idx + 0] = positionsData[idx + 0];
|
prevPositionsData[idx + 0] = positionsData[idx + 0];
|
||||||
prevPositionsData[idx + 1] = positionsData[idx + 1];
|
prevPositionsData[idx + 1] = positionsData[idx + 1];
|
||||||
@@ -78,27 +94,35 @@ export class ClothComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- GRAPH COLORING: Constraints in 4 Phasen füllen ---
|
// --- GRAPH COLORING: Fill constraints in 4 phases ---
|
||||||
// Phase 0: Horizontal Gerade
|
// Phase 0: Horizontal Even
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let y = 0; y < gridHeight; y++) {
|
||||||
for (let x = 0; x < gridWidth - 1; x += 2) addConstraint(constraintsP0, y * gridWidth + x, y * gridWidth + x + 1);
|
for (let x = 0; x < gridWidth - 1; x += 2) {
|
||||||
|
addConstraint(constraintsP0, y * gridWidth + x, y * gridWidth + x + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Phase 1: Horizontal Ungerade
|
// Phase 1: Horizontal Odd
|
||||||
for (let y = 0; y < gridHeight; y++) {
|
for (let y = 0; y < gridHeight; y++) {
|
||||||
for (let x = 1; x < gridWidth - 1; x += 2) addConstraint(constraintsP1, y * gridWidth + x, y * gridWidth + x + 1);
|
for (let x = 1; x < gridWidth - 1; x += 2) {
|
||||||
|
addConstraint(constraintsP1, y * gridWidth + x, y * gridWidth + x + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Phase 2: Vertikal Gerade
|
// Phase 2: Vertical Even
|
||||||
for (let y = 0; y < gridHeight - 1; y += 2) {
|
for (let y = 0; y < gridHeight - 1; y += 2) {
|
||||||
for (let x = 0; x < gridWidth; x++) addConstraint(constraintsP2, y * gridWidth + x, (y + 1) * gridWidth + x);
|
for (let x = 0; x < gridWidth; x++) {
|
||||||
|
addConstraint(constraintsP2, y * gridWidth + x, (y + 1) * gridWidth + x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Phase 3: Vertikal Ungerade
|
// Phase 3: Vertical Odd
|
||||||
for (let y = 1; y < gridHeight - 1; y += 2) {
|
for (let y = 1; y < gridHeight - 1; y += 2) {
|
||||||
for (let x = 0; x < gridWidth; x++) addConstraint(constraintsP3, y * gridWidth + x, (y + 1) * gridWidth + x);
|
for (let x = 0; x < gridWidth; x++) {
|
||||||
|
addConstraint(constraintsP3, y * gridWidth + x, (y + 1) * gridWidth + x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsData = new Float32Array(8);
|
const paramsData = new Float32Array(8);
|
||||||
|
|
||||||
// --- 3. CREATE GPU STORAGE BUFFERS ---
|
// --- 2. CREATE GPU STORAGE BUFFERS ---
|
||||||
const positionsBuffer = new StorageBuffer(engine, positionsData.byteLength);
|
const positionsBuffer = new StorageBuffer(engine, positionsData.byteLength);
|
||||||
positionsBuffer.update(positionsData);
|
positionsBuffer.update(positionsData);
|
||||||
|
|
||||||
@@ -108,24 +132,44 @@ export class ClothComponent {
|
|||||||
const velocitiesBuffer = new StorageBuffer(engine, velocitiesData.byteLength);
|
const velocitiesBuffer = new StorageBuffer(engine, velocitiesData.byteLength);
|
||||||
const paramsBuffer = new StorageBuffer(engine, paramsData.byteLength);
|
const paramsBuffer = new StorageBuffer(engine, paramsData.byteLength);
|
||||||
|
|
||||||
// Erstelle 4 separate Buffer für die 4 Phasen
|
// Create 4 separate buffers for the 4 phases
|
||||||
const cBuffer0 = new StorageBuffer(engine, constraintsP0.length * 4); cBuffer0.update(new Float32Array(constraintsP0));
|
const createAndPopulateBuffer = (data: number[]): StorageBuffer => {
|
||||||
const cBuffer1 = new StorageBuffer(engine, constraintsP1.length * 4); cBuffer1.update(new Float32Array(constraintsP1));
|
const buffer = new StorageBuffer(engine, data.length * 4);
|
||||||
const cBuffer2 = new StorageBuffer(engine, constraintsP2.length * 4); cBuffer2.update(new Float32Array(constraintsP2));
|
buffer.update(new Float32Array(data));
|
||||||
const cBuffer3 = new StorageBuffer(engine, constraintsP3.length * 4); cBuffer3.update(new Float32Array(constraintsP3));
|
return buffer;
|
||||||
|
};
|
||||||
|
|
||||||
// --- 4. SETUP COMPUTE SHADERS ---
|
const cBuffer0 = createAndPopulateBuffer(constraintsP0);
|
||||||
|
const cBuffer1 = createAndPopulateBuffer(constraintsP1);
|
||||||
|
const cBuffer2 = createAndPopulateBuffer(constraintsP2);
|
||||||
|
const cBuffer3 = createAndPopulateBuffer(constraintsP3);
|
||||||
|
|
||||||
|
// --- 3. SETUP COMPUTE SHADERS ---
|
||||||
const csIntegrate = new ComputeShader("integrate", engine, { computeSource: CLOTH_INTEGRATE_COMPUTE_WGSL }, {
|
const csIntegrate = new ComputeShader("integrate", engine, { computeSource: CLOTH_INTEGRATE_COMPUTE_WGSL }, {
|
||||||
bindingsMapping: { "p": { group: 0, binding: 0 }, "positions": { group: 0, binding: 1 }, "prev_positions": { group: 0, binding: 2 }, "velocities": { group: 0, binding: 3 } }
|
bindingsMapping: {
|
||||||
|
"p": { group: 0, binding: 0 },
|
||||||
|
"positions": { group: 0, binding: 1 },
|
||||||
|
"prev_positions": { group: 0, binding: 2 },
|
||||||
|
"velocities": { group: 0, binding: 3 }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
csIntegrate.setStorageBuffer("p", paramsBuffer); csIntegrate.setStorageBuffer("positions", positionsBuffer); csIntegrate.setStorageBuffer("prev_positions", prevPositionsBuffer); csIntegrate.setStorageBuffer("velocities", velocitiesBuffer);
|
csIntegrate.setStorageBuffer("p", paramsBuffer);
|
||||||
|
csIntegrate.setStorageBuffer("positions", positionsBuffer);
|
||||||
|
csIntegrate.setStorageBuffer("prev_positions", prevPositionsBuffer);
|
||||||
|
csIntegrate.setStorageBuffer("velocities", velocitiesBuffer);
|
||||||
|
|
||||||
// Hilfsfunktion, um die 4 Solve-Shader sauber zu erstellen
|
// Helper function to create the 4 solve shaders
|
||||||
const createSolver = (name: string, cBuffer: StorageBuffer) => {
|
const createSolver = (name: string, cBuffer: StorageBuffer): ComputeShader => {
|
||||||
const cs = new ComputeShader(name, engine, { computeSource: CLOTH_SOLVE_COMPUTE_WGSL }, {
|
const cs = new ComputeShader(name, engine, { computeSource: CLOTH_SOLVE_COMPUTE_WGSL }, {
|
||||||
bindingsMapping: { "p": { group: 0, binding: 0 }, "positions": { group: 0, binding: 1 }, "constraints": { group: 0, binding: 2 } }
|
bindingsMapping: {
|
||||||
|
"p": { group: 0, binding: 0 },
|
||||||
|
"positions": { group: 0, binding: 1 },
|
||||||
|
"constraints": { group: 0, binding: 2 }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
cs.setStorageBuffer("p", paramsBuffer); cs.setStorageBuffer("positions", positionsBuffer); cs.setStorageBuffer("constraints", cBuffer);
|
cs.setStorageBuffer("p", paramsBuffer);
|
||||||
|
cs.setStorageBuffer("positions", positionsBuffer);
|
||||||
|
cs.setStorageBuffer("constraints", cBuffer);
|
||||||
return cs;
|
return cs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,12 +179,19 @@ export class ClothComponent {
|
|||||||
const csSolve3 = createSolver("solve3", cBuffer3);
|
const csSolve3 = createSolver("solve3", cBuffer3);
|
||||||
|
|
||||||
const csVelocity = new ComputeShader("velocity", engine, { computeSource: CLOTH_VELOCITY_COMPUTE_WGSL }, {
|
const csVelocity = new ComputeShader("velocity", engine, { computeSource: CLOTH_VELOCITY_COMPUTE_WGSL }, {
|
||||||
bindingsMapping: { "p": { group: 0, binding: 0 }, "positions": { group: 0, binding: 1 }, "prev_positions": { group: 0, binding: 2 }, "velocities": { group: 0, binding: 3 } }
|
bindingsMapping: {
|
||||||
|
"p": { group: 0, binding: 0 },
|
||||||
|
"positions": { group: 0, binding: 1 },
|
||||||
|
"prev_positions": { group: 0, binding: 2 },
|
||||||
|
"velocities": { group: 0, binding: 3 }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
csVelocity.setStorageBuffer("p", paramsBuffer); csVelocity.setStorageBuffer("positions", positionsBuffer); csVelocity.setStorageBuffer("prev_positions", prevPositionsBuffer); csVelocity.setStorageBuffer("velocities", velocitiesBuffer);
|
csVelocity.setStorageBuffer("p", paramsBuffer);
|
||||||
|
csVelocity.setStorageBuffer("positions", positionsBuffer);
|
||||||
|
csVelocity.setStorageBuffer("prev_positions", prevPositionsBuffer);
|
||||||
|
csVelocity.setStorageBuffer("velocities", velocitiesBuffer);
|
||||||
|
|
||||||
// --- 5. SETUP RENDER MESH ---
|
// --- 4. SETUP RENDER MESH ---
|
||||||
// We create a ground mesh that matches our grid size, but we will OVERWRITE its vertices in the shader.
|
|
||||||
const clothMesh = MeshBuilder.CreateGround("cloth", { width: 10, height: 10, subdivisions: gridWidth - 1 }, scene);
|
const clothMesh = MeshBuilder.CreateGround("cloth", { width: 10, height: 10, subdivisions: gridWidth - 1 }, scene);
|
||||||
|
|
||||||
const clothMaterial = new ShaderMaterial("clothMat", scene, {
|
const clothMaterial = new ShaderMaterial("clothMat", scene, {
|
||||||
@@ -164,22 +215,21 @@ export class ClothComponent {
|
|||||||
camera.radius = 15;
|
camera.radius = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 6. RENDER LOOP ---
|
// --- 5. RENDER LOOP ---
|
||||||
scene.onBeforeRenderObservable.clear();
|
scene.onBeforeRenderObservable.clear();
|
||||||
scene.onBeforeRenderObservable.add(() => {
|
scene.onBeforeRenderObservable.add(() => {
|
||||||
|
|
||||||
paramsData[0] = 0.016;
|
paramsData[0] = 0.016;
|
||||||
paramsData[1] = -9.81;
|
paramsData[1] = -9.81;
|
||||||
paramsData[2] = 0.0001; // Compliance (sehr klein = steifer Stoff)
|
paramsData[2] = 0.0001; // Compliance (very small = stiff fabric)
|
||||||
paramsData[3] = numVertices;
|
paramsData[3] = numVertices;
|
||||||
paramsBuffer.update(paramsData);
|
paramsBuffer.update(paramsData);
|
||||||
|
|
||||||
const dispatchXVertices = Math.ceil(numVertices / 64);
|
const dispatchXVertices = Math.ceil(numVertices / 64);
|
||||||
|
|
||||||
// 1. Positionen vorhersehen
|
// 1. Predict positions
|
||||||
csIntegrate.dispatch(dispatchXVertices, 1, 1);
|
csIntegrate.dispatch(dispatchXVertices, 1, 1);
|
||||||
|
|
||||||
// 2. XPBD Solver (Substeps) - Jede Farbe einzeln lösen!
|
// 2. XPBD Solver (Substeps) - Solve each color individually
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
csSolve0.dispatch(Math.ceil((constraintsP0.length / 4) / 64), 1, 1);
|
csSolve0.dispatch(Math.ceil((constraintsP0.length / 4) / 64), 1, 1);
|
||||||
csSolve1.dispatch(Math.ceil((constraintsP1.length / 4) / 64), 1, 1);
|
csSolve1.dispatch(Math.ceil((constraintsP1.length / 4) / 64), 1, 1);
|
||||||
@@ -187,7 +237,7 @@ export class ClothComponent {
|
|||||||
csSolve3.dispatch(Math.ceil((constraintsP3.length / 4) / 64), 1, 1);
|
csSolve3.dispatch(Math.ceil((constraintsP3.length / 4) / 64), 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Geschwindigkeiten aktualisieren
|
// 3. Update velocities
|
||||||
csVelocity.dispatch(dispatchXVertices, 1, 1);
|
csVelocity.dispatch(dispatchXVertices, 1, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
// --- SHARED DATA STRUCTURES ---
|
/**
|
||||||
|
* File: cloth.shader.ts
|
||||||
|
* Description: WGSL shaders for cloth simulation and rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// --- SHARED DATA STRUCTURES ---
|
||||||
export const CLOTH_SHARED_STRUCTS = `
|
export const CLOTH_SHARED_STRUCTS = `
|
||||||
struct Params {
|
struct Params {
|
||||||
dt: f32, // Time step per substep
|
dt: f32, // Time step per substep
|
||||||
@@ -21,7 +26,7 @@ export const CLOTH_VERTEX_SHADER_WGSL = `
|
|||||||
// Storage Buffer
|
// Storage Buffer
|
||||||
var<storage, read> positions : array<vec4<f32>>;
|
var<storage, read> positions : array<vec4<f32>>;
|
||||||
|
|
||||||
// Babylon Preprocessor-Magie
|
// Babylon Preprocessor Magic
|
||||||
uniform viewProjection : mat4x4<f32>;
|
uniform viewProjection : mat4x4<f32>;
|
||||||
varying vUV : vec2<f32>;
|
varying vUV : vec2<f32>;
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@ export const CLOTH_VERTEX_SHADER_WGSL = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// FRAGMENT SHADER (Bleibt exakt gleich)
|
// FRAGMENT SHADER
|
||||||
// ==========================================
|
// ==========================================
|
||||||
export const CLOTH_FRAGMENT_SHADER_WGSL = `
|
export const CLOTH_FRAGMENT_SHADER_WGSL = `
|
||||||
varying vUV : vec2<f32>;
|
varying vUV : vec2<f32>;
|
||||||
@@ -98,13 +103,13 @@ export const CLOTH_INTEGRATE_COMPUTE_WGSL = CLOTH_SHARED_STRUCTS + `
|
|||||||
export const CLOTH_SOLVE_COMPUTE_WGSL = CLOTH_SHARED_STRUCTS + `
|
export const CLOTH_SOLVE_COMPUTE_WGSL = CLOTH_SHARED_STRUCTS + `
|
||||||
@group(0) @binding(0) var<storage, read> p : Params;
|
@group(0) @binding(0) var<storage, read> p : Params;
|
||||||
@group(0) @binding(1) var<storage, read_write> positions : array<vec4<f32>>;
|
@group(0) @binding(1) var<storage, read_write> positions : array<vec4<f32>>;
|
||||||
@group(0) @binding(2) var<storage, read> constraints : array<vec4<f32>>; // <--- Nur "read", da wir sie hier nicht verändern
|
@group(0) @binding(2) var<storage, read> constraints : array<vec4<f32>>; // <--- Read-only as we do not modify them here
|
||||||
|
|
||||||
@compute @workgroup_size(64)
|
@compute @workgroup_size(64)
|
||||||
fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {
|
fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {
|
||||||
let idx = global_id.x;
|
let idx = global_id.x;
|
||||||
|
|
||||||
// HIER: Wir fragen die GPU direkt, wie groß das übergebene Array ist!
|
// Query the GPU directly for the length of the passed array
|
||||||
if (idx >= arrayLength(&constraints)) { return; }
|
if (idx >= arrayLength(&constraints)) { return; }
|
||||||
|
|
||||||
let constraint = constraints[idx];
|
let constraint = constraints[idx];
|
||||||
|
|||||||
Reference in New Issue
Block a user