Skip to content
Snippets Groups Projects
Commit 0e30e3a7 authored by Christoph-Anton Schwierz's avatar Christoph-Anton Schwierz
Browse files

framebuffer animation1 raytracing2

parent f6e8b16d
Branches
No related tags found
No related merge requests found
Showing
with 320 additions and 4 deletions
...@@ -15,6 +15,6 @@ ...@@ -15,6 +15,6 @@
<script type="module" src="/src/cg/perspectiveDivide.ts"></script> <script type="module" src="/src/cg/perspectiveDivide.ts"></script>
<script type="module" src="/src/cg/walkToVec.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/basis.ts"></script-->
<script type="module" src="/src/cg/raytracing1.ts"></script> <script type="module" src="/src/cg/raytracing2.ts"></script>
</body> </body>
</html> </html>
src/cg/animation1 frames/render.frame.0.png

434 B

src/cg/animation1 frames/render.frame.1.png

445 B

src/cg/animation1 frames/render.frame.10.png

427 B

src/cg/animation1 frames/render.frame.2.png

553 B

src/cg/animation1 frames/render.frame.4.png

445 B

src/cg/animation1 frames/render.frame.5.png

488 B

src/cg/animation1 frames/render.frame.6.png

440 B

src/cg/animation1 frames/render.frame.7.png

440 B

src/cg/animation1 frames/render.frame.8.png

444 B

src/cg/animation1 frames/render.frame.9.png

443 B

/*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)
}
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
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
...@@ -4,8 +4,8 @@ import { vecDotProduct, vecMultiplyScalar, vecSubtract } from "./utils"; ...@@ -4,8 +4,8 @@ import { vecDotProduct, vecMultiplyScalar, vecSubtract } from "./utils";
const pg = new Playground(); const pg = new Playground();
const sphere = { const sphere = {
position: [0, .5, -3], //C - Center of sphere position: [0, .5, -4], //C - Center of sphere
radius: 0.5 radius: 2
} }
pg.visCamera(-1); pg.visCamera(-1);
pg.gridXZ() pg.gridXZ()
......
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
...@@ -167,6 +167,12 @@ export type Matrix4 = [ ...@@ -167,6 +167,12 @@ export type Matrix4 = [
number, number, number, number number, number, number, number
]; ];
export interface ISphere{
position: Array<number>,
radius: number,
color?: Array<number>
}
export function multVec3Matrix4(v: Vec3, m: Matrix4):Vec3 { export function multVec3Matrix4(v: Vec3, m: Matrix4):Vec3 {
const v4: Vec4 = [v[0], v[1], v[2], 1] const v4: Vec4 = [v[0], v[1], v[2], 1]
...@@ -206,4 +212,22 @@ export function matrix3ToMatrix4(m: Matrix3): Matrix4 { ...@@ -206,4 +212,22 @@ export function matrix3ToMatrix4(m: Matrix3): Matrix4 {
] ]
return m4; 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment