Elysia Tools

Parcourez outils, exemples, explications visuelles et guides thématiques dans un espace clair.

Catégories populaires

Dossiers

Langue
ZHDERUPTENFRES

Elysia Tools

Navigation mobile

Technologies web

API Graphique WebGPU

API graphique moderne pour les graphiques 3D haute performance et le calcul GPU dans le navigateur

Exemples

Entrées de cette collection

Code sourcejavascriptIntermédiaire

Configuration de Base WebGPU

Initialiser WebGPU et créer un pipeline de rendu simple

Difficulté

6/10

Temps estimé

20 min

Étiquettes

webgpu, graphics, rendering, 3d, shaders

Prérequis

JavaScript, Graphics concepts, Shaders basics

javascript
// Basic WebGPU Setup
class WebGPURenderer {
  constructor(canvas) {
    this.canvas = canvas;
    this.device = null;
    this.context = null;
    this.format = null;
  }

  async init() {
    // Check WebGPU support
    if (!navigator.gpu) {
      throw new Error('WebGPU not supported');
    }

    // Get adapter and device
    const adapter = await navigator.gpu.requestAdapter();
    if (!adapter) {
      throw new Error('No appropriate GPU adapter found');
    }

    this.device = await adapter.requestDevice();

    // Configure canvas context
    this.context = this.canvas.getContext('webgpu');
    this.format = navigator.gpu.getPreferredCanvasFormat();

    this.context.configure({
      device: this.device,
      format: this.format,
      alphaMode: 'opaque',
    });

    console.log('WebGPU initialized successfully');
  }

  createBasicPipeline() {
    // Vertex shader
    const vertexShaderCode = `
      struct VertexOutput {
        @builtin(position) position: vec4<f32>,
        @location(0) color: vec3<f32>,
      }

      @vertex
      fn main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
        var output: VertexOutput;

        // Triangle vertices
        let pos = array<vec2<f32>, 3>(
          vec2<f32>(-0.5, -0.5),
          vec2<f32>( 0.5, -0.5),
          vec2<f32>( 0.0,  0.5)
        );

        let colors = array<vec3<f32>, 3>(
          vec3<f32>(1.0, 0.0, 0.0), // Red
          vec3<f32>(0.0, 1.0, 0.0), // Green
          vec3<f32>(0.0, 0.0, 1.0)  // Blue
        );

        output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
        output.color = colors[vertexIndex];

        return output;
      }
    `;

    // Fragment shader
    const fragmentShaderCode = `
      @fragment
      fn main(@location(0) color: vec3<f32>) -> @location(0) vec4<f32> {
        return vec4<f32>(color, 1.0);
      }
    `;

    // Create shaders
    const vertexShader = this.device.createShaderModule({
      code: vertexShaderCode
    });

    const fragmentShader = this.device.createShaderModule({
      code: fragmentShaderCode
    });

    // Create pipeline
    const pipeline = this.device.createRenderPipeline({
      layout: 'auto',
      vertex: {
        module: vertexShader,
        entryPoint: 'main',
      },
      fragment: {
        module: fragmentShader,
        entryPoint: 'main',
        targets: [{
          format: this.format,
        }],
      },
      primitive: {
        topology: 'triangle-list',
      },
    });

    return pipeline;
  }

  render(pipeline) {
    // Create command encoder
    const commandEncoder = this.device.createCommandEncoder();

    // Create render pass
    const textureView = this.context.getCurrentTexture().createView();
    const renderPass = commandEncoder.beginRenderPass({
      colorAttachments: [{
        view: textureView,
        clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store',
      }],
    });

    renderPass.setPipeline(pipeline);
    renderPass.draw(3); // Draw 3 vertices for a triangle
    renderPass.end();

    // Submit commands
    this.device.queue.submit([commandEncoder.finish()]);
  }

  async start() {
    await this.init();
    const pipeline = this.createBasicPipeline();

    // Animation loop
    const frame = () => {
      this.render(pipeline);
      requestAnimationFrame(frame);
    };
    frame();
  }
}

// Usage
async function main() {
  const canvas = document.getElementById('canvas');
  const renderer = new WebGPURenderer(canvas);

  try {
    await renderer.start();
  } catch (error) {
    console.error('WebGPU initialization failed:', error);
  }
}

// Check for WebGPU support before running
if (navigator.gpu) {
  main();
} else {
  console.warn('WebGPU is not supported in this browser');
}
Code sourcejavascriptIntermédiaire

