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.....

 


Sky Box


Skybox is a trick to sit within a textured cube - so when the camera looks around - you think you're in an open world. Later on when we get to lighting and reflections - the shiny surfaces need to reflect something - and the skybox is the solution.


A cube has  sides. Make a texture to each side (6 textures). These textures are seamlessly mapped - so they wrap around without...
A cube has sides. Make a texture to each side (6 textures). These textures are seamlessly mapped - so they wrap around without showing the edges. Then sitting inside that cube you think you're in the open world.


Functions Used: performance.now(), getContext(), requestAdapter(), getPreferredCanvasFormat(), createCommandEncoder(), beginRenderPass(), setPipeline(), draw(), end(), submit(), getCurrentTexture(), createView(), createShaderModule()


var script document.createElement('script');
script.type  'text/javascript';
script.async false;
script.src   'https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.6.0/gl-matrix-min.js';
document.head.appendChild(script); 


const 
canvas document.createElement('canvas');
document.body.appendChildcanvas );
canvas.width  canvas.height 512;
console.logcanvas.widthcanvas.height );
const 
context canvas.getContext('webgpu');

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

const 
adapter await gpu.requestAdapter();
const 
device  await adapter.requestDevice();

const 
presentationSize = [ canvas.clientWidth,
                           
canvas.clientHeight  ];
const 
presentationFormat navigator.gpu.getPreferredCanvasFormat(); // context.getPreferredFormat(adapter); - no longer supported
console.logpresentationFormat  );

context.configure({ devicedevice,
                    
formatpresentationFormat,
                    
alphaMode'opaque' });

var 
positions =  new Float32Array([
    -
111,  -1,-11,   1,-11,   111// front
    
-11,-1,  -1,-1,-1,   1,-1,-1,   11,-1// back
     
1,-11,   1,-1,-1,   11,-1,   111// left
    
-1,-11,  -1,-1,-1,  -11,-1,  -111// right
    
    
-1,-1,-1,  -1,-11,   1,-11,   1,-1,-1,
    -
11,-1,  -111,   111,   11,-]);

var 
indices = new Uint32Array([
    
012,    023,   
      
456,    467,
    
8910,   810,11,  
      
12,13,14,     12,14,15,
    
16,17,18,   16,18,19,  
      
20,21,22,   20,22,23 ]);

var 
texCoords  = new Float32Array([
    
0,00,11,1,  1,0
    
1,01,10,1,  0,0
  
    
0,11,11,00,0,
    
1,10,10,01,0,
  
    
0,0,01,01,1
    
0,0,11,11,]);

var 
texIds = new Int32Array([
    
0000// front
    
1111// back
    
2222// right
    
3333// left
    
4444// up
    
5555  // down
]);


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

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

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

const 
indicesBuffer device.createBuffer({
  
size:  indices.byteLength,
  
usageGPUBufferUsage.INDEX GPUBufferUsage.COPY_DST
});

device.queue.writeBuffer(positionBuffer,  0positions);
device.queue.writeBuffer(texCoordsBuffer0texCoords);
device.queue.writeBuffer(texIdsBuffer,    0texIds   );
device.queue.writeBuffer(indicesBuffer ,  0indices  );

var 
vertWGSL = ` 
struct Uniforms {
  viewMatrix : mat4x4<f32>,
  projMatrix : mat4x4<f32>
};
@binding(0) @group(0) var<uniform> uniforms : Uniforms;

struct VSOut {
    @builtin(position) Position: vec4<f32>,
    @location(0)       uvs     : vec2<f32>,
    @location(1)       texId   : f32
};

@vertex
fn main(@location(0) inPos     : vec3<f32>,
        @location(1) texCoords : vec2<f32>,
        @location(2) texId     : i32        ) -> VSOut  

  var vm : mat4x4<f32> = uniforms.viewMatrix;
  vm[3][0]  = 0.0;
  vm[3][1]  = 0.0;
  vm[3][2]  = 0.0;
  
  var vsOut: VSOut;
  vsOut.Position = uniforms.projMatrix * vm * vec4<f32>( inPos*2.0, 1.0);
  vsOut.uvs      = texCoords;
  vsOut.texId    = f32(texId);
  return vsOut;

}
`;

var 
fragWGSL = `
@group(0) @binding(1) var mySampler: sampler;
@group(0) @binding(2) var myTexture: texture_2d_array<f32>;

@fragment
fn main( @location(0) uvs    : vec2<f32>,
         @location(1) texId  : f32       ) -> @location(0) vec4<f32> 
{
    let tid = i32(texId);
    //if ( tid < 0 )
    //{
    //    return vec4<f32>(1.0, 0.0, 0.0, 1.0);
    //}
    return textureSample(myTexture, mySampler, uvs, tid );
}
`;

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

let textureSampler device.createSampler({
     
minFilter"linear",
     
magFilter"linear"
});

