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