Mapping de Texture WebGPU

Appliquer des textures aux objets 3D avec WebGPU

Difficulté

7/10

Temps estimé

25 min

Étiquettes

webgpu, texture, mapping, graphics, rendering

Prérequis

JavaScript, WebGPU basics, Texture mapping concepts

javascript
// WebGPU Texture Mapping
class TextureRenderer extends WebGPURenderer {
  async createTexturedPipeline(texture) {
    // Vertex shader with UV coordinates
    const vertexShaderCode = `
      struct VertexInput {
        @location(0) position: vec2<f32>,
        @location(1) uv: vec2<f32>,
      }

      struct VertexOutput {
        @builtin(position) position: vec4<f32>,
        @location(0) uv: vec2<f32>,
      }

      @vertex
      fn main(input: VertexInput) -> VertexOutput {
        var output: VertexOutput;
        output.position = vec4<f32>(input.position, 0.0, 1.0);
        output.uv = input.uv;
        return output;
      }
    `;

    // Fragment shader with texture sampling
    const fragmentShaderCode = `
      @group(0) @binding(0) var textureSampler: sampler;
      @group(0) @binding(1) var textureData: texture_2d<f32>;

      @fragment
      fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
        return textureSample(textureData, textureSampler, uv);
      }
    `;

    // Create shaders
    const vertexShader = this.device.createShaderModule({
      code: vertexShaderCode
    });

    const fragmentShader = this.device.createShaderModule({
      code: fragmentShaderCode
    });

    // Create vertex buffer (position + UV)
    const vertices = new Float32Array([
      // Position (x, y)    UV (u, v)
      -0.8, -0.8,   0.0, 1.0,  // Bottom left
       0.8, -0.8,   1.0, 1.0,  // Bottom right
       0.8,  0.8,   1.0, 0.0,  // Top right
      -0.8,  0.8,   0.0, 0.0,  // Top left
    ]);

    const vertexBuffer = this.device.createBuffer({
      size: vertices.byteLength,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });
    this.device.queue.writeBuffer(vertexBuffer, 0, vertices);

    // Create texture sampler
    const sampler = this.device.createSampler({
      magFilter: 'linear',
      minFilter: 'linear',
      addressModeU: 'repeat',
      addressModeV: 'repeat',
    });

    // Create bind group layout and bind group
    const bindGroupLayout = this.device.createBindGroupLayout({
      entries: [
        {
          binding: 0,
          visibility: GPUShaderStage.FRAGMENT,
          sampler: { type: 'filtering' },
        },
        {
          binding: 1,
          visibility: GPUShaderStage.FRAGMENT,
          texture: { sampleType: 'float' },
        },
      ],
    });

    const bindGroup = this.device.createBindGroup({
      layout: bindGroupLayout,
      entries: [
        { binding: 0, resource: sampler },
        { binding: 1, resource: texture.createView() },
      ],
    });

    // Create pipeline
    const pipeline = this.device.createRenderPipeline({
      layout: this.device.createPipelineLayout({
        bindGroupLayouts: [bindGroupLayout],
      }),
      vertex: {
        module: vertexShader,
        entryPoint: 'main',
        buffers: [{
          arrayStride: 4 * 4, // 4 floats per vertex (x, y, u, v)
          attributes: [
            {
              format: 'float32x2',
              offset: 0,
              shaderLocation: 0, // position
            },
            {
              format: 'float32x2',
              offset: 2 * 4,
              shaderLocation: 1, // uv
            },
          ],
        }],
      },
      fragment: {
        module: fragmentShader,
        entryPoint: 'main',
        targets: [{
          format: this.format,
        }],
      },
      primitive: {
        topology: 'triangle-strip',
      },
    });

    return { pipeline, vertexBuffer, bindGroup };
  }

  async loadTexture(imageUrl) {
    const response = await fetch(imageUrl);
    const imageBitmap = await createImageBitmap(await response.blob());

    const texture = this.device.createTexture({
      size: [imageBitmap.width, imageBitmap.height, 1],
      format: 'rgba8unorm',
      usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
    });

    this.device.queue.copyExternalImageToTexture(
      { source: imageBitmap },
      { texture: texture },
      [imageBitmap.width, imageBitmap.height]
    );

    return texture;
  }

