www.xbdev.net
xbdev - software development
Thursday February 19, 2026
Home | Contact | Support | WebGPU Graphics and Compute ... | WebGPU.. Games, Tutorials, Demos, Projects, and Code.....
     
 

WebGPU..

Games, Tutorials, Demos, Projects, and Code.....

 


Transparent Cubes


Transparency introduces its own challenges (other than just 'enabling' alpha mode) - you also have to make sure you draw the objects in the correct order.


Cube inside a cube (with the outer cube transparent).
Cube inside a cube (with the outer cube transparent).


Functions Used: setVertexBuffer(), setIndexBuffer(), drawIndexed(), createBuffer(), getMappedRange(), getContext(), requestAdapter(), getPreferredCanvasFormat(), createCommandEncoder(), beginRenderPass(), setPipeline(), draw(), end(), submit(), getCurrentTexture(), createView(), createShaderModule()


Enabling the alpha mode in the context configuration - also the 'blend' details in the fragment shader (pipeline). Then it's just a matter of setting the alpha information. Instead of setting the alpha for each vertex - we pass an alpha value as a uniform to the fragment shader - then for each cube we can set the alpha value.

Make sure you draw the cubes in the correct order. If you draw the larger cube first - the alpha won't be able to 'mix' with anything behind it - as the little cube hasn't be drawn yet. Also, the big cube will update the depth buffer - so the little cube which sits inside the big cube won't be drawn (it'll be discarded due to the depth).



// Load matrix library on dynamically (on-the-fly)
let matprom await fetch'https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.6.0/gl-matrix-min.js' );
let mattex  await matprom.text();
var 
script   document.createElement('script');
script.type  'text/javascript';
script.innerHTML mattex;
document.head.appendChild(script); 


const 
canvas document.createElement('canvas');
document.body.appendChildcanvas );
canvas.width  canvas.height 512;

const 
gpu navigator.gpu;
console.log'navigator.gpu:'gpu );

const 
adapter await gpu.requestAdapter();
const 
device  await adapter.requestDevice();
const 
context canvas.getContext('webgpu');

const 
presentationFormat navigator.gpu.getPreferredCanvasFormat();  // context. getPreferredFormat(adapter); - no longer supported
context.configure({
  
device,
  
formatpresentationFormat,
  
alphaMode'premultiplied',  /* IMPORTANT IMPORTANT - vs default opacity */
});

////////////////////////////////////////
// Create vertex buffers and load data
////////////////////////////////////////

function createCube()
{
    
// unit cube
    
const cubeVertexArray = new Float32Array([
      
// position,   color
       
1, -1,  1,   100,   
      -
1, -1,  1,   100,  
      -
1, -1, -1,   100,   
       
1, -1, -1,   100,  
       
1, -1,  1,   100,   
      -
1, -1, -1,   100,   

       
1,  1,  1,   010
       
1, -1,  1,   010,   
       
1, -1, -1,   010,   
       
1,  1, -1,   010,   
       
1,  1,  1,   010,  
       
1, -1, -1,   010

      -
1,  1,  1,   001,   
       
1,  1,  1,   001
       
1,  1, -1,   001,  
      -
1,  1, -1,   001,   
      -
1,  1,  1,   001,   
       
1,  1, -1,   001,   

      -
1, -1,  1,   110,  
      -
1,  1,  1,   110,  
      -
1,  1, -1,   110,  
      -
1, -1, -1,   110,  
      -
1, -1,  1,   110,  
      -
1,  1, -1,   110,  

       
1,  1,  1,   101,  
      -
1,  1,  1,   101,  
      -
1, -1,  1,   101,  
      -
1, -1,  1,   101,  
       
1, -1,  1,   101,  
       
1,  1,  1,   101,  

       
1, -1, -1,   011,  
      -
1, -1, -1,   011,  
      -
1,  1, -1,   011,  
       
1,  1, -1,   011,  
       
1, -1, -1,   011,  
      -
1,  1, -1,   011
    ]);

    const 
gpuBuffer device.createBuffer({ size:  cubeVertexArray.byteLength,
                                            
usageGPUBufferUsage.VERTEX GPUBufferUsage.COPY_DST });

    
device.queue.writeBuffer(gpuBuffer0cubeVertexArray);
  
    return { 
buffer:gpuBuffernumVertices:36stride:6*};
}

