const vertexShaderGlow = ` uniform vec3 viewVector; uniform float c; uniform float p; varying float intensity; varying vec3 vColor; vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { vec3 vNormal = normalize( normalMatrix * normal ); vec3 vNormel = normalize( normalMatrix * viewVector ); intensity = pow( c - dot(vNormal, vNormel), p ); gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); vColor = hsv2rgb(vec3(fract(gl_Position[2] / 270.0), 0.7, 0.7)); //vColor = vec3((sin(position[2] / 360.0) + 1.0) * 0.5, (sin(position[2] / 1000.0) + 1.0) * 0.5, (sin(position[2] / 70.0) + 1.0) * 0.5); //vColor = vec3(0.5, 0, 0.5); } `; const fragmentShaderGlow = ` varying vec3 vColor; varying float intensity; void main() { vec3 glow = vColor * intensity; gl_FragColor = vec4( glow, 1.0 ); } `; const fragmentShader = ` #include uniform vec3 iResolution; uniform float iTime; uniform sampler2D iChannel0; // By Daedelus: https://www.shadertoy.com/user/Daedelus // license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. #define TIMESCALE 0.25 #define TILES 8 #define COLOR 0.7, 1.6, 2.8 varying vec2 vUv; varying float z; void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord.xy / iResolution.xy; uv.x *= iResolution.x / iResolution.y; vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES)); float p = 1.0 - mod(noise.r + noise.g + noise.b + (iTime + z) * float(TIMESCALE), 1.0); p = min(max(p * 3.0 - 1.8, 0.1), 2.0); vec2 r = mod(uv * float(TILES), 1.0); r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0)); p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0); fragColor = vec4(COLOR, 1.0) * p; } void main() { mainImage(gl_FragColor, vUv * iResolution.xy); } `; const vertexShader = ` varying vec2 vUv; varying float z; void main() { vUv = uv; z = -position[1]; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `; const diameterOfTunnel = 100; const spawnInterval = 40; let lastSpawned = 0; var gn; var headSet = false; var betaReference = 0.0; var leftRightMove = 0.0; var upDownMove = 0.0; var speed = 5.0; let score = 0; let removed = 0; let running = false; let frameCount = 0; function logger(text) { console.log(text); } function init_gn() { var args = { logger: logger }; gn = new GyroNorm(); gn.init(args).then(function() { var isAvailable = gn.isAvailable(); if (!isAvailable.deviceOrientationAvailable) { console.log({ message: 'Device orientation is not available.' }); } if (!isAvailable.accelerationAvailable) { console.log({ message: 'Device acceleration is not available.' }); } if (!isAvailable.accelerationIncludingGravityAvailable) { console.log({ message: 'Device acceleration incl. gravity is not available.' }); } if (!isAvailable.rotationRateAvailable) { console.log({ message: 'Device rotation rate is not available.' }); } start_gn(); }).catch(function(e) { console.error(e); }); document.addEventListener('keydown', e => { if (e.key === "ArrowLeft") { leftRightMove = -10.0; } else if (e.key === "ArrowRight") { leftRightMove = 10.0; } else if (e.key === "ArrowDown") { upDownMove = -10.0; } else if (e.key === "ArrowUp") { upDownMove = 10.0; } }); document.addEventListener('keyup', e => { if (e.key === "ArrowLeft") { leftRightMove = 0; } else if (e.key === "ArrowRight") { leftRightMove = 0; } else if (e.key === "ArrowDown") { upDownMove = 0; } else if (e.key === "ArrowUp") { upDownMove = 0; } }); } function stop_gn() { gn.stop(); } function start_gn() { gn.start(gnCallBack); } function gnCallBack(data) { if (!headSet) { betaReference = data.do.beta; } else { let leftRight = data.do.alpha > 180.0 ? "right" : "left"; leftRightMove = data.do.alpha > 180.0 ? 360.0 - data.do.alpha : -data.do.alpha; let upDown = data.do.beta > betaReference ? "up" : "down"; upDownMove = data.do.beta - betaReference; } } function set_head_gn() { gn.setHeadDirection(); headSet = true; } document.getElementById("set_head").addEventListener("click", () => { document.getElementById("log").innerHTML += "clicked button
"; set_head_gn(); }); init_gn(); let scene, renderer; const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 10000); const geometrySphere = new THREE.SphereGeometry(11, 32, 32); const materialCube = new THREE.MeshBasicMaterial(); const customMaterial = new THREE.ShaderMaterial({ uniforms: { "c": { type: "f", value: 0.8 }, "p": { type: "f", value: 2.4 }, viewVector: { type: "v3", value: camera.position } }, vertexShader: vertexShaderGlow, //vertexColors: true, fragmentShader: fragmentShaderGlow, side: THREE.FrontSide, blending: THREE.AdditiveBlending, transparent: true }); const loader = new THREE.TextureLoader(); const texture = loader.load('/bayer.png'); texture.minFilter = THREE.NearestFilter; texture.magFilter = THREE.NearestFilter; texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; const uniforms = { iTime: { value: 0 }, iResolution: { value: new THREE.Vector3(1, 1, 1) }, iChannel0: { value: texture }, }; const material = new THREE.ShaderMaterial({ vertexShader, fragmentShader, uniforms, }); const borderGeometry = new THREE.PlaneGeometry(1, 1); const cubes = []; const borders = []; init(); animate(); document.getElementById("start").onclick = () => { document.getElementById("start").style.zIndex = -10; document.getElementById("start").style.visibility = "hidden"; speed = 5.0; score = 0; for (let i = 0; i < cubes.length; i++) { scene.remove(cubes[i]); } cubes.length = 0; removed = 0; running = true; } function gameOver() { running = false; document.getElementById("score").innerText = document.getElementById("score").innerText + " - Game Over!"; document.getElementById("start").style.zIndex = 0; document.getElementById("start").style.visibility = "visible"; document.getElementById("start").innerText = "Restart"; } function init() { // scene = new THREE.Scene(); camera.position.set(0, 10, 25); camera.lookAt(scene.position); const ambientLight = new THREE.AmbientLight(0xffffff, 0.7); scene.add(ambientLight); const pointLight = new THREE.PointLight(0xffffff, 0.8); camera.add(pointLight); scene.add(camera); renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); window.addEventListener('resize', onWindowResize); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } function animate() { requestAnimationFrame(animate); uniforms.iTime.value = frameCount / 60.0; frameCount++; if (running) { for (let i = 0; i < cubes.length; i++) { if (cubes[i].material !== materialCube) { continue; } let x1 = cubes[i].position.x; let y1 = cubes[i].position.y; let z1 = cubes[i].position.z; let x2 = camera.position.x; let y2 = camera.position.y; let z2 = camera.position.z; if (z2 > z1) { // account for very fast speeds // (prevents clipping through obstacles) z2 = Math.max(z1, z2 - speed); } let dist_squared = (x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2; if (dist_squared <= (cubes[i].geometry.parameters.radius * cubes[i].scale.x) ** 2) { gameOver(); break; } } camera.position.z -= 0.5 * speed; speed += 0.01; camera.position.x += 0.1 * leftRightMove; camera.position.y += 0.1 * upDownMove; while (camera.position.z < lastSpawned - spawnInterval) { lastSpawned -= spawnInterval; // randomly spawn large spheres let scale = Math.random() < 0.1 ? 5.0 : 1.0; const meshCube = new THREE.Mesh(geometrySphere, materialCube); meshCube.position.z = camera.position.z - speed * 210; meshCube.position.x = (2 * Math.random() - 1.0) * diameterOfTunnel; meshCube.position.y = (2 * Math.random() - 1.0) * 0.9 * diameterOfTunnel; meshCube.scale.multiplyScalar(scale); const outlineMesh = new THREE.Mesh(geometrySphere, customMaterial); outlineMesh.scale.multiplyScalar(scale * 1.17); outlineMesh.position.z = meshCube.position.z; outlineMesh.position.x = meshCube.position.x; outlineMesh.position.y = meshCube.position.y; meshCube.add(outlineMesh); scene.add(outlineMesh); cubes.push(outlineMesh); scene.add(meshCube); cubes.push(meshCube); } } renderer.render(scene, camera); for (let i = 0; i < cubes.length; i++) { if (cubes[i].position.z > camera.position.z + cubes[i].geometry.parameters.radius * cubes[i].scale.x + 20) { scene.remove(cubes[i]); cubes.splice(i, 1); i--; removed++; } } if (removed >= 2 || score == 0) { score += removed / 2; removed = 0; document.getElementById("score").innerText = score; } if (borders.length == 0 || borders[borders.length - 1].position.z - camera.position.z > -1200) { const newZ = borders.length == 0 ? -200 : borders[borders.length - 1].position.z - diameterOfTunnel * 2; let border = new THREE.Mesh(borderGeometry, material); //border.position.x = -70; border.position.y = -diameterOfTunnel; border.rotation.x = -Math.PI / 2; border.position.z = newZ; border.scale.multiplyScalar(diameterOfTunnel * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); border.position.y = diameterOfTunnel; border.rotation.x = Math.PI / 2; border.position.z = newZ; border.scale.multiplyScalar(diameterOfTunnel * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); border.position.x = -diameterOfTunnel; border.rotation.y = Math.PI / 2; border.position.z = newZ; border.scale.multiplyScalar(diameterOfTunnel * 2); scene.add(border); borders.push(border); border = new THREE.Mesh(borderGeometry, material); border.position.x = diameterOfTunnel; border.rotation.y = -Math.PI / 2; border.position.z = newZ; border.scale.multiplyScalar(diameterOfTunnel * 2); scene.add(border); borders.push(border); } for (let i = 0; i < borders.length; i++) { if (borders[i].position.z > camera.position.z + diameterOfTunnel + 50) { scene.remove(borders[i]); borders.splice(i, 1); i--; } } }