  async renderTexturedQuad(textureUrl) {
    await this.init();

    const texture = await this.loadTexture(textureUrl);
    const { pipeline, vertexBuffer, bindGroup } = await this.createTexturedPipeline(texture);

    const frame = () => {
      const commandEncoder = this.device.createCommandEncoder();
      const textureView = this.context.getCurrentTexture().createView();

      const renderPass = commandEncoder.beginRenderPass({
        colorAttachments: [{
          view: textureView,
          clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
          loadOp: 'clear',
          storeOp: 'store',
        }],
      });

      renderPass.setPipeline(pipeline);
      renderPass.setVertexBuffer(0, vertexBuffer);
      renderPass.setBindGroup(0, bindGroup);
      renderPass.draw(4); // Draw 4 vertices for a quad
      renderPass.end();

      this.device.queue.submit([commandEncoder.finish()]);
      requestAnimationFrame(frame);
    };

    frame();
  }
}

// Usage
async function renderTexturedImage() {
  const canvas = document.getElementById('canvas');
  const renderer = new TextureRenderer(canvas);

  await renderer.renderTexturedQuad('texture.jpg');
}

renderTexturedImage();
Code sourcejavascriptComplexe

Compute Shaders WebGPU

Calcul GPU avec compute shaders pour le traitement parallèle

Difficulté

9/10

Temps estimé

30 min

Étiquettes

webgpu, compute, shaders, gpu, parallel

Prérequis

JavaScript, WebGPU, GPU computing, Parallel programming

javascript
// WebGPU Compute Shaders for Parallel Processing
class ComputeRenderer {
  async init() {
    if (!navigator.gpu) throw new Error('WebGPU not supported');

    const adapter = await navigator.gpu.requestAdapter();
    this.device = await adapter.requestDevice();
  }

  async createComputePipeline() {
    // Compute shader for parallel matrix multiplication
    const computeShaderCode = `
      @group(0) @binding(0) var<storage, read> matrixA: array<f32>;
      @group(0) @binding(1) var<storage, read> matrixB: array<f32>;
      @group(0) @binding(2) var<storage, read_write> result: array<f32>;

      struct Params {
        size: u32,
      }

      @group(0) @binding(3) var<uniform> params: Params;

      @compute @workgroup_size(16, 16)
      fn main(@builtin(global_invocation_id) id: vec3<u32>) {
        let row = id.x;
        let col = id.y;

        if (row >= params.size || col >= params.size) {
          return;
        }

        var sum = 0.0;
        for (var i = 0u; i < params.size; i = i + 1u) {
          sum = sum + matrixA[row * params.size + i] * matrixB[i * params.size + col];
        }

        result[row * params.size + col] = sum;
      }
    `;

    const computeShader = this.device.createShaderModule({
      code: computeShaderCode
    });

    const pipeline = this.device.createComputePipeline({
      layout: 'auto',
      compute: {
        module: computeShader,
        entryPoint: 'main',
      },
    });

    return pipeline;
  }

  createMatrices(size) {
    const matrixA = new Float32Array(size * size);
    const matrixB = new Float32Array(size * size);
    const result = new Float32Array(size * size);

    // Initialize matrices with random values
    for (let i = 0; i < size * size; i++) {
      matrixA[i] = Math.random();
      matrixB[i] = Math.random();
    }

    return { matrixA, matrixB, result };
  }

