obj loading, lighting

This commit is contained in:
Luca Conte 2025-05-15 17:37:10 +02:00
parent e842eb3d66
commit 2525f321ee
12 changed files with 84022 additions and 565 deletions

View File

@ -6,10 +6,12 @@
<title>CG1 MDI</title>
<script src="matrix-math.js"></script>
<script src="shader.js"></script>
<script src="wavefront.js"></script>
<script src="script.js"></script>
<style>
:root {
font-size: 20px;
--padding: 10px;
}
html, body {
@ -44,10 +46,10 @@
display: block;
background-color: #181825;
}
#stats, #controls {
#stats {
position: absolute;
top: 20px;
padding: 20px;
top: var(--padding);
padding: var(--padding);
display: block;
border: solid 2px #11111b;
background-color: #313244;
@ -55,11 +57,7 @@
}
#stats {
right: 20px;
}
#controls {
left: 20px;
right: var(--padding);
}
</style>
</head>
@ -69,28 +67,6 @@
<div id="stats">
FPS: <span id="fps"></span> - <span id="ms"></span>
</div>
<div id="controls">
<div>
Click & Drag to move camera
</div>
<div>
Scroll to zoom
</div>
<div>
LookAt Matrix: <input type="range" min=0 max=1 step=0.01 value=0 id="interpolateLookAt" autocomplete="off">
</div>
<div>
Projection Matrix: <input type="range" min=0 max=1 step=0.01 value=0 id="interpolateProjection" autocomplete="off" disabled="yes">
<div style="margin-left: 20px;">Invert Z-Axis: <input type="checkbox" id="invertZAxis" checked autocomplete="off"></div>
</div>
<div>
Use virtual camera: <input type="checkbox" id="displayMatricesVirtually" checked autocomplete="off">
</div>
<div>
Backface Culling: <input type="checkbox" id="backfaceCulling" autocomplete="off">
</div>
</div>
</div>
</body>
</html>

View File

