This commit is contained in:
Tom 2025-01-23 14:25:22 +00:00
parent 2e05be2a70
commit 3157992b04
4 changed files with 41 additions and 270 deletions

View File

@ -80,6 +80,23 @@
<script src="/assets/mathjax/tex-mml-svg.js" id="MathJax-script" defer></script>
{% endif %}
{% if page.model_viewer %}
<script async src="/node_modules/es-module-shims/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "/node_modules/three/build/three.module.min.js",
"three/addons/": "/node_modules/three/examples/jsm/",
"lil-gui": "/node_modules/lil-gui/dist/lil-gui.esm.min.js"
}
}
</script>
<script src="/assets/js/outline-model-viewer/index.js" type="module"></script>
{% endif %}
{% if page.load_klipse %}
<link rel="stylesheet" type="text/css" href="/assets/klipse/codemirror.css">
<script src="/assets/klipse/setup_klipse.js" defer></script>

View File

@ -20,25 +20,11 @@ alt: An image of the text "{...}" to suggest the idea of a template.
image_class: invertable # For images that look good when inverted in dark modes by default they're dimmed
mathjax: true
model_viewer: true
load_klipse: true
head: |
<script async src="/node_modules/es-module-shims/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "/node_modules/three/build/three.module.min.js",
"three/addons/": "/node_modules/three/examples/jsm/",
"lil-gui": "/node_modules/lil-gui/dist/lil-gui.esm.min.js"
}
}
</script>
<script src="/assets/js/projects.js" type="module"></script>
<meta name="robots" content="noindex">
---
@ -209,176 +195,7 @@ footnote[^2].
* * *
<canvas style ="width: 100%;" id="myCanvas"></canvas>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { DragControls } from "three/addons/controls/DragControls.js";
let scene, camera, renderer;
let orbitControls, dragControls;
let sphereA, sphereB;
let arrowA, arrowB, arrowCross;
let objectsToDrag = [];
const d = 1;
init();
animate();
function init() {
const canvas = document.getElementById('myCanvas');
// --- Scene ---
scene = new THREE.Scene();
const aspect = canvas.clientWidth / canvas.clientHeight;
camera = new THREE.OrthographicCamera(
-d * aspect, // left
d * aspect, // right
d, // top
-d, // bottom
-100, // near
100 // far
);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
// --- Renderer (use the existing canvas) ---
renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas, antialias: true });
renderer.setSize(canvas.clientWidth, canvas.clientHeight,);
// --- OrbitControls ---
orbitControls = new OrbitControls(camera, renderer.domElement);
// orbitControls.enableRotate = false; // Keep isometric
orbitControls.enablePan = false;
orbitControls.enableDamping = true;
orbitControls.dampingFactor = 0.05;
// const gridHelper = new THREE.GridHelper(5, 25, 0x444444, 0x888888);
// scene.add(gridHelper);
// --- Spheres for vector endpoints (draggable) ---
const sphereGeom = new THREE.SphereGeometry(0.08, 16, 16);
const sphereMat = new THREE.MeshStandardMaterial({ color: 0x000000 });
sphereA = new THREE.Mesh(sphereGeom, sphereMat);
sphereB = new THREE.Mesh(sphereGeom, sphereMat);
// Initial positions
sphereA.position.set(0, 0, 1);
sphereB.position.set(1, 0, 0);
scene.add(sphereA);
scene.add(sphereB);
objectsToDrag.push(sphereA, sphereB);
// --- Lights ---
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
dirLight.position.set(5, 5, 10);
scene.add(dirLight);
// --- Arrows for A, B, and A x B ---
const headLength = 0.1;
const headWidth = 0.1;
arrowA = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0x000000, headLength, headWidth
);
arrowB = new THREE.ArrowHelper(
new THREE.Vector3(0, 1, 0).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0x000000, headLength, headWidth
);
// Cross product arrow in red
arrowCross = new THREE.ArrowHelper(
new THREE.Vector3(0, 0, 1).normalize(),
new THREE.Vector3(0, 0, 0),
1,
0xff0000, headLength, headWidth
);
scene.add(arrowA);
scene.add(arrowB);
scene.add(arrowCross);
// --- DragControls ---
dragControls = new DragControls(objectsToDrag, camera, renderer.domElement);
// Disable orbiting during drag
dragControls.addEventListener('dragstart', function () {
orbitControls.enabled = false;
});
dragControls.addEventListener('dragend', function () {
orbitControls.enabled = true;
});
// Keep spheres in XZ plane, update arrows
dragControls.addEventListener('drag', (event) => {
event.object.position.y = 0;
updateArrows();
});
updateArrows();
window.addEventListener('resize', onWindowResize, false);
}
// Update arrow directions and lengths based on sphere positions
function updateArrows() {
const A = new THREE.Vector3().copy(sphereA.position);
const B = new THREE.Vector3().copy(sphereB.position);
// Update arrow A
const lengthA = A.length();
arrowA.setLength(lengthA, 0.2 * lengthA, 0.2 * lengthA);
arrowA.setDirection(A.clone().normalize());
arrowA.position.set(0, 0, 0);
// Update arrow B
const lengthB = B.length();
arrowB.setLength(lengthB, 0.2 * lengthB, 0.2 * lengthB);
arrowB.setDirection(B.clone().normalize());
arrowB.position.set(0, 0, 0);
// A x B
const cross = new THREE.Vector3().crossVectors(A, B);
const lengthCross = cross.length();
let directionCross = cross.clone().normalize();
// If cross is zero (or close to zero), set a default
if (isNaN(directionCross.x)) {
directionCross.set(0, 0, 1);
}
arrowCross.setDirection(directionCross);
arrowCross.setLength(lengthCross, 0.2 * lengthCross, 0.2 * lengthCross);
arrowCross.position.set(0, 0, 0);
}
function onWindowResize() {
const aspect = canvas.clientWidth / canvas.clientHeight;
camera.left = -d * aspect;
camera.right = d * aspect;
camera.top = d;
camera.bottom = -d;
camera.updateProjectionMatrix();
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
}
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
renderer.render(scene, camera);
}
</script>
<figure class="multiple">
<img src="/assets/blog/alpha_test/original.jpg" class = "no-dim">
@ -388,3 +205,20 @@ function animate() {
<figcaption> Here are some images, (top left) original, (top right) white subtracted and replaced with alpha, (bottom left) same but brightened, (bottom right) ai background removal tool (loses shadow) </figcaption>
</figure>
## 3D models
<figure>
<img class="outline-model-poster no-wc" src = "/assets/projects/bike_lights/thumbnail.svg">
<outline-model-viewer model = "/assets/blog/vector_magnet/vector_magnet.glb" zoom=500 camera='{"position":[3.118,3.203,10.1],"rotation":[-0.3104,0.2858,0.0902],"zoom":428.68750000000136,"target":[0,0,0]}'>
</outline-model-viewer>
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
<figcaption class="has-wc">An interactive point cloud view. Should show a billboard if JS is disabled.</figcaption>
</figure>
<figure>
<img class="no-wc" src="{{page.assets}}/rear_stereo/point_cloud_preview.png">
<point-cloud-viewer model="/assets/blog/heic_depth_map/rear_stereo/pointcloud.pcd" camera = '{"type":"perspective","position":[-3.598,-0.4154,1.971],"rotation":[0.2078,-1.06,0.1819],"zoom":1,"target":[0,0,0]}'>
</point-cloud-viewer>
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
<figcaption class="has-wc">An interactive point cloud view. Should show a billboard if JS is disabled.</figcaption>
</figure>

View File

@ -22,7 +22,6 @@ head: |
}
}
</script>
<script src="/assets/js/projects.js" type="module"></script>
draft: true
---

