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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
<title>Black holes with raytracing</title>
<script src="webgl-fmi.js"></script>
<script src="shaders.js"></script>
<style>
body {
padding: 0;
margin: 0;
font: 18px sans-serif;
color: white;
}
canvas {
display: block;
pos: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
/* cursor: none; */
}
#noscript, #picasso {
background-color: red;
}
.indent {
padding-left: 1em;
box-sizing: border-box;
margin: 0;
}
.hide {
display: none;
}
h2 {
border-bottom: 2px solid gray;
}
body > div, #noscript {
position: absolute;
left: 0;
top: 0;
}
#menu {
height: 100vh;
width: 100vw;
color: white;
}
#menu > * {
position: absolute;
top: 0;
margin: 0.5em;
padding: 0.2em;
border: 2px solid white;
width: auto;
background-color: black;
box-sizing: border-box;
}
#paused {
left: 0;
font-size: 25px;
cursor: pointer;
}
#settings {
right: 0;
padding: 0.5em;
max-width: 25em;
max-height: calc(100vh - 1em);
overflow: auto;
}
#fps {
left: auto;
right: 0;
}
</style>
<script src="camera-movement.js"></script>
<script src="skybox.js"></script>
<script src="level.js"></script>
<script>
function start()
{
canvas = document.getElementById('picasso');
canvas.addEventListener('webglcontextlost',function(event){event.preventDefault();},false);
canvas.addEventListener('webglcontextrestored',function(){init();},false);
mouseInit();
init();
}
const PERSPECTIVE_ANGLE = 30;
const PERSPECTIVE_NEAR = 1;
function init()
{
/* Get WebGL stuff */
gl = getContext('picasso');
glprog = getProgram(vShader,fShader);
getVariables();
/* WebGL settings */
gl.enable(gl.DEPTH_TEST);
gl.clearColor(1,1,1,1);
// For proper transparency
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
/* Matricies (for color in shaders) */
identity();
gl.uniform1i(uUseNormalMatrix,false);
gl.uniform3f(uAmbientColor,0.4,0.4,0.4);
gl.uniform3f(uDiffuseColor,1,1,1);
gl.uniform3f(uLightDir,0,0,-1);
/* Fragment shader settings */
gl.uniform3fv(uStarColor, STAR_COLOR);
gl.uniform4f(uBlack, 0,0,0,1);
gl.uniform4f(uBHBlack, 0,0,0,1);
gl.uniform1i(uDisableRayMarching, false);
/* Create physical objects */
skybox = new Skybox(10000, [0,0,0], undefined);
createLevels();
/* Since we have the gl context, run stuff that depend on it */
initMenu();
resizeglcanvas();
}
function resizeglcanvas() {
gl.canvas.width = gl.canvas.offsetWidth;
gl.canvas.height = gl.canvas.offsetHeight;
gl.viewport(0,0,gl.canvas.width,gl.canvas.height);
perspective(PERSPECTIVE_ANGLE,gl.canvas.width/gl.canvas.height,PERSPECTIVE_NEAR,40000);
drawFrame();
}
window.onresize = resizeglcanvas;
var pauseAnimations = false;
var t = 0;
var lastTime = 0;
var frames = 0;
function drawFrame()
{
/* Update time t */
if (startT == -1) { startT = Date.now(); t = 0; }
var candidat = Math.floor(Date.now() - startT) / 100; // tenths of a second
if (pauseAnimations) startT += (candidat - t)*100;
else t = candidat;
/* Update fps counter */
var now = Math.floor(Date.now() / 1000);
if (now - lastTime >= 1) {
fpsElem.innerText = Math.floor(frames / (now - lastTime));
frames = 0;
lastTime = now;
}
gl.clear(gl.COLOR_BUFFER_BIT+gl.DEPTH_BUFFER_BIT);
updateCamera();
/* Draw scene objects */
if (!pauseAnimations || t == 0) animations.forEach((a) => a(t));
skybox.draw();
stars.forEach((s) => s.draw());
frames++;
requestAnimationFrame(drawFrame);
}
</script>
</head>
<body onload="start()">
<canvas id="picasso">We need a canvas, but it doesn't exist!</canvas>
<noscript><div id="noscript">Your browser doesn't support JavaScript or it has been disabled!</div></noscript>
<div id="menu">
<div id="paused">Click here to move around</div>
<div id="settings">
<h2>About</h2>
<p>
Simulation of the light-bending effects of black holes.<br>
WASD to move around, space and shift for up and down, mouse cursor to look around.<br>
Escape will toggle between this overlay and your ability to move around (also done with the button on the top left).
</p>
<h2>Level</h2>
<div>
<select id="level-select"></select>
<p id="level-description"></p>
<p>
Pause animation: <input type="checkbox" id="pause-movement">
</p>
</div>
<h2>Settings</h2>
<div>
Disable ray marching: <input type="checkbox" id="disable-ray-marching"><br>
<p class="indent">
Ray marching is the method I use to trace light when it enters a black hole.
This option is here, so you can see how the objects look with the black hole removed.
</p>
Show (ray marching) debugging colors: <input type="checkbox" id="debug-mode"><br>
<p class="indent">
Works only with enabled ray marching.
Green for rays that couldn't escape.
Red for rays that didn't hit anything (or hit the skybox).
</p><br>
Show coordinates: <input type="checkbox" id="coordinates-cb"><br>
Show fps: <input type="checkbox" id="fps-cb"><br>
Only horizontal mouse movement: <input type="checkbox" id="only-horizontal"><br>
Only vertical mouse movement: <input type="checkbox" id="only-vertical">
</div>
</div>
</div>
<div id="coordinates"></div>
<div id="fps"></div>
<script src="menu.js"></script>
</body>
|