Created
May 14, 2025 02:58
-
-
Save EncodeTheCode/ef07ad0056d5fc95ff83ff5cd7323e10 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>3D Room FPS Style</title> | |
<style> | |
body { margin: 0; overflow: hidden; } | |
canvas { display: block; background: #000; } | |
</style> | |
</head> | |
<body> | |
<canvas id="canvas"></canvas> | |
<script> | |
const canvas = document.getElementById("canvas"); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
const gl = canvas.getContext("webgl"); | |
if (!gl) alert("WebGL not supported"); | |
const vs = ` | |
attribute vec4 position; | |
uniform mat4 model, view, projection; | |
void main() { | |
gl_Position = projection * view * model * position; | |
} | |
`; | |
const fs = ` | |
precision mediump float; | |
uniform vec4 color; | |
void main() { | |
gl_FragColor = color; | |
} | |
`; | |
function compileShader(type, source) { | |
const shader = gl.createShader(type); | |
gl.shaderSource(shader, source); | |
gl.compileShader(shader); | |
return shader; | |
} | |
const program = gl.createProgram(); | |
gl.attachShader(program, compileShader(gl.VERTEX_SHADER, vs)); | |
gl.attachShader(program, compileShader(gl.FRAGMENT_SHADER, fs)); | |
gl.linkProgram(program); | |
gl.useProgram(program); | |
const posAttr = gl.getAttribLocation(program, "position"); | |
const modelUni = gl.getUniformLocation(program, "model"); | |
const viewUni = gl.getUniformLocation(program, "view"); | |
const projUni = gl.getUniformLocation(program, "projection"); | |
const colorUni = gl.getUniformLocation(program, "color"); | |
function mat4() { | |
return new Float32Array(16); | |
} | |
function perspective(out, fov, aspect, near, far) { | |
const f = 1.0 / Math.tan(fov / 2); | |
out[0] = f / aspect; | |
out[5] = f; | |
out[10] = (far + near) / (near - far); | |
out[11] = -1; | |
out[14] = (2 * far * near) / (near - far); | |
return out; | |
} | |
function lookAt(out, eye, center, up) { | |
const x0 = eye[0] - center[0]; | |
const x1 = eye[1] - center[1]; | |
const x2 = eye[2] - center[2]; | |
const len = Math.hypot(x0, x1, x2); | |
const z = [x0 / len, x1 / len, x2 / len]; | |
const x = [ | |
up[1] * z[2] - up[2] * z[1], | |
up[2] * z[0] - up[0] * z[2], | |
up[0] * z[1] - up[1] * z[0] | |
]; | |
const l = Math.hypot(...x); | |
x[0] /= l; x[1] /= l; x[2] /= l; | |
const y = [ | |
z[1] * x[2] - z[2] * x[1], | |
z[2] * x[0] - z[0] * x[2], | |
z[0] * x[1] - z[1] * x[0] | |
]; | |
out.set([ | |
x[0], y[0], z[0], 0, | |
x[1], y[1], z[1], 0, | |
x[2], y[2], z[2], 0, | |
-(x[0] * eye[0] + x[1] * eye[1] + x[2] * eye[2]), | |
-(y[0] * eye[0] + y[1] * eye[1] + y[2] * eye[2]), | |
-(z[0] * eye[0] + z[1] * eye[1] + z[2] * eye[2]), | |
1 | |
]); | |
return out; | |
} | |
function identity(out) { | |
out.set([1, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1]); | |
return out; | |
} | |
function translate(out, x, y, z) { | |
identity(out); | |
out[12] = x; | |
out[13] = y; | |
out[14] = z; | |
return out; | |
} | |
const cube = new Float32Array([ | |
-1,-1,-1, 1,-1,-1, 1,1,-1, -1,1,-1, | |
-1,-1,1, 1,-1,1, 1,1,1, -1,1,1 | |
]); | |
const indices = new Uint16Array([ | |
0,1,2, 2,3,0, | |
4,5,6, 6,7,4, | |
0,1,5, 5,4,0, | |
2,3,7, 7,6,2, | |
1,2,6, 6,5,1, | |
3,0,4, 4,7,3 | |
]); | |
const vbo = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); | |
gl.bufferData(gl.ARRAY_BUFFER, cube, gl.STATIC_DRAW); | |
gl.enableVertexAttribArray(posAttr); | |
gl.vertexAttribPointer(posAttr, 3, gl.FLOAT, false, 0, 0); | |
const ibo = gl.createBuffer(); | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); | |
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); | |
const proj = mat4(); | |
const view = mat4(); | |
const model = mat4(); | |
perspective(proj, Math.PI / 4, canvas.width / canvas.height, 0.1, 100); | |
gl.enable(gl.DEPTH_TEST); | |
let keys = {}; | |
let mouseDown = false; | |
let camera = { | |
x: 0, y: 1.6, z: 5, | |
rotY: 0, | |
move(forward, strafe) { | |
this.x += Math.sin(this.rotY) * forward + Math.cos(this.rotY) * strafe; | |
this.z -= Math.cos(this.rotY) * forward - Math.sin(this.rotY) * strafe; | |
} | |
}; | |
document.addEventListener("keydown", e => keys[e.key.toLowerCase()] = true); | |
document.addEventListener("keyup", e => keys[e.key.toLowerCase()] = false); | |
document.addEventListener("mousedown", e => { if (e.button === 0) mouseDown = true; }); | |
document.addEventListener("mouseup", e => { if (e.button === 0) mouseDown = false; }); | |
function render() { | |
if (keys['w'] && mouseDown) camera.move(0.2, 0); | |
if (keys['s']) camera.move(-0.2, 0); | |
if (keys['a']) camera.move(0, 0.2); | |
if (keys['d']) camera.move(0, -0.2); | |
if (keys['arrowleft']) camera.rotY -= 0.03; | |
if (keys['arrowright']) camera.rotY += 0.03; | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
let cx = Math.sin(camera.rotY); | |
let cz = Math.cos(camera.rotY); | |
lookAt(view, [camera.x, camera.y, camera.z], [camera.x + cx, camera.y, camera.z - cz], [0, 1, 0]); | |
gl.uniformMatrix4fv(projUni, false, proj); | |
gl.uniformMatrix4fv(viewUni, false, view); | |
// Walls (room) | |
let walls = [ | |
[0, 0, -10], [0, 0, 10], [-10, 0, 0], [10, 0, 0], [0, 5, 0], [0, -5, 0] | |
]; | |
walls.forEach(pos => { | |
translate(model, ...pos); | |
gl.uniformMatrix4fv(modelUni, false, model); | |
gl.uniform4f(colorUni, 0.3, 0.3, 0.3, 1); | |
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); | |
}); | |
// Boxes | |
for (let i = -4; i <= 4; i += 4) { | |
for (let j = -4; j <= 4; j += 4) { | |
translate(model, i, -4, j); | |
gl.uniformMatrix4fv(modelUni, false, model); | |
gl.uniform4f(colorUni, 0.6, 0.4, 0.3, 1); | |
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); | |
} | |
} | |
requestAnimationFrame(render); | |
} | |
render(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment