I am trying to make a 3d physics engine in JavaScript just for fun.
The only problem is 3d rendering as I don’t know how it is done so can anyone explain the idea of 3d rendering.
Here is an example of a 3d perspective cube rotating.
<style>
*{
margin: 0;
}
canvas {
background: black;
}
</style>
<canvas></canvas>
<script>
var canvas = document.querySelector("canvas")
var fov = [120 * Math.PI / 180, 90 * Math.PI / 180]
canvas.width = innerWidth
var dist = (canvas.width / 2) / Math.tan(fov[0] / 2)
canvas.height = (Math.tan(fov[1] / 2) * dist) * 2
if (canvas.height > innerHeight) {
canvas.height = innerHeight
var dist = (canvas.height / 2) / Math.tan(fov[1] / 2)
canvas.width = (Math.tan(fov[0] / 2) * dist) * 2
}
var c = canvas.getContext("2d")
c.fillStyle = "red"
c.strokeStyle = "white"
var solids = []
function rotateX(vector, angle) {
angle = angle * Math.PI / 180
var l = Math.hypot(vector.y, vector.z)
var cAngle = Math.atan2(vector.z, vector.y)
cAngle += angle
vector.y = Math.cos(cAngle) * l
vector.z = Math.sin(cAngle) * l
}
function rotateY(vector, angle) {
angle = angle * Math.PI / 180
var l = Math.hypot(vector.x, vector.z)
var cAngle = Math.atan2(vector.z, vector.x)
cAngle += angle
vector.x = Math.cos(cAngle) * l
vector.z = Math.sin(cAngle) * l
}
function rotateZ(vector, angle) {
angle = angle * Math.PI / 180
var l = Math.hypot(vector.x, vector.y)
var cAngle = Math.atan2(vector.y, vector.x)
cAngle += angle
vector.x = Math.cos(cAngle) * l
vector.y = Math.sin(cAngle) * l
}
class Vertex {
constructor(x, y, z) {
this.x = x,
this.y = y,
this.z = z
}
}
class Cube {
constructor(pos, size) {
this.size = size
this.pos = pos
this.angleX = 0
this.angleY = 0
this.angleZ = 30
this.vertices = []
this.vertices.push(new Vertex(0, 0, 0))
this.vertices.push(new Vertex(size, 0, 0))
this.vertices.push(new Vertex(0, size, 0))
this.vertices.push(new Vertex(0, 0, size))
this.vertices.push(new Vertex(size, size, 0))
this.vertices.push(new Vertex(size, 0, size))
this.vertices.push(new Vertex(0, size, size))
this.vertices.push(new Vertex(size, size, size))
}
draw() {
this.vertices.forEach(v => {
rotateX(v, 1)
rotateY(v, 1)
rotateZ(v, 1)
var d = v.z + this.pos.z
var width = (Math.tan(fov[0] / 2) * d) * 2
var height = (Math.tan(fov[1] / 2) * d) * 2
var x = (width / 2 - v.x - this.pos.x) * canvas.width / width
var y = (height / 2 - v.y - this.pos.y) * canvas.height / height
this.vertices.forEach(v2 => {
var d2 = v2.z + this.pos.z
var width2 = (Math.tan(fov[0] / 2) * d2) * 2
var height2 = (Math.tan(fov[1] / 2) * d2) * 2
var x2 = (width2 / 2 - v2.x - this.pos.x) * canvas.width / width2
var y2 = (height2 / 2 - v2.y - this.pos.y) * canvas.height / height2
this.vertices.forEach(v3 => {
if (v != v2 && v2 != v3) {
var d3 = v3.z + this.pos.z
var width3 = (Math.tan(fov[0] / 2) * d3) * 2
var height3 = (Math.tan(fov[1] / 2) * d3) * 2
var x3 = (width3 / 2 - v3.x - this.pos.x) * canvas.width / width3
var y3 = (height3 / 2 - v3.y - this.pos.y) * canvas.height / height3
c.beginPath()
c.moveTo(x, y)
c.lineTo(x2, y2)
c.lineTo(x3, y3)
c.lineTo(x, y)
c.fillStyle = "red"
c.fill()
}
})
})
})
this.vertices.forEach(v => {
var d = v.z + this.pos.z
var width = (Math.tan(fov[0] / 2) * d) * 2
var height = (Math.tan(fov[1] / 2) * d) * 2
var x = (width / 2 - v.x - this.pos.x) * canvas.width / width
var y = (height / 2 - v.y - this.pos.y) * canvas.height / height
this.vertices.forEach(v2 => {
var d2 = v2.z + this.pos.z
var width2 = (Math.tan(fov[0] / 2) * d2) * 2
var height2 = (Math.tan(fov[1] / 2) * d2) * 2
var x2 = (width2 / 2 - v2.x - this.pos.x) * canvas.width / width2
var y2 = (height2 / 2 - v2.y - this.pos.y) * canvas.height / height2
if (Math.round(Math.hypot(v.x - v2.x, v.y - v2.y, v.z - v2.z)) == this.size) {
var d2 = v2.z + this.pos.z
var width2 = (Math.tan(fov[0] / 2) * d2) * 2
var height2 = (Math.tan(fov[1] / 2) * d2) * 2
var x2 = (width2 / 2 - v2.x - this.pos.x) * canvas.width / width2
var y2 = (height2 / 2 - v2.y - this.pos.y) * canvas.height / height2
c.beginPath()
c.moveTo(x, y)
c.lineTo(x2, y2)
c.stroke()
}
})
c.beginPath()
c.arc(x, y, 3, 0, Math.PI * 2, false)
c.fillStyle = "blue"
//c.fill()
//c.stroke()
})
}
}
solids.push(new Cube({x: 0, y: 0, z: 300}, 50))
function animate() {
requestAnimationFrame(animate)
c.clearRect(0, 0, canvas.width, canvas.height)
solids.forEach(solid => {
solid.draw()
})
}
animate()
I searched a lot about an article that explains this but I didn’t find anything useful