├── .gitignore
├── src
├── assets
│ ├── roots2_rgbd.png
│ └── ruins_rgbd.png
├── style.css
└── index.js
├── package.json
├── index.html
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .parcel-cache
--------------------------------------------------------------------------------
/src/assets/roots2_rgbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thygate/depthmap-viewer-three/HEAD/src/assets/roots2_rgbd.png
--------------------------------------------------------------------------------
/src/assets/ruins_rgbd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thygate/depthmap-viewer-three/HEAD/src/assets/ruins_rgbd.png
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | margin: 0;
4 | }
5 | .dropzone {
6 | box-sizing: border-box;
7 | display: none;
8 | position: fixed;
9 | width: 100%;
10 | height: 100%;
11 | left: 0;
12 | top: 0;
13 | z-index: 99999;
14 | background: rgba(#60a7dc,.8);
15 | border: 11px dashed #60a7dc;
16 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "depthmap-viewer-three",
3 | "version": "1.0.0",
4 | "description": "",
5 | "default": "index.html",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "serve ."
9 | },
10 | "author": "thygate",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "serve": "^14.2.4"
14 | },
15 | "dependencies": {
16 | "three": "^0.146.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | depthmap-viewer-three
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # depthmap-viewer-three
2 |
3 | Simple web-based interactive depthmap viewer. Using [threejs](https://threejs.org/) to render a plane with a displacement map.
4 |
5 | `Drag-and-drop` images with combined-rgb-and-depth-horizontally into the window to view them.
6 |
7 | LIVE at https://thygate.github.io/depthmap-viewer-three
8 |
9 | ## Example input image
10 |
11 | 
12 | >Image was generated with stable diffusion using [AUTOMATIC1111's Stable Diffusion Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) and [stable-diffusion-webui-depthmap-script](https://github.com/thygate/stable-diffusion-webui-depthmap-script).
13 |
14 | ## Running
15 |
16 | The following installs dev and non-dev dependencies, including `three` for
17 | rendering, and `serve` which is used to serve the website locally:
18 |
19 | ```
20 | npm clean-install
21 | npm start
22 | ```
23 |
24 | To install for production without dev dependencies (namely, without `serve`)
25 | run the following then host the files anywhere you want:
26 |
27 | ```
28 | npm clean-install --production
29 | ```
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
3 | import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
4 |
5 | const MyTexture = new URL("./assets/roots2_rgbd.png", import.meta.url);
6 |
7 | let mesh;
8 | let material;
9 | let image_ar;
10 |
11 | const settings = {
12 | metalness: 0.0,
13 | roughness: 0.14,
14 | ambientIntensity: 0.85,
15 | displacementScale: 5,
16 | displacementBias: -0.5,
17 | };
18 |
19 | // init
20 | const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000);
21 | camera.position.z = 3;
22 |
23 | const scene = new THREE.Scene();
24 |
25 | const ambientLight = new THREE.AmbientLight( 0xffffff, 0.5 );
26 | scene.add( ambientLight );
27 |
28 | const pointLight = new THREE.PointLight( 0xff0000, 0.5 );
29 | pointLight.position.z = 2500;
30 | scene.add( pointLight );
31 |
32 |
33 | const renderer = new THREE.WebGLRenderer( { antialias: true } );
34 | renderer.setSize( window.innerWidth, window.innerHeight );
35 | renderer.setAnimationLoop( animation );
36 | //renderer.xr.enabled = true;
37 | renderer.toneMapping = THREE.ACESFilmicToneMapping;
38 | renderer.toneMappingExposure = 1;
39 | renderer.outputEncoding = THREE.sRGBEncoding;
40 | document.body.appendChild( renderer.domElement );
41 |
42 | // animation
43 | function animation( time ) {
44 |
45 | //mesh.rotation.x = time / 2000;
46 | //mesh.rotation.y = time / 1000;
47 |
48 | renderer.render( scene, camera );
49 |
50 | }
51 |
52 | function onWindowResize() {
53 |
54 | const aspect = window.innerWidth / window.innerHeight;
55 | camera.aspect = aspect;
56 | camera.updateProjectionMatrix();
57 |
58 | renderer.setSize( window.innerWidth, window.innerHeight );
59 |
60 | }
61 | window.addEventListener( 'resize', onWindowResize );
62 |
63 |
64 | // orbit controls
65 | const controls = new OrbitControls( camera, renderer.domElement );
66 | controls.enableZoom = true;
67 | controls.enableDamping = true;
68 |
69 |
70 | const image = new Image();
71 | image.onload = function() {
72 |
73 | if (mesh) {
74 | mesh.geometry.dispose();
75 | mesh.material.dispose();
76 | scene.remove( mesh );
77 | }
78 |
79 | image_ar = image.width / image.height / 2;
80 |
81 | const ctx = document.createElement('canvas').getContext('2d');
82 | ctx.canvas.width = image.width / 2;
83 | ctx.canvas.height = image.height;
84 | ctx.drawImage(image, 0, 0, image.width / 2, image.height, 0, 0, image.width / 2, image.height);
85 | const myrgbmap = new THREE.CanvasTexture(ctx.canvas);
86 |
87 | const ctx2 = document.createElement('canvas').getContext('2d');
88 | ctx2.canvas.width = image.width / 2;
89 | ctx2.canvas.height = image.height;
90 | ctx2.drawImage(image, image.width / 2, 0, image.width / 2, image.height, 0, 0, image.width / 2, image.height);
91 | const mydepthmap = new THREE.CanvasTexture(ctx2.canvas);
92 |
93 |
94 | // material
95 | material = new THREE.MeshStandardMaterial( {
96 |
97 | color: 0xaaaaaa,
98 | roughness: settings.roughness,
99 | metalness: settings.metalness,
100 |
101 | map: myrgbmap,
102 |
103 | displacementMap: mydepthmap,
104 | displacementScale: settings.displacementScale,
105 | displacementBias: settings.displacementBias,
106 |
107 | side: THREE.DoubleSide
108 |
109 | } );
110 |
111 | // generating geometry and add mesh to scene
112 | const geometry = new THREE.PlaneGeometry( 10, 10, 512, 512 );
113 | mesh = new THREE.Mesh( geometry, material );
114 | mesh.scale.y = 1.0 / image_ar;
115 | mesh.scale.multiplyScalar( 0.23 );
116 | scene.add( mesh );
117 |
118 | }
119 | image.src = MyTexture;
120 |
121 |
122 | // setup gui
123 | const gui = new GUI();
124 | gui.add( settings, 'metalness' ).min( 0 ).max( 1 ).onChange( function ( value ) {
125 | material.metalness = value;
126 | } );
127 | gui.add( settings, 'roughness' ).min( 0 ).max( 1 ).onChange( function ( value ) {
128 | material.roughness = value;
129 | } );
130 | gui.add( settings, 'ambientIntensity' ).min( 0 ).max( 1 ).onChange( function ( value ) {
131 | ambientLight.intensity = value;
132 | } );
133 | gui.add( settings, 'displacementScale' ).min( 0 ).max( 30.0 ).onChange( function ( value ) {
134 | material.displacementScale = value;
135 | } );
136 | gui.add( settings, 'displacementBias' ).min( -10 ).max( 10 ).onChange( function ( value ) {
137 |
138 | material.displacementBias = value;
139 | } );
140 |
141 |
142 | // setup drop zone
143 | var dropZone = document.getElementById('dropzone');
144 |
145 | function showDropZone() {
146 | dropZone.style.display = "block";
147 | }
148 | function hideDropZone() {
149 | dropZone.style.display = "none";
150 | }
151 |
152 | function allowDrag(e) {
153 | if (true) { // Test that the item being dragged is a valid one
154 | e.dataTransfer.dropEffect = 'copy';
155 | e.preventDefault();
156 | }
157 | }
158 |
159 | function handleDrop(e) {
160 | e.preventDefault();
161 | hideDropZone();
162 | const fileList = event.dataTransfer.files;
163 | if (fileList.length > 0) {
164 | readImage(fileList[0]);
165 | }
166 | }
167 |
168 | function readImage(file) {
169 | const reader = new FileReader();
170 | reader.addEventListener('load', (event) => {
171 | image.src = event.target.result;
172 | });
173 | reader.readAsDataURL(file);
174 | }
175 |
176 | window.addEventListener('dragenter', function(e) {
177 | showDropZone();
178 | });
179 | dropZone.addEventListener('dragenter', allowDrag);
180 | dropZone.addEventListener('dragover', allowDrag);
181 | dropZone.addEventListener('dragleave', function(e) {
182 | console.log('dragleave');
183 | hideDropZone();
184 | });
185 | dropZone.addEventListener('drop', handleDrop);
186 |
187 |
188 | // listen for messages
189 | window.addEventListener('message', function(e) {
190 | if (e.data?.imagedata) {
191 | image.src = e.data.imagedata;
192 | }
193 | });
194 |
--------------------------------------------------------------------------------