![OpenVDB - Web implementation of volumetric grids](https://private-user-images.githubusercontent.com/9549760/246583801-fb7a04c1-9c69-44c9-addb-f6969b3017c6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjMzMDE2NDMsIm5iZiI6MTcyMzMwMTM0MywicGF0aCI6Ii85NTQ5NzYwLzI0NjU4MzgwMS1mYjdhMDRjMS05YzY5LTQ0YzktYWRkYi1mNjk2OWIzMDE3YzYucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDgxMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA4MTBUMTQ0OTAzWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZmJlOTc5YWY5M2ZjODA4NmUyNzJhZTdhZWRjYzA5YmY0ODRmODg1NTVhMzgzZTA3ZWIyZTVmZGY5ZjgwODcwZiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.VYfc6vkhrpjwLFOX06d2Q8D8OIjrfuB3sIIrFULjoGc)
This project indirectly ports OpenVDB file format and tools to JavaScript, TypeScript, and Node. Specific 3D library implementations can be found in sub-directories.
VDB is a volumetric data format originally implemented in C++ (AcademySoftwareFoundation/openvdb.) It allows to efficiently store voxel structures of this like smoke, fire, and fluid simulations - as well as normal 3D models converted to voxels.
Keep in mind that, due to memory-access and multithreading limitations of JS, this repo may be more similar to a read-only NanoVDB, instead of the original OpenVDB. Code is nevertheless based on original OpenVDB.
import * as Three from 'three';
import * as OpenVDB from 'openvdb/three';
const scene = new Three.Scene();
// NOTE To load an existing VDB model - use VDBLoader:
new OpenVDB.VDBLoader().load('./assets/bunny.vdb', function (vdb) {
const fogVolume = new OpenVDB.FogVolume(vdb, {
resolution: 100,
progressive: true,
steps: 20,
absorbance: 1.0,
baseColor: 0xff00ff,
});
scene.add(fogVolume);
});
// NOTE To create a primitive volumetric shape - use volume converters directly:
const primitiveVolume = new OpenVDB.SphereVolume();
const fogPrimitive = new OpenVDB.FogVolume(primitiveVolume, {
resolution: 50,
progressive: false,
steps: 50,
absorbance: 0.5,
baseColor: 0xff00ff,
});
scene.add(fogPrimitive);
import * as Three from 'three';
import * as OpenVDB from 'openvdb/three';
export const VDBComponent = () => {
const [model, setModel] = useState(new Three.Object3D());
useEffect(() => {
new OpenVDB.VDBLoader().load('./assets/bunny.vdb', function (vdb) {
const fogVolume = new OpenVDB.FogVolume(vdb, {
resolution: 100,
progressive: true,
steps: 20,
absorbance: 1.0,
baseColor: 0xff00ff,
});
setModel(fogVolume);
});
return () => {
fogVolume.dispose();
};
}, []);
return <primitive object={model} />;
};
import * as OpenVDB from 'openvdb';
const vdbReader = await OpenVDB.loadVDB('./assets/bunny.vdb');
vdbReader.grids.forEach(function (grid) {
const voxelValue = grid.getValue({ x: 0.0, y: 0.0, z: 1.0 });
});
loadVDB( url ): Promise<OpenVDB.OpenVDBReader>
Loads OpenVDB file and parses its grids. Grids can be accessed via .grids
property of the value returned from the Promise.
Each grid has a .getValue( position: Three.Vector3 ): float
method, which returns a voxel value for the given position. See src/openvdb/three/VolumeToFog.js
for an example of VDB file parsing.
-
VDBLoader( manager: Three.LoadingManager )
-
VDBLoader.load( url, onLoad, onProgress, onError ): void
Loads VDB model. onLoad is called when VDB is loaded and parsed. onError is called when error happens. onProgress is currently not used.
VolumeToBbox( vdb: OpenVDB.OpenVDBReader | OpenVDB.GridDescriptor | Array<OpenVDB.GridDescriptor> ): Three.Object3D
Converts given OpenVDBReader
to Three.Object3D
. Resulting object consists of wireframe bounding boxes of all internal nodes within the given VDB. If multiple grids are given - each grid is drawn in a different wireframe color.
VolumeToFog( vdb: OpenVDB.OpenVDBReader | OpenVDB.GridDescriptor | Array<OpenVDB.GridDescriptor>, { resolution, steps, progressive, absorbance, opacity, radius, baseColor }, onConverted: () => void, onProgress: ({ convertedVoxels, totalVoxels, convertedGrids, totalGrids }) => void ): Three.Object3D
Converts given OpenVDBReader
to a volumetric Three.Object3D
. onConverted
and onProgress
are called as the VDB is converted to a fog. Most importantly:
resolution: number
- (required) resolution of the resulting 3D texture. Keep in mind this will be risen to the power of 3 - so resolution 200 results in 8 million voxels.steps: number
- (default: 100) detail of the resulting fog volume. High amount of steps combined with large amount of lights in the scene may decrease performance significantly.progressive: boolean
- set totrue
to render the fog on-the-go as it is being parsed, while preserving 60fps.absorbance: number
- (default:1.0
)1.0
means fog absorbs all the light going through it, lower values all light to partially traverse through the fog.densityScale: number
- scales density of the entire grid.densityCutoff: number
- defines sharpness of the shape.opacity: number
- alpha opacity of the fog (doesn't affect light calculations.)baseColor: Three.Color | string | number
- (default:0x000000
) albedo color of the fog volume.emissiveGrid
- VDB emission grid.baseColorGrid
- VDB grid equivalent ofbaseColor
.maskGrid
- static VDB grid mask applied to each voxel. Not affected by UV offset.lights
- bitmask of light types affecting the volume (lights.useDirectionalLights | lights.usePointLights | lights.useSpotLights | lights.useHemisphereLights | lights.useEnvironment
)
VolumeToLevelSet( vdb: OpenVDB.OpenVDBReader | OpenVDB.GridDescriptor | Array<OpenVDB.GridDescriptor>, onConverted: () => void, onProgress: () => void ): Three.Object3D
Not yet finished. Converts the given OpenVDBReader
to a 3D mesh using marching cubes.
CubeVolume(): OpenVDB.GridDescriptor
Generates a 1x1x1 volumetric cube.
SphereVolume(): OpenVDB.GridDescriptor
Generates a 1x1x1 volumetric sphere.
CloudVolume( { height: number, density: number } ): OpenVDB.GridDescriptor
Generates a 1x1x1 volumetric clouds based on noise inputs.
ParametricVolume( valueFunction(position: Three.Vector3) => number ): OpenVDB.GridDescriptor
Generates a 1x1x1 volume with values defined using a valueFunction
. valueFunction
is called with position of each voxel within the volumetric bounding cube - and expects a value between 0.0
and 1.0
to be returned (0.0
meaning given voxel is fully transparent, 1.0
meaning given voxel is fully opaque.)
Based on files hosted on https://www.openvdb.org/download/.
Model | Support | Notes |
---|---|---|
armadillo.vdb | ✅ Yes | |
buddha.vdb | ✅ Yes | |
bunny.vdb | ✅ Yes | |
bunny_cloud.vdb | ✅ Yes | |
crawler.vdb | ❓ Partial | Exceeds memory limits |
cube.vdb | ✅ Yes | |
dragon.vdb | ✅ Yes | |
emu.vdb | ✅ Yes | |
explosion.vdb | ✅ Yes | |
fire.vdb | ✅ Yes | |
icosahedron.vdb | ✅ Yes | |
iss.vdb | ✅ Yes | |
smoke1.vdb | ✅ Yes | |
smoke2.vdb | ✅ Yes | |
space.vdb | ❓ Partial | Exceeds memory limits |
sphere.vdb | ✅ Yes | |
torus.vdb | ✅ Yes | |
torus_knot.vdb | ✅ Yes | |
utahteapot.vdb | ✅ Yes | |
venusstatue.vdb | ✅ Yes | |
boat_points.vdb | ❓ Partial | Loads ok, points rendering not fully implemented |
bunny_points.vdb | ❓ Partial | Loads ok, points rendering not fully implemented |
sphere_points.vdb | ❓ Partial | Loads ok, points rendering not fully implemented |
waterfall_points.vdb | ❓ Partial | Loads ok, points rendering not fully implemented |
git clone https://github.com/mjurczyk/openvdb
cd ./openvdb
npm run i
npm run dev
To avoid re-coding same things over again, parts of the library may be based / taken directly from the following:
- gkjohnson/three-mesh-bvh: BVH implementation
- manzt/numcodecs.js: BLOSC & ZLIB compression
- nodeca/pako: ZLIB compression
Be sure to ⭐️ those!