quaternion 3d logic

WebGL/WebGL2/WebGPU available via data uri.
no drift, calculated from origin.
generate unit quaternion(1===sin**2+cos**2).
rotation order switchable(q0*q1!==q1*q0).

quaternion

quaternionRotated
`p+2.*cross(q.xyz,cross(q.xyz,p)+q.w*p)`

getRotated
(q0,q1,precision)=>`vec4${precision}(c(c(p,${q0}),${q1}),0.)+vec4${precision}(0.,0.,1.,2.)`

vertexShaderSrc

WebGL
precision highp float;attribute vec3 p;uniform vec4 q0;uniform vec4 q1;
vec3 c(vec3 p,vec4 q){return ${quaternionRotated};}
void main(){gl_Position=${getRotated(`q0`,`q1`,``)};}

WebGL2
#version 300 es\nprecision highp float;in vec3 p;layout(std140)uniform q{vec4 q0;vec4 q1;};
vec3 c(vec3 p,vec4 q){return ${quaternionRotated};}
void main(){gl_Position=${getRotated(`q0`,`q1`,``)};}

WebGPU
struct Q{q0:vec4f,q1:vec4f}@binding(0)@group(0)var<uniform>q:Q;
fn c(p:vec3f,q:vec4f)->vec3f{return ${quaternionRotated};}
@vertex fn main(@location(0)p:vec3f)->@builtin(position)vec4f{return ${getRotated(`q.q0`,`q.q1`,`f`)};}

3d

fragmentShaderSrc

WebGL
precision highp float;
void main(){gl_FragColor=vec4(${color.map(v=>v+`.`).join(`,`)});}

WebGL2
#version 300 es\nprecision highp float;out vec4 c;
void main(){c=vec4(${color.map(v=>v+`.`).join(`,`)});}

WebGPU
@fragment fn main()->@location(0)vec4f{return vec4f(${color.map(v=>v+`.`).join(`,`)});}

step0: configure

WebGPU

01. request adapter
await navigator.gpu.requestAdapter()

02. request device
await adapter.requestDevice()

03. configure
context.configure({device:device,format:navigator.gpu.getPreferredCanvasFormat(),alphaMode:`premultiplied`})

step1: bind

WebGL, WebGL2

01. create program
context.createProgram()

02. create vertexShader
context.createShader(context.VERTEX_SHADER)

03. set vertexShaderSrc
context.shaderSource(vertexShader,vertexShaderSrc)

04. compile vertexShader
context.compileShader(vertexShader)

05. attach vertexShader
context.attachShader(program,vertexShader)

06. create fragmentShader
context.createShader(context.FRAGMENT_SHADER)

07. set fragmentShaderSrc
context.shaderSource(fragmentShader,fragmentShaderSrc)

08. compile fragmentShader
context.compileShader(fragmentShader)

09. attach fragmentShader
context.attachShader(program,fragmentShader)

10. link program
context.linkProgram(program)

11. clear color
context.clearColor(0,0,0,0)

12. use program
context.useProgram(program)

13. enable vertexAttribArray
context.enableVertexAttribArray(0)

14. bind arrayBuffer
context.bindBuffer(context.ARRAY_BUFFER,context.createBuffer())

15. set p
context.bufferData(context.ARRAY_BUFFER,p,context.STATIC_DRAW)

16. bind elementArrayBuffer
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER,context.createBuffer())

17. set is
context.bufferData(context.ELEMENT_ARRAY_BUFFER,is,context.STATIC_DRAW)

18. vertexAttribPointer
context.vertexAttribPointer(0,3,context.FLOAT,false,0,0)

WebGL

19. get q0Location
context.getUniformLocation(program,`q0`)

20. get q1Location
context.getUniformLocation(program,`q1`)

WebGL2

19. create uniformBuffer
context.createBuffer()

20. uniformBlockBinding
context.uniformBlockBinding(program,0,0)

21. bind uniformBuffer
context.bindBuffer(context.UNIFORM_BUFFER,uniformBuffer)

22. bind uniformBufferBase
context.bindBufferBase(context.UNIFORM_BUFFER,0,uniformBuffer)

WebGPU

01. create layout
configuration.device.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{}}]})

02. create pipeline
configuration.device.createRenderPipeline({
    layout:configuration.device.createPipelineLayout({bindGroupLayouts:[layout]})
    ,primitive:{topology:topology}
    ,vertex:{
        buffers:[{
            attributes:[{format:`float32x3`,offset:0,shaderLocation:0}]
            ,arrayStride:12
        }]
        ,module:configuration.device.createShaderModule({code:vertexShaderSrc})
    }
    ,fragment:{
        targets:[{format:configuration.format}]
        ,module:configuration.device.createShaderModule({code:fragmentShaderSrc})
    }
})

03. create renderBundleEncoder
configuration.device.createRenderBundleEncoder({colorFormats:[configuration.format]})

04. renderBundleEncoder set pipeline
renderBundleEncoder.setPipeline(pipeline)

05. create vertexBuffer
configuration.device.createBuffer({usage:GPUBufferUsage.VERTEX,size:p.byteLength,mappedAtCreation:true})

06. set p
new Float32Array(vertexBuffer.getMappedRange()).set(p)

07. vertexBuffer unmap
vertexBuffer.unmap()

08. renderBundleEncoder set vertexBuffer
renderBundleEncoder.setVertexBuffer(0,vertexBuffer)

09. create uniformBuffer
configuration.device.createBuffer({usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST,size:32})

10. renderBundleEncoder set bindGroup
renderBundleEncoder.setBindGroup(0,configuration.device.createBindGroup({entries:[{binding:0,resource:{buffer:uniformBuffer}}],layout:layout}))

11. create indexBuffer
configuration.device.createBuffer({usage:GPUBufferUsage.INDEX,size:is.byteLength,mappedAtCreation:true})

12. set is
new Uint16Array(indexBuffer.getMappedRange()).set(is)

13. unmap indexBuffer
indexBuffer.unmap()

14. renderBundleEncoder set indexBuffer
renderBundleEncoder.setIndexBuffer(indexBuffer,`uint16`)

15. renderBundleEncoder draw indexed
renderBundleEncoder.drawIndexed(is.length)

16. get bundle
renderBundleEncoder.finish()

17. create descriptor
{colorAttachments:[{loadOp:`clear`,storeOp:`store`,clearValue:[0,0,0,0]}]}

step2: render

WebGL

01. set q0Location
context.uniform4fv(q0Location,q0)

02. set q1Location
context.uniform4fv(q1Location,q1)

WebGL2

01. create q
q0 concat q1

02. set q
context.bufferData(context.UNIFORM_BUFFER,q,context.STATIC_DRAW)

WebGL, WebGL2

03. clear
context.clear(context.COLOR_BUFFER_BIT|context.DEPTH_BUFFER_BIT)

04. draw elements
context.drawElements(mode,is.length,context.UNSIGNED_SHORT,0)

WebGPU

01. create q
q0 concat q1

02. set q
configuration.device.queue.writeBuffer(uniformBuffer,0,q)

03. descriptor set view
descriptor.colorAttachments[0].view=context.getCurrentTexture().createView()

04. create commandEncoder
configuration.device.createCommandEncoder()

05. get renderPass
commandEncoder.beginRenderPass(descriptor)

06. renderPass execute bundles
renderPass.executeBundles([bundle])

07. renderPass end
renderPass.end()

08. submit
configuration.device.queue.submit([commandEncoder.finish()])