@ -369,6 +369,18 @@ function mat4Inverse(out, m) {
return;
}
function mat4vec4Multiply(out, inMat, inVec) {
let result = [0, 0, 0, 0];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
result[i] += inMat[i + j * 4] * inVec[j];
}
}
out[0] = result[0];
out[1] = result[1];
out[2] = result[2];
out[3] = result[3];
}
/**
* subtracts b from a, storing the result in out
@ -507,4 +519,12 @@ function mat3Inverse(out, inm) {
mat3Adjoint(result, inm);
mat3MultiplyScalar(result, result, 1 / mat3Determinant(inm));
mat3Copy(result, out);
}
function mat3MakeNormal(out, inm) {
let result = [];
mat3From4(result, inm);
mat3Inverse(result, result);
mat3Transpose(result, result);
mat3Copy(result, out);
}

34
src/obj/cube.obj Normal file
View File

@ -0,0 +1,34 @@
# Blender 4.4.3
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 1.000000 0.000000
vt 0.000000 1.000000
vt 0.000000 0.000000
vt 1.000000 1.000000
s 0
f 5/1/1 3/2/1 1/3/1
f 3/1/2 8/2/2 4/3/2
f 7/1/3 6/2/3 8/3/3
f 2/1/4 8/2/4 6/3/4
f 1/1/5 4/2/5 2/3/5
f 5/1/6 2/2/6 6/3/6
f 5/1/1 7/4/1 3/2/1
f 3/1/2 7/4/2 8/2/2
f 7/1/3 5/4/3 6/2/3
f 2/1/4 4/4/4 8/2/4
f 1/1/5 3/4/5 4/2/5
f 5/1/6 1/4/6 2/2/6

33819
src/obj/monkey.obj Normal file

File diff suppressed because it is too large Load Diff

33905
src/obj/smooth_monkey.obj Normal file

File diff suppressed because it is too large Load Diff

16109
src/obj/teapot.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,36 +3,16 @@
let cv, gl;
let program;
let floorProgram;
let hVertices;
let hVertexBuffer;
class Object {
vertexBuffer;
numVertices;
}
let cubeVertices;
let cubeVertexBuffer;
let lineCubeVertices;
let lineCubeVertexBuffer;
let originVertices;
let originVertexBuffer;
let floorVertices;
let floorVertexBuffer;
let cameraVertices;
let cameraVertexBuffer;
let o = new Object();
let lastFrame = Date.now();
let interpolateProjection = 0;
let interpolateLookAt = 0;
let displayMatricesVirtually = true;
let virtualRealInterpolation = 1;
let invertZAxis = true;
let cameraPitch = 0.565;
let cameraYaw = 0.375;
let cameraDistance = 4;
@ -64,43 +44,6 @@ async function init() {
// input handling
document.getElementById("interpolateProjection").addEventListener("input", (e) => {
interpolateProjection = e.target.value;
if (interpolateProjection <= 0) {
document.getElementById("interpolateLookAt").disabled = "";
} else {
document.getElementById("interpolateLookAt").disabled = "yes";
}
});
document.getElementById("invertZAxis").addEventListener("input", (e) => {
invertZAxis = e.target.checked;
});
document.getElementById("interpolateLookAt").addEventListener("input", (e) => {
interpolateLookAt = e.target.value;
if (interpolateLookAt >= 1) {
document.getElementById("interpolateProjection").disabled = "";
} else {
document.getElementById("interpolateProjection").disabled = "yes";
}
});
document.getElementById("displayMatricesVirtually").addEventListener("input", (e) => {
displayMatricesVirtually = e.target.checked;
if (!displayMatricesVirtually) {
document.getElementById("invertZAxis").checked = true;
document.getElementById("invertZAxis").disabled = true;
} else {
document.getElementById("invertZAxis").checked = invertZAxis;
document.getElementById("invertZAxis").disabled = false;
}
});
document.getElementById("backfaceCulling").addEventListener("input", (e) => {
if (e.target.checked) {
gl.enable(gl.CULL_FACE);
} else {
gl.disable(gl.CULL_FACE);
}
});
cv.addEventListener("mousedown", (e) => {
if (e.button == 0) {
mouseDragging = true;
@ -116,7 +59,7 @@ async function init() {
});
document.addEventListener("mousemove", (e) => {
if (mouseDragging && displayMatricesVirtually) {
if (mouseDragging) {
cameraYaw += e.movementX / 100;
cameraPitch += e.movementY / 100;
@ -130,303 +73,49 @@ async function init() {
if (cameraDistance > 15) cameraDistance = 15;
e.preventDefault();
});
// end input handling
console.log("compiling shaders...");
program = await buildShaderProgram(gl, "shaders/vertex.glsl", "shaders/fragment.glsl");
floorProgram = await buildShaderProgram(gl, "shaders/floor-vertex.glsl", "shaders/floor-fragment.glsl");
console.log("creating vertex buffer");
hVertices = [
// X // Y
// left vertical bar
-0.35, 0.6, 0.0, 1.0, 0.3, 0.3,
-0.35, -0.6, 0.0, 1.0, 0.3, 0.3,
-0.2, 0.6, 0.0, 1.0, 0.3, 0.3,
let f = await readObjFile("obj/smooth_monkey.obj");
o = new Object();
-0.2, 0.6, 0.0, 1.0, 0.3, 0.3,
-0.35, -0.6, 0.0, 1.0, 0.3, 0.3,
-0.2, -0.6, 0.0, 1.0, 0.3, 0.3,
// right vertical bar
0.35, -0.6, 0.0, 1.0, 0.3, 0.3,
0.35, 0.6, 0.0, 1.0, 0.3, 0.3,
0.2, 0.6, 0.0, 1.0, 0.3, 0.3,
0.35, -0.6, 0.0, 1.0, 0.3, 0.3,
0.2, 0.6, 0.0, 1.0, 0.3, 0.3,
0.2, -0.6, 0.0, 1.0, 0.3, 0.3,
// middle bar
-0.2, 0.1, 0.0, 1.0, 0.3, 0.3,
-0.2, -0.1, 0.0, 1.0, 0.3, 0.3,
0.2, 0.1, 0.0, 1.0, 0.3, 0.3,
0.2, 0.1, 0.0, 1.0, 0.3, 0.3,
-0.2, -0.1, 0.0, 1.0, 0.3, 0.3,
0.2, -0.1, 0.0, 1.0, 0.3, 0.3,
// bottom bar
-0.35, -0.7, 0.0, 1.0, 0.3, 0.3,
-0.35, -0.9, 0.0, 1.0, 0.3, 0.3,
0.35, -0.9, 0.0, 1.0, 0.3, 0.3,
-0.35, -0.7, 0.0, 1.0, 0.3, 0.3,
0.35, -0.9, 0.0, 1.0, 0.3, 0.3,
0.35, -0.7, 0.0, 1.0, 0.3, 0.3,
];
cubeVertices = [
// Back face (red)
-1, -1, -1, 1, 0.5, 0.5,
-1, 1, -1, 1, 0.5, 0.5,
1, 1, -1, 1, 0.5, 0.5,
-1, -1, -1, 1, 0.5, 0.5,
1, 1, -1, 1, 0.5, 0.5,
1, -1, -1, 1, 0.5, 0.5,
// Front face (green)
-1, -1, 1, 0.5, 1, 0.5,
1, -1, 1, 0.5, 1, 0.5,
1, 1, 1, 0.5, 1, 0.5,
-1, -1, 1, 0.5, 1, 0.5,
1, 1, 1, 0.5, 1, 0.5,
-1, 1, 1, 0.5, 1, 0.5,
// Left face (blue)
-1, 1, -1, 0.5, 0.5, 1,
-1, -1, -1, 0.5, 0.5, 1,
-1, 1, 1, 0.5, 0.5, 1,
-1, 1, 1, 0.5, 0.5, 1,
-1, -1, -1, 0.5, 0.5, 1,
-1, -1, 1, 0.5, 0.5, 1,
// Right face (yellow)
1, -1, -1, 1, 1, 0.5,
1, 1, -1, 1, 1, 0.5,
1, 1, 1, 1, 1, 0.5,
1, -1, -1, 1, 1, 0.5,
1, 1, 1, 1, 1, 0.5,
1, -1, 1, 1, 1, 0.5,
// Top face (magenta)
-1, 1, -1, 1, 0.5, 1,
-1, 1, 1, 1, 0.5, 1,
1, 1, 1, 1, 0.5, 1,
-1, 1, -1, 1, 0.5, 1,
1, 1, 1, 1, 0.5, 1,
1, 1, -1, 1, 0.5, 1,
// Bottom face (cyan)
-1, -1, 1, 0.5, 1, 1,
-1, -1, -1, 0.5, 1, 1,
1, -1, 1, 0.5, 1, 1,
1, -1, 1, 0.5, 1, 1,
-1, -1, -1, 0.5, 1, 1,
1, -1, -1, 0.5, 1, 1
];
lineCubeVertices = [
1, 1, 1, 1, 0, 0,
1, 1, -1, 1, 0, 0,
1, 1, 1, 1, 0, 0,
1, -1, 1, 1, 0, 0,
1, 1, 1, 1, 0, 0,
-1, 1, 1, 1, 0, 0,
1, 1, -1, 1, 0, 0,
1, -1, -1, 1, 0, 0,
1, 1, -1, 1, 0, 0,
-1, 1, -1, 1, 0, 0,
1, -1, 1, 1, 0, 0,
1, -1, -1, 1, 0, 0,
1, -1, 1, 1, 0, 0,
-1, -1, 1, 1, 0, 0,
-1, 1, 1, 1, 0, 0,
-1, 1, -1, 1, 0, 0,
-1, 1, 1, 1, 0, 0,
-1, -1, 1, 1, 0, 0,
1, -1, -1, 1, 0, 0,
-1, -1, -1, 1, 0, 0,
-1, -1, 1, 1, 0, 0,
-1, -1, -1, 1, 0, 0,
-1, 1, -1, 1, 0, 0,
-1, -1, -1, 1, 0, 0,
];
originVertices = [
0, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0,
0, 0, 0, 0, 0, 1,
0, 0, 1, 0, 0, 1,
];
floorVertices = [
1, 0, 1, 0, 0, 0,
-1, 0, -1, 0, 0, 0,
-1, 0, 1, 0, 0, 0,
-1, 0, -1, 0, 0, 0,
1, 0, 1, 0, 0, 0,
1, 0, -1, 0, 0, 0,
];
cameraVertices = [
// Cube Body (dark gray)
// Front face
-1, -1, 1, 0.196, 0.196, 0.196,
1, -1, 1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
-1, -1, 1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
-1, 1, 1, 0.196, 0.196, 0.196,
// Back face
-1, -1, -1, 0.196, 0.196, 0.196,
1, 1, -1, 0.196, 0.196, 0.196,
1, -1, -1, 0.196, 0.196, 0.196,
-1, -1, -1, 0.196, 0.196, 0.196,
-1, 1, -1, 0.196, 0.196, 0.196,
1, 1, -1, 0.196, 0.196, 0.196,
// Left face
-1, -1, -1, 0.196, 0.196, 0.196,
-1, -1, 1, 0.196, 0.196, 0.196,
-1, 1, 1, 0.196, 0.196, 0.196,
-1, -1, -1, 0.196, 0.196, 0.196,
-1, 1, 1, 0.196, 0.196, 0.196,
-1, 1, -1, 0.196, 0.196, 0.196,
// Right face
1, -1, -1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
1, -1, 1, 0.196, 0.196, 0.196,
1, -1, -1, 0.196, 0.196, 0.196,
1, 1, -1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
// Top face
-1, 1, -1, 0.196, 0.196, 0.196,
-1, 1, 1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
-1, 1, -1, 0.196, 0.196, 0.196,
1, 1, 1, 0.196, 0.196, 0.196,
1, 1, -1, 0.196, 0.196, 0.196,
// Bottom face
-1, -1, -1, 0.196, 0.196, 0.196,
1, -1, 1, 0.196, 0.196, 0.196,
-1, -1, 1, 0.196, 0.196, 0.196,
-1, -1, -1, 0.196, 0.196, 0.196,
1, -1, -1, 0.196, 0.196, 0.196,
1, -1, 1, 0.196, 0.196, 0.196,
// Lens (light gray)
// Front face
-0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
// Back face
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
// Left face
-0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
// Right face
0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
// Top face
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, 0.5, -1.0, 0.706, 0.706, 0.706,
0.5, 0.5, -1.6, 0.706, 0.706, 0.706,
// Bottom face
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.0, 0.706, 0.706, 0.706,
0.5, -0.5, -1.6, 0.706, 0.706, 0.706,
-0.5, -0.5, -1.6, 0.706, 0.706, 0.706
];
hVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, hVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(hVertices), gl.STATIC_DRAW);
cubeVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVertices), gl.STATIC_DRAW);
lineCubeVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lineCubeVertices), gl.STATIC_DRAW);
originVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, originVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(originVertices), gl.STATIC_DRAW);
floorVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(floorVertices), gl.STATIC_DRAW);
cameraVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cameraVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cameraVertices), gl.STATIC_DRAW);
// unbind buffer
o.vertexBuffer = gl.createBuffer();
o.numVertices = f.length / 8;
gl.bindBuffer(gl.ARRAY_BUFFER, o.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(f), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// set clear colour
gl.clearColor(...hexToRgb("181825"), 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.enable(gl.CULL_FACE);
gl.frontFace(gl.CCW);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable( gl.LINE_SMOOTH );
gl.enable( gl.POLYGON_SMOOTH );
gl.hint( gl.LINE_SMOOTH_HINT, gl.NICEST );
gl.hint( gl.POLYGON_SMOOTH_HINT, gl.NICEST )
// start drawing
requestAnimationFrame(draw);
}
function setAttribPointers() {
let positionAttribLocation = gl.getAttribLocation(program, "vertPosition");
let colorAttribLocation = gl.getAttribLocation(program, "vertColor");
let normalAttribLocation = gl.getAttribLocation(program, "vertNormal");
let textureAttribLocation = gl.getAttribLocation(program, "vertTexture");
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, gl.FALSE, 6 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.vertexAttribPointer(colorAttribLocation, 3, gl.FLOAT, gl.FALSE, 6 * Float32Array.BYTES_PER_ELEMENT, 3 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.vertexAttribPointer(textureAttribLocation, 2, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 3 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribPointer(normalAttribLocation, 3, gl.FLOAT, gl.FALSE, 8 * Float32Array.BYTES_PER_ELEMENT, 5 * Float32Array.BYTES_PER_ELEMENT);
gl.enableVertexAttribArray(positionAttribLocation);
gl.enableVertexAttribArray(colorAttribLocation);
gl.enableVertexAttribArray(textureAttribLocation);
gl.enableVertexAttribArray(normalAttribLocation);
}
let frameCount = 0;
@ -464,193 +153,48 @@ async function draw() {
hue += deltaTime / 5;
if (hue > 1) hue = 0;
if (displayMatricesVirtually) {
virtualRealInterpolation = Math.min(virtualRealInterpolation + deltaTime * 2, 1);
} else {
virtualRealInterpolation = Math.max(virtualRealInterpolation - deltaTime * 2, 0);
}
smoothCameraDistance += (cameraDistance - smoothCameraDistance) / 8;
updateStats(deltaTime);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let identity = [];
mat4Identity(identity);
// Real Matrices
let realProjectionMatrix = [];
mat4BuildPerspective(realProjectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.1, 30);
let projectionMatrix = [];
mat4BuildPerspective(projectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.1, 30);
let realViewMatrix = [];
let realCameraPosition = [Math.cos(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch), Math.sin(cameraPitch) * smoothCameraDistance, Math.sin(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch)];
let realCameraLookAt = [0, 0, 0];
let realCameraUp = [0, 1, 0];
mat4BuildLookAt(realViewMatrix, realCameraPosition, realCameraLookAt, realCameraUp);
let viewMatrix = [];
let cameraPosition = [Math.cos(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch), Math.sin(cameraPitch) * smoothCameraDistance, Math.sin(cameraYaw) * smoothCameraDistance * Math.cos(cameraPitch)];
let cameraLookAt = [0, 0, 0];
let cameraUp = [0, 1, 0];
mat4BuildLookAt(viewMatrix, cameraPosition, cameraLookAt, cameraUp);
mat4Interpolate(realProjectionMatrix, identity, realProjectionMatrix, virtualRealInterpolation * virtualRealInterpolation);
mat4Interpolate(realViewMatrix, identity, realViewMatrix, virtualRealInterpolation);
let normalMatrix = [];
mat3MakeNormal(normalMatrix, viewMatrix);
gl.useProgram(program);
gl.uniformMatrix4fv(gl.getUniformLocation(program, "realProjectionMatrix"), gl.FALSE, new Float32Array(realProjectionMatrix));
gl.uniformMatrix4fv(gl.getUniformLocation(program, "realViewMatrix"), gl.FALSE, new Float32Array(realViewMatrix));
gl.uniformMatrix4fv(gl.getUniformLocation(program, "projectionMatrix"), gl.FALSE, new Float32Array(projectionMatrix));
gl.uniformMatrix4fv(gl.getUniformLocation(program, "modelViewMatrix"), gl.FALSE, new Float32Array(viewMatrix));
gl.uniformMatrix3fv(gl.getUniformLocation(program, "normalMatrix"), gl.FALSE, new Float32Array(normalMatrix));
gl.useProgram(floorProgram);
gl.uniformMatrix4fv(gl.getUniformLocation(floorProgram, "realProjectionMatrix"), gl.FALSE, new Float32Array(realProjectionMatrix));
let lightPosition = [Math.cos(hue * Math.PI * 2) * 3, 2.0, Math.sin(hue * Math.PI * 2) * 3, 1.0];
mat4vec4Multiply(lightPosition, viewMatrix, lightPosition);
// Virtual Matrices
gl.useProgram(program);
let virtualModelViewLocation = gl.getUniformLocation(program, "virtualModelViewMatrix");
let virtualProjectionLocation = gl.getUniformLocation(program, "virtualProjectionMatrix");
let colorOverrideLocation = gl.getUniformLocation(program, "colorOverride");
gl.uniform3f(gl.getUniformLocation(program, "Light.position"), ...lightPosition.slice(0, 3));
gl.uniform3f(gl.getUniformLocation(program, "Light.color"), 1, 1, 1);
gl.uniform3f(gl.getUniformLocation(program, "Material.ambientColor"), 0.1, 0.1, 0.1);
gl.uniform3f(gl.getUniformLocation(program, "Material.emitColor"), 0.05, 0.05, 0.0);
gl.uniform3f(gl.getUniformLocation(program, "Material.diffColor"), 0.7, 0.8, 0.1);
gl.uniform3f(gl.getUniformLocation(program, "Material.specColor"), 1, 1, 0.8);
gl.uniform1f(gl.getUniformLocation(program, "Material.shininess"), 10);
let virtualProjectionMatrix = [];
mat4BuildPerspective(virtualProjectionMatrix, 90.0 / 180.0 * Math.PI, cv.width / cv.height, 0.9, 5);
if (!invertZAxis) {
let zAxisFlipMatrix = [];
mat4Identity(zAxisFlipMatrix);
zAxisFlipMatrix[10] = -1;
mat4Interpolate(zAxisFlipMatrix, identity, zAxisFlipMatrix, virtualRealInterpolation);
mat4Multiply(virtualProjectionMatrix, zAxisFlipMatrix, virtualProjectionMatrix);
}
let inverseVirtualProjectionMatrix = [];
mat4Inverse(inverseVirtualProjectionMatrix, virtualProjectionMatrix);
let virtualViewMatrix = [];
let virtualCameraPosition = [1.2, 1.2, 2];
let virtualCameraLookAt = [0, 0, -1];
let virtualCameraUp = [0, 1, 0];
mat4BuildLookAt(virtualViewMatrix, virtualCameraPosition, virtualCameraLookAt, virtualCameraUp);
let inverseVirtualViewMatrix = [];
mat4Inverse(inverseVirtualViewMatrix, virtualViewMatrix);
mat4Interpolate(virtualProjectionMatrix, identity, virtualProjectionMatrix, interpolateProjection);
mat4Interpolate(virtualViewMatrix, identity, virtualViewMatrix, interpolateLookAt);
gl.uniformMatrix4fv(virtualProjectionLocation, gl.FALSE, new Float32Array(virtualProjectionMatrix));
let modelMatrix = [];
mat4Identity(modelMatrix);
let modelViewMatrix = [];
mat4Multiply(modelViewMatrix, virtualViewMatrix, modelMatrix);
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(modelViewMatrix));
// draw scene
// draw H
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, hVertexBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, o.vertexBuffer);
setAttribPointers();
gl.drawArrays(gl.TRIANGLES, 0, hVertices.length / 6);
// draw line cube
gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer);
setAttribPointers();
gl.uniform3fv(colorOverrideLocation, [1, 1, 0.5]);
gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6);
gl.uniform3fv(colorOverrideLocation, [0, 0, 0]);
// draw solid cube 1
let cube1ModelView = [];
mat4Identity(cube1ModelView);
mat4Scale(cube1ModelView, cube1ModelView, [0.3, 0.3, 0.3]);
mat4RotateY(cube1ModelView, cube1ModelView, 0.3);
mat4Translate(cube1ModelView, cube1ModelView, [-0.5, 0.3, -0.7]);
mat4Multiply(cube1ModelView, virtualViewMatrix, cube1ModelView);
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(cube1ModelView));
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer);
setAttribPointers();
gl.drawArrays(gl.TRIANGLES, 0, cubeVertices.length / 6);
// draw solid cube 2
let cube2ModelView = [];
mat4Identity(cube2ModelView);
mat4Scale(cube2ModelView, cube2ModelView, [0.4, 0.4, 0.4]);
mat4RotateY(cube2ModelView, cube2ModelView, 2.6);
mat4Translate(cube2ModelView, cube2ModelView, [1.6, 0.3, -1]);
mat4Multiply(cube2ModelView, virtualViewMatrix, cube2ModelView);
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(cube2ModelView));
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexBuffer);
setAttribPointers();
gl.drawArrays(gl.TRIANGLES, 0, cubeVertices.length / 6);
// draw virtual camera
let virtualCameraModelView = [];
mat4Identity(virtualCameraModelView);
mat4Scale(virtualCameraModelView, virtualCameraModelView, [0.1, 0.1, 0.1]);
mat4Translate(virtualCameraModelView, virtualCameraModelView, [0, 0, 0.16]);
mat4Multiply(virtualCameraModelView, inverseVirtualViewMatrix, virtualCameraModelView);
mat4Multiply(virtualCameraModelView, virtualViewMatrix, virtualCameraModelView);
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(virtualCameraModelView));
gl.bindBuffer(gl.ARRAY_BUFFER, cameraVertexBuffer);
setAttribPointers();
if (displayMatricesVirtually && interpolateProjection <= 0) {
gl.drawArrays(gl.TRIANGLES, 0, cameraVertices.length / 6);
}
let frustomModelMatrix = [];
mat4Multiply(frustomModelMatrix, inverseVirtualViewMatrix, inverseVirtualProjectionMatrix);
mat4Multiply(frustomModelMatrix, virtualViewMatrix, frustomModelMatrix);
// mat4Copy(inverseVirtualProjectionMatrix, frustomModelMatrix);
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(frustomModelMatrix));
gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer);
setAttribPointers();
gl.uniform3fv(colorOverrideLocation, [1, 0, 1]);
gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6);
gl.uniform3fv(colorOverrideLocation, [0, 0, 0]);
// draw origin
gl.uniformMatrix4fv(virtualModelViewLocation, gl.FALSE, new Float32Array(identity));
gl.uniformMatrix4fv(virtualProjectionLocation, gl.FALSE, new Float32Array(identity));
gl.bindBuffer(gl.ARRAY_BUFFER, originVertexBuffer);
setAttribPointers();
gl.drawArrays(gl.LINES, 0, originVertices.length / 6);
// draw origin cube
if (interpolateProjection < 1 && interpolateLookAt > 0) {
gl.bindBuffer(gl.ARRAY_BUFFER, lineCubeVertexBuffer);
setAttribPointers();
gl.uniform3fv(colorOverrideLocation, [1, 0, 0]);
gl.drawArrays(gl.LINES, 0, lineCubeVertices.length / 6);
gl.uniform3fv(colorOverrideLocation, [0, 0, 0]);
}
// draw floor
gl.useProgram(floorProgram);
let floorModelView = [];
mat4Identity(floorModelView);
mat4Scale(floorModelView, floorModelView, [10, 0, 10]);
mat4Multiply(floorModelView, realViewMatrix, floorModelView);
gl.uniformMatrix4fv(gl.getUniformLocation(floorProgram, "realModelViewMatrix"), gl.FALSE, new Float32Array(floorModelView));
gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexBuffer);
setAttribPointers();
gl.drawArrays(gl.TRIANGLES, 0, floorVertices.length / 6);
gl.drawArrays(gl.TRIANGLES, 0, o.numVertices);
frameTimes.push(performance.now() - frameStart);
lastFrame = now;

View File

@ -1,6 +0,0 @@
precision mediump float;
varying vec3 vPosition;
void main() {
gl_FragColor = mix(vec4(1,1,1,0.5), vec4(0.8,0.8,0.8,0.5), step(0.0, sin(vPosition.x * 3.14159 * 20.0) * sin(vPosition.z * 3.14159 * 20.0)));
}

View File

@ -1,14 +0,0 @@
precision mediump float;
attribute vec3 vertPosition;
attribute vec3 vertColor;
uniform mat4 realModelViewMatrix;
uniform mat4 realProjectionMatrix;
varying vec3 vPosition;
void main() {
vPosition = vertPosition;
gl_Position = realProjectionMatrix * realModelViewMatrix * vec4(vertPosition, 1.0);
}

View File

@ -1,6 +1,33 @@
precision mediump float;
varying vec3 fragColor;
uniform struct {
vec3 position;
vec3 color;
} Light;
uniform struct {
vec3 ambientColor;
vec3 emitColor;
vec3 diffColor;
vec3 specColor;
float shininess;
} Material;
varying vec3 vNormal;
varying vec2 vTexture;
varying vec3 vPosition;
void main() {
gl_FragColor = vec4(fragColor, 1.0);
vec3 pleaseDontDeleteMe = vec3(vTexture, 0.0);
vec3 eyeDir = normalize(-vPosition);
vec3 lightDir = normalize(Light.position - vPosition);
vec3 r = reflect(-lightDir, vNormal);
vec3 ambient = Material.ambientColor;
vec3 emit = Material.emitColor;
vec3 diff = max(dot(lightDir, vNormal), 0.0) * Material.diffColor * Light.color;
vec3 spec = pow(max(dot(r, eyeDir), 0.0), Material.shininess) * Material.specColor * Light.color;
gl_FragColor = vec4(ambient + emit + diff + spec, 1);
}

View File

@ -1,21 +1,21 @@
precision mediump float;
attribute vec3 vertPosition;
attribute vec3 vertColor;
attribute vec3 vertNormal;
attribute vec2 vertTexture;
uniform mat4 virtualModelViewMatrix;
uniform mat4 virtualProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
uniform mat4 realViewMatrix;
uniform mat4 realProjectionMatrix;
uniform vec3 colorOverride;
varying vec3 fragColor;
varying vec3 vNormal;
varying vec2 vTexture;
varying vec3 vPosition;
void main() {
float colorOverrideActive = step(0.001, length(colorOverride));
fragColor = colorOverride * colorOverrideActive + vertColor * (1.0 - colorOverrideActive);
vNormal = normalize(normalMatrix * vertNormal);
vTexture = vertTexture;
vPosition = (modelViewMatrix * vec4(vertPosition, 1.0)).xyz;
gl_Position = realProjectionMatrix * realViewMatrix * virtualProjectionMatrix * virtualModelViewMatrix * vec4(vertPosition, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(vertPosition, 1.0);
}

43
src/wavefront.js Normal file
View File

@ -0,0 +1,43 @@
async function readObjFile(path) {
const res = await fetch(path);
const text = await res.text();
let vertices = [];
let textures = [];
let normals = [];
let result = [];
const lines = text.replace("\r","").split("\n");
for (let line of lines) {
const parts = line.split(/\s+/);
switch (parts[0]) {
case "v":
vertices.push(parts.slice(1, 4).map(parseFloat));
break;
case "vt":
textures.push(parts.slice(1, 3).map(parseFloat));
break;
case "vn":
normals.push(parts.slice(1, 4).map(parseFloat));
break;
case "f":
for (const part of parts.slice(1, 4)) {
const [v, vt, vn] = part.split("/").map((i) => parseInt(i) - 1);
result.push(...vertices[v], ...textures[vt], ...normals[vn]);
}
// support for quads:
if (parts.length > 4) {
for (let i of [1, 3, 4]) {
const part = parts[i];
const [v, vt, vn] = part.split("/").map((i) => parseInt(i) - 1);
result.push(...vertices[v], ...textures[vt], ...normals[vn]);
}
}
}
}
return result;
}