// -------------------------------------------------
// texture image
// -------------------------------------------------
/*
let imgs = [ 'https://webgpulab.xbdev.net/var/images/sky01/rt.png',  // 0-1-2-3
             'https://webgpulab.xbdev.net/var/images/sky01/lf.png',  // 2-3-0-1
             'https://webgpulab.xbdev.net/var/images/sky01/ft.png',  // 1-2-3-0
             'https://webgpulab.xbdev.net/var/images/sky01/bk.png',  // 3-0-1-2
             'https://webgpulab.xbdev.net/var/images/sky01/dn.png',
               'https://webgpulab.xbdev.net/var/images/sky01/up.png'
              ]; 
*/

/*
let imgs = [ 'https://webgpulab.xbdev.net/var/images/sky03/rt.jpg',  
             'https://webgpulab.xbdev.net/var/images/sky03/lf.jpg', 
             'https://webgpulab.xbdev.net/var/images/sky03/ft.jpg',  
             'https://webgpulab.xbdev.net/var/images/sky03/bk.jpg', 
             'https://webgpulab.xbdev.net/var/images/sky03/dn.jpg',
               'https://webgpulab.xbdev.net/var/images/sky03/up.jpg'
              ]; 
*/

let imgs = [ 'https://webgpulab.xbdev.net/var/images/sky05/posz.jpg'
             
'https://webgpulab.xbdev.net/var/images/sky05/negz.jpg',
             
'https://webgpulab.xbdev.net/var/images/sky05/posx.jpg',
             
'https://webgpulab.xbdev.net/var/images/sky05/negx.jpg',
             
'https://webgpulab.xbdev.net/var/images/sky05/negy.jpg',
               
'https://webgpulab.xbdev.net/var/images/sky05/posy.jpg'
              
]; 


/*
let imgs = [ 'https://webgpulab.xbdev.net/var/images/sky02/posz.jpg', 
             'https://webgpulab.xbdev.net/var/images/sky02/negz.jpg',
             'https://webgpulab.xbdev.net/var/images/sky02/posx.jpg',
             'https://webgpulab.xbdev.net/var/images/sky02/negx.jpg',
             'https://webgpulab.xbdev.net/var/images/sky02/negy.jpg',
               'https://webgpulab.xbdev.net/var/images/sky02/posy.jpg'
              ];
*/
const basicTexture device.createTexture({
    
size: [102410246],
    
formatpresentationFormat // "bgra8unorm", // "rgba8unorm",
    
usage0x4 0x2 // GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING
    //usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT 
});


let textureData= new Uint8Array1024 1024 4); // 6 images

// scale/stretch all images 
for (let i=0i<imgs.lengthi++)
{
  const 
img document.createElement("img");
  
img.src imgs[i];
  
await img.decode();

  const 
imageCanvas document.createElement('canvas');
  
imageCanvas.width =  1024// img width;
  
imageCanvas.height 1024// img height;
  
const imageCanvasContext imageCanvas.getContext('2d');
  
imageCanvasContext.drawImage(img00imageCanvas.widthimageCanvas.height);
  const 
imageData imageCanvasContext.getImageData(00imageCanvas.widthimageCanvas.height);

  for (
let x=0x<1024*1024*4x++)
  {
     
textureData[1024*1024*4*x] = imageData.data];
  }
}



device.queue.writeTexture(
            { 
texturebasicTexture },
            
textureData,
            {   
offset     :  0,
                
bytesPerRow:  10244// width * 4 (4 bytes per float)
                
rowsPerImage1024     // height
             
},
            [ 
1024  ,  1024,  6  ]   );


// GPUTextureView 
let texView basicTexture.createView( { formatpresentationFormat 
                                         
dimension'2d-array',
                                         
aspect'all',
                                         
arrayLayerCount}  );

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

const projectionMatrix     mat4.create();
const 
viewMatrix           mat4.create();

mat4.perspective(projectionMatrixMath.PI 2canvas.width canvas.height0.110.0)
mat4.lookAt(viewMatrix, [00, -3], [000], [010]);
//mat4.multiply(viewProjectionMatrix, projectionMatrix, viewMatrix);

const vertexUniformBuffer device.createBuffer({
  
size128,
  
usageGPUBufferUsage.UNIFORM GPUBufferUsage.COPY_DST
});

device.queue.writeBuffer(vertexUniformBuffer,   0,  viewMatrix       );
device.queue.writeBuffer(vertexUniformBuffer,   64projectionMatrix );

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

const sceneUniformBindGroupLayout device.createBindGroupLayout({
  
entries: [
    { 
binding0visibilityGPUShaderStage.VERTEX,   buffer:  { type"uniform"  }   }, 
    { 
binding1visibilityGPUShaderStage.FRAGMENTsampler: { type"filtering"}   },
    { 
binding2visibilityGPUShaderStage.FRAGMENTtexture: { sampleType"float",
                                                                  
viewDimension"2d-array"} } 
  ]
});