  async matrixMultiplication(size = 512) {
    const { matrixA, matrixB, result } = this.createMatrices(size);

    // Create GPU buffers
    const bufferA = this.device.createBuffer({
      size: matrixA.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });

    const bufferB = this.device.createBuffer({
      size: matrixB.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
    });

    const bufferResult = this.device.createBuffer({
      size: result.byteLength,
      usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
    });

    // CPU buffer for reading results
    const readBuffer = this.device.createBuffer({
      size: result.byteLength,
      usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
    });

    // Write data to GPU
    this.device.queue.writeBuffer(bufferA, 0, matrixA);
    this.device.queue.writeBuffer(bufferB, 0, matrixB);

    // Uniform buffer for matrix size
    const uniformBuffer = this.device.createBuffer({
      size: 4, // 1 uint32
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });

    const sizeArray = new Uint32Array([size]);
    this.device.queue.writeBuffer(uniformBuffer, 0, sizeArray);

    // Create bind group
    const pipeline = await this.createComputePipeline();
    const bindGroup = this.device.createBindGroup({
      layout: pipeline.getBindGroupLayout(0),
      entries: [
        { binding: 0, resource: { buffer: bufferA } },
        { binding: 1, resource: { buffer: bufferB } },
        { binding: 2, resource: { buffer: bufferResult } },
        { binding: 3, resource: { buffer: uniformBuffer } },
      ],
    });

    // Execute compute shader
    const commandEncoder = this.device.createCommandEncoder();
    const computePass = commandEncoder.beginComputePass();

    computePass.setPipeline(pipeline);
    computePass.setBindGroup(0, bindGroup);
    computePass.dispatchWorkgroups(
      Math.ceil(size / 16),
      Math.ceil(size / 16)
    );
    computePass.end();

    // Copy result to readable buffer
    commandEncoder.copyBufferToBuffer(bufferResult, 0, readBuffer, 0, result.byteLength);

    // Submit commands
    this.device.queue.submit([commandEncoder.finish()]);

    // Read results
    await readBuffer.mapAsync(GPUMapMode.READ);
    const gpuResult = new Float32Array(readBuffer.getMappedRange());
    const cpuResult = new Float32Array(gpuResult.slice(0));
    readBuffer.unmap();

    return cpuResult;
  }

  // Particle simulation compute shader
  async createParticleSimulation() {
    const particleShaderCode = `
      struct Particle {
        position: vec2<f32>,
        velocity: vec2<f32>,
      }

      @group(0) @binding(0) var<storage, read_write> particles: array<Particle>;

      struct Params {
        deltaTime: f32,
        count: u32,
      }

      @group(0) @binding(1) var<uniform> params: Params;

      @compute @workgroup_size(64)
      fn main(@builtin(global_invocation_id) id: vec3<u32>) {
        let index = id.x;
        if (index >= params.count) {
          return;
        }

        var particle = particles[index];

        // Apply gravity
        particle.velocity.y = particle.velocity.y - 9.81 * params.deltaTime;

        // Update position
        particle.position = particle.position + particle.velocity * params.deltaTime;

        // Bounce off bottom
        if (particle.position.y < -1.0) {
          particle.position.y = -1.0;
          particle.velocity.y = -particle.velocity.y * 0.8;
        }

        // Bounce off sides
        if (abs(particle.position.x) > 1.0) {
          particle.position.x = sign(particle.position.x) * 1.0;
          particle.velocity.x = -particle.velocity.x * 0.8;
        }

        particles[index] = particle;
      }
    `;

    const shader = this.device.createShaderModule({
      code: particleShaderCode
    });

    return this.device.createComputePipeline({
      layout: 'auto',
      compute: {
        module: shader,
        entryPoint: 'main',
      },
    });
  }
}

// Usage
async function runComputeExample() {
  const compute = new ComputeRenderer();
  await compute.init();

  // Matrix multiplication benchmark
  console.time('GPU Matrix Multiplication');
  const gpuResult = await compute.matrixMultiplication(512);
  console.timeEnd('GPU Matrix Multiplication');

  // For comparison, CPU implementation
  function cpuMatrixMultiply(A, B, size) {
    const result = new Float32Array(size * size);
    for (let i = 0; i < size; i++) {
      for (let j = 0; j < size; j++) {
        let sum = 0;
        for (let k = 0; k < size; k++) {
          sum += A[i * size + k] * B[k * size + j];
        }
        result[i * size + j] = sum;
      }
    }
    return result;
  }

  const { matrixA, matrixB } = compute.createMatrices(512);
  console.time('CPU Matrix Multiplication');
  const cpuResult = cpuMatrixMultiply(matrixA, matrixB, 512);
  console.timeEnd('CPU Matrix Multiplication');

  console.log('Performance improvement achieved!');
}

if (navigator.gpu) {
  runComputeExample();
}
Code sourcejavascriptComplexe

Rendu de Scène 3D

Créer une scène 3D complète avec éclairage et contrôles de caméra

Difficulté

10/10

Temps estimé

35 min

Étiquettes

webgpu, 3d, lighting, scene, graphics

Prérequis

JavaScript, WebGPU, 3D graphics, Linear algebra

javascript
// WebGPU 3D Scene with Lighting
class Scene3D {
  constructor(canvas) {
    this.canvas = canvas;
    this.device = null;
    this.context = null;
    this.format = null;
    this.pipeline = null;
    this.uniformBuffer = null;
    this.camera = {
      position: [0, 0, 5],
      rotation: [0, 0, 0],
      fov: Math.PI / 4,
      aspect: canvas.width / canvas.height,
      near: 0.1,
      far: 100
    };
  }