View File

@ -9,21 +9,7 @@ social_image: /assets/blog/heic_depth_map/thumbnail.png
alt: An image of the text "{...}" to suggest the idea of a template.
head: |
<script async src="/node_modules/es-module-shims/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "/node_modules/three/build/three.module.min.js",
"three/addons/": "/node_modules/three/examples/jsm/",
"lil-gui": "/node_modules/lil-gui/dist/lil-gui.esm.min.js"
}
}
</script>
<script src="/assets/js/projects.js" type="module"></script>
model_viewer: true
---
You know how iPhones do this fake depth of field effect where they blur the background? Did you know that the depth information used to do that effect is stored in the file?
@ -104,74 +90,9 @@ Click and drag to spin me around. It didn't really capture my nose very well, I
<point-cloud-viewer model="/assets/blog/heic_depth_map/rear_stereo/pointcloud.pcd" camera = '{"type":"perspective","position":[-3.598,-0.4154,1.971],"rotation":[0.2078,-1.06,0.1819],"zoom":1,"target":[0,0,0]}'>
</point-cloud-viewer>
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
<figcaption class="has-wc">An interactive point cloud view.</figcaption>
<figcaption class="has-wc">An interactive point cloud view of the depth data from the rear facing camera of my phone.</figcaption>
</figure>
<script type="module">
// import * as THREE from "three";
// import { OrbitControls } from "three/addons/controls/OrbitControls.js";
// import { DragControls } from "three/addons/controls/DragControls.js";
// import { PCDLoader } from 'three/addons/loaders/PCDLoader.js';
// import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
// init('canvas-id-1', '{{page.assets}}/rear_stereo/pointcloud.pcd');
// init('canvas-id-2', '{{page.assets}}/front_facing/pointcloud.pcd');
// function init(canvas_id, url) {
// let render, gui, orbitControls;
// let canvas = document.getElementById(canvas_id);
// const loader = new PCDLoader();
// let scene = new THREE.Scene();
// scene.add( new THREE.AxesHelper( 1 ) );
// loader.load(url, function ( points ) {
// points.geometry.center();
// points.geometry.rotateZ( -Math.PI/2 );
// points.name = 'depth_map';
// scene.add( points );
// points.material.color = new THREE.Color(0x999999);
// points.material.size = 0.001
// render();
// } );
// // --- Scene ---
// const aspect = canvas.clientWidth / canvas.clientHeight;
// let camera = new THREE.PerspectiveCamera( 30, aspect, 0.01, 40 );
// camera.position.set( -2, 2, 3);
// camera.lookAt(0, 0, 0);
// // --- Renderer (use the existing canvas) ---
// let renderer = new THREE.WebGLRenderer({ alpha: true, canvas: canvas, antialias: true });
// renderer.setSize(canvas.clientWidth, canvas.clientHeight,);
// render = () => renderer.render(scene, camera);
// // --- OrbitControls ---
// orbitControls = new OrbitControls(camera, renderer.domElement);
// orbitControls.addEventListener( 'change', render);
// const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
// scene.add(ambientLight);
// const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
// dirLight.position.set(5, 5, 10);
// scene.add(dirLight);
// window.addEventListener('resize', onWindowResize, false);
// function onWindowResize() {
// camera.aspect = canvas.clientWidth / canvas.clientHeight;
// camera.updateProjectionMatrix();
// renderer.setSize(canvas.clientWidth, canvas.clientHeight);
// }
// }
</script>
## Update
After looking a bit more into this, I found the [Apple docs on capturing depth information](https://developer.apple.com/documentation/avfoundation/capturing-photos-with-depth) which explains that for phones with two or more front cameras, they use the difference in the two images to estimate depth, while the front facing camera on modern phones has [an IR camera](https://developer.apple.com/documentation/avfoundation/avcapturedevice/devicetype-swift.struct/builtintruedepthcamera) that uses a grid of dots to estimate true depth like the good old kinect sensor.
@ -181,7 +102,7 @@ So I had a go with the front facing camera too:
<figure class="two-wide">
<img src="{{page.assets}}/front_facing/rgb.png">
<img src="{{page.assets}}/front_facing/depth.png">
<figcaption> A lovely picture of my face and a depth map of it. </figcaption>
<figcaption> A lovely picture of my face and a depth map of it.</figcaption>
</figure>
The depth information, while lower resolution, is much better. My nose really pops in this one!
@ -191,5 +112,5 @@ The depth information, while lower resolution, is much better. My nose really po
<point-cloud-viewer model="/assets/blog/heic_depth_map/front_facing/pointcloud.pcd" camera = '{"type":"perspective","position":[-3.682,0.3606,1.82],"rotation":[-0.1955,-1.104,-0.1751],"zoom":1,"target":[0,0,0]}'>
</point-cloud-viewer>
<figcaption class="no-wc">If you have JS enabled this is interactive.</figcaption>
<figcaption class="has-wc">An interactive point cloud view.</figcaption>
<figcaption class="has-wc">An interactive point cloud view of the depth from the front facing camera of an iPhone.</figcaption>
</figure>