-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
139 lines (125 loc) · 6 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>Indirect3D</title>
<style>
* {
box-sizing: border-box;
}
body {
background-color: #fff;
font-family: Georgia, "Times New Roman", Times, serif;
}
article,
canvas,
h1, h2,
p, ul {
margin: 20px auto;
width: 640px;
}
dt {
font-weight: bold;
}
li > ul {
margin: 0;
}
canvas {
background-color: #000;
display: block;
}
label {
padding: 3px;
display: inline-block;
border: 1px solid #fff;
}
label:hover {
border: 1px inset #ccc;
}
div > div {
padding: 3px 0;
}
</style>
</head>
<body>
<article>
<a href="http://github.com/jsocol/indirect3d"><img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
<div id="indirect-container"></div>
<div id="scene-controls">
<label>Blue depth <input id="blue-depth-input" type="number" size="3" value="1.0" step="0.1"></label>
<label>Red depth <input id="red-depth-input" type="number" size="3" value="-2.0" step="0.1"></label>
<div>
Camera position
<label>X <input id="camera-x" type="number" size="3" value="0.0" step="0.1"></label>
<label>Y <input id="camera-y" type="number" size="3" value="0.0" step="0.1"></label> (+Y is up)
<label>Z <input id="camera-z" type="number" size="3" value="30.0" step="0.1"></label>
</div>
<div>
Camera direction
<label>X <input id="direction-x" type="number" size="3" value="0.0" step="0.1" disabled></label>
<label>Y <input id="direction-y" type="number" size="3" value="0.0" step="0.1" disabled></label> (+Y is up)
<label>Z <input id="direction-z" type="number" size="3" value="-1.0" step="0.1" disabled></label>
</div>
<div>
Lights:
<label>Basic <input id="light-0" type="checkbox" checked></label>,
<label>Key <input id="light-1" type="checkbox" checked></label>,
<label>Sun <input id="light-2" type="checkbox" checked></label>,
Sun direction:
<label>X <input id="sun-x" type="number" size="3" value="-0.5" step="0.1"></label>
<label>Y <input id="sun-y" type="number" size="3" value="-0.5" step="0.1"></label>
<label>Z <input id="sun-z" type="number" size="3" value="-0.5" step="0.1"></label>
</div>
<label>Field of view angle (degrees) <input id="fovy" type="number" size="3" value="45" step="1"></label>
<span id="fps"></span>
</div>
<h1>Indirect3D</h1>
<p>Indirect3D is a completely ridiculous attempt to implement a significant portion of the DirectX / Direct3D API using a HTML canvas's <strong>2d</strong> context. That means doing all the math in JS.</p>
<p>I started this project at least 10 years ago. It's based on DX9, which came out in 2002—but it's not <em>quite</em> that old.</p>
<p>In 2021, for no especially clear reason, I started working on it again. A rough changelog is below.</p>
<p>In modern browsers in 2021, it actually runs fairly quickly. As long as it's not trying to do much.</p>
<p>To start or stop some random animation, hit <strong>Enter</strong>.</p>
<p>In the scene above, the red and blue triangle have the same vertex model coordinates and same xy translation with offset z depth. You can control that with the red and blue depth controls above.</p>
<p>Try WASD and QE 😉.</p>
<p>Finally you can mess with the field of view angle if you want. I don't know what normal cameras are.</p>
<p>The pretty rainbow triangle moves back and forth along the world z-axis, while a colorful little pyramid sits stoically.</p>
<h2>Changelog</h2>
<dl>
<dt>August 2021</dt>
<dd>Convert to TypeScript. Add webpack and TS configs.</dd>
<dd>Organize into more, smaller files.</dd>
<dd>Fix bug transposing g and b color channels in triangle list.</dd>
<dd>Refactor and clean-up LINELIST and LINESTRIP modes to share common functionality.</dd>
<dd>Implement barycentric coordinate calculation for triangle color blending.</dd>
<dd>Calculate triangle color blending in CIELAB space rather than sRGB space.</dd>
<dd>Fix row-major/column-major disconnect and transpose transformation matrices.</dd>
<dd>Fix triangle z-depth calculation by projecting a ray from the screen back to the plane of the triangle.</dd>
<dd>Adding that gave me backface culling for free, score!</dd>
<dd>Fix alpha blending, which was confused about background/foreground.</dd>
<dd>Added keyboard controls, hahahaha.</dd>
<dd>Fixed TRIANGLELIST and TRIANGLESTRIP modes and added TRIANGLEFAN.</dd>
<dd>Switched triangle color calculation from screen space to view space and I think it's a little more stable?</dd>
<dd>Added lighting.</dd>
<dt>December 2022</dt>
<dd>Implement DrawIndexedPrimitive, SetStreamSource, and SetIndices.</dd>
<dd>Implement directional lights and LightEnable.</dd>
</dl>
<h2>Known issues and ToDos</h2>
<ul>
<li>Line color blending is still done in sRGB space. Will probably look better in CIELAB.</li>
<li>There's a lot of packing and unpacking RGBA values into Int32 and it's sometimes error prone. Is it necessary? Could <code>Color</code> be a struct (or a few structs for different color spaces)?</li>
<li>The aliasing, oh god the aliasing!</li>
<li>So... depth is hard.</li>
</ul>
<h2>Things that work pretty well</h2>
<ul>
<li>Barycentric coordinates work great for triangle coloring. (Even if they're in the wrong coordinate system.)</li>
<li>sRGB—XYZ—CIELAB color space conversion, and doing the color blending in CIELAB, looks real nice.</li>
<li>Alpha blending is now working correctly!</li>
<li>Backface culling was fairly straightforward.</li>
</ul>
</article>
<script src="./build/index.js"></script>
</body>
</html>
<!-- vim: set ts=2 sts=2 sw=2: -->