Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/bench/gc/test_SunSpider_3d-raytrace.lua
2725 views
1
--[[
2
* Copyright (C) 2007 Apple Inc. All rights reserved.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
]]
25
local function prequire(name) local success, result = pcall(require, name); return success and result end
26
local bench = script and require(script.Parent.bench_support) or prequire("bench_support") or require("../bench_support")
27
28
function test()
29
30
local size = 30
31
32
function createVector(x,y,z)
33
return { x,y,z };
34
end
35
36
function sqrLengthVector(self)
37
return self[1] * self[1] + self[2] * self[2] + self[3] * self[3];
38
end
39
40
function lengthVector(self)
41
return math.sqrt(self[1] * self[1] + self[2] * self[2] + self[3] * self[3]);
42
end
43
44
function addVector(self, v)
45
self[1] = self[1] + v[1];
46
self[2] = self[2] + v[2];
47
self[3] = self[3] + v[3];
48
return self;
49
end
50
51
function subVector(self, v)
52
self[1] = self[1] - v[1];
53
self[2] = self[2] - v[2];
54
self[3] = self[3] - v[3];
55
return self;
56
end
57
58
function scaleVector(self, scale)
59
self[1] = self[1] * scale;
60
self[2] = self[2] * scale;
61
self[3] = self[3] * scale;
62
return self;
63
end
64
65
function normaliseVector(self)
66
local len = math.sqrt(self[1] * self[1] + self[2] * self[2] + self[3] * self[3]);
67
self[1] = self[1] / len;
68
self[2] = self[2] / len;
69
self[3] = self[3] / len;
70
return self;
71
end
72
73
function add(v1, v2)
74
return { v1[1] + v2[1], v1[2] + v2[2], v1[3] + v2[3] };
75
end
76
77
function sub(v1, v2)
78
return { v1[1] - v2[1], v1[2] - v2[2], v1[3] - v2[3] };
79
end
80
81
function scalev(v1, v2)
82
return { v1[1] * v2[1], v1[2] * v2[2], v1[3] * v2[3] };
83
end
84
85
function dot(v1, v2)
86
return v1[1] * v2[1] + v1[2] * v2[2] + v1[3] * v2[3];
87
end
88
89
function scale(v, scale)
90
return { v[1] * scale, v[2] * scale, v[3] * scale };
91
end
92
93
function cross(v1, v2)
94
return { v1[2] * v2[3] - v1[3] * v2[2],
95
v1[3] * v2[1] - v1[1] * v2[3],
96
v1[1] * v2[2] - v1[2] * v2[1] };
97
98
end
99
100
function normalise(v)
101
local len = lengthVector(v);
102
return { v[1] / len, v[2] / len, v[3] / len };
103
end
104
105
function transformMatrix(self, v)
106
local vals = self;
107
local x = vals[1] * v[1] + vals[2] * v[2] + vals[3] * v[3] + vals[4];
108
local y = vals[5] * v[1] + vals[6] * v[2] + vals[7] * v[3] + vals[8];
109
local z = vals[9] * v[1] + vals[10] * v[2] + vals[11] * v[3] + vals[12];
110
return { x, y, z };
111
end
112
113
function invertMatrix(self)
114
local temp = {}
115
local tx = -self[4];
116
local ty = -self[8];
117
local tz = -self[12];
118
for h = 0,2 do
119
for v = 0,2 do
120
temp[h + v * 4 + 1] = self[v + h * 4 + 1];
121
end
122
end
123
124
for i = 0,10 do
125
self[i + 1] = temp[i + 1];
126
end
127
128
self[4] = tx * self[1] + ty * self[2] + tz * self[3];
129
self[8] = tx * self[5] + ty * self[6] + tz * self[7];
130
self[12] = tx * self[9] + ty * self[10] + tz * self[11];
131
return self;
132
end
133
134
-- Triangle intersection using barycentric coord method
135
function Triangle(p1, p2, p3)
136
local this = {}
137
138
local edge1 = sub(p3, p1);
139
local edge2 = sub(p2, p1);
140
local normal = cross(edge1, edge2);
141
if (math.abs(normal[1]) > math.abs(normal[2])) then
142
if (math.abs(normal[1]) > math.abs(normal[3])) then
143
this.axis = 0;
144
else
145
this.axis = 2;
146
end
147
else
148
if (math.abs(normal[2]) > math.abs(normal[3])) then
149
this.axis = 1;
150
else
151
this.axis = 2;
152
end
153
end
154
155
local u = (this.axis + 1) % 3;
156
local v = (this.axis + 2) % 3;
157
local u1 = edge1[u + 1];
158
local v1 = edge1[v + 1];
159
160
local u2 = edge2[u + 1];
161
local v2 = edge2[v + 1];
162
this.normal = normalise(normal);
163
this.nu = normal[u + 1] / normal[this.axis + 1];
164
this.nv = normal[v + 1] / normal[this.axis + 1];
165
this.nd = dot(normal, p1) / normal[this.axis + 1];
166
local det = u1 * v2 - v1 * u2;
167
this.eu = p1[u + 1];
168
this.ev = p1[v + 1];
169
this.nu1 = u1 / det;
170
this.nv1 = -v1 / det;
171
this.nu2 = v2 / det;
172
this.nv2 = -u2 / det;
173
this.material = { 0.7, 0.7, 0.7 };
174
175
176
this.intersect = function(self, orig, dir, near, far)
177
local u = (self.axis + 1) % 3;
178
local v = (self.axis + 2) % 3;
179
local d = dir[self.axis + 1] + self.nu * dir[u + 1] + self.nv * dir[v + 1];
180
local t = (self.nd - orig[self.axis + 1] - self.nu * orig[u + 1] - self.nv * orig[v + 1]) / d;
181
182
if (t < near or t > far) then
183
return nil;
184
end
185
186
local Pu = orig[u + 1] + t * dir[u + 1] - self.eu;
187
local Pv = orig[v + 1] + t * dir[v + 1] - self.ev;
188
local a2 = Pv * self.nu1 + Pu * self.nv1;
189
190
if (a2 < 0) then
191
return nil;
192
end
193
194
local a3 = Pu * self.nu2 + Pv * self.nv2;
195
if (a3 < 0) then
196
return nil;
197
end
198
199
if ((a2 + a3) > 1) then
200
return nil;
201
end
202
203
return t;
204
end
205
206
return this
207
end
208
209
function Scene(a_triangles)
210
local this = {}
211
this.triangles = a_triangles;
212
this.lights = {};
213
this.ambient = {0,0,0};
214
this.background = {0.8,0.8,1};
215
216
this.intersect = function(self, origin, dir, near, far)
217
local closest = nil;
218
for i = 0,#self.triangles-1 do
219
local triangle = self.triangles[i + 1];
220
local d = triangle:intersect(origin, dir, near, far);
221
if (d == nil or d > far or d < near) then
222
-- continue;
223
else
224
far = d;
225
closest = triangle;
226
end
227
end
228
229
if (not closest) then
230
return { self.background[1],self.background[2],self.background[3] };
231
end
232
233
local normal = closest.normal;
234
local hit = add(origin, scale(dir, far));
235
if (dot(dir, normal) > 0) then
236
normal = { -normal[1], -normal[2], -normal[3] };
237
end
238
239
local colour = nil;
240
if (closest.shader) then
241
colour = closest.shader(closest, hit, dir);
242
else
243
colour = closest.material;
244
end
245
246
-- do reflection
247
local reflected = nil;
248
if (colour.reflection or 0 > 0.001) then
249
local reflection = addVector(scale(normal, -2*dot(dir, normal)), dir);
250
reflected = self:intersect(hit, reflection, 0.0001, 1000000);
251
if (colour.reflection >= 0.999999) then
252
return reflected;
253
end
254
end
255
256
local l = { self.ambient[1], self.ambient[2], self.ambient[3] };
257
258
for i = 0,#self.lights-1 do
259
local light = self.lights[i + 1];
260
local toLight = sub(light, hit);
261
local distance = lengthVector(toLight);
262
scaleVector(toLight, 1.0/distance);
263
distance = distance - 0.0001;
264
265
if (self:blocked(hit, toLight, distance)) then
266
-- continue;
267
else
268
local nl = dot(normal, toLight);
269
if (nl > 0) then
270
addVector(l, scale(light.colour, nl));
271
end
272
end
273
end
274
275
l = scalev(l, colour);
276
if (reflected) then
277
l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection));
278
end
279
280
return l;
281
end
282
283
this.blocked = function(self, O, D, far)
284
local near = 0.0001;
285
local closest = nil;
286
for i = 0,#self.triangles-1 do
287
local triangle = self.triangles[i + 1];
288
local d = triangle:intersect(O, D, near, far);
289
if (d == nil or d > far or d < near) then
290
--continue;
291
else
292
return true;
293
end
294
end
295
296
return false;
297
end
298
299
return this
300
end
301
302
local zero = { 0,0,0 };
303
304
-- this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where
305
-- that somewhere is
306
function Camera(origin, lookat, up)
307
local this = {}
308
309
local zaxis = normaliseVector(subVector(lookat, origin));
310
local xaxis = normaliseVector(cross(up, zaxis));
311
local yaxis = normaliseVector(cross(xaxis, subVector({ 0,0,0 }, zaxis)));
312
local m = {};
313
m[1] = xaxis[1]; m[2] = xaxis[2]; m[3] = xaxis[3];
314
m[5] = yaxis[1]; m[6] = yaxis[2]; m[7] = yaxis[3];
315
m[9] = zaxis[1]; m[10] = zaxis[2]; m[11] = zaxis[3];
316
m[4] = 0; m[8] = 0; m[12] = 0;
317
invertMatrix(m);
318
m[4] = 0; m[8] = 0; m[12] = 0;
319
this.origin = origin;
320
this.directions = {};
321
this.directions[1] = normalise({ -0.7, 0.7, 1 });
322
this.directions[2] = normalise({ 0.7, 0.7, 1 });
323
this.directions[3] = normalise({ 0.7, -0.7, 1 });
324
this.directions[4] = normalise({ -0.7, -0.7, 1 });
325
this.directions[1] = transformMatrix(m, this.directions[1]);
326
this.directions[2] = transformMatrix(m, this.directions[2]);
327
this.directions[3] = transformMatrix(m, this.directions[3]);
328
this.directions[4] = transformMatrix(m, this.directions[4]);
329
330
this.generateRayPair = function(self, y)
331
rays = { {}, {} }
332
rays[1].origin = self.origin;
333
rays[2].origin = self.origin;
334
rays[1].dir = addVector(scale(self.directions[1], y), scale(self.directions[4], 1 - y));
335
rays[2].dir = addVector(scale(self.directions[2], y), scale(self.directions[3], 1 - y));
336
return rays;
337
end
338
339
function renderRows(camera, scene, pixels, width, height, starty, stopy)
340
for y = starty,stopy-1 do
341
local rays = camera:generateRayPair(y / height);
342
for x = 0,width-1 do
343
local xp = x / width;
344
local origin = addVector(scale(rays[1].origin, xp), scale(rays[2].origin, 1 - xp));
345
local dir = normaliseVector(addVector(scale(rays[1].dir, xp), scale(rays[2].dir, 1 - xp)));
346
local l = scene:intersect(origin, dir, 0, math.huge);
347
pixels[y + 1][x + 1] = l;
348
end
349
end
350
end
351
352
this.render = function(self, scene, pixels, width, height)
353
local cam = self;
354
local row = 0;
355
renderRows(cam, scene, pixels, width, height, 0, height);
356
end
357
358
return this
359
end
360
361
function raytraceScene()
362
local startDate = 13154863;
363
local numTriangles = 2 * 6;
364
local triangles = {}; -- numTriangles);
365
local tfl = createVector(-10, 10, -10);
366
local tfr = createVector( 10, 10, -10);
367
local tbl = createVector(-10, 10, 10);
368
local tbr = createVector( 10, 10, 10);
369
local bfl = createVector(-10, -10, -10);
370
local bfr = createVector( 10, -10, -10);
371
local bbl = createVector(-10, -10, 10);
372
local bbr = createVector( 10, -10, 10);
373
374
-- cube!!!
375
-- front
376
local i = 0;
377
378
triangles[i + 1] = Triangle(tfl, tfr, bfr); i = i + 1;
379
triangles[i + 1] = Triangle(tfl, bfr, bfl); i = i + 1;
380
-- back
381
triangles[i + 1] = Triangle(tbl, tbr, bbr); i = i + 1;
382
triangles[i + 1] = Triangle(tbl, bbr, bbl); i = i + 1;
383
-- triangles[i-1].material = [0.7,0.2,0.2];
384
-- triangles[i-1].material.reflection = 0.8;
385
-- left
386
triangles[i + 1] = Triangle(tbl, tfl, bbl); i = i + 1;
387
-- triangles[i-1].reflection = 0.6;
388
triangles[i + 1] = Triangle(tfl, bfl, bbl); i = i + 1;
389
-- triangles[i-1].reflection = 0.6;
390
-- right
391
triangles[i + 1] = Triangle(tbr, tfr, bbr); i = i + 1;
392
triangles[i + 1] = Triangle(tfr, bfr, bbr); i = i + 1;
393
-- top
394
triangles[i + 1] = Triangle(tbl, tbr, tfr); i = i + 1;
395
triangles[i + 1] = Triangle(tbl, tfr, tfl); i = i + 1;
396
-- bottom
397
triangles[i + 1] = Triangle(bbl, bbr, bfr); i = i + 1;
398
triangles[i + 1] = Triangle(bbl, bfr, bfl); i = i + 1;
399
400
-- Floor!!!!
401
local green = createVector(0.0, 0.4, 0.0);
402
green.reflection = 0; --
403
local grey = createVector(0.4, 0.4, 0.4);
404
grey.reflection = 1.0;
405
local floorShader = function(tri, pos, view)
406
local x = ((pos[1]/32) % 2 + 2) % 2;
407
local z = ((pos[3]/32 + 0.3) % 2 + 2) % 2;
408
if ((x < 1) ~= (z < 1)) then
409
--in the real world we use the fresnel term...
410
-- local angle = 1-dot(view, tri.normal);
411
-- angle *= angle;
412
-- angle *= angle;
413
-- angle *= angle;
414
--grey.reflection = angle;
415
return grey;
416
else
417
return green;
418
end
419
end
420
421
local ffl = createVector(-1000, -30, -1000);
422
local ffr = createVector( 1000, -30, -1000);
423
local fbl = createVector(-1000, -30, 1000);
424
local fbr = createVector( 1000, -30, 1000);
425
triangles[i + 1] = Triangle(fbl, fbr, ffr); i = i + 1;
426
triangles[i-1 + 1].shader = floorShader;
427
triangles[i + 1] = Triangle(fbl, ffr, ffl); i = i + 1;
428
triangles[i-1 + 1].shader = floorShader;
429
430
local _scene = Scene(triangles);
431
_scene.lights[1] = createVector(20, 38, -22);
432
_scene.lights[1].colour = createVector(0.7, 0.3, 0.3);
433
_scene.lights[2] = createVector(-23, 40, 17);
434
_scene.lights[2].colour = createVector(0.7, 0.3, 0.3);
435
_scene.lights[3] = createVector(23, 20, 17);
436
_scene.lights[3].colour = createVector(0.7, 0.7, 0.7);
437
_scene.ambient = createVector(0.1, 0.1, 0.1);
438
-- _scene.background = createVector(0.7, 0.7, 1.0);
439
440
local pixels = {};
441
for y = 0,size-1 do
442
pixels[y + 1] = {};
443
for x = 0,size-1 do
444
pixels[y + 1][x + 1] = 0;
445
end
446
end
447
448
local _camera = Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0));
449
_camera:render(_scene, pixels, size, size);
450
451
return pixels;
452
end
453
454
function arrayToCanvasCommands(pixels)
455
local s = '<!DOCTYPE html><html><head><title>Test</title></head><body><canvas id="renderCanvas" width="' .. size .. 'px" height="' .. size .. 'px"></canvas><scr' .. 'ipt>\nvar pixels = [';
456
for y = 0,size-1 do
457
s = s .. "[";
458
for x = 0,size-1 do
459
s = s .. "[" .. math.floor(pixels[y + 1][x + 1][1] * 255) .. "," .. math.floor(pixels[y + 1][x + 1][2] * 255) .. "," .. math.floor(pixels[y + 1][x + 1][3] * 255) .. "],";
460
end
461
s = s .. "],";
462
end
463
s = s .. '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\
464
\n\
465
\n\
466
var size = ' .. size .. ';\n\
467
canvas.fillStyle = "red";\n\
468
canvas.fillRect(0, 0, size, size);\n\
469
canvas.scale(1, -1);\n\
470
canvas.translate(0, -size);\n\
471
\n\
472
if (!canvas.setFillColor)\n\
473
canvas.setFillColor = function(r, g, b, a) {\n\
474
this.fillStyle = "rgb("+[Math.floor(r), Math.floor(g), Math.floor(b)]+")";\n\
475
}\n\
476
\n\
477
for (var y = 0; y < size; y++) {\n\
478
for (var x = 0; x < size; x++) {\n\
479
var l = pixels[y][x];\n\
480
canvas.setFillColor(l[0], l[1], l[2], 1);\n\
481
canvas.fillRect(x, y, 1, 1);\n\
482
}\n\
483
}</scr' .. 'ipt></body></html>';
484
485
return s;
486
end
487
488
testOutput = arrayToCanvasCommands(raytraceScene());
489
490
--local f = io.output("output.html")
491
--f:write(testOutput)
492
--f:close()
493
494
local expectedLength = 11599;
495
local testLength = #testOutput
496
497
if (testLength ~= expectedLength) then
498
assert(false, "Error: bad result: expected length " .. expectedLength .. " but got " .. testLength);
499
end
500
501
end
502
503
bench.runCode(test, "3d-raytrace")
504
505