// --------------------------------------------------------------------------

let basicVertWGSL = `
@group(0) @binding(0) var<uniform> timer : f32;

struct Transforms {
    model      : mat4x4<f32>,
    view       : mat4x4<f32>,
    projection : mat4x4<f32>,
};
@group(0) @binding(1) var<uniform> transforms : Transforms;

struct VertexOutput {
  @builtin(position) Position : vec4<f32>,
  @location(0) fragColor      : vec3<f32>
};

@vertex
fn main(@location(0) position : vec3<f32>,
        @location(1) color    : vec3<f32>) -> VertexOutput {
        
  var mvp = transforms.projection * transforms.view * transforms.model;
        
  var output : VertexOutput;
  output.Position     = mvp * vec4<f32>(position, 1.0);
  output.fragColor    = color;
  return output;
}
`;

let basicPixelWGSL = `
@group(0) @binding(2) var<uniform> alpha : f32;

@fragment
fn main(@location(0) fragColor:   vec3<f32>) -> @location(0) vec4<f32> {
 
  return vec4<f32>(fragColor, alpha);
  
  // if you want a 'constant' color for the shape
  // return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
`;

// ----------------------------------------------------------------

function buildMatrixpr// position, rotation, scale
{
    
// if not set fall back to default values
    
if (!s= {x:1y:1z:1};
    if (!
r= {x:0y:0z:0};
    if (!
p= {x:0y:0z:0};
  
    
// Create the matrix in Javascript (using matrix library)
    
const modelMatrix          mat4.create();

    
// create the model transform with a rotation and translation
    
let translateMat mat4.create();   mat4.fromTranslationtranslateMatObject.values(p) );
    
let rotateXMat   mat4.create();   mat4.fromXRotation(rotateXMatr.x);
    
let rotateYMat   mat4.create();   mat4.fromYRotation(rotateYMatr.y);
    
let rotateZMat   mat4.create();   mat4.fromZRotation(rotateZMatr.z);
    
let scaleMat     mat4.create();   mat4.fromScaling(scaleMatObject.values(s) );

    
mat4.multiply(modelMatrixmodelMatrix,   translateMat);
    
mat4.multiply(modelMatrixmodelMatrix,   rotateXMat);
    
mat4.multiply(modelMatrixmodelMatrix,   rotateYMat);
    
mat4.multiply(modelMatrixmodelMatrix,   rotateZMat);
    
mat4.multiply(modelMatrixmodelMatrix,   scaleMat);
    return 
modelMatrix;
}

// build a model matrix (scale, rotate and position it wherever we want)
let modelMatrix buildMatrix();
   
// setup the projection
let projectionMatrix mat4.create(); 
mat4.perspective(projectionMatrixMath.PI 2canvas.width canvas.height0.001500.0);

// default camera `lookat` - camera is at -4 units down the z-axis looking at '0,0,0'
let viewMatrix mat4.create();
mat4.lookAt(viewMatrix, [0,0,-3.0],  [0,0,0], [010]);


let mvpUniformBuffer device.createBuffer({
  
size64*3,
  
usageGPUBufferUsage.UNIFORM GPUBufferUsage.COPY_DST
});

device.queue.writeBuffer(mvpUniformBuffer,      0,      modelMatrix);
device.queue.writeBuffer(mvpUniformBuffer,      64,     viewMatrix);
device.queue.writeBuffer(mvpUniformBuffer,      128,    projectionMatrix);

// ----------------------------------------------------------------

const timerUniformBuffer device.createBuffer({ size:  4// single float for the timer
                                               
usageGPUBufferUsage.UNIFORM GPUBufferUsage.COPY_DST});

let timeData = new Float32Array( [0.0]);
device.queue.writeBuffer(timerUniformBuffer,   0,    timeData );

// ----------------------------------------------------------------

const alphaUniformBuffer device.createBuffer({ size:  4// single float for the timer
                                               
usageGPUBufferUsage.UNIFORM GPUBufferUsage.COPY_DST});

let alphaData = new Float32Array( [1.0]);
device.queue.writeBuffer(alphaUniformBuffer,   0,    alphaData );

// ----------------------------------------------------------------

const sceneUniformBindGroupLayout device.createBindGroupLayout({
  
entries: [ { binding0visibilityGPUShaderStage.VERTEX,   buffer: { type"uniform" } },
             { 
binding1visibilityGPUShaderStage.VERTEX,   buffer: { type"uniform" } },
             { 
binding2visibilityGPUShaderStage.FRAGMENTbuffer: { type"uniform" } },
           ]
});

const 
sceneUniformBindGroup device.createBindGroup({
  
layoutsceneUniformBindGroupLayout,
  
entries: [ {  binding0resource: { buffertimerUniformBuffer  } },
             {  
binding1resource: { buffermvpUniformBuffer    } },
             {  
binding2resource: { bufferalphaUniformBuffer  } },
           ]
});

// ----------------------------------------------------------------

let cubeData createCube();

// ----------------------------------------------------------------

const pipeline device.createRenderPipeline({
  
layoutdevice.createPipelineLayout({bindGroupLayouts: [sceneUniformBindGroupLayout]}),
  
vertex: {
    
moduledevice.createShaderModule({
      
codebasicVertWGSL
    
}),
    
entryPoint"main",
    
buffers: [ {arrayStridecubeData.stride,
                
attributes: [ {shaderLocation0offset0,      format'float32x3' }, // position
                              
{shaderLocation1offset3*4,    format'float32x3'  // color
             
] } ]
  },
  
fragment: {
    
moduledevice.createShaderModule({ codebasicPixelWGSL }),
    
entryPoint"main",
    
targets: [{ formatpresentationFormat,
                
blend: { color: {srcFactor:'src-alpha'dstFactor:"one-minus-src-alpha" ,operation:"add"},
                               
alpha: {srcFactor:'one',       dstFactor:"one" ,                operation:"add"}  }
              }]
  },
  
primitive: {
    
topology"triangle-list",
    
cullMode'back'
  
},
  
depthStencil: {
    
format"depth24plus",
    
depthWriteEnabledtrue,
    
depthCompare"less"
  
}
});

    
const 
depthTexture device.createTexture({
  
size: [canvas.widthcanvas.height1],
  
format"depth24plus",
  
usage:  GPUTextureUsage.RENDER_ATTACHMENT
})

let rotation = {x:0y:0z:0};

function 
draw() {
  
  
// update uniform buffer
  
timeData[0] += 0.005;
  
device.queue.writeBuffer(timerUniformBuffer0timeData);
    
  
// Draw 2 cubes - on inside the other
  
for (let k=0k<2k++)
  {
      
// update rotation on local cube
      
rotation.+= 0.02;
      
rotation.+= 0.03;
      
rotation.+= 0.01;
    
      
let scale = {x:1y:1z:1};
      if ( 
k==)
      { 
         
alphaData[0] = 1.0;
         
scale.scale.scale.0.5
      }
      if ( 
k==)
      {
         
alphaData[0] = 0.2;
         
scale.scale.scale.1.0
      }

      
device.queue.writeBuffer(alphaUniformBuffer,   0,    alphaData );
      
    
      
modelMatrix buildMatrixnullrotationscale );
      
device.queue.writeBuffer(mvpUniformBuffer,      0,      modelMatrix);
    
    
      const 
renderPassDescription = {
          
colorAttachments: [{
            
viewcontext.getCurrentTexture().createView(),
            
loadOp: ( k=="clear":"load" ), 
            
clearValue: [0.90.90.91], // clear screen color
            
storeOp'store'
          
}],
          
depthStencilAttachment: {
            
viewdepthTexture.createView(),
            
depthLoadOp:  ( k=="clear":"load"), 
            
depthClearValue1,
            
depthStoreOp"store",
          }
      };

      
renderPassDescription.colorAttachments[0].view context.getCurrentTexture().createView();

      const 
commandEncoder device.createCommandEncoder();
      const 
renderPass commandEncoder.beginRenderPass(renderPassDescription);

      
renderPass.setPipeline(pipeline);
      
renderPass.setVertexBuffer(0cubeData.buffer);
      
renderPass.setBindGroup(0sceneUniformBindGroup);
      
renderPass.draw(cubeData.numVertices100);
      
renderPass.end();

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

  
requestAnimationFrame(draw);
};

draw();




Resources and Links


• WebGPU Lab Example [LINK]


















WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Compute graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright wgsl webgpugems shading language cookbook kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.