/* * MonteRay Alpha 3 Copyright (c) 2022 Tech Labs, Inc. * * This renderer has been created as an alternative to the default Three.js * WebGL renderer, and will (at some future time) support most if not all * Three.js scenes and materials. * * https://techlabsinc.github.io/MonteRay/LICENSE * * @author TechLabsInc / http://github.com/TechLabsInc */ if (!MonteRay) { var MonteRay = {}; // Constants MonteRay.DepthRenderMode = 0; MonteRay.RandomRenderMode = 1; MonteRay.LinearRenderMode = 2; MonteRay.CenterRenderMode = 3; // Custom classes MonteRay.Color = function (r, g, b, a) { if (g === undefined && b === undefined) { return this.set(r); } if (a === undefined) { return this.setRGB(r, g, b); } return this.setRGBA(r, g, b, a); } Object.assign(MonteRay.Color.prototype, { isMonteRayColor: true, r: 1, g: 1, b: 1, a: 1, maxVal: function () { return Math.max(this.r, Math.max(this.g, this.b)); }, maxValA: function () { return Math.max(this.r, Math.max(this.g, Math.max(this.b, this.a))); }, set: function (value) { if (value && value.isMonteRayColor) { this.copy(value); } else if (value && value.isColor) { this.fromTHREEColor(value, 1); } else if (typeof value === 'number') { this.setHex(value); } else if (typeof value === 'string') { this.fromTHREEColor(new THREE.Color().setStyle(value)); } return this; }, setRGB: function (r, g, b) { this.r = r; this.g = g; this.b = b; return this; }, setRGBA: function (r, g, b, a) { this.r = r; this.g = g; this.b = b; this.a = a; return this; }, setHex: function (hex) { hex = Math.floor(hex); if (hex.toString(16).length > 6) { this.r = (hex >> 24 & 255) / 255; this.g = (hex >> 16 & 255) / 255; this.b = (hex >> 8 & 255) / 255; this.a = (hex & 255) / 255; } else { this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; } return this; }, setScalar: function (scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; }, setScalarA: function (scalar) { this.r = scalar; this.g = scalar; this.b = scalar; this.a = scalar; return this; }, setR: function (r) { this.r = r; return this; }, setG: function (g) { this.g = g; return this; }, setB: function (b) { this.b = b; return this; }, setA: function (a) { this.a = a; return this; }, setComponent: function (index, value) { switch (index) { case 0: this.r = value; break; case 1: this.g = value; break; case 2: this.b = value; break; case 3: this.a = value; break; default: throw new Error('index is out of range: ' + index); } return this; }, getComponent: function (index) { switch (index) { case 0: return this.r; case 1: return this.g; case 2: return this.b; case 3: return this.a; default: throw new Error('index is out of range: ' + index); } }, clone: function () { return new this.constructor(this.r, this.g, this.b, this.a); }, copy: function (c) { this.r = c.r; this.g = c.g; this.b = c.b; this.a = c.a; return this; }, add: function (c) { this.r += c.r; this.g += c.g; this.b += c.b; return this; }, addA: function (c) { this.r += c.r; this.g += c.g; this.b += c.b; this.a += c.a; return this; }, addScalar: function (s) { this.r += s; this.g += s; this.b += s; return this; }, addScalarA: function (s) { this.r += s; this.g += s; this.b += s; this.a += s; return this; }, addColors: function (a, b) { this.r = a.r + b.r; this.g = a.g + b.g; this.b = a.b + b.b; return this; }, addColorsA: function (a, b) { this.r = a.r + b.r; this.g = a.g + b.g; this.b = a.b + b.b; this.a = a.a + b.a; return this; }, sub: function (c) { this.r -= c.r; this.g -= c.g; this.b -= c.b; return this; }, subA: function (c) { this.r -= c.r; this.g -= c.g; this.b -= c.b; this.a -= c.a; return this; }, subScalar: function (s) { this.r -= s; this.g -= s; this.b -= s; return this; }, subScalarA: function (s) { this.r -= s; this.g -= s; this.b -= s; this.a -= s; return this; }, subColors: function (a, b) { this.r = a.r - b.r; this.g = a.g - b.g; this.b = a.b - b.b; return this; }, subColorsA: function (a, b) { this.r = a.r - b.r; this.g = a.g - b.g; this.b = a.b - b.b; this.a = a.a - b.a; return this; }, multiply: function (c) { this.r *= c.r; this.g *= c.g; this.b *= c.b; return this; }, multiplyA: function (c) { this.r *= c.r; this.g *= c.g; this.b *= c.b; this.a *= c.a; return this; }, multiplyScalar: function (scalar) { this.r *= scalar; this.g *= scalar; this.b *= scalar; return this; }, multiplyScalarA: function (scalar) { this.r *= scalar; this.g *= scalar; this.b *= scalar; this.a *= scalar; return this; }, divideScalar: function (scalar) { return this.multiplyScalar(1 / scalar); }, divideScalarA: function (scalar) { return this.multiplyScalarA(1 / scalar); }, min: function (c) { this.r = Math.min(this.r, c.r); this.g = Math.min(this.g, c.g); this.b = Math.min(this.b, c.b); return this; }, minA: function (c) { this.r = Math.min(this.r, c.r); this.g = Math.min(this.g, c.g); this.b = Math.min(this.b, c.b); this.a = Math.min(this.a, c.a); return this; }, max: function (c) { this.r = Math.max(this.r, c.r); this.g = Math.max(this.g, c.g); this.b = Math.max(this.b, c.b); return this; }, maxA: function (c) { this.r = Math.max(this.r, c.r); this.g = Math.max(this.g, c.g); this.b = Math.max(this.b, c.b); this.a = Math.max(this.a, c.a); return this; }, clamp: function (min, max) { this.r = Math.max(min.r, Math.min(max.r, this.r)); this.g = Math.max(min.g, Math.min(max.g, this.g)); this.b = Math.max(min.b, Math.min(max.b, this.b)); return this; }, clampA: function (min, max) { this.r = Math.max(min.r, Math.min(max.r, this.r)); this.g = Math.max(min.g, Math.min(max.g, this.g)); this.b = Math.max(min.b, Math.min(max.b, this.b)); this.a = Math.max(min.a, Math.min(max.a, this.a)); return this; }, clampScalar: function (minVal, maxVal) { this.r = Math.max(minVal, Math.min(maxVal, this.r)); this.g = Math.max(minVal, Math.min(maxVal, this.g)); this.b = Math.max(minVal, Math.min(maxVal, this.b)); return this; }, clampScalarA: function (minVal, maxVal) { this.r = Math.max(minVal, Math.min(maxVal, this.r)); this.g = Math.max(minVal, Math.min(maxVal, this.g)); this.b = Math.max(minVal, Math.min(maxVal, this.b)); this.a = Math.max(minVal, Math.min(maxVal, this.a)); return this; }, clampV: function () { this.clampScalarA(0, 1); }, invert: function () { this.r = -this.r; this.g = -this.g; this.b = -this.b; return this; }, invertA: function () { this.r = -this.r; this.g = -this.g; this.b = -this.b; this.a = -this.a; return this; }, lerp: function (c, alpha) { this.r += (c.r - this.r) * alpha; this.g += (c.g - this.g) * alpha; this.b += (c.b - this.b) * alpha; return this; }, lerpA: function (c, alpha) { this.r += (c.r - this.r) * alpha; this.g += (c.g - this.g) * alpha; this.b += (c.b - this.b) * alpha; this.a += (c.a - this.a) * alpha; return this; }, lerpColors: function (v1, v2, alpha) { this.r = v1.r + (v2.r - v1.r) * alpha; this.g = v1.g + (v2.g - v1.g) * alpha; this.b = v1.b + (v2.b - v1.b) * alpha; return this; }, lerpColorsA: function (v1, v2, alpha) { this.r = v1.r + (v2.r - v1.r) * alpha; this.g = v1.g + (v2.g - v1.g) * alpha; this.b = v1.b + (v2.b - v1.b) * alpha; this.a = v1.a + (v2.a - v1.a) * alpha; return this; }, equals: function (c) { return ((c.r === this.r) && (c.g === this.g) && (c.b === this.b)); }, equalsA: function (c) { return ((c.r === this.r) && (c.g === this.g) && (c.b === this.b) && (c.a === this.a)); }, fromArray: function (array, offset) { if (offset == undefined) { offset = 0; } this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; this.a = array[offset + 3]; return this; }, toArray: function (array, offset) { if (array == undefined) { array = []; } if (offset == undefined) { offset = 0; } array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; array[offset + 3] = this.a; return array; }, fromBufferAttribute: function (attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); this.a = attribute.getW(index); return this; }, fromTHREEColor: function (c, a) { try { if (a == undefined) { a = 1; } this.setRGB(c.r, c.g, c.b, a); } catch (e) { this.setRGB(0, 0, 0, 1); } return this; }, random: function () { this.r = Math.random(); this.g = Math.random(); this.b = Math.random(); return this; }, randomA: function () { this.r = Math.random(); this.g = Math.random(); this.b = Math.random(); this.a = Math.random(); return this; }, gammaCorrect: function () { this.r = Math.pow(this.r, 1 / 2.2); this.g = Math.pow(this.g, 1 / 2.2); this.b = Math.pow(this.b, 1 / 2.2); return this; }, reverseGamma: function () { this.r = Math.pow(this.r, 2.2); this.g = Math.pow(this.g, 2.2); this.b = Math.pow(this.b, 2.2); return this; } }); MonteRay.Color.imageDataToHex = function (r, g, b, a) { return (r) << 24 ^ (g) << 16 ^ (b) << 8 ^ (a) << 0; } } MonteRay.PathtracingRenderer = function (parameters) { parameters = parameters || {}; MonteRayEngine.parameters = parameters; var self = this; console.log("Rendering provided by MonteRay " + MonteRayEngine.VERSION + " https://techlabsinc.github.io/MonteRay/LICENSE"); var canvas; if (parameters.canvas) { canvas = parameters.canvas; } else { canvas = document.createElement('canvas'); } var context = canvas.getContext('2d', { alpha: parameters.alpha === true }); var canvasWidth, canvasHeight; var canvasHalfWidth, canvasHalfHeight; var pixelRatio; var clearColor = new MonteRay.Color(0x000000); this.domElement = canvas; this.autoClear = true; this.setClearColor = function (color, alpha) { clearColor.setScalar(color); clearColor.setA(alpha); }; this.getClearColor = function () { return clearColor.toColor(); }; this.setPixelRatio = function (res) { pixelRatio = res; this.setSize(canvasWidth / pixelRatio, canvasHeight / pixelRatio); }; this.setSize = function (width, height) { canvasWidth = Math.round(width * pixelRatio); canvasHeight = Math.round(height * pixelRatio); canvasHalfWidth = canvasWidth / 2; canvasHalfHeight = canvasHeight / 2; canvas.style.width = width + "px"; canvas.style.height = height + "px"; }; this.setSize(canvas.width, canvas.height); this.setPixelRatio(1); this.clear = function () { }; try { if (MeshBVHLib) { if (!parameters.BVHAcceleration || parameters.BVHAcceleration && parameters.BVHAcceleration != false) { THREE.BufferGeometry.prototype.computeBoundsTree = MeshBVHLib.computeBoundsTree; THREE.BufferGeometry.prototype.disposeBoundsTree = MeshBVHLib.disposeBoundsTree; THREE.Mesh.prototype.raycast = MeshBVHLib.acceleratedRaycast; } } else { console.warn("MonteRay.PathtracingRenderer: BVH acceleration not supported."); parameters.BVHAcceleration = false; } } catch (e) { console.warn("MonteRay.PathtracingRenderer: BVH acceleration not supported."); parameters.BVHAcceleration = false; } var dnumber = 1; this.download = function () { var el = document.createElement("a"); el.setAttribute("href", canvas.toDataURL()); el.setAttribute("download", document.title + "_f" + dnumber + "_" + this.currentSamples() + "spp.png"); dnumber++; el.style.display = "none"; document.body.appendChild(el); el.onclick = function (e) { e.stopPropagation(); } el.click(); document.body.removeChild(el); } var bs = { lngth: 1, len: 1, uod: true, ts: 0, tts: 0 }; var spp = 1; var cw = canvasWidth; var ch = canvasHeight; this.clearTextureCache = function () { MonteRayEngine.clearTextureCache(); }; this.currentSamples = function () { return spp; }; this.getSize = function () { return new THREE.Vector2(cw, ch); }; this.getPixelRatio = function () { return pixelRatio; }; var toRender = []; var crender = 0; this.resetSamples = function () { for (var s = 0; s < toRender.length; s++) { toRender[s][2] = 2; } crender = 0; spp = 1; if (MonteRayEngine.resetWorkers) { MonteRayEngine.resetWorkers(); } }; var pool = []; var c; MonteRayEngine.requestTexture = function (texture) { //console.log(texture); var texdrows = []; if (texture instanceof THREE.DataTexture == true) { if (texture.image.data) { var texd = texture.image.data; for (var i = 0; i < texd.length; i += 3) { //texdrows.push([texd[i] * Math.pow(2, texd[i + 3] - 128), texd[i + 1] * Math.pow(2, texd[i + 3] - 128), texd[i + 2] * Math.pow(2, texd[i + 3] - 128), 255]); texdrows.push([Math.pow(texd[i], 1 / 2.2), Math.pow(texd[i + 1], 1 / 2.2), Math.pow(texd[i + 2], 1 / 2.2), 1]); } } } else { if (!c) { c = document.createElement('canvas').getContext('2d'); } else { c.clearRect(0, 0, c.canvas.width, c.canvas.height); } c.canvas.width = texture.image.width; c.canvas.height = texture.image.height; c.drawImage(texture.image, 0, 0); var texd = c.getImageData(0, 0, c.canvas.width, c.canvas.height).data; for (var i = 0; i < texd.length; i += 4) { if (texd[i + 3] == 0) { texdrows.push([1, 1, 1, 1]); } else { texdrows.push([texd[i] / 255, texd[i + 1] / 255, texd[i + 2] / 255, texd[i + 3] / 255]); } } c.clearRect(0, 0, c.canvas.width, c.canvas.height); c.canvas.width = 1; c.canvas.height = 1; } if (texdrows.length > 0) { var res = []; for (var i = 0; i < texdrows.length; i += texture.image.width) { res.push(texdrows.slice(i, i + texture.image.width)); } return res; } }; this.render = function (scene, camera) { var rayIn = false; cw = canvasWidth; ch = canvasHeight; var chw = canvasHalfWidth; var chh = canvasHalfHeight; canvas.width = cw; canvas.height = ch; spp = 1; // setup //context.clearRect(0, 0, canvasWidth, canvasHeight); var ptr = []; var j = 0; MonteRayEngine.pixSize.set(1 / cw, 1 / ch); for (var y = 0; y < ch; y++) { for (var x = 0; x < cw; x++) { ptr.push(new THREE.Vector2((((x + 0.5) / cw) * 2 - 1), (-((y + 0.5) / ch) * 2 + 1))); toRender.push([ptr[ptr.length - 1], { distance: Infinity }, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); j++; } } try { document.getElementById("sa").innerHTML = "Analyzing scene..."; } catch (e) {} if (!parameters.threads) { MonteRayEngine.updateScene(scene, camera); MonteRayEngine.processScene(scene); } // pre-rendering cycle try { document.getElementById("sa").innerHTML = "Pre-rendering..."; } catch (e) {} var renderedImage = context.createImageData(cw, ch); /*for (var j = 0; j < ptr.length; j++) { var outputColor = new MonteRay.Color(0, 0, 0, 0); var pixelcast = ptr[j]; toRender.push([pixelcast, { distance: Infinity }, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); /*MonteRayEngine.eye.setFromCamera(pixelcast, camera); var intersect = MonteRayEngine.eye.intersectObjects(scene.children, true)[0]; if (intersect == undefined) { outputColor.add(MonteRayEngine.getBackground(scene, MonteRayEngine.eye)); if (parameters.antialiasBackground) { //outputColor.setRGB(Math.random(), Math.random(), Math.random()); //outputColor.setScalar3(Math.random()); toRender.push([pixelcast, { distance: Infinity }, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); } } else { try { var mat = MonteRayEngine.getPixelMaterial(intersect); if (mat.type == "GLOW") { var intense = 1; if (mat.emissiveIntensity) { intense = mat.emissiveIntensity; } outputColor.copy(mat.color); outputColor.add(mat.emissive.multiplyScalar(intense)); if (parameters.antialiasBackground) { toRender.push([pixelcast, intersect, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); } } else { outputColor.setScalar(intersect.distance / camera.far); toRender.push([pixelcast, intersect, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); if (parameters.alpha) { outputColor.setA(0); } } } catch (e) { console.log(e); outputColor.setScalar(intersect.distance / camera.far); toRender.push([pixelcast, intersect, 2, new MonteRay.Color(0, 0, 0, 1), j * 4]); if (parameters.alpha) { outputColor.setA(0); } } }* / renderedImage.data[(j * 4)] = outputColor.r * 255; renderedImage.data[(j * 4) + 1] = outputColor.g * 255; renderedImage.data[(j * 4) + 2] = outputColor.b * 255; renderedImage.data[(j * 4) + 3] = outputColor.a * 255; } //context.putImageData(renderedImage, 0, 0);*/ if (!parameters.renderMode || (parameters.renderMode && parameters.renderMode == MonteRay.DepthRenderMode)) { toRender.sort(function (a, b) { return a[1].distance - b[1].distance; }); } else if (parameters.renderMode && parameters.renderMode == MonteRay.CenterRenderMode) { var cn = new THREE.Vector3(0, 0); var v1 = new THREE.Vector2(); var v2 = new THREE.Vector2(); toRender.sort(function (a, b) { v1.set(a[0].x * camera.aspect, a[0].y); v2.set(b[0].x * camera.aspect, b[0].y); return v1.distanceToSquared(cn) - v2.distanceToSquared(cn); }); } else if (parameters.renderMode && parameters.renderMode == MonteRay.RandomRenderMode) { for (var i = 0; i < toRender.length; i++) { var swap = Math.random() * toRender.length | 0; var tmp = toRender[swap]; toRender[swap] = toRender[i]; toRender[i] = tmp; } } /*var len = toRender.length; var nhp = []; var sections = 2; var index = 1; var cnt = 0; while (cnt < len) { if (index < sections) { var id = Math.floor((len / sections) * index); if (toRender[id] != 0) { nhp.push(toRender[id]); toRender[id] = 0; cnt++; } index++; } else { index = 1; sections *= 2; } } toRender = nhp;*/ /*var tmp = []; var nme = 2; for (var i = 0; i < nme; i += 1) { tmp[i] = []; } for (var i = 0; i < toRender.length; i += nme) { for (var j = 0; j < Math.min(nme, toRender.length - i); j += 1) { tmp[j].push(toRender[i + j]); } } toRender = []; for (var i = 0; i < nme; i += 1) { toRender = toRender.concat(tmp[i]); } delete tmp;*/ crender = 0; if (!parameters.fps) { bs.lngth = toRender.length; if (parameters.pixelBatchSize) { bs.lngth = parameters.pixelBatchSize; } } try { document.getElementById("sa").innerHTML = cw + "x" + ch + "
" + "Rendering 1 sample per pixel..."; } catch (e) {} var v = new MonteRay.Color(); MonteRayEngine.randomOffset.set(((Math.random() - 0.5) * MonteRayEngine.pixSize.x * 2), ((Math.random() - 0.5) * MonteRayEngine.pixSize.y * 2)); if (parameters.threads) { MonteRayEngine.resetWorkers = function () { for (var w = 0; w < pool.length; w++) { pool[w].postMessage({ task: "INIT", camera: camera.toJSON(), }); pool[w].invalidated = true; } }; var renderedImageUpdated = false; scene.traverse(function (obj) { if (obj.material) { for (x of Object.keys(obj.material)) { if (obj.material[x]) { if (obj.material[x].image) { if (!Array.isArray(obj.material[x].image.data)) { console.log(obj.material[x]); var texture = obj.material[x]; var texdrows = []; if (!c) { c = document.createElement('canvas').getContext('2d'); } else { c.clearRect(0, 0, c.canvas.width, c.canvas.height); } c.canvas.width = texture.image.width; c.canvas.height = texture.image.height; c.drawImage(texture.image, 0, 0); var texd = c.getImageData(0, 0, c.canvas.width, c.canvas.height).data; var newtex = new THREE.DataTexture(texd, c.canvas.width, c.canvas.height); for (prop in obj.material[x]) { //console.log(prop, typeof obj.material[x][prop]); if (typeof obj.material[x][prop] != "function" && prop != "image") { newtex[prop] = obj.material[x][prop]; } } newtex.type = THREE.IntType; newtex.transformUv = THREE.Texture.prototype.transformUv; obj.material[x].dispose(); obj.material[x] = newtex; console.log(obj.material[x]); /*for (var i = 0; i < texd.length; i += 4) { if (texd[i + 3] == 0) { texdrows.push([1, 1, 1, 1]); } else { texdrows.push([texd[i] / 255, texd[i + 1] / 255, texd[i + 2] / 255, texd[i + 3] / 255]); } }*/ c.clearRect(0, 0, c.canvas.width, c.canvas.height); c.canvas.width = 1; c.canvas.height = 1; /*if (texdrows.length > 0) { var res = []; for (var i = 0; i < texdrows.length; i += texture.image.width) { res.push(texdrows.slice(i, i + texture.image.width)); } }*/ } } } } } }); var jsonScene = scene.toJSON(); jsonScene.MRImages = {}; /*for (i in jsonScene.images) { jsonScene.MRImages[String(jsonScene.images[i].uuid)] = true; console.log(scene.getObjectByProperty("uuid", String(jsonScene.images[i].uuid))); jsonScene.images[i].url = { data: true, type: "Int8Array" }; // replace real images with dummy stubs just so the ObjectLoader will keep the UUID's the same }*/ console.log(jsonScene); console.log(scene); //console.log(jsonScene); var jsonCamera = camera.toJSON(); //console.log(scene.background); if (scene.background.isMonteRayColor) { var jsonBackground = { type: "Color", r: scene.background.r, g: scene.background.g, b: scene.background.b, a: scene.background.a }; } else if (scene.background instanceof THREE.DataTexture) { var jsonBackground = { itype: "THREE.DataTexture", data: scene.background.image.data, width: scene.background.image.width, height: scene.background.image.height, repeat: scene.background.repeat, flipY: scene.background.flipY, type: scene.background.type }; } console.log("HERE"); console.log(scene.background); //console.log(jsonBackground); var tstats = []; function queueBatch(t) { t.bs.tts = Date.now(); MonteRayEngine.adjustBatchSize(t.bs); t.bs.ts = Date.now(); var nbatch = []; t.queuedPixels = []; for (var i = 0; i < Math.min(t.bs.lngth, toRender.length - crender); i++) { nbatch.push(toRender[crender + i]); t.queuedPixels.push(crender + i); } crender += t.bs.lngth; if (crender > toRender.length) { crender = 0; MonteRayEngine.randomOffset.set(((Math.random() - 0.5) * MonteRayEngine.pixSize.x * 2), ((Math.random() - 0.5) * MonteRayEngine.pixSize.y * 2)); spp++; /*try { document.getElementById("sa").innerHTML = cw + "x" + ch + "
" + "Rendering " + spp + " samples per pixel..."; } catch (e) {}*/ } t.postMessage({ task: "RNDR", randomOffsetX: MonteRayEngine.randomOffset.x, randomOffsetY: MonteRayEngine.randomOffset.y, pixels: JSON.stringify(nbatch) }); } var workerIndex = 0; while (pool.length < parameters.threads) { var w = new Worker("../Build/MonteRayThread.js"); w.bs = { lngth: 1, len: 1, uod: true, ts: 0, tts: 0 }; tstats[workerIndex] = w.bs; w.id = 1 + workerIndex++; w.onmessage = function (e) { switch (e.data.response) { case "DONE": if (!this.invalidated) { var pixelBatch = JSON.parse(e.data.pixels); for (var q = 0; q < pixelBatch.length; q++) { toRender[this.queuedPixels[q]] = [new THREE.Vector2(pixelBatch[q][0].x, pixelBatch[q][0].y), pixelBatch[q][1], pixelBatch[q][2], new MonteRay.Color(pixelBatch[q][3].r, pixelBatch[q][3].g, pixelBatch[q][3].b, pixelBatch[q][3].a), pixelBatch[q][4]]; v.copy(pixelBatch[q][3]).gammaCorrect(); renderedImage.data[pixelBatch[q][4]] = v.r * 255; renderedImage.data[pixelBatch[q][4] + 1] = v.g * 255; renderedImage.data[pixelBatch[q][4] + 2] = v.b * 255; renderedImage.data[pixelBatch[q][4] + 3] = v.a * 255; } renderedImageUpdated = true; } else { delete this.invalidated; } queueBatch(this); break; case "TEXTURE": break; } }; w.postMessage({ task: "INIT", parameters: parameters, scene: jsonScene, background: jsonBackground, camera: jsonCamera, pixSize: MonteRayEngine.pixSize, randomOffset: MonteRayEngine.randomOffset }); queueBatch(w); pool.push(w); } } function renderPixel() { if (!parameters.threads) { try { var pixelcast = toRender[crender]; var pixelBatch = []; for (var q = 0; q < Math.min(bs.lngth, toRender.length - crender); q++) { pixelBatch.push(toRender[crender + q]); } crender += bs.lngth; var pixelBatch = MonteRayEngine.renderBatch(pixelBatch, scene, camera); for (var q = 0; q < pixelBatch.length; q++) { v.copy(pixelBatch[q][3]).gammaCorrect(); renderedImage.data[pixelBatch[q][4]] = v.r * 255; renderedImage.data[pixelBatch[q][4] + 1] = v.g * 255; renderedImage.data[pixelBatch[q][4] + 2] = v.b * 255; renderedImage.data[pixelBatch[q][4] + 3] = v.a * 255; } if (crender >= toRender.length) { //context.putImageData(renderedImage, 0, 0); crender = 0; if (parameters.maxSamples || parameters.downloadInterval) { if (spp >= parameters.maxSamples || spp % parameters.downloadInterval == 0) { context.putImageData(renderedImage, 0, 0); self.download(); if (spp >= parameters.maxSamples) { try { document.getElementById("sa").innerHTML = "Render Complete"; } catch (e) {} if (parameters.onRenderCompleted) { return parameters.onRenderCompleted(); } else { return; } } } } spp++; MonteRayEngine.randomOffset.set(((Math.random() - 0.5) * MonteRayEngine.pixSize.x * 2), ((Math.random() - 0.5) * MonteRayEngine.pixSize.y * 2)); if (parameters.onSampleCompleted) { parameters.onSampleCompleted(); MonteRayEngine.updateScene(scene, camera); } try { document.getElementById("sa").innerHTML = cw + "x" + ch + "
" + "Rendering " + spp + " samples per pixel..."; } catch (e) {} } context.putImageData(renderedImage, 0, 0); if (parameters.fps) { bs.tts = Date.now(); MonteRayEngine.adjustBatchSize(bs); try { document.getElementById("sa").innerHTML = cw + "x" + ch + "
" + spp + "spp " + bs.lngth + "ppf " + Math.round(1 / ((bs.tts - bs.ts) / 1000)) + "fps"; } catch (e) {} bs.ts = Date.now(); } } catch (e) { console.error(e); } } else { if (renderedImageUpdated == true) { var ss = cw + "x" + ch + "
" + spp + "spp
"; for (var s = 0; s < tstats.length; s++) { ss += tstats[s].lngth + "ppf
"; } document.getElementById("sa").innerHTML = ss; context.putImageData(renderedImage, 0, 0); renderedImageUpdated = false; } } requestAnimationFrame(renderPixel); } requestAnimationFrame(renderPixel); }; }; //esm var PathtracingRenderer = MonteRay.PathtracingRenderer; //esm var Color = MonteRay.Color; //esm export { PathtracingRenderer, Color };