  async init() {
    if (!navigator.gpu) throw new Error('WebGPU not supported');

    const adapter = await navigator.gpu.requestAdapter();
    this.device = await adapter.requestDevice();

    this.context = this.canvas.getContext('webgpu');
    this.format = navigator.gpu.getPreferredCanvasFormat();

    this.context.configure({
      device: this.device,
      format: this.format,
      alphaMode: 'opaque',
    });

    await this.createPipeline();
    await this.createGeometry();
    this.createUniforms();
  }

  async createPipeline() {
    // Vertex shader with 3D transformations
    const vertexShaderCode = `
      struct Uniforms {
        modelMatrix: mat4x4<f32>,
        viewMatrix: mat4x4<f32>,
        projectionMatrix: mat4x4<f32>,
        normalMatrix: mat4x4<f32>,
        cameraPosition: vec3<f32>,
        lightPosition: vec3<f32>,
        lightColor: vec3<f32>,
        lightIntensity: f32,
      }

      struct VertexInput {
        @location(0) position: vec3<f32>,
        @location(1) normal: vec3<f32>,
        @location(2) color: vec3<f32>,
      }

      struct VertexOutput {
        @builtin(position) position: vec4<f32>,
        @location(0) worldPosition: vec3<f32>,
        @location(1) worldNormal: vec3<f32>,
        @location(2) color: vec3<f32>,
      }

      @group(0) @binding(0) var<uniform> uniforms: Uniforms;

      @vertex
      fn main(input: VertexInput) -> VertexOutput {
        var output: VertexOutput;

        // Transform position to world space
        let worldPos = uniforms.modelMatrix * vec4<f32>(input.position, 1.0);
        output.worldPosition = worldPos.xyz;

        // Transform normal to world space
        output.worldNormal = normalize((uniforms.normalMatrix * vec4<f32>(input.normal, 0.0)).xyz);

        // Transform to clip space
        output.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos;

        // Pass through color
        output.color = input.color;

        return output;
      }
    `;

    // Fragment shader with Phong lighting
    const fragmentShaderCode = `
      struct Uniforms {
        modelMatrix: mat4x4<f32>,
        viewMatrix: mat4x4<f32>,
        projectionMatrix: mat4x4<f32>,
        normalMatrix: mat4x4<f32>,
        cameraPosition: vec3<f32>,
        lightPosition: vec3<f32>,
        lightColor: vec3<f32>,
        lightIntensity: f32,
      }

      @group(0) @binding(0) var<uniform> uniforms: Uniforms;

      @fragment
      fn main(
        @location(0) worldPosition: vec3<f32>,
        @location(1) worldNormal: vec3<f32>,
        @location(2) color: vec3<f32>
      ) -> @location(0) vec4<f32> {
        // Phong lighting model

        // Ambient
        let ambient = vec3<f32>(0.1, 0.1, 0.1);

        // Diffuse
        let lightDir = normalize(uniforms.lightPosition - worldPosition);
        let diffuse = max(dot(worldNormal, lightDir), 0.0) * uniforms.lightColor * uniforms.lightIntensity;

        // Specular
        let viewDir = normalize(uniforms.cameraPosition - worldPosition);
        let reflectDir = reflect(-lightDir, worldNormal);
        let specular = pow(max(dot(viewDir, reflectDir), 0.0), 32.0) * uniforms.lightColor;

        let finalColor = (ambient + diffuse + specular) * color;
        return vec4<f32>(finalColor, 1.0);
      }
    `;

    const vertexShader = this.device.createShaderModule({
      code: vertexShaderCode
    });

    const fragmentShader = this.device.createShaderModule({
      code: fragmentShaderCode
    });

    this.pipeline = this.device.createRenderPipeline({
      layout: 'auto',
      vertex: {
        module: vertexShader,
        entryPoint: 'main',
        buffers: [
          {
            arrayStride: 3 * 4, // position
            attributes: [{ format: 'float32x3', offset: 0, shaderLocation: 0 }]
          },
          {
            arrayStride: 3 * 4, // normal
            attributes: [{ format: 'float32x3', offset: 0, shaderLocation: 1 }]
          },
          {
            arrayStride: 3 * 4, // color
            attributes: [{ format: 'float32x3', offset: 0, shaderLocation: 2 }]
          }
        ]
      },
      fragment: {
        module: fragmentShader,
        entryPoint: 'main',
        targets: [{ format: this.format }]
      },
      primitive: {
        topology: 'triangle-list',
        cullMode: 'back'
      },
      depthStencil: {
        depthWriteEnabled: true,
        depthCompare: 'less',
        format: 'depth24plus'
      }
    });
  }

  async createGeometry() {
    // Create a cube
    const positions = new Float32Array([
      // Front face
      -1, -1,  1,  1, -1,  1,  1,  1,  1, -1,  1,  1,
      // Back face
      -1, -1, -1, -1,  1, -1,  1,  1, -1,  1, -1, -1,
      // Top face
      -1,  1, -1, -1,  1,  1,  1,  1,  1,  1,  1, -1,
      // Bottom face
      -1, -1, -1,  1, -1, -1,  1, -1,  1, -1, -1,  1,
      // Right face
       1, -1, -1,  1,  1, -1,  1,  1,  1,  1, -1,  1,
      // Left face
      -1, -1, -1, -1, -1,  1, -1,  1,  1, -1,  1, -1
    ]);

    const normals = new Float32Array([
      // Front, Back, Top, Bottom, Right, Left
      0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1,
      0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
      0, 1, 0,  0, 1, 0,  0, 1, 0,  0, 1, 0,
      0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
      1, 0, 0,  1, 0, 0,  1, 0, 0,  1, 0, 0,
      -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0
    ]);

    const colors = new Float32Array([
      1, 0, 0,  1, 0, 0,  1, 0, 0,  1, 0, 0, // Red
      0, 1, 0,  0, 1, 0,  0, 1, 0,  0, 1, 0, // Green
      0, 0, 1,  0, 0, 1,  0, 0, 1,  0, 0, 1, // Blue
      1, 1, 0,  1, 1, 0,  1, 1, 0,  1, 1, 0, // Yellow
      1, 0, 1,  1, 0, 1,  1, 0, 1,  1, 0, 1, // Magenta
      0, 1, 1,  0, 1, 1,  0, 1, 1,  0, 1, 1  // Cyan
    ]);

    const indices = new Uint16Array([
      0,  1,  2,   0,  2,  3,   // Front
      4,  5,  6,   4,  6,  7,   // Back
      8,  9,  10,  8,  10, 11,  // Top
      12, 13, 14,  12, 14, 15,  // Bottom
      16, 17, 18,  16, 18, 19,  // Right
      20, 21, 22,  20, 22, 23   // Left
    ]);

    this.positionBuffer = this.device.createBuffer({
      size: positions.byteLength,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

    this.normalBuffer = this.device.createBuffer({
      size: normals.byteLength,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

    this.colorBuffer = this.device.createBuffer({
      size: colors.byteLength,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
    });

    this.indexBuffer = this.device.createBuffer({
      size: indices.byteLength,
      usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
    });

    this.device.queue.writeBuffer(this.positionBuffer, 0, positions);
    this.device.queue.writeBuffer(this.normalBuffer, 0, normals);
    this.device.queue.writeBuffer(this.colorBuffer, 0, colors);
    this.device.queue.writeBuffer(this.indexBuffer, 0, indices);

    this.indexCount = indices.length;
  }

  createUniforms() {
    const uniformSize =
      64 + // modelMatrix (4x4)
      64 + // viewMatrix (4x4)
      64 + // projectionMatrix (4x4)
      64 + // normalMatrix (4x4)
      12 + // cameraPosition (vec3)
      12 + // lightPosition (vec3)
      12 + // lightColor (vec3)
      4;   // lightIntensity (float)

    this.uniformBuffer = this.device.createBuffer({
      size: uniformSize,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });

    this.bindGroup = this.device.createBindGroup({
      layout: this.pipeline.getBindGroupLayout(0),
      entries: [{ binding: 0, resource: { buffer: this.uniformBuffer } }]
    });

    // Create depth texture
    this.depthTexture = this.device.createTexture({
      size: [this.canvas.width, this.canvas.height],
      format: 'depth24plus',
      usage: GPUTextureUsage.RENDER_ATTACHMENT,
    });
  }

  // Matrix helper functions
  createIdentityMatrix() {
    return new Float32Array([
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ]);
  }

  createTranslationMatrix(x, y, z) {
    return new Float32Array([
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      x, y, z, 1
    ]);
  }

  createRotationMatrix(angleX, angleY, angleZ) {
    const cosX = Math.cos(angleX), sinX = Math.sin(angleX);
    const cosY = Math.cos(angleY), sinY = Math.sin(angleY);
    const cosZ = Math.cos(angleZ), sinZ = Math.sin(angleZ);

    const rotX = new Float32Array([
      1, 0, 0, 0,
      0, cosX, sinX, 0,
      0, -sinX, cosX, 0,
      0, 0, 0, 1
    ]);

    const rotY = new Float32Array([
      cosY, 0, -sinY, 0,
      0, 1, 0, 0,
      sinY, 0, cosY, 0,
      0, 0, 0, 1
    ]);

    const rotZ = new Float32Array([
      cosZ, sinZ, 0, 0,
      -sinZ, cosZ, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ]);

    return this.multiplyMatrices(this.multiplyMatrices(rotX, rotY), rotZ);
  }

  createPerspectiveMatrix(fov, aspect, near, far) {
    const f = 1.0 / Math.tan(fov / 2);
    const rangeInv = 1 / (near - far);

    return new Float32Array([
      f / aspect, 0, 0, 0,
      0, f, 0, 0,
      0, 0, (near + far) * rangeInv, -1,
      0, 0, near * far * rangeInv * 2, 0
    ]);
  }

  multiplyMatrices(a, b) {
    const result = new Float32Array(16);
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        result[i * 4 + j] = 0;
        for (let k = 0; k < 4; k++) {
          result[i * 4 + j] += a[i * 4 + k] * b[k * 4 + j];
        }
      }
    }
    return result;
  }

  updateUniforms(time) {
    const modelMatrix = this.multiplyMatrices(
      this.createTranslationMatrix(0, 0, 0),
      this.createRotationMatrix(time * 0.5, time * 0.3, time * 0.2)
    );

    const viewMatrix = this.createIdentityMatrix();
    viewMatrix[14] = -5; // Move camera back

    const projectionMatrix = this.createPerspectiveMatrix(
      this.camera.fov,
      this.camera.aspect,
      this.camera.near,
      this.camera.far
    );

    const uniformData = new Float32Array([
      ...modelMatrix,
      ...viewMatrix,
      ...projectionMatrix,
      ...modelMatrix, // Normal matrix (simplified)
      ...this.camera.position,
      2, 2, 2, // Light position
      1, 1, 1, // Light color
      1.0       // Light intensity
    ]);

    this.device.queue.writeBuffer(this.uniformBuffer, 0, uniformData);
  }

  render(time) {
    this.updateUniforms(time);

    const commandEncoder = this.device.createCommandEncoder();
    const textureView = this.context.getCurrentTexture().createView();

    const renderPass = commandEncoder.beginRenderPass({
      colorAttachments: [{
        view: textureView,
        clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store',
      }],
      depthStencilAttachment: {
        view: this.depthTexture.createView(),
        depthClearValue: 1.0,
        depthLoadOp: 'clear',
        depthStoreOp: 'store',
      },
    });

    renderPass.setPipeline(this.pipeline);
    renderPass.setBindGroup(0, this.bindGroup);
    renderPass.setVertexBuffer(0, this.positionBuffer);
    renderPass.setVertexBuffer(1, this.normalBuffer);
    renderPass.setVertexBuffer(2, this.colorBuffer);
    renderPass.setIndexBuffer(this.indexBuffer, 'uint16');
    renderPass.drawIndexed(this.indexCount);
    renderPass.end();

    this.device.queue.submit([commandEncoder.finish()]);
  }

  async start() {
    await this.init();

    const frame = (time) => {
      this.render(time * 0.001); // Convert to seconds
      requestAnimationFrame(frame);
    };

    frame(0);
  }
}

// Usage
async function render3DScene() {
  const canvas = document.getElementById('canvas');
  const scene = new Scene3D(canvas);

  try {
    await scene.start();
  } catch (error) {
    console.error('3D scene initialization failed:', error);
  }
}

if (navigator.gpu) {
  render3DScene();
}

Outils

Outils souvent utilisés avec cet exemple

Associé

Collections d’exemples associées

Associé

Dossiers associés

Dossier

Debogage d'Observabilite avec Logs, Traces, Webhooks et Charges JSON

Parsez les logs, decodez les traces, inspectez les preuves webhook/API, explorez les charges JSON et extrayez les motifs recurrents avant la note d'incident.

Dossier

Métadonnées audio, tags, chapitres et pochettes

Préparez des fichiers de musique ou de podcast avec des tags vérifiés, une pochette, des chapitres et des métadonnées propres.

Dossier

Workflow utilitaire OpenAPI

Generez types et documentation OpenAPI, verifiez les ruptures, validez les reponses et renforcez le contrat par stress et mutation tests.

Dossier

Capture autorisée de webhooks et contrôle de confiance du endpoint

Capturer et rejouer des requêtes webhook contrôlées, examiner les preuves de livraison et vérifier le certificat HTTPS du récepteur sans confondre certificat valide et confiance métier.

Accueil
Outils3212
Exemples593
Visualisations471
Visionneuses de fichiers103
Dossiers239
Étiquettes333
Images, audio et vidéo657

Outils de traitement, conversion et optimisation d’images, d’audio et de vidéo

Maths, dates et finance647

Calculateurs, outils numériques, logique de dates, statistiques et finance

Design et couleur298

Outils de couleur, mise en page, graphisme, style visuel et assistance design

Texte et rédaction209

Outils de rédaction, nettoyage, formatage, extraction et analyse de texte

Développement et Web193

Utilitaires développeur, réseau, débogage Web et assistants d’automatisation

Conversion et encodage175

Outils de conversion de formats, fichiers, archives, unités et encodages

Lancement d’un quiz de prospection et transmission des contacts
Textes commémoratifs : validation familiale et remise pour impression
Habitudes privées : sauvegarde, restauration et partage vérifié
D’un article aux brouillons X et LinkedIn : revue factuelle et livraison
Revue de cours Markdown interactif et livraison hors ligne

© 2026 Elysia Tools. Tous droits réservés.

DocumentationTarifsÀ proposConditions d'utilisationPolitique de confidentialitéCookiesContact

Media

Filigrane d'Images en Lot

Ajouter des filigranes à plusieurs images avec contrôle de position

Development

Générateur de Data URI

Convertit les fichiers en Data URI (Base64 ou pourcentage-encodé) pour intégrer images, polices et ressources directement dans HTML, CSS ou Markdown

Media

Renommage d'Images par Lot

Renommer plusieurs fichiers image en utilisant des modèles, remplacement de texte, numérotation et conversion de casse. Renvoie les fichiers renommés en téléchargement ZIP.

Development

Constructeur d'art direction responsive Picture / srcset

Génère un balisage <picture> complet avec recadrages art direction : éléments <source media> par point d'arrêt, candidates 1x/2x ou descripteurs de largeur par modèle {w}, couches AVIF/WebP optionnelles, gestion de sizes, width/height anti-CLS, et déclinaisons HTML et JSX avec notes de lint fondées sur la spécification.

Development

Generateur de Documentation API

Genere une documentation API elegante a partir de OpenAPI ou de commentaires annotés

Media

Convertisseur ICO vers WebP

Convertir les fichiers d'icônes ICO au format WebP avec compression moderne et support de transparence

Media

Convertir JPEG en AVIF

Convertir les images JPEG au format AVIF avec compression de nouvelle génération et rapport qualité/taille supérieur

Media

Convertir JPEG en JPEG Progressif

Convertir les images JPEG au format JPEG progressif pour de meilleures performances de chargement et une expérience utilisateur améliorée

Technologies web

WebAssembly (WASM) Calcul Haute Performance

WebAssembly pour les calculs haute performance dans le navigateur avec une vitesse quasi-native

Médias

Exemples SVG

Exemples de graphiques vectoriels évolutifs (SVG) démontrant diverses fonctionnalités et techniques SVG

Technologies web

Exemples de Communication Temps Réel WebRTC

Exemples complets WebRTC pour communication audio/vidéo P2P, canaux de données, partage d'écran et serveur de signalisation

Documents

Exemples PDF

Exemples PDF generes par les outils du 2026-02-01 au 2026-02-10

Traitement de texte

Échantillons de HTML avec Images

Échantillons de code source HTML avec des images pour tester l'extraction

IA et apprentissage automatique

Exemples OpenAI API

Exemples complets de l'API OpenAI incluant les modèles GPT, la génération d'images DALL-E, le traitement audio Whisper et les appels de fonctions