心 勿噪
function CreateFPSCounter() { var mFrame; var mTo; var mFPS; var mLastTime; var mDeltaTime; var iReset = function (time) { time = time || 0; mFrame = 0; mTo = time; mFPS = 60.0; mLastTime = time; mDeltaTime = 0; } var iCount = function (time) { mFrame++; mDeltaTime = time - mLastTime; mLastTime = time; if ((time - mTo) > 500.0) { mFPS = 1000.0 * mFrame / (time - mTo); mFrame = 0; mTo = time; return true; } return false; } var iGetFPS = function () { return mFPS; } var iGetDeltaTime = function () { return mDeltaTime; } return { Reset: iReset, Count: iCount, GetFPS: iGetFPS, GetDeltaTime: iGetDeltaTime }; } function RequestFullScreen(ele) { if (ele == null) ele = document.documentElement; if (ele.requestFullscreen) ele.requestFullscreen(); else if (ele.msRequestFullscreen) ele.msRequestFullscreen(); else if (ele.mozRequestFullScreen) ele.mozRequestFullScreen(); else if (ele.webkitRequestFullscreen) ele.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); } function Init() { if (window.init_flag === undefined) { console.log("init_flag"); var iMouse = window.iMouse = { x: 0, y: 0, z: 0, w: 0 }; var cv = document.getElementById("displayPort"); var fpsConter = CreateFPSCounter(); var context = cv.getContext('2d'); var W = cv.clientWidth; H = cv.clientHeight; var imageData = context.getImageData(0, 0, W, H); var pixels = imageData.data; for (var i = 0; i < W * H; ++i) pixels[4 * i + 3] = 255; function MainLoop(deltaTime) { var tracer = window.rayTracer; if (tracer == null) return; if (iMouse.z > 0.) tracer.ResetFrames(); var frames = tracer.CountFrames(); var i = 0, color; for (var y = 0; y < H; ++y) { for (var x = 0; x < W; ++x, ++i) { color = tracer.Render(x, y, W, H); pixels[i++] = (color.r * 255) | 0; pixels[i++] = (color.g * 255) | 0; pixels[i++] = (color.b * 255) | 0; } } context.putImageData(imageData, 0, 0); // console.log(deltaTime, fpsConter.GetFPS()) } function elementPos(element) { var x = 0, y = 0; while (element.offsetParent) { x += element.offsetLeft; y += element.offsetTop; element = element.offsetParent; } return { x: x, y: y }; } function getMouseCoords(ev, canvasElement) { var pos = elementPos(canvasElement); var mcx = (ev.pageX - pos.x) * canvasElement.width / canvasElement.offsetWidth; var mcy = (ev.pageY - pos.y) * canvasElement.height / canvasElement.offsetHeight; return { x: mcx, y: mcy }; } cv.onmousemove = function (ev) { var mouse = getMouseCoords(ev, cv); iMouse.x = mouse.x; iMouse.y = mouse.y; } cv.onmousedown = function (ev) { if (ev.button === 0) iMouse.z = 1; else if (ev.button === 2) { iMouse.w = 1; RequestFullScreen(cv); } } document.onmouseup = function (ev) { if (ev.button === 0) iMouse.z = 0; else if (ev.button === 2) iMouse.w = 0; } ; (function (loopFunc) { var fisrt = true; function L(timestamp) { if (fisrt) { fisrt = false, fpsConter.Reset(timestamp) } else { fpsConter.Count(timestamp); } loopFunc(fpsConter.GetDeltaTime()); requestAnimationFrame(L) }; requestAnimationFrame(L); })(MainLoop); window.init_flag = true; } } function CreateTracer() { try { (new Function(document.getElementById("codeEditor").value))(); window.rayTracer = new window.pt(); window.iMouse.x = window.iMouse.y = 0; var cv = document.getElementById("displayPort"); var W = cv.clientWidth; H = cv.clientHeight; window.rayTracer.CreateBackBuffer(W, H); } catch(e) { alert(e) } } Init(); CreateTracer();
(function copy() { var e1 = document.getElementById("oldCode"); var e2 = document.getElementById("codeEditor"); if (e1 && e2) { e2.value = e1.innerText; } })()
window.pt = (function () { // 3维向量 function Vec3(x, y, z) { x = x === undefined ? 0 : x; this.x = x; this.y = y === undefined ? x : y; this.z = z === undefined ? x : z; } Vec3.prototype = { Set: function (x, y, z) { this.x = x, this.y = y, this.z = z; return this; }, Copy: function (other) { this.x = other.x, this.y = other.y, this.z = other.z; return this; }, Add: function (other) { return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z); }, AddSelf: function (other) { this.x += other.x, this.y += other.y, this.z += other.z; return this; }, Sub: function (other) { return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z); }, SubSelf: function (other) { this.x -= other.x, this.y -= other.y, this.z -= other.z; return this; }, Scale: function (factor) { return new Vec3(this.x * factor, this.y * factor, this.z * factor); }, ScaleSelf: function (factor) { this.x *= factor, this.y *= factor, this.z *= factor; return this; }, Length: function () { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); }, Normalize: function () { return this.Scale(1. / this.Length()); }, NormalizeSelf: function () { var l = 1. / Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); this.x *= l; this.y *= l; this.z *= l; return this; }, Dot: function (other) { return this.x * other.x + this.y * other.y + this.z * other.z; }, Cross: function (other) { return new Vec3(this.y * other.z - this.z * other.y, this.z * other.x - this.x * other.z, this.x * other.y - this.y * other.x); } } var eye = new Vec3(0, 0, -2.41); var up = new Vec3(0, 1, 0); var zero = new Vec3(0, 0, 0); var one = new Vec3(1, 1, 1); var sqrt2 = Math.sqrt(2); // 颜色 function Color(r, g, b) { this.r = r; this.g = g; this.b = b; } Color.prototype = { Set: function (r, g, b) { this.r = r; this.g = g; this.b = b; return this; }, Copy: function (other) { this.r = other.r, this.g = other.g, this.b = other.b; return this; }, Scale: function (factor) { return new Color(this.r * factor, this.g * factor, this.b * factor); }, ScaleSelf: function (factor) { this.r *= factor, this.g *= factor, this.b *= factor; return this; }, Multiply: function (other) { return new Color(this.r * other.r, this.g * other.g, this.b * other.b); }, MultiplySelf: function (other) { this.r *= other.r, this.g *= other.g, this.b *= other.b; return this; }, Lerp: function (other, t) { return new Color((1 - t) * this.r + t * other.r, (1 - t) * this.g + t * other.g, (1 - t) * this.b + t * other.b); }, Gamma: function (s) { var inv = 1/s; return new Color(Math.pow(this.r, inv), Math.pow(this.g, inv), Math.pow(this.b, inv)); }, GammaSelf: function (s) { var inv = 1/s; return this.Set(Math.pow(this.r, inv), Math.pow(this.g, inv), Math.pow(this.b, inv)); return this; } } function MakeColor255(r, g, b) { return new Color(r / 255, g / 255, b / 255); } var colorWhite = new Color(1, 1, 1); var colorBlack = new Color(0, 0, 0); var colorRed = new Color(1, 0, 0); var colorGreen = new Color(0, 1, 0); var colorBlue = new Color(0, 0, 1); var colorSky = new Color(0.5, 0.7, 1.0); // 光线 function Ray(o, d, t) { this.origin = o; this.direction = d; this.t = t || 1; } Ray.prototype = { GetOrigin: function () { return this.origin; }, GetDirection: function () { return this.direction; }, GetT: function () { return this.t; }, GetPoint: function (t) { return this.origin.Add(this.direction.Scale(typeof t === 'number' ? t : this.t)); }, } function Material(type, parameters) { this.type = type; this.parameters = parameters == null ? {} : parameters; } // 球体 function Sphere(center, radius, material) { this.center = center; this.radius = radius; this.material = material; } Sphere.prototype = { Intersection(ray) { var oc = ray.GetOrigin().Sub(this.center); var b = oc.Dot(ray.GetDirection()); var c = oc.Dot(oc) - this.radius * this.radius; var h = b * b - c; if (h < 0) return -1; return -b - Math.sqrt(h); }, HitNormal(hit) { return hit.Sub(this.center).ScaleSelf(1. / this.radius); } } // 相机 function Camera(eye, target, up) { this.eye = new Vec3().Copy(eye); this.target = new Vec3().Copy(target); this.up = new Vec3().Copy(up); this.CalcUVW(eye); this.an = 0; this.ro = new Vec3().Copy(this.eye); } Camera.prototype = { CalcUVW(eye) { this.w = this.target.Sub(eye).NormalizeSelf(); this.u = this.w.Cross(this.up).NormalizeSelf(); this.v = this.u.Cross(this.w); }, GetRay(u, v, width, height) { if (iMouse.z > 0.) { var an = 2 * Math.PI * (iMouse.x / width); this.ro = new Vec3().Set(this.eye.z * Math.sin(an), 0.0, this.eye.z * Math.cos(an)); this.CalcUVW(this.ro); this.an = an; } var ro = this.ro; var rd = this.target.Sub(this.eye).AddSelf(new Vec3(u * sqrt2, v * sqrt2, 0.)).NormalizeSelf(); var x = rd.x, y = rd.y, z = rd.z; rd.x = x * this.u.x + y * this.v.x + z * this.w.x; rd.y = x * this.u.y + y * this.v.y + z * this.w.y; rd.z = x * this.u.z + y * this.v.z + z * this.w.z; return new Ray(ro, rd, 0); } } // 后缓冲区 function BackBuffer(width, height, frames) { this.width = width; this.height = height; this.data = new Array(width * height * 4); } BackBuffer.prototype = { Clear: function (r, g, b) { r = r === undefined ? 0 : r; g = g === undefined ? r : g; b = b === undefined ? r : b; var size = this.width * this.height; for (var i = 0; i < size; ++i) { this.data[4 * i + 0] = r; this.data[4 * i + 1] = g; this.data[4 * i + 2] = b; this.data[4 * i + 3] = 1; } }, Read: function (x, y) { var offset = (y * this.width + x) * 4; var r = this.data[offset + 0]; var g = this.data[offset + 1]; var b = this.data[offset + 2]; return [r, g, b]; }, Write: function (x, y, r, g, b) { var offset = (y * this.width + x) * 4; this.data[offset + 0] = r; this.data[offset + 1] = g; this.data[offset + 2] = b; } } // 追踪器 function Tracer() { this.camera = new Camera(eye, zero, up); this.spheres = new Array( new Sphere(new Vec3(-0.5, 0.0, 0.), 1.0, new Material(1, { refractRate: 1.5, color: MakeColor255(196, 32, 128) })), new Sphere(new Vec3(0.825, -0.5, -0.5), 0.5, new Material(2, { color: new Color(0.1, 1., 0.125) })), new Sphere(new Vec3(-0.75, -0.65, -1.), 0.2, new Material(2, { fuzz: 0.3, color: colorBlue })), new Sphere(new Vec3(0.25, 0.15, -1.), 0.25, new Material(3, { refractRate: 1.2, glass : true, fuzz: 0.3, color: colorWhite })), new Sphere(new Vec3(-1.05, -0.25, -1.), 0.25, new Material(3, { refractRate: 1.2, fuzz: 0.3, color: colorWhite })), new Sphere(new Vec3(0.0, -10000.85, -1.0), 10000.0, new Material(1, null)) ); this.frames = 0; } Tracer.prototype = { RandomPointInUnitSphere: function () { return new Vec3( Math.random() * 2. - 1., Math.random() * 2. - 1., Math.random() * 2. - 1. ); }, // 基向量 Frisvad: function (n, f, r) { if (n.z < -0.999999) { f.Set(0., -1, 0); r.Set(-1, 0, 0); } else { var a = 1. / (1. + n.z); var b = -n.x * n.y * a; f.Set(1. - n.x * n.x * a, b, -n.x); r.Set(b, 1. - n.y * n.y * a, -n.y); } }, // cos权重随机半球采样 CosWeightedRandomHemisphereDirection(normal) { var x = Math.random(), y = Math.random(); var u = new Vec3(), v = new Vec3(), w = normal; this.Frisvad(w, u, v); var ra = Math.sqrt(y); var rx = ra * Math.cos(2 * Math.PI * x); var ry = ra * Math.sin(2 * Math.PI * x); var rz = Math.sqrt(1.0 - y); u.x = rx * u.x + ry * v.x + rz * w.x; u.y = rx * u.y + ry * v.y + rz * w.y; u.z = rx * u.z + ry * v.z + rz * w.z; return u.NormalizeSelf(); }, Scatered: function (color, ray, sphere) { var material = sphere.material; var hit = ray.GetPoint(); if (material.type === 1) { return this.Diffused(color, hit, sphere.HitNormal(hit), ray.GetDirection()) } else if (material.type === 2) { return this.Reflected(color, hit, sphere.HitNormal(hit), ray.GetDirection(), material.parameters.fuzz); } else if (material.type === 3) { return this.Refracted(color, hit, sphere.HitNormal(hit), ray.GetDirection(), material.parameters.refractRate, material.parameters.glass); } }, Diffused: function (color, hit, normal, direction) { color.ScaleSelf(0.5); var target = hit.Add(normal).AddSelf(this.RandomPointInUnitSphere()); return new Ray(hit, target.SubSelf(hit).NormalizeSelf()); //return new Ray(hit, this.CosWeightedRandomHemisphereDirection(normal)); }, Reflected: function (color, hit, normal, direction, fuzz) { function reflect(direction, normal) { var l = -2 * direction.Dot(normal); return normal.Scale(l).AddSelf(direction); } color.ScaleSelf(0.5); var n = this.RandomPointInUnitSphere().ScaleSelf(fuzz || 0); var r = reflect(direction, normal); return new Ray(hit, r.AddSelf(n).NormalizeSelf()); }, Refracted: function (color, hit, normal, direction, refractRate, glass) { var glass = glass === undefined ? false : glass; var outwardNormal, realRate; var cosWi = direction.Dot(normal); if (cosWi > 0) { outwardNormal = normal.Scale(-1); realRate = glass ? 1.0 / refractRate : refractRate; //realRate = 1.0 / refractRate; //realRate = refractRate; cosWi = direction.Dot(outwardNormal); } else { outwardNormal = normal; realRate = glass ? refractRate : 1.0 / refractRate; //realRate = refractRate; //realRate = 1.0 / refractRate; } var cos2Wo = 1.0 - realRate * realRate * (1.0 - cosWi * cosWi); if (cos2Wo > 0) { var y = outwardNormal.Scale(-Math.sqrt(cos2Wo)); var x = outwardNormal.Scale(-cosWi).AddSelf(direction).ScaleSelf(realRate); var refractRay = x.AddSelf(y); return new Ray(hit, refractRay); } else { return this.Reflected(color, hit, normal, direction); } }, Trace: function (ray, deep) { var color = new Color().Copy(colorWhite); if (deep > 5) { color.Copy(colorBlack); return color; } var hitSomething = false, min = 999, t, sphere; this.spheres.forEach(function (sp) { t = sp.Intersection(ray); if (t > 0. && t < min) { hitSomething = true; min = t; sphere = sp; } }); if (hitSomething) { ray.t = min; var material = sphere.material; if (material.parameters.color) color.Copy(material.parameters.color); var newRay = this.Scatered(color, ray, sphere, color); color.MultiplySelf(this.Trace(newRay, deep + 1)); } else { var t = 0.5 * (ray.direction.y + 1.0); return colorWhite.Lerp(colorSky, t); } return color; }, Render: function (x, y, width, height) { // antialiasing var u = (x + Math.random()) / width, v = (y + Math.random()) / height; u = (u * 2 - 1.) * (width / height); v = 1. - v * 2; var ray = this.camera.GetRay(u, v, width, height); var color = this.Trace(ray, 0); // backbuffer var buffer = this.buffer; if (buffer) { var frames = this.frames; var c = buffer.Read(x, y); var f = (frames - 1) / frames; var r = c[0], g = c[1], b = c[2]; r = r * f + color.r / frames; g = g * f + color.g / frames; b = b * f + color.b / frames; buffer.Write(x, y, r, g, b); color.Set(r, g, b) } // gamma corrected color.GammaSelf(2.2); return color; }, CreateBackBuffer: function (width, height) { var buffer = new BackBuffer(width, height); buffer.Clear(); this.buffer = buffer; }, CountFrames: function () { return ++this.frames; }, ResetFrames: function () { this.frames = 0; return this.frames; }, } return Tracer; })()
posted on 2019-12-07 15:21 LSH 阅读(566) 评论(0) 编辑 收藏 引用
Powered by: C++博客 Copyright © LSH