raytrace-engine
Version:
A simple CPU-based ray tracer written in **vanilla JavaScript**, rendering directly to an HTML5 `<canvas>` element — no WebGL, no external libraries.
1 lines • 5.11 kB
JavaScript
!function(t,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r();else if("function"==typeof define&&define.amd)define([],r);else{var e=r();for(var o in e)("object"==typeof exports?exports:t)[o]=e[o]}}(self,()=>(()=>{"use strict";const t={subtractVectors:function(t,r){return{x:t.x-r.x,y:t.y-r.y,z:t.z-r.z}},addVectors:function(t,r){return{x:t.x+r.x,y:t.y+r.y,z:t.z+r.z}},dotProduct:function(t,r){return t.x*r.x+t.y*r.y+t.z*r.z},scaleVector:function(t,r){return{x:t.x*r,y:t.y*r,z:t.z*r}},magnitudeOfVector:function(t){return Math.sqrt(Math.pow(t.x,2)+Math.pow(t.y,2)+Math.pow(t.z,2))},normalizeVector:function(t){var r=this.magnitudeOfVector(t);return 0===r?t:this.scaleVector(t,1/r)},transformVector:function(t,r){var e=t.x,o=t.y,n=t.z;return{x:e*r[0][0]+o*r[1][0]+n*r[2][0],y:e*r[0][1]+o*r[1][1]+n*r[2][1],z:e*r[0][2]+o*r[1][2]+n*r[2][2]}},rotateVectorAroundYaxis:function(t,r){var e=[[Math.cos(r),0,-Math.sin(r)],[0,1,0],[Math.sin(r),0,Math.cos(r)]];return this.transformVector(t,e)},quadraticEquationRoots:function(t,r,e){var o=Math.pow(r,2)-4*t*e;return o<0?{t1:null,t2:null}:{t1:(-r+Math.sqrt(o))/(2*t),t2:(-r-Math.sqrt(o))/(2*t)}}};function r(t,r){var o="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!o){if(Array.isArray(t)||(o=function(t,r){if(t){if("string"==typeof t)return e(t,r);var o={}.toString.call(t).slice(8,-1);return"Object"===o&&t.constructor&&(o=t.constructor.name),"Map"===o||"Set"===o?Array.from(t):"Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?e(t,r):void 0}}(t))||r&&t&&"number"==typeof t.length){o&&(t=o);var n=0,i=function(){};return{s:i,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var c,a=!0,u=!1;return{s:function(){o=o.call(t)},n:function(){var t=o.next();return a=t.done,t},e:function(t){u=!0,c=t},f:function(){try{a||null==o.return||o.return()}finally{if(u)throw c}}}}function e(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,o=Array(r);e<r;e++)o[e]=t[e];return o}var o,n,i;function c(r,e){return t.subtractVectors(t.scaleVector(t.scaleVector(e,2),t.dotProduct(e,r)),r)}function a(e){var n=e.color,i=e.intersectionPoint,a=e.normalToShapeSurface,s=e.reverseDirectionVector,l=e.specular;if(!i)return n;var f,d,V=0,v=r(o);try{for(v.s();!(d=v.n()).done;){var y=d.value;if("ambient"===y.type)V+=y.intensity;else{var p=null;if("point"===y.type?(p=t.subtractVectors(y.position,i),f=1):"directional"===y.type&&(p=y.direction,f=Number.POSITIVE_INFINITY),null!==u({tMin:.001,tMax:f,originVector:i,directionVector:p}).closestShape)continue;var m=t.dotProduct(p,a);if(m>0){var h=t.magnitudeOfVector(p),x=t.magnitudeOfVector(a);V+=y.intensity*(m/(h*x))}if(-1!==l){var g=c(p,a),b=t.dotProduct(g,s);if(b>0){var I=t.magnitudeOfVector(g),M=t.magnitudeOfVector(s);V+=y.intensity*Math.pow(b/(I*M),l)}}}}}catch(t){v.e(t)}finally{v.f()}return{r:n.r*V,g:n.g*V,b:n.b*V}}function u(t){var e,o=t.tMin,n=t.tMax,c=t.originVector,a=t.directionVector,u=Number.POSITIVE_INFINITY,s=null,f=r(i);try{for(f.s();!(e=f.n()).done;){var d=e.value,V=l({originVector:c,directionVector:a,shape:d}),v=V.t1,y=V.t2;v>=o&&v<=n&&v<u&&(u=v,s=d),y>=o&&y<=n&&y<u&&(u=y,s=d)}}catch(t){f.e(t)}finally{f.f()}return{closestT:u,closestShape:s}}function s(r){var e,o=r.tMin,i=r.tMax,l=r.originVector,f=r.directionVector,d=r.recursionLimit,V=u({tMin:o,tMax:i,originVector:l,directionVector:f}),v=V.closestShape,y=V.closestT;if(null===v)return n;var p,m=t.addVectors(l,t.scaleVector(f,y)),h=v.color,x=t.normalizeVector(t.subtractVectors(m,v.center)),g=t.subtractVectors({x:0,y:0,z:0},f);if(d<=0||!v.reflective||0===v.reflective||v.reflective<=0)return a({color:h,intersectionPoint:m,normalToShapeSurface:x,reverseDirectionVector:g,specular:null!==(p=v.specular)&&void 0!==p?p:-1});var b=c(g,x),I=s({tMin:.001,tMax:Number.POSITIVE_INFINITY,originVector:m,directionVector:b,recursionLimit:d-1});return a({color:{r:h.r*(1-v.reflective)+I.r*v.reflective,g:h.g*(1-v.reflective)+I.g*v.reflective,b:h.b*(1-v.reflective)+I.b*v.reflective},intersectionPoint:m,normalToShapeSurface:x,reverseDirectionVector:g,specular:null!==(e=v.specular)&&void 0!==e?e:-1})}function l(r){var e=r.directionVector,o=r.originVector,n=r.shape,i=n.radius,c=t.subtractVectors(o,n.center),a=t.dotProduct(e,e),u=2*t.dotProduct(c,e),s=t.dotProduct(c,c)-Math.pow(i,2),l=t.quadraticEquationRoots(a,u,s),f=l.t1,d=l.t2;return{t1:null!=f?f:Number.POSITIVE_INFINITY,t2:null!=d?d:Number.POSITIVE_INFINITY}}return self.onmessage=function(r){var e=r.data,c=e.shapeData,a=e.lightData,u=e.noIntersectionColor,l=e.tMin,f=e.tMax,d=e.originVector,V=e.pixels,v=e.ratioW,y=e.ratioH,p=e.distanceFromCameraToViewport,m=e.recursionLimit,h=e.cameraAngle;o=a,n=u,i=c;var x=V.map(function(r){var e=r.x,o=r.y,n=t.subtractVectors({x:e*v,y:o*y,z:p},d);return 0!==h&&(n=t.rotateVectorAroundYaxis(n,h)),{x:e,y:o,color:s({tMin:l,tMax:f,originVector:d,directionVector:n,recursionLimit:m})}});self.postMessage(x)},{}})());