const 
uniformBindGroup device.createBindGroup({
  
layout:   sceneUniformBindGroupLayout,
  
entries: [
    { 
binding 0resource: { buffervertexUniformBuffer } },
    { 
binding 1resourcetextureSampler },
    { 
binding 2resourcetexView        },
   ],
});

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


const pipeline device.createRenderPipeline({
    
layoutdevice.createPipelineLayout({bindGroupLayouts: [sceneUniformBindGroupLayout]}),
    
vertex:   {  module    device.createShaderModule({ 
                             
code vertWGSL }),
                 
entryPoint'main',
                 
buffers    : [ { arrayStride12attributes: [{ shaderLocation0,
                                                                  
format"float32x3",
                                                                  
offset0  }]        },
                                { 
arrayStride8,  attributes: [{ shaderLocation1,
                                                                  
format"float32x2",
                                                                  
offset0  }]        },
                                { 
arrayStride4,  attributes: [{ shaderLocation2,
                                                                  
format"sint32"// negative means no tex
                                                                  
offset0  }]        }
                              ]},
    
fragment: {  module    device.createShaderModule({ 
                             
code fragWGSL,     }),
                 
entryPoint'main',
                 
targets: [{  format presentationFormat  }] },
    
primitive: { topology  'triangle-list',
                 
frontFace "ccw",
                 
cullMode  'none',
                 
stripIndexFormatundefined }
                 
});

// GPURenderPassDescriptor 
const renderPassDescriptor = { 
           
colorAttachments:  [{    
           
view     undefined// asign later in frame
           
loadOp:"clear"clearValue: { r0.0g0.5b0.5a1.0 },
           
storeOp  'store' }]
};



let mi = new mouseinputcanvas0,0.00.4  );

function 
frame() 
{
    
let rot mi.getPos();
    
let dis mi.getPos().d;
    
let yaw   rot.x;
    
let pitch rot.y;

    const 
ta vec3.fromValues0.00.00.0 );

    
let cos Math.cos;
    
let sin Math.sin;

    
let dir vec3.create();
    
dir[2] = -cosyaw   ) * cospitch );
    
dir[1] = sinpitch );
    
dir[0] = sinyaw   ) * cospitch );

    
let va vec3.create(); vec3.addvatadir );
    
vec3.scalevavadis );
  
    
mat4.perspective(projectionMatrixMath.PI 2canvas.width canvas.height0.015000.0)
    
mat4.lookAt(viewMatrixva,  ta, [010]);
  

    
device.queue.writeBuffer(vertexUniformBuffer,   0,  viewMatrix       );
    
device.queue.writeBuffer(vertexUniformBuffer,   64projectionMatrix );


    
//  ---------------------
  
    
renderPassDescriptor.colorAttachments[0].view context.getCurrentTexture().createView();

    const 
commandEncoder device.createCommandEncoder();

    const 
renderPass commandEncoder.beginRenderPass(renderPassDescriptor);
    
renderPass.setPipeline(pipeline);

    
renderPass.setBindGroup(0uniformBindGroup);
    
renderPass.setVertexBuffer(0positionBuffer);
    
renderPass.setVertexBuffer(1texCoordsBuffer);
    
renderPass.setVertexBuffer(2texIdsBuffer);
    
renderPass.setIndexBuffer(indicesBuffer'uint32');
    
renderPass.drawIndexed(indices.length100);

    
renderPass.end();
    
device.queue.submit([commandEncoder.finish()]);

    
// if you want constant updates (animated) - keep refreshing
    
requestAnimationFrame(frame);
};
frame();




function 
mouseinput(elinxayadd)
{
   var 
base = {x:xay:yad:dd};
   var 
tmp  = {x:xay:yad:dd};
   var 
el elin;
   
el.addEventListener('mousedown'downListener);
   
el.addEventListener('mouseup',   upListener  );
   
el.addEventListener('mouseout',  upListener  );
   
el.addEventListener("wheel",     wheel       );
   var 
start = {x:0y:0};
  
   function 
downListener(e){
     if (
e.button !== ) return; // left
       
     
start = {x:e.pageXy:e.pageY};
     
tmp   = {x:base.x,  y:base.y,  d:base.};
     
el.addEventListener('mousemove'moveListener)
   }
   function 
upListener(e){
     
el.removeEventListener('mousemove'moveListener)
   }
   function 
moveListener(e){
     
//console.log('moving..');
     
const diffX = (e.pageX start.x);
     const 
diffY = (e.pageY start.y);
     const 
delta 0.01;
     
base.tmp.diffX*delta;
     
base.tmp.diffY*delta;
   }
   function 
wheel(e){
     
base.-= e.deltaY 0.0001;
   }
  
   
this.getPos = function()
   {
     return 
base;
   }
}
// end mouseinput


console.log('ready... click mouse and drag to look around..');






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.