Add gyro and webgl demo

This commit is contained in:
Paul Brinkmeier 2025-07-21 08:30:08 +02:00
parent 611cee6f40
commit 1635eb9ede
7 changed files with 1989 additions and 0 deletions

43
cg/webgl/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
background-color: #000;
}
.view {
display: flex;
width: 100vw;
height: 100vh;
justify-content: center;
align-items: center;
}
@media (max-aspect-ratio: 1) {
.view-canvas {
width: 100vw;
height: 100vw;
}
}
@media (min-aspect-ratio: 1) {
.view-canvas {
width: 100vh;
height: 100vh;
}
}
</style>
</head>
<body>
<div class="view">
<canvas id="canvas" class="view-canvas" width="64" height="64"></canvas>
</div>
<script src="./m4.js"></script>
<script src="./webgl.js"></script>
</body>
</html>

1467
cg/webgl/m4.js Normal file

File diff suppressed because it is too large Load Diff

323
cg/webgl/webgl.js Normal file
View File

@ -0,0 +1,323 @@
"use strict";
const v3 = {
add: function (v1, v2) {
return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]];
},
normalize: function (v) {
return v3.scale(v, 1 / v3.norm(v));
},
norm: function (v) {
return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2) + Math.pow(v[2], 2));
},
scale: function (v, f) {
return [v[0] * f, v[1] * f, v[2] * f];
},
};
function getGyroPermission(){
if(!DeviceOrientationEvent.requestPermission){
return Promise.resolve("granted")
}
else{
return DeviceOrientationEvent.requestPermission()
}
}
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
function createProgram(gl, shaders) {
const program = gl.createProgram();
for (const shader of shaders) {
gl.attachShader(program, shader);
}
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(program));
}
return program;
}
function initIsocahedron(vertices, elements) {
const t = 1 + Math.sqrt(5) / 2;
vertices.push(
v3.normalize([-1, t, 0]),
v3.normalize([1, t, 0]),
v3.normalize([-1, -t, 0]),
v3.normalize([1, -t, 0]),
v3.normalize([0, -1, t]),
v3.normalize([0, 1, t]),
v3.normalize([0, -1, -t]),
v3.normalize([0, 1, -t]),
v3.normalize([t, 0, -1]),
v3.normalize([t, 0, 1]),
v3.normalize([-t, 0, -1]),
v3.normalize([-t, 0, 1]),
);
elements.push(
0, 11, 5,
0, 5, 1,
0, 1, 7,
0, 7, 10,
0, 10, 11,
1, 5, 9,
5, 11, 4,
11, 10, 2,
10, 7, 6,
7, 1, 8,
3, 9, 4,
3, 4, 2,
3, 2, 6,
3, 6, 8,
3, 8, 9,
4, 9, 5,
2, 4, 11,
6, 2, 10,
8, 6, 7,
9, 8, 1,
);
}
function getMidpoint(vertices, midpoints, i0, i1) {
const k = `${i0}_${i1}`;
if (midpoints.has(k)) {
return vertices[midpoints[k]];
}
const p0 = vertices[i0];
const p1 = vertices[i1];
const midpoint = v3.normalize(v3.scale(v3.add(p0, p1), 0.5));
const midpointIndex = vertices.length;
midpoints.set(k, midpointIndex);
vertices.push(midpoint);
return midpointIndex;
}
function subdivideIsocahedron(vertices, elements, midpoints) {
const newElements = [];
for (let i = 0; i < elements.length; i += 3) {
const p0 = elements[i];
const p1 = elements[i + 1];
const p2 = elements[i + 2];
const m0 = getMidpoint(vertices, midpoints, p0, p1);
const m1 = getMidpoint(vertices, midpoints, p1, p2);
const m2 = getMidpoint(vertices, midpoints, p2, p0);
newElements.push(
m0, m1, m2,
p0, m0, m2,
p1, m1, m0,
p2, m2, m1
);
}
return newElements;
}
function generateIsocahedron() {
const r = 1;
const n = 3;
const tau = 2 * Math.PI;
const vertices = [];
const normals = [];
let elements = [];
initIsocahedron(vertices, elements);
const midpoints = new Map();
for (let i = 0; i < 1; i++) {
elements = subdivideIsocahedron(vertices, elements, midpoints);
}
return {
vertices: [].concat(...vertices),
// as we want to approximate a unit sphere, all the vertices are unit vectors
normals: [].concat(...vertices),
elements
};
}
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
const vertexShader = createShader(gl, gl.VERTEX_SHADER, `
attribute vec4 a_position;
attribute vec3 a_normal;
uniform mat4 u_matrix;
uniform mat4 u_world;
uniform mat4 u_worldIT;
uniform vec3 u_light;
uniform vec3 u_camera;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec3 v_pos;
void main() {
gl_Position = u_matrix * a_position;
v_pos = a_position.rgb;
v_pos.r = abs(v_pos.r);
v_pos.g = abs(v_pos.g);
v_pos.b = abs(v_pos.b);
v_normal = mat3(u_worldIT) * a_normal;
vec3 surfaceWorldPos = (u_world * a_position).xyz;
v_surfaceToLight = u_light - surfaceWorldPos;
v_surfaceToView = u_camera - surfaceWorldPos;
}
`);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, `
precision highp float;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec3 v_pos;
void main() {
vec3 halfVector = normalize(normalize(v_surfaceToLight) + normalize(v_surfaceToView));
float ka = 0.3;
float kd = 0.8;
float ks = 0.6;
float gamma = 100.0;
gl_FragColor = vec4(v_pos, 1.0);
gl_FragColor.rgb *= ka + (kd * dot(normalize(v_normal), normalize(v_surfaceToLight)));
gl_FragColor.rgb += ks * pow(max(0.0, dot(normalize(v_normal), halfVector)), gamma);
}
`);
const program = createProgram(gl, [vertexShader, fragmentShader]);
const {vertices, normals, elements} = generateIsocahedron();
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const elementBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
const positionAttribLocation = gl.getAttribLocation(program, "a_position");
const normalAttribLocation = gl.getAttribLocation(program, "a_normal");
const matrixLocation = gl.getUniformLocation(program, "u_matrix");
const worldMatrixLocation = gl.getUniformLocation(program, "u_world");
const worldITMatrixLocation = gl.getUniformLocation(program, "u_worldIT");
const lightVecLocation = gl.getUniformLocation(program, "u_light");
const cameraVecLocation = gl.getUniformLocation(program, "u_camera");
const aspectRatio = gl.canvas.width / gl.canvas.height;
const zNear = 1;
const zFar = 2000;
const rotation = [0, 0, 0];
gl.useProgram(program);
gl.enableVertexAttribArray(positionAttribLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
positionAttribLocation,
3, // element size
gl.FLOAT,
false, // normalize
0, // stride
0 // offset
);
gl.enableVertexAttribArray(normalAttribLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalAttribLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
function drawScene () {
window.requestAnimationFrame(drawScene);
const t = performance.now() / 1000;
const lightPos = [10, 10, 30];
const projectionMatrix = m4.perspective(Math.PI / 180 * 60, aspectRatio, zNear, zFar);
const cameraPos = [0, 0, 30];
const target = [0, 0, 0];
const up = [0, 1, 0];
const cameraMatrix = m4.lookAt(cameraPos, target, up);
const viewMatrix = m4.inverse(cameraMatrix);
let worldMatrix = m4.identity();
worldMatrix = m4.xRotate(worldMatrix, rotation[0]);
worldMatrix = m4.yRotate(worldMatrix, rotation[1]);
worldMatrix = m4.zRotate(worldMatrix, rotation[2]);
worldMatrix = m4.scale(worldMatrix, 12, 12, 12);
const worldITMatrix = m4.transpose(m4.inverse(worldMatrix));
const viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
const worldViewProjectionMatrix = m4.multiply(viewProjectionMatrix, worldMatrix);
/*
let matrix = projectionMatrix;
matrix = m4.translate(matrix, 0, 0, -30);
matrix = m4.xRotate(matrix, rotation[0]);
matrix = m4.yRotate(matrix, rotation[1]);
matrix = m4.zRotate(matrix, rotation[2]);
matrix = m4.scale(matrix, 10, 10, 10);
*/
gl.uniformMatrix4fv(matrixLocation, false, worldViewProjectionMatrix);
gl.uniformMatrix4fv(worldMatrixLocation, false, worldMatrix);
gl.uniformMatrix4fv(worldITMatrixLocation, false, worldITMatrix);
gl.uniform3fv(lightVecLocation, lightPos);
gl.uniform3fv(cameraVecLocation, cameraPos);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(
gl.TRIANGLES,
elements.length, // count
gl.UNSIGNED_SHORT,
0, // offset
);
}
drawScene();
canvas.addEventListener("click", e => {
getGyroPermission()
.then(response => {
window.addEventListener("deviceorientation", e => {
rotation[0] = 2 * (-e.beta / 180 * Math.PI);
rotation[1] = 2 * (-e.gamma / 180 * Math.PI);
rotation[2] = 2 * (-e.alpha / 180 * Math.PI + Math.PI / 2);
});
});
});

BIN
gyro/AufnahmeLIna.mp3 Normal file

Binary file not shown.

130
gyro/Untitled-1.html Normal file
View File

@ -0,0 +1,130 @@
<!DOCTYPE html>
<html>
<body>
<h1>colles gyro dingens</h1>
<marquee>ich bin der Davidmann2</marquee>
<fieldset>
<legend>
status
</legend>
<strong id="status">
loading
</strong>
</fieldset>
<fieldset>
<legend>
gyro
</legend>
<strong id="gyro">
loading
</strong>
</fieldset>
<fieldset>
<legend>
gyro
</legend>
<strong id="gyro2">
loading
</strong>
</fieldset>
<script>
function loadAudioFile(fileName){
return (
fetch(fileName)
.then(response=>{
return(response.arrayBuffer())
})
.then(arrayBuffer=>{
return(ctx.decodeAudioData(arrayBuffer))
})
.then(audioBuffer=>{
loaded++
document.getElementById("status").innerHTML = "loaded " + loaded + "/" + fileNames.length
return(audioBuffer)
})
)
}
const fileNames = ["AufnahmeLIna.mp3", "blatters.mp3"]
let buffers = null
let loaded = 0
Promise.all(fileNames.map(loadAudioFile))
.then(audioBuffers=>{
document.getElementById("status").innerHTML = "Done loading"
buffers = audioBuffers
})
</script>
<script>
let samples = null
const ctx = new AudioContext()
function playStuff(){
console.log("imStuff")
if(samples == null){
samples = []
for(const buffer of buffers){
const sample = ctx.createBufferSource()
sample.buffer = buffer
const gain = ctx.createGain()
sample.connect(gain)
gain.connect(ctx.destination)
sample.start()
samples.push({sample:sample, gainNode:gain})
}
}
}
</script>
<script>
function stopStuff(){
for(sample of samples){
sample.sample.stop()
}
samples = null
}
</script>
<script>
function getGyroPermission(){
if(!DeviceOrientationEvent.requestPermission){
return Promise.resolve("granted")
}
else{
return DeviceOrientationEvent.requestPermission()
}
}
function initGyro(){
//alert(DeviceOrientationEvent.requestPermission())
getGyroPermission().then(response => {
window.addEventListener("deviceorientation", e => {
document.querySelector("#gyro").innerHTML = e.alpha / 360
document.querySelector("#gyro2").innerHTML = (e.beta + 180) / 360
if(samples != null){
samples[0].gainNode.gain.value = e.alpha / 360
samples[1].gainNode.gain.value = (e.beta + 180) / 360
}
})
})
}
</script>
<button onclick="playStuff()">PLAY</button>
<button onclick="stopStuff()">STOP</button>
<button onclick="initGyro()">START GYRO</button>
</body>
</html>

BIN
gyro/blatters.mp3 Normal file

Binary file not shown.

26
gyro/index.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<body>
<h1>Is it working?</h1>
<button id="perm">click me for permissions</button>
<div style="color: red" id="disp">lalala</div>
<script>
document.querySelector("#perm").addEventListener("click", e => {
const ac = new AudioContext();
const osc = ac.createOscillator();
osc.type = "square";
osc.frequency.setValueAtTime(440, ac.currentTime);
osc.connect(ac.destination);
osc.start();
DeviceMotionEvent.requestPermission().then(response => {
window.addEventListener("devicemotion", e => {
document.querySelector("#disp").innerHTML = Math.round(e.acceleration.x);
osc.frequency.setValueAtTime(440 * Math.pow(2, e.acceleration.x / 10), ac.currentTime);
});
});
});
</script>
</body>
</html>