Skip to content

Commit f9f09ad

Browse files
Merge pull request #11 from rougier/post-custom-3d-engine
A new post about custom 3D engine
2 parents a02eef1 + c7dcd10 commit f9f09ad

27 files changed

+8359
-0
lines changed
1.56 MB
Loading
884 KB
Loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from matplotlib.collections import PolyCollection
4+
5+
# Data processing
6+
V, F = [], []
7+
with open("bunny.obj") as f:
8+
for line in f.readlines():
9+
if line.startswith('#'):
10+
continue
11+
values = line.split()
12+
if not values:
13+
continue
14+
if values[0] == 'v':
15+
V.append([float(x) for x in values[1:4]])
16+
elif values[0] == 'f' :
17+
F.append([int(x) for x in values[1:4]])
18+
V, F = np.array(V), np.array(F)-1
19+
V = (V-(V.max(0)+V.min(0))/2) / max(V.max(0)-V.min(0))
20+
T = V[F][...,:2]
21+
22+
# Rendering
23+
fig = plt.figure(figsize=(6,6))
24+
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1], aspect=1, frameon=False)
25+
collection = PolyCollection(T, closed=True, linewidth=0.1,
26+
facecolor="None", edgecolor="black")
27+
ax.add_collection(collection)
28+
plt.savefig("bunny-1.png", dpi=300)
29+
plt.show()
962 KB
Loading
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from matplotlib.collections import PolyCollection
4+
5+
6+
def frustum(left, right, bottom, top, znear, zfar):
7+
M = np.zeros((4, 4), dtype=np.float32)
8+
M[0, 0] = +2.0 * znear / (right - left)
9+
M[1, 1] = +2.0 * znear / (top - bottom)
10+
M[2, 2] = -(zfar + znear) / (zfar - znear)
11+
M[0, 2] = (right + left) / (right - left)
12+
M[2, 1] = (top + bottom) / (top - bottom)
13+
M[2, 3] = -2.0 * znear * zfar / (zfar - znear)
14+
M[3, 2] = -1.0
15+
return M
16+
17+
def perspective(fovy, aspect, znear, zfar):
18+
h = np.tan(0.5*np.radians(fovy)) * znear
19+
w = h * aspect
20+
return frustum(-w, w, -h, h, znear, zfar)
21+
22+
# Data processing
23+
V, F = [], []
24+
with open("bunny.obj") as f:
25+
for line in f.readlines():
26+
if line.startswith('#'):
27+
continue
28+
values = line.split()
29+
if not values:
30+
continue
31+
if values[0] == 'v':
32+
V.append([float(x) for x in values[1:4]])
33+
elif values[0] == 'f' :
34+
F.append([int(x) for x in values[1:4]])
35+
V, F = np.array(V), np.array(F)-1
36+
37+
V = (V-(V.max(0)+V.min(0))/2) / max(V.max(0)-V.min(0))
38+
V = np.c_[V, np.ones(len(V))] @ perspective(25,1,1,100).T
39+
V /= V[:,3].reshape(-1,1)
40+
T = V[F][...,:2]
41+
42+
# Rendering
43+
fig = plt.figure(figsize=(6,6))
44+
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1], aspect=1, frameon=False)
45+
collection = PolyCollection(T, closed=True, linewidth=0.1,
46+
facecolor="None", edgecolor="black")
47+
ax.add_collection(collection)
48+
plt.savefig("bunny-2.png", dpi=300)
49+
plt.show()
1.19 MB
Loading
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from matplotlib.collections import PolyCollection
4+
5+
6+
def frustum(left, right, bottom, top, znear, zfar):
7+
M = np.zeros((4, 4), dtype=np.float32)
8+
M[0, 0] = +2.0 * znear / (right - left)
9+
M[1, 1] = +2.0 * znear / (top - bottom)
10+
M[2, 2] = -(zfar + znear) / (zfar - znear)
11+
M[0, 2] = (right + left) / (right - left)
12+
M[2, 1] = (top + bottom) / (top - bottom)
13+
M[2, 3] = -2.0 * znear * zfar / (zfar - znear)
14+
M[3, 2] = -1.0
15+
return M
16+
17+
def perspective(fovy, aspect, znear, zfar):
18+
h = np.tan(0.5*np.radians(fovy)) * znear
19+
w = h * aspect
20+
return frustum(-w, w, -h, h, znear, zfar)
21+
22+
# Data processing
23+
V, F = [], []
24+
with open("bunny.obj") as f:
25+
for line in f.readlines():
26+
if line.startswith('#'):
27+
continue
28+
values = line.split()
29+
if not values:
30+
continue
31+
if values[0] == 'v':
32+
V.append([float(x) for x in values[1:4]])
33+
elif values[0] == 'f' :
34+
F.append([int(x) for x in values[1:4]])
35+
V, F = np.array(V), np.array(F)-1
36+
37+
V = (V-(V.max(0)+V.min(0))/2) / max(V.max(0)-V.min(0))
38+
V += (0,0,-3.5)
39+
V = np.c_[V, np.ones(len(V))] @ perspective(25,1,1,100).T
40+
V /= V[:,3].reshape(-1,1)
41+
T = V[F][...,:2]
42+
43+
# Rendering
44+
fig = plt.figure(figsize=(6,6))
45+
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1], aspect=1, frameon=False)
46+
collection = PolyCollection(T, closed=True, linewidth=0.1,
47+
facecolor="None", edgecolor="black")
48+
ax.add_collection(collection)
49+
plt.savefig("bunny-3.png", dpi=300)
50+
plt.show()
1.16 MB
Loading
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from matplotlib.collections import PolyCollection
4+
5+
6+
def frustum(left, right, bottom, top, znear, zfar):
7+
M = np.zeros((4, 4), dtype=np.float32)
8+
M[0, 0] = +2.0 * znear / (right - left)
9+
M[1, 1] = +2.0 * znear / (top - bottom)
10+
M[2, 2] = -(zfar + znear) / (zfar - znear)
11+
M[0, 2] = (right + left) / (right - left)
12+
M[2, 1] = (top + bottom) / (top - bottom)
13+
M[2, 3] = -2.0 * znear * zfar / (zfar - znear)
14+
M[3, 2] = -1.0
15+
return M
16+
17+
def perspective(fovy, aspect, znear, zfar):
18+
h = np.tan(0.5*np.radians(fovy)) * znear
19+
w = h * aspect
20+
return frustum(-w, w, -h, h, znear, zfar)
21+
def translate(x, y, z):
22+
return np.array([[1, 0, 0, x],
23+
[0, 1, 0, y],
24+
[0, 0, 1, z],
25+
[0, 0, 0, 1]], dtype=float)
26+
27+
def xrotate(theta):
28+
t = np.pi * theta / 180
29+
c, s = np.cos(t), np.sin(t)
30+
return np.array([[1, 0, 0, 0],
31+
[0, c, -s, 0],
32+
[0, s, c, 0],
33+
[0, 0, 0, 1]], dtype=float)
34+
35+
def yrotate(theta):
36+
t = np.pi * theta / 180
37+
c, s = np.cos(t), np.sin(t)
38+
return np.array([[ c, 0, s, 0],
39+
[ 0, 1, 0, 0],
40+
[-s, 0, c, 0],
41+
[ 0, 0, 0, 1]], dtype=float)
42+
43+
# Data processing
44+
V, F = [], []
45+
with open("bunny.obj") as f:
46+
for line in f.readlines():
47+
if line.startswith('#'):
48+
continue
49+
values = line.split()
50+
if not values:
51+
continue
52+
if values[0] == 'v':
53+
V.append([float(x) for x in values[1:4]])
54+
elif values[0] == 'f' :
55+
F.append([int(x) for x in values[1:4]])
56+
V, F = np.array(V), np.array(F)-1
57+
58+
V = (V-(V.max(0)+V.min(0))/2) / max(V.max(0)-V.min(0))
59+
60+
model = xrotate(20) @ yrotate(45)
61+
view = translate(0,0,-3.5)
62+
proj = perspective(25, 1, 1, 100)
63+
MVP = proj @ view @ model
64+
65+
V = np.c_[V, np.ones(len(V))] @ MVP.T
66+
V /= V[:,3].reshape(-1,1)
67+
T = V[F][...,:2]
68+
69+
# Rendering
70+
fig = plt.figure(figsize=(6,6))
71+
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1], aspect=1, frameon=False)
72+
collection = PolyCollection(T, closed=True, linewidth=0.1,
73+
facecolor="None", edgecolor="black")
74+
ax.add_collection(collection)
75+
plt.savefig("bunny-4.png", dpi=300)
76+
plt.show()
1.58 MB
Loading

0 commit comments

Comments
 (0)