mirror of
https://github.com/gpujs/gpu.js.git
synced 2025-12-08 20:35:56 +00:00
feat: introduce WebGL._replaceOutputTexture and WebGL._replaceSubOutputTextures to cut down on resource usage feat: All supportable Math.methods added fix: Safari not able to render texture arguments feat: CPU gets a pipeline that acts like GPU with/without immutable
521 lines
19 KiB
HTML
521 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width initial-scale=1">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<title>GPU.js Raytracer</title>
|
|
<meta name="author" content="Stacey Tay">
|
|
<meta name="description" content="A simple raytracer built with GPU.js.">
|
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
|
|
<style>
|
|
html, body {
|
|
font-family: 'Open Sans', sans-serif;
|
|
}
|
|
canvas {
|
|
display: block;
|
|
margin: auto;
|
|
}
|
|
.center {
|
|
text-align: center;
|
|
}
|
|
#container {
|
|
margin: auto;
|
|
max-width: 600px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="container">
|
|
<div class="center">
|
|
<p>
|
|
From https://staceytay.com/raytracer/. A simple ray tracer with Lambertian and specular reflection,
|
|
built with <a href="http://gpu.rocks/">GPU.js</a>. Read more
|
|
about ray tracing and GPU.js in
|
|
my <a href="http://staceytay.com/2016/04/20/a-parallelized-ray-tracer-in-the-browser.html">blog
|
|
post</a>. Code available
|
|
on <a href="https://github.com/staceytay/raytracer/">GitHub</a>.
|
|
</p>
|
|
</div>
|
|
<div class="center">
|
|
<label for="cpu"><input type="radio" name="mode" value="cpu" id="cpu">CPU</label>
|
|
<label for="gpu"><input type="radio" name="mode" value="gpu" id="gpu">GPU</label>
|
|
</div>
|
|
<div class="center">
|
|
<label for="lambert"><input checked="checked" type="checkbox" id="lambert" value="lambert">Lambertian reflectance</label><br>
|
|
<label for="specular"><input checked="checked" type="checkbox" id="specular" value="specular">Specular reflection</label>
|
|
</div>
|
|
<div class="center">
|
|
<input type="button" value="Pause" onclick="togglePause ()" id="pause" />
|
|
</div>
|
|
<div class="center">
|
|
<span id="fps"></span><span> fps</span>
|
|
</div>
|
|
<div id="canvas-holder"></div>
|
|
</div>
|
|
<script src="../dist/gpu-browser.min.js"></script>
|
|
<script>
|
|
class Vector {
|
|
constructor(x, y, z) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
toArray() {
|
|
return [this.x, this.y, this.z];
|
|
}
|
|
static times(k, v) {
|
|
return new Vector(k * v.x, k * v.y, k * v.z);
|
|
}
|
|
static minus(v1, v2) {
|
|
return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
|
|
}
|
|
static magnitude(v) {
|
|
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
|
}
|
|
static norm(v) {
|
|
const mag = Vector.magnitude(v);
|
|
const div = (mag === 0) ? Infinity : 1.0 / mag;
|
|
return Vector.times(div, v);
|
|
}
|
|
static cross(v1, v2) {
|
|
return new Vector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
|
|
}
|
|
}
|
|
const stringOfMode = function (mode) {
|
|
switch (mode) {
|
|
case 1: return 'cpu';
|
|
case 0: return 'gpu';
|
|
}
|
|
};
|
|
const canvasHeight = (window.innerWidth < 600) ? (window.innerWidth - 20) : 600;
|
|
const canvasWidth = canvasHeight;
|
|
const initialMode = 1;
|
|
const camera = [
|
|
0, 0, 16, 0, 0, 1, 45
|
|
];
|
|
const lights = [
|
|
[-16, 16, 8],
|
|
[16, 16, 8],
|
|
];
|
|
const things = [
|
|
[0, 13,
|
|
1.0, 0.0, 0.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
-2, 2, -2, 1],
|
|
[0, 13,
|
|
0.0, 1.0, 0.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
0, 2, 0, 1],
|
|
[0, 13,
|
|
0.0, 0.0, 1.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
2, 2, 2, 1],
|
|
[0, 13,
|
|
0.0, 1.0, 1.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
-2, -2, 2, 1],
|
|
[0, 13,
|
|
1.0, 0.0, 1.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
0, -2, 0, 1],
|
|
[0, 13,
|
|
1.0, 1.0, 0.0,
|
|
0.3, 0.7, 0.2, 1.0,
|
|
2, -2, -2, 1],
|
|
];
|
|
const constants = {
|
|
LIGHTSCOUNT: lights.length,
|
|
THINGSCOUNT: things.length
|
|
};
|
|
const opt = function (mode) {
|
|
return {
|
|
constants: constants,
|
|
graphical: true,
|
|
mode: stringOfMode(mode),
|
|
output: [canvasWidth, canvasHeight],
|
|
tactic: 'precision',
|
|
};
|
|
};
|
|
const gpu = new GPU();
|
|
const cpu = new GPU({ mode: 'cpu'});
|
|
function vectorDotProduct(V1x, V1y, V1z, V2x, V2y, V2z) {
|
|
return (V1x * V2x) + (V1y * V2y) + (V1z * V2z);
|
|
}
|
|
function unitVectorX(Vx, Vy, Vz) {
|
|
const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));
|
|
const div = 1.0 / magnitude;
|
|
return div * Vx;
|
|
}
|
|
function unitVectorY(Vx, Vy, Vz) {
|
|
const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));
|
|
const div = 1.0 / magnitude;
|
|
return div * Vy;
|
|
}
|
|
function unitVectorZ(Vx, Vy, Vz) {
|
|
const magnitude = Math.sqrt((Vx * Vx) + (Vy * Vy) + (Vz * Vz));
|
|
const div = 1.0 / magnitude;
|
|
return div * Vz;
|
|
}
|
|
function sphereNormalX(Sx, Sy, Sz, radius, Px, Py, Pz) {
|
|
const SPx = Px - Sx;
|
|
const SPy = Py - Sy;
|
|
const SPz = Pz - Sz;
|
|
const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);
|
|
let div = Infinity;
|
|
if (magnitude > 0) {
|
|
div = 1.0 / magnitude;
|
|
}
|
|
return div * SPx;
|
|
}
|
|
function sphereNormalY(Sx, Sy, Sz, radius, Px, Py, Pz) {
|
|
const SPx = Px - Sx;
|
|
const SPy = Py - Sy;
|
|
const SPz = Pz - Sz;
|
|
const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);
|
|
let div = Infinity;
|
|
if (magnitude > 0) {
|
|
div = 1.0 / magnitude;
|
|
}
|
|
return div * SPy;
|
|
}
|
|
function sphereNormalZ(Sx, Sy, Sz, radius, Px, Py, Pz) {
|
|
const SPx = Px - Sx;
|
|
const SPy = Py - Sy;
|
|
const SPz = Pz - Sz;
|
|
const magnitude = (SPx * SPx) + (SPy * SPy) + (SPz * SPz);
|
|
let div = Infinity;
|
|
if (magnitude > 0) {
|
|
div = 1.0 / magnitude;
|
|
}
|
|
return div * SPz;
|
|
}
|
|
function vectorReflectX(Vx, Vy, Vz, Nx, Ny, Nz) {
|
|
const V1x = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Nx;
|
|
return (V1x * 2) - Vx;
|
|
}
|
|
function vectorReflectY(Vx, Vy, Vz, Nx, Ny, Nz) {
|
|
const V1y = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Ny;
|
|
return (V1y * 2) - Vy;
|
|
}
|
|
function vectorReflectZ(Vx, Vy, Vz, Nx, Ny, Nz) {
|
|
const V1z = ((Vx * Nx) + (Vy * Ny) + (Vz * Nz)) * Nz;
|
|
return (V1z * 2) - Vz;
|
|
}
|
|
function sphereIntersectionDistance(Sx, Sy, Sz, radius, Ex, Ey, Ez, Vx, Vy, Vz) {
|
|
const EOx = Sx - Ex;
|
|
const EOy = Sy - Ey;
|
|
const EOz = Sz - Ez;
|
|
const v = (EOx * Vx) + (EOy * Vy) + (EOz * Vz);
|
|
const discriminant = (radius * radius)
|
|
- ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))
|
|
+ (v * v);
|
|
if (discriminant < 0) {
|
|
return Infinity;
|
|
}
|
|
else {
|
|
return v - Math.sqrt(discriminant);
|
|
}
|
|
}
|
|
const kernelFunctions = [
|
|
vectorDotProduct,
|
|
unitVectorX, unitVectorY, unitVectorZ,
|
|
sphereNormalX, sphereNormalY, sphereNormalZ,
|
|
vectorReflectX, vectorReflectY, vectorReflectZ,
|
|
sphereIntersectionDistance
|
|
];
|
|
kernelFunctions.forEach(function (f) {
|
|
cpu.addFunction(f);
|
|
gpu.addFunction(f);
|
|
});
|
|
const createKernel = function (mode) {
|
|
return (mode === 0 ? gpu : cpu).createKernel(function (camera, lights, things, eyeV, rightV, upV, halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection) {
|
|
const x = this.thread.x;
|
|
const y = this.thread.y;
|
|
const rayPx = camera[0];
|
|
const rayPy = camera[1];
|
|
const rayPz = camera[2];
|
|
const xCompVx = ((x * pixelWidth) - halfWidth) * rightV[0];
|
|
const xCompVy = ((x * pixelWidth) - halfWidth) * rightV[1];
|
|
const xCompVz = ((x * pixelWidth) - halfWidth) * rightV[2];
|
|
const yCompVx = ((y * pixelHeight) - halfHeight) * upV[0];
|
|
const yCompVy = ((y * pixelHeight) - halfHeight) * upV[1];
|
|
const yCompVz = ((y * pixelHeight) - halfHeight) * upV[2];
|
|
const sumVx = eyeV[0] + xCompVx + yCompVx;
|
|
const sumVy = eyeV[1] + xCompVy + yCompVy;
|
|
const sumVz = eyeV[2] + xCompVz + yCompVz;
|
|
const rayVx = unitVectorX(sumVx, sumVy, sumVz);
|
|
const rayVy = unitVectorY(sumVx, sumVy, sumVz);
|
|
const rayVz = unitVectorZ(sumVx, sumVy, sumVz);
|
|
let closest = this.constants.THINGSCOUNT;
|
|
let closestDistance = Infinity;
|
|
for (let i = 0; i < this.constants.THINGSCOUNT; i++) {
|
|
const distance = sphereIntersectionDistance(things[i][9], things[i][10], things[i][11], things[i][12], rayPx, rayPy, rayPz, rayVx, rayVy, rayVz);
|
|
if (distance < closestDistance) {
|
|
closest = i;
|
|
closestDistance = distance;
|
|
}
|
|
}
|
|
if (closestDistance < Infinity) {
|
|
const px = rayPx + rayVx * closestDistance;
|
|
const py = rayPy + rayVy * closestDistance;
|
|
const pz = rayPz + rayVz * closestDistance;
|
|
const sx = things[closest][9];
|
|
const sy = things[closest][10];
|
|
const sz = things[closest][11];
|
|
const sRadius = things[closest][12];
|
|
const snVx = sphereNormalX(sx, sy, sz, sRadius, px, py, pz);
|
|
const snVy = sphereNormalY(sx, sy, sz, sRadius, px, py, pz);
|
|
const snVz = sphereNormalZ(sx, sy, sz, sRadius, px, py, pz);
|
|
const sRed = things[closest][2];
|
|
const sGreen = things[closest][3];
|
|
const sBlue = things[closest][4];
|
|
const ambient = things[closest][7];
|
|
const lambert = things[closest][6];
|
|
let lambertAmount = 0;
|
|
if (lambertianReflectance > 0 && lambert > 0) {
|
|
for (let i = 0; i < this.constants.LIGHTSCOUNT; i++) {
|
|
const LPx = px - lights[i][0];
|
|
const LPy = py - lights[i][1];
|
|
const LPz = pz - lights[i][2];
|
|
const uLPx = unitVectorX(LPx, LPy, LPz);
|
|
const uLPy = unitVectorY(LPx, LPy, LPz);
|
|
const uLPz = unitVectorZ(LPx, LPy, LPz);
|
|
let closestDistance_1 = Infinity;
|
|
for (let j = 0; j < this.constants.THINGSCOUNT; j++) {
|
|
let distance = Infinity;
|
|
const EOx = things[j][9] - px;
|
|
const EOy = things[j][10] - py;
|
|
const EOz = things[j][11] - pz;
|
|
const v = (EOx * uLPx) + (EOy * uLPy) + (EOz * uLPz);
|
|
const radius = things[j][12];
|
|
const discriminant = (radius * radius)
|
|
- ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))
|
|
+ (v * v);
|
|
if (discriminant >= 0) {
|
|
distance = v - Math.sqrt(discriminant);
|
|
}
|
|
if (distance < closestDistance_1) {
|
|
closestDistance_1 = distance;
|
|
}
|
|
}
|
|
if (closestDistance_1 > -0.005) {
|
|
const PLx = -LPx;
|
|
const PLy = -LPy;
|
|
const PLz = -LPz;
|
|
const uPLx = unitVectorX(PLx, PLy, PLz);
|
|
const uPLy = unitVectorY(PLx, PLy, PLz);
|
|
const uPLz = unitVectorZ(PLx, PLy, PLz);
|
|
const contribution = vectorDotProduct(uPLx, uPLy, uPLz, snVx, snVy, snVz);
|
|
if (contribution > 0) {
|
|
lambertAmount += contribution;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (lambertianReflectance > 0) {
|
|
lambertAmount = Math.min(1, lambertAmount);
|
|
}
|
|
const specular = things[closest][5];
|
|
let cVx = 0;
|
|
let cVy = 0;
|
|
let cVz = 0;
|
|
if (specularReflection > 0 && specular > 0) {
|
|
const rRayPx = px;
|
|
const rRayPy = py;
|
|
const rRayPz = pz;
|
|
const rRayVx = vectorReflectX(rayVx, rayVy, rayVz, snVx, snVy, snVz);
|
|
const rRayVy = vectorReflectY(rayVx, rayVy, rayVz, snVx, snVy, snVz);
|
|
const rRayVz = vectorReflectZ(rayVx, rayVy, rayVz, snVx, snVy, snVz);
|
|
let closest_1 = this.constants.THINGSCOUNT;
|
|
let closestDistance_2 = Infinity;
|
|
for (let i = 0; i < this.constants.THINGSCOUNT; i++) {
|
|
const distance = sphereIntersectionDistance(things[i][9], things[i][10], things[i][11], things[i][12], rRayPx, rRayPy, rRayPz, rRayVx, rRayVy, rRayVz);
|
|
if (distance < closestDistance_2) {
|
|
closest_1 = i;
|
|
closestDistance_2 = distance;
|
|
}
|
|
}
|
|
let reflectedRed = 1;
|
|
let reflectedGreen = 1;
|
|
let reflectedBlue = 1;
|
|
if (closestDistance_2 < Infinity) {
|
|
const px_1 = rRayPx + rRayVx * closestDistance_2;
|
|
const py_1 = rRayPy + rRayVy * closestDistance_2;
|
|
const pz_1 = rRayPz + rRayVz * closestDistance_2;
|
|
const sx_1 = things[closest_1][9];
|
|
const sy_1 = things[closest_1][10];
|
|
const sz_1 = things[closest_1][11];
|
|
const sRadius_1 = things[closest_1][12];
|
|
const snVx_1 = sphereNormalX(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);
|
|
const snVy_1 = sphereNormalY(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);
|
|
const snVz_1 = sphereNormalZ(sx_1, sy_1, sz_1, sRadius_1, px_1, py_1, pz_1);
|
|
const rsRed = things[closest_1][2];
|
|
const rsGreen = things[closest_1][3];
|
|
const rsBlue = things[closest_1][4];
|
|
const rambient = things[closest_1][7];
|
|
const rlambert = things[closest_1][6];
|
|
let rlambertAmount = 0;
|
|
if (lambertianReflectance > 0 && rlambert > 0) {
|
|
for (let i = 0; i < this.constants.LIGHTSCOUNT; i++) {
|
|
const LPx = px_1 - lights[i][0];
|
|
const LPy = py_1 - lights[i][1];
|
|
const LPz = pz_1 - lights[i][2];
|
|
const uLPx = unitVectorX(LPx, LPy, LPz);
|
|
const uLPy = unitVectorY(LPx, LPy, LPz);
|
|
const uLPz = unitVectorZ(LPx, LPy, LPz);
|
|
let closestDistance_3 = Infinity;
|
|
for (let j = 0; j < this.constants.THINGSCOUNT; j++) {
|
|
let distance = Infinity;
|
|
const EOx = things[j][9] - px_1;
|
|
const EOy = things[j][10] - py_1;
|
|
const EOz = things[j][11] - pz_1;
|
|
const v = (EOx * uLPx) + (EOy * uLPy) + (EOz * uLPz);
|
|
const radius = things[j][12];
|
|
const discriminant = (radius * radius)
|
|
- ((EOx * EOx) + (EOy * EOy) + (EOz * EOz))
|
|
+ (v * v);
|
|
if (discriminant >= 0) {
|
|
distance = v - Math.sqrt(discriminant);
|
|
}
|
|
if (distance < closestDistance_3) {
|
|
closestDistance_3 = distance;
|
|
}
|
|
}
|
|
if (closestDistance_3 > -0.005) {
|
|
const PLx = -LPx;
|
|
const PLy = -LPy;
|
|
const PLz = -LPz;
|
|
const uPLx = unitVectorX(PLx, PLy, PLz);
|
|
const uPLy = unitVectorY(PLx, PLy, PLz);
|
|
const uPLz = unitVectorZ(PLx, PLy, PLz);
|
|
const contribution = vectorDotProduct(uPLx, uPLy, uPLz, snVx_1, snVy_1, snVz_1);
|
|
if (contribution > 0) {
|
|
rlambertAmount += contribution;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (lambertianReflectance > 0)
|
|
rlambertAmount = Math.min(1, rlambertAmount);
|
|
reflectedRed = (rsRed * rlambertAmount * rlambert) + (rsRed * rambient);
|
|
reflectedGreen = (rsGreen * rlambertAmount * rlambert) + (rsGreen * rambient);
|
|
reflectedBlue = (rsBlue * rlambertAmount * rlambert) + (rsBlue * rambient);
|
|
cVx = cVx + (specular * reflectedRed);
|
|
cVy = cVy + (specular * reflectedGreen);
|
|
cVz = cVz + (specular * reflectedBlue);
|
|
}
|
|
}
|
|
const red = cVx + (sRed * lambertAmount * lambert) + (sRed * ambient);
|
|
const green = cVy + (sGreen * lambertAmount * lambert) + (sGreen * ambient);
|
|
const blue = cVz + (sBlue * lambertAmount * lambert) + (sBlue * ambient);
|
|
this.color(red, green, blue);
|
|
}
|
|
else {
|
|
this.color(0.95, 0.95, 0.95);
|
|
}
|
|
}, opt(mode));
|
|
};
|
|
const fps = {
|
|
startTime: 0,
|
|
frameNumber: 0,
|
|
getFPS: function () {
|
|
this.frameNumber++;
|
|
const d = new Date().getTime();
|
|
const currentTime = (d - this.startTime) / 1000;
|
|
const result = Math.floor(this.frameNumber / currentTime);
|
|
if (currentTime > 1) {
|
|
this.startTime = new Date().getTime();
|
|
this.frameNumber = 0;
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
const cameraPoint = new Vector(camera[0], camera[1], camera[2]);
|
|
const cameraVector = new Vector(camera[3], camera[4], camera[5]);
|
|
const eyeVector = Vector.norm(Vector.minus(cameraVector, cameraPoint));
|
|
const vpRight = Vector.norm(Vector.cross(eyeVector, new Vector(0, 1, 0)));
|
|
const vpUp = Vector.norm(Vector.cross(vpRight, eyeVector));
|
|
const fovRadians = Math.PI * (camera[6] / 2) / 180;
|
|
const heightWidthRatio = canvasHeight / canvasWidth;
|
|
const halfWidth = Math.tan(fovRadians);
|
|
const halfHeight = heightWidthRatio * halfWidth;
|
|
const cameraWidth = halfWidth * 2;
|
|
const cameraHeight = halfHeight * 2;
|
|
const pixelWidth = cameraWidth / (canvasWidth - 1);
|
|
const pixelHeight = cameraHeight / (canvasHeight - 1);
|
|
const gpuKernel = createKernel(0);
|
|
const cpuKernel = createKernel(1);
|
|
const gpuCanvas = gpuKernel.canvas;
|
|
const cpuCanvas = cpuKernel.canvas;
|
|
document.getElementById(stringOfMode(initialMode)).checked = true;
|
|
const canvasContainer = document.getElementById('canvas-holder');
|
|
canvasContainer.appendChild(gpuCanvas);
|
|
canvasContainer.appendChild(cpuCanvas);
|
|
let canvas;
|
|
if (initialMode === 1) {
|
|
showCPUCanvas();
|
|
} else {
|
|
showGPUCanvas();
|
|
}
|
|
let requestId = null;
|
|
function togglePause() {
|
|
if (requestId) {
|
|
if (document.getElementById('pause').value === 'Pause') {
|
|
cancelAnimationFrame(requestId);
|
|
document.getElementById('pause').value = 'Play';
|
|
}
|
|
else {
|
|
requestId = requestAnimationFrame(renderLoop);
|
|
document.getElementById('pause').value = 'Pause';
|
|
}
|
|
}
|
|
};
|
|
const f = document.getElementById('fps');
|
|
const lambert = document.getElementById('lambert');
|
|
const specular = document.getElementById('specular');
|
|
const cpuProcessor = document.getElementById('cpu');
|
|
function renderLoop() {
|
|
f.innerHTML = fps.getFPS().toString();
|
|
const lambertianReflectance = lambert.checked ? 1 : 0;
|
|
const specularReflection = specular.checked ? 1 : 0;
|
|
if (cpuProcessor.checked) {
|
|
cpuKernel(camera, lights, things, eyeVector.toArray(), vpRight.toArray(), vpUp.toArray(), halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection);
|
|
if (canvas !== cpuKernel.canvas) {
|
|
showCPUCanvas();
|
|
}
|
|
} else {
|
|
|
|
gpuKernel(camera, lights, things, eyeVector.toArray(), vpRight.toArray(), vpUp.toArray(), halfHeight, halfWidth, pixelHeight, pixelWidth, lambertianReflectance, specularReflection);
|
|
if (canvas !== gpuKernel.canvas) {
|
|
showGPUCanvas();
|
|
}
|
|
}
|
|
for (let i = 0; i < things.length; i++) {
|
|
const thing = things[i];
|
|
const height = canvasHeight / (halfHeight * 2 * 100);
|
|
if (thing[10] < height) {
|
|
thing[10] = (thing[10] + 0.02) % (height + 1);
|
|
} else {
|
|
thing[10] = -1 * height;
|
|
}
|
|
}
|
|
requestId = requestAnimationFrame(renderLoop);
|
|
}
|
|
window.onload = renderLoop;
|
|
|
|
function showCPUCanvas() {
|
|
cpuCanvas.style.display = '';
|
|
gpuCanvas.style.display = 'none';
|
|
canvas = cpuCanvas;
|
|
}
|
|
|
|
function showGPUCanvas() {
|
|
cpuCanvas.style.display = 'none';
|
|
gpuCanvas.style.display = '';
|
|
canvas = gpuCanvas;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|