From 0e30e3a770e285fc18d53a0b4ed68c111e6fa819 Mon Sep 17 00:00:00 2001 From: fdai7322 <christoph-anton.schwierz@informatik.hs-fulda.de> Date: Mon, 1 Jul 2024 20:30:52 +0200 Subject: [PATCH] framebuffer animation1 raytracing2 --- index.html | 2 +- src/cg/animation1 frames/render.frame.0.png | Bin 0 -> 434 bytes src/cg/animation1 frames/render.frame.1.png | Bin 0 -> 445 bytes src/cg/animation1 frames/render.frame.10.png | Bin 0 -> 427 bytes src/cg/animation1 frames/render.frame.2.png | Bin 0 -> 553 bytes src/cg/animation1 frames/render.frame.4.png | Bin 0 -> 445 bytes src/cg/animation1 frames/render.frame.5.png | Bin 0 -> 488 bytes src/cg/animation1 frames/render.frame.6.png | Bin 0 -> 440 bytes src/cg/animation1 frames/render.frame.7.png | Bin 0 -> 440 bytes src/cg/animation1 frames/render.frame.8.png | Bin 0 -> 444 bytes src/cg/animation1 frames/render.frame.9.png | Bin 0 -> 443 bytes src/cg/animation1.ts | 53 ++++++ src/cg/framebuffer.ts | 164 +++++++++++++++++++ src/cg/helper.ts | 14 ++ src/cg/raytracing1.ts | 6 +- src/cg/raytracing2.ts | 61 +++++++ src/cg/utils.ts | 24 +++ 17 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 src/cg/animation1 frames/render.frame.0.png create mode 100644 src/cg/animation1 frames/render.frame.1.png create mode 100644 src/cg/animation1 frames/render.frame.10.png create mode 100644 src/cg/animation1 frames/render.frame.2.png create mode 100644 src/cg/animation1 frames/render.frame.4.png create mode 100644 src/cg/animation1 frames/render.frame.5.png create mode 100644 src/cg/animation1 frames/render.frame.6.png create mode 100644 src/cg/animation1 frames/render.frame.7.png create mode 100644 src/cg/animation1 frames/render.frame.8.png create mode 100644 src/cg/animation1 frames/render.frame.9.png create mode 100644 src/cg/animation1.ts create mode 100644 src/cg/framebuffer.ts create mode 100644 src/cg/helper.ts create mode 100644 src/cg/raytracing2.ts diff --git a/index.html b/index.html index 1deccb1..d2a125a 100644 --- a/index.html +++ b/index.html @@ -15,6 +15,6 @@ <script type="module" src="/src/cg/perspectiveDivide.ts"></script> <script type="module" src="/src/cg/walkToVec.ts"></script> <script type="module" src="/src/cg/basis.ts"></script--> - <script type="module" src="/src/cg/raytracing1.ts"></script> + <script type="module" src="/src/cg/raytracing2.ts"></script> </body> </html> diff --git a/src/cg/animation1 frames/render.frame.0.png b/src/cg/animation1 frames/render.frame.0.png new file mode 100644 index 0000000000000000000000000000000000000000..9023bea0ce683eb56f8a9149f59ec7f4e52bcf80 GIT binary patch literal 434 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z?kFd z;uum9_x7@(-~k7L!w&NG7v>v@gsc(FSn_f2+q-3(TNo9EB<naFSvn;<9#(WH7&05m z@C&M>DYQLSY&yboq`<~y!i+|VzIF~FV}ZjT1ss#uk}TwuJkB`q9Cseogd7kxx8#^o VPsn{}V_;xl@O1TaS?83{1OQq0kFEd! literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.1.png b/src/cg/animation1 frames/render.frame.1.png new file mode 100644 index 0000000000000000000000000000000000000000..601be26d15159e44b40b002fade435a4c1f3f713 GIT binary patch literal 445 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*z3- z;uum9_x9RBP6kDRqZelW>n`KJ-5{>mp3W#FWWVF_5}uqcwlf?;)htaMLdF7zHwri= zu_akt=e9IXKd%0ubpLCG^^SM`OADxc6JilmNmFP`R%|-LbEH7WWx|X`iN023U1CZe vXB>ErJF#?1c07#eP%vaRl;IwUn&f9)VM<ln=p)O(z`)??>gTe~DWM4fY$=V% literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.10.png b/src/cg/animation1 frames/render.frame.10.png new file mode 100644 index 0000000000000000000000000000000000000000..71574d343c1b746990b6ae5905acea9f1fd3138e GIT binary patch literal 427 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z?kak z;uum9_x7@*AOizOi^KZ*%<MPJ!UhlCTAezeF)Lx6!VO`OB?>bbd6u_|uoyQy+%lml zfz4o!SdhROhPKO2TuqV(3Zgt54VWeTxHUP_7!GGDvL56~u+SaV1Purl?oTb}8B!(Q TuXJQ!U|{fc^>bP0l+XkK<Nk%^ literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.2.png b/src/cg/animation1 frames/render.frame.2.png new file mode 100644 index 0000000000000000000000000000000000000000..31d95714c83e04a363b103829f200131feed2055 GIT binary patch literal 553 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&!1&nH z#WAEJ?(Hl`E@nr8ma2dMU!0X^S;+A$?_$QYZSQ~iN>&74FF7Nc^2R@4LQ{{Vg!OXq zpb3V|$<MzyaT%p0yqOc{;dqRvXYOTg%@Z>kHx^eZvPv2ol=<l|QAlPxcJ5`XNXnT5 zH!SxOsrkR$lik1XeLZXN_73;LXDaIyCPi393uO5{;!^5dx|wxUh~$uX$8Ir)DK&EQ ShR+NP3=E#GelF{r5}E+==H9&k literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.4.png b/src/cg/animation1 frames/render.frame.4.png new file mode 100644 index 0000000000000000000000000000000000000000..cd4c48b9649df640d173da01bd606b19477b12c3 GIT binary patch literal 445 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*z3- z;uum9_xAEe-Ub7KqZdy9b8ctN2s#=hXDW1T-~MBhjWz_CB)CnuA;_WRamIn?xD!jK zWXHpZ4h2JILm6&Cl{AI6WW}Z<JVy$2TqewDl;~^a5Hc1xyive0i7m-OY{Y1Ka^}Fi zo7vB19<F@me*WBpd$*hJ6#xI7>WD44HY&(Z?PN;**0?#0fq{X+)78&qol`;+04A=M A1poj5 literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.5.png b/src/cg/animation1 frames/render.frame.5.png new file mode 100644 index 0000000000000000000000000000000000000000..4d95157b2786dd0940b2157907367580d10848f9 GIT binary patch literal 488 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z_`fM z#WAEJ?(JR2E@nr8BM0XFx4-qfy@^G)<$Wwi=BGFBe=g;z(UdaqpK!xnp+mut*-(aC zP$f;FEm^VY2+xrM9hV6+8YTK#IfRS_4sR52Okzv25L5Cv<G^#=iKSDr<6*=Q*0jm+ zL-qUbd;kAGzf^3~PrGyBtd^^9+lZt*{#p|{Ve_uD&W>hr=V>hXm0v}cDfRbZKVt?4 O1_n=8KbLh*2~7aH0=4@9 literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.6.png b/src/cg/animation1 frames/render.frame.6.png new file mode 100644 index 0000000000000000000000000000000000000000..c8166ae26ad816c30733c678f101af0d6f2bfc0d GIT binary patch literal 440 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*ywz z;uum9_x5feFM|Tlkqc+{X9zSjP7e?<dHZhmT)ET)%~=V{6mAHLEK!)j$g{jvgvGew z;g$(a32X*)#DWCQFtlBE;%bsSP!Q$eXuvGt$F0ec#&9@Ok@X-?f`#r7*R(JC#7s-; rchfiDXFbUF@qq}7b_m5jFJ|2MkSW#wid8TJ0|SGntDnm{r-UW|K|_!b literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.7.png b/src/cg/animation1 frames/render.frame.7.png new file mode 100644 index 0000000000000000000000000000000000000000..3f996840ce41307bd287e561c6001b6844abb5ec GIT binary patch literal 440 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*ywz z;uum9_xAEe!2=2cM-D9hzo?MaZzit`bH<YWo8?j;1ehFLqi{n|WQoEIMxN!ZA}q!Y z54TKcN?<dXBNilZhN11U6IYYufr2OxM+0UFKW<HqG={^OimV5D5-fB_q^1-86L#O- qZ2SH5ALlm;mUEgCJh_kq%FgaHQ!01jBn<`z1_n=8KbLh*2~7a)%#rW_ literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.8.png b/src/cg/animation1 frames/render.frame.8.png new file mode 100644 index 0000000000000000000000000000000000000000..f294eaa6ad42e03a33ead3ad9a7b8a3e0d44f8d3 GIT binary patch literal 444 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*y$# z;uum9_xAEe(FOyaBLUU_7djti^IZ7o@?qgg`Nt<4ZP1i5Q1*Dk!6K-VrqGtG*mQ*F zNP&*agc*$zeXSfq#sY^o3OFXQC0U3md7N?JIqt;LDcSKbqC>%u*-(aiq-r{#dTyqr x_2)Mm=GVUXw13Y(X#tfot|mxuO^BGle`Y&V>H(F4{R|8Y44$rjF6*2UngDMdlQRGS literal 0 HcmV?d00001 diff --git a/src/cg/animation1 frames/render.frame.9.png b/src/cg/animation1 frames/render.frame.9.png new file mode 100644 index 0000000000000000000000000000000000000000..33829f286078b48109a8275d78c56148cf7bf47a GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0y~yU`PRB4mJh`hJr^^Ll_ts7>k44ofy`glX=O&z*y?( z;uum9_x7?PAA_R6(F-$w%g-`>UAW}YLeGq-e|?jUHfTy2D0{pS=1}rD<G^#=iKSDr z<6%UHf+4e^47Z?4nnGK$V$%_xBLzAx6J|6@^tEyb84DcVDBzgHmSiC|stFVjau4Us n+-!WEF-G6!(bqHi512oHVM;B3srZwDfq}u()z4*}Q$iB}j2DqA literal 0 HcmV?d00001 diff --git a/src/cg/animation1.ts b/src/cg/animation1.ts new file mode 100644 index 0000000..9815ef8 --- /dev/null +++ b/src/cg/animation1.ts @@ -0,0 +1,53 @@ +/*import Framebuffer from "./framebuffer"; + +const width = 100 +const height = 100 +const framebuffer = new Framebuffer(width, height); + +let currentFrame = 0 + +// Step from 0 to 1 +for (let i = 0; i <= 1; i += 0.1) { + framebuffer.clear() + + // remap 0 1 to -1 1 + const remapped = i * 2 - 1; console.info(remapped) + + // fit remapped i into actual values + // and use absolute value from remapped + const val = Math.round((height-1) * Math.abs(remapped)); + + framebuffer.draw(50, val, [255, 0, 0]) + + framebuffer.update(); + framebuffer.save("frame." + ++currentFrame) +}*/ + +import { easeOutBounce } from "./helper"; +import Framebuffer from "./framebuffer"; + +const width = 100 +const height = 100 +const framebuffer = new Framebuffer(width, height); + +let currentFrame = 0 + + +for (let i = 0; i <= 1; i += 0.1) { + framebuffer.clear(); + + // remap 0 1 to -1 1 + //const remapped = i * 2 - 1; console.info(remapped) + + // fit remapped i into actual values + // and use absolute value from remapped + //const val = Math.round((height-1) * Math.abs(remapped)); + + const c = height * easeOutBounce(i) + framebuffer.draw(width * i, Math.floor(c), [255, 0, 0]) + + framebuffer.update(); + framebuffer.save("frame." + ++currentFrame) + +} + diff --git a/src/cg/framebuffer.ts b/src/cg/framebuffer.ts new file mode 100644 index 0000000..fb7e2ed --- /dev/null +++ b/src/cg/framebuffer.ts @@ -0,0 +1,164 @@ +export type Color3 = [number, number, number]; +export type Vector3 = [number, number, number]; + +export interface ICoord2D { + x: number, + y: number +} + +export default class Framebuffer { + private canvas: HTMLCanvasElement + private ctx + private buffer + private color: Color3 + private saveLink: HTMLAnchorElement + private saveButton: HTMLButtonElement + private logField: HTMLUListElement + + constructor(width = 100, height = 100) { + + // Remove stored images + for (var key in sessionStorage) { + if (key.indexOf("render.") === 0) { + sessionStorage.removeItem(key); + } + } + + this.color = [0, 0, 0] + const appEl = document.getElementById("app"); + appEl.style.display = "flex"; + appEl.style.height = "100vh"; + appEl.style.width = "100vw"; + appEl.style.flexDirection = "column"; + appEl.style.justifyContent = "center"; + appEl.style.alignItems = "center"; + + this.canvas = document.createElement("canvas"); + this.canvas.id = "framebuffer"; + this.canvas.width = width; + this.canvas.height = height; + this.canvas.style.borderWidth = "2px" + + const header = document.createElement("h2"); + header.innerText = "framebuffer: " + width + "x" + height; + appEl.appendChild(header); + appEl.appendChild(this.canvas); + + this.ctx = this.canvas.getContext("2d"); + + if (!this.ctx) { + console.warn("No canvas 2d context!") + return + } + + // Setup save link + this.saveLink = document.createElement("a"); + this.saveLink.id = "save"; + + this.saveLink.style.display = "none"; + appEl.appendChild(this.saveLink); + + // Save button + this.saveButton = document.createElement("button"); + this.saveButton.innerText = "Download Frames"; + appEl.appendChild(this.saveButton); + + this.saveButton.addEventListener("click", () => this.download()) + + // Log + this.logField = document.createElement("ul"); + this.logField.id = "log"; + appEl.appendChild(this.logField); + + // Get pixel data + this.buffer = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); + + } + + + draw(x: number, y: number, color: Color3 = [80, 80, 80], parameters?: {}): void { + + const defaults = { + } + + const params = { ...defaults, ...parameters } + this.color = color; + + if (!this.buffer) return; + + // The first (red) component for this pixel + var offset = 4 * x + (this.buffer.width * 4) * y; + + const redIndex = offset; + const greenIndex = redIndex + 1; + const blueIndex = greenIndex + 1; + const alphaIndex = blueIndex + 1; + + this.buffer.data[redIndex] = this.color[0]; + this.buffer.data[greenIndex] = this.color[1]; + this.buffer.data[blueIndex] = this.color[2]; + this.buffer.data[alphaIndex] = 255; + } + + + update() { + if (!this.ctx) return; + + this.ctx.putImageData(this.buffer, 0, 0); + } + + save(name: string) { + sessionStorage.setItem("render." + name, this.canvas.toDataURL("image/png").replace("image/png", "image/octet-stream")); + + } + + download() { + for (var key in sessionStorage) { + if (key.indexOf("render.") === 0) { + sessionStorage.getItem(key) + console.info("Downloading", key) + this.saveLink.setAttribute('download', key + '.png'); + this.saveLink.setAttribute('href', sessionStorage[key]); + this.saveLink.click(); + } + } + } + + log(content: string) { + this.logField.innerText = content; + } + + clear() { + this.ctx.reset(); + this.buffer = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); + this.update(); + } + + // Type definition for a 3D vector + + + // Function to convert from raster space to screen space + rasterToScreen(x: number, y: number, width: number, height: number, distance: number): Vector3 { + // Convert from raster space to normalized device coordinates (NDC) + const ndcX = (x + 0.5) / width; + const ndcY = (y + 0.5) / height; + + // Convert from NDC to screen space + const screenX = 2 * ndcX - 1; + const screenY = 1 - 2 * ndcY; + + // Return the screen space coordinates as a Vector3 + return [screenX, screenY, distance]; + } + + /* Example usage: + const rasterX = 50; + const rasterY = 50; + const canvasWidth = 100; + const canvasHeight = 100; + + const screenCoords = this.rasterToScreen(this.rasterX, this.rasterY, this.canvasWidth, this.canvasHeight); + console.log(screenCoords); // Output: [0, 0, 0]*/ + + +} \ No newline at end of file diff --git a/src/cg/helper.ts b/src/cg/helper.ts new file mode 100644 index 0000000..6f14633 --- /dev/null +++ b/src/cg/helper.ts @@ -0,0 +1,14 @@ +export function easeOutBounce(x: number): number { + const n1 = 7.5625; + const d1 = 2.75; + + if (x < 1 / d1) { + return n1 * x * x; + } else if (x < 2 / d1) { + return n1 * (x -= 1.5 / d1) * x + 0.75; + } else if (x < 2.5 / d1) { + return n1 * (x -= 2.25 / d1) * x + 0.9375; + } else { + return n1 * (x -= 2.625 / d1) * x + 0.984375; + } + } \ No newline at end of file diff --git a/src/cg/raytracing1.ts b/src/cg/raytracing1.ts index 97636d3..9b44583 100644 --- a/src/cg/raytracing1.ts +++ b/src/cg/raytracing1.ts @@ -4,8 +4,8 @@ import { vecDotProduct, vecMultiplyScalar, vecSubtract } from "./utils"; const pg = new Playground(); const sphere = { - position: [0, .5, -3], //C - Center of sphere - radius: 0.5 + position: [0, .5, -4], //C - Center of sphere + radius: 2 } pg.visCamera(-1); pg.gridXZ() @@ -14,7 +14,7 @@ const o = [0, 0, 0]; //O - Origin const co = vecSubtract(o, sphere.position) // O - C const rsq = sphere.radius * sphere.radius; -const step = 1 / 8 +const step = 1/8 for (let yCoord = -1; yCoord <= 1; yCoord += step) { diff --git a/src/cg/raytracing2.ts b/src/cg/raytracing2.ts new file mode 100644 index 0000000..4fec61b --- /dev/null +++ b/src/cg/raytracing2.ts @@ -0,0 +1,61 @@ +import Framebuffer from "./framebuffer"; +import { ISphere, Vec3, raySphereIntersect } from "./utils"; + +const width = 200 +const height = 200 +const framebuffer = new Framebuffer(width, height); +const imagePlaneDist = -1 + +const tNear = 1; +const tFar = 1000; + +const spheres = [ + { + position: [0, 0, -3],// <--- + radius: 2,// <--- + color: [255,0,0]// <--- + }, + { + position: [3,2,-3],// <--- + radius: 2,// <--- + color: [0,255,0]// <--- + }, + //... add more spheres if you like +] + +const o:Vec3 = [0,0,0]//<--- the camera/viewer origin + +// Loop over framebuffer pixels +for (let x = 0; x <= width; x++) { + for (let y = 0; y <= height; y++) { + + const v = framebuffer.rasterToScreen(x, y, width, height, imagePlaneDist)// <--- convert raster to screen space + + let closestSphere = null; + let closestIntersection = 9999; + + for (let i = 0; i < spheres.length; i++) { + + const [t1, t2] = raySphereIntersect(v, o, spheres[i] as ISphere)// <--- Calculate intersections + + if (t1 < closestIntersection && tNear < t1 && t1 < tFar) { + closestIntersection = t1; + closestSphere = spheres[i] + } + + if (t2 < closestIntersection && tNear < t2 && t2 < tFar) { + closestIntersection = t2; + closestSphere = spheres[i]; + } + } + if (!closestSphere) { + framebuffer.draw(x,y,[0,0,50]) // <--- Draw a background color + } else { + framebuffer.draw(x,y,closestSphere.color) // <--- Draw color of closest sphere + } + + } + +} +framebuffer.update(); +framebuffer.save("spheres."); \ No newline at end of file diff --git a/src/cg/utils.ts b/src/cg/utils.ts index 3441189..1a4beb8 100644 --- a/src/cg/utils.ts +++ b/src/cg/utils.ts @@ -167,6 +167,12 @@ export type Matrix4 = [ number, number, number, number ]; +export interface ISphere{ + position: Array<number>, + radius: number, + color?: Array<number> +} + export function multVec3Matrix4(v: Vec3, m: Matrix4):Vec3 { const v4: Vec4 = [v[0], v[1], v[2], 1] @@ -206,4 +212,22 @@ export function matrix3ToMatrix4(m: Matrix3): Matrix4 { ] return m4; } + +export function raySphereIntersect(v: Vec3, o: Vec3, sphere: ISphere): [number, number] { + const ov = vecSubtract(v, o); // V - O + + const co = vecSubtract(o, sphere.position) // O - C + const rsq = sphere.radius * sphere.radius; + + const a = vecDotProduct(ov, ov); // a = (V - O)*(V _ O) + const b = 2* vecDotProduct(ov, co); // b = 2 * ((V - O)*(O - C)) + const c = vecDotProduct(co, co) - rsq; // c = ((O - C)*(O - C))-r^2 + + const discriminant = (b*b) - (4*a*c); // b^2 - 4*a*c + + const t1 = (-b + Math.sqrt(discriminant)) / (2 * a) + const t2 = (-b - Math.sqrt(discriminant)) / (2 * a) + + return [t1, t2]; +} // \ No newline at end of file -- GitLab