realistic-diamond-shader
v0.1.2
Published
A WebGL2 BVH ray-traced diamond material for Three.js with HDR and EXR environment support.
Maintainers
Readme
realistic-diamond-shader
A WebGL2 diamond material for Three.js. It performs per-pixel BVH ray tracing for internal reflections and refractions, models chromatic dispersion, and samples equirectangular HDR or EXR environment maps.
The package is framework-agnostic: use it with plain Three.js, React Three
Fiber, Vue, or any renderer that gives you a Three.js WebGLRenderer.
Features
- Internal BVH ray tracing through real diamond geometry.
- Chromatic fire, Fresnel reflection, transmission, absorption, and gamma control.
- Runtime controls that update uniforms without recompiling the shader.
- HDR and EXR equirectangular environment-map loading.
- TypeScript declarations and an ESM-only bundle.
- No model files, HDRIs, analytics, network calls, or framework dependency.
Requirements
- Three.js
0.160.xthrough0.189.x. three-mesh-bvh0.9.x.- WebGL2. The shader uses GLSL 3 and integer BVH textures.
Install
npm install realistic-diamond-shader three three-mesh-bvhthree and three-mesh-bvh are peer dependencies. Your application must use
one compatible copy of each rather than bundling a second copy through this package.
Quick start
import * as THREE from 'three';
import {
createDiamondBVH,
createDiamondMaterial,
loadDiamondEnvironment,
setDiamondEnvironmentRotation,
updateDiamondCamera
} from 'realistic-diamond-shader';
const renderer = new THREE.WebGLRenderer();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100);
const environmentMap = await loadDiamondEnvironment('/assets/diamond.exr');
const diamondMaterials = [];
gltf.scene.traverse((mesh) => {
if (!mesh.isMesh || !mesh.name.toLowerCase().includes('diamond')) return;
const { bvh } = createDiamondBVH(mesh.geometry);
const material = createDiamondMaterial({
environmentMap,
bvh,
ior: 2.42,
dispersion: 0.004,
environmentIntensity: 1.5,
rayBounces: 3,
facetSharpness: 1
});
mesh.material = material;
diamondMaterials.push(material);
});
setDiamondEnvironmentRotation(diamondMaterials[0], { x: 0, y: 30, z: 0 });
function render() {
requestAnimationFrame(render);
for (const material of diamondMaterials) updateDiamondCamera(material, camera);
renderer.render(scene, camera);
}
render();API
createDiamondBVH(geometry, options?)
Builds an acceleration structure for one THREE.BufferGeometry and returns
{ boundsTree, bvh }. Pass the returned bvh to createDiamondMaterial.
Keep that object and source geometry alive while the material is rendered. A
second call for the same geometry reuses the prepared GPU BVH data. The default
leaf size is ten triangles, which reduces BVH node and texture fetches for
typical faceted gem meshes.
createDiamondMaterial(options)
Creates a THREE.ShaderMaterial.
createDiamondMaterial({ environmentMap, bvh, ...diamondOptions })environmentMap must be a loaded equirectangular THREE.Texture; bvh must
come from createDiamondBVH.
setDiamondOptions(material, options)
Updates material uniforms in place. This is suitable for UI sliders.
| Option | Default | Accepted values | Meaning |
| --- | ---: | --- | --- |
| ior | 2.42 | >= 1 | Refractive index. |
| dispersion | 0.004 | >= 0 | Chromatic fire strength. |
| rayBounces | 3 | integer, 1–12 | Internal ray bounces. Higher values cost more GPU time. |
| facetSharpness | 1 | 0–1 | Blend between smooth and geometric facet normals. |
| environmentIntensity | 1.5 | >= 0 | Reflection/refraction brightness. |
| gamma | 1.09 | > 0 | Internal-light contrast. |
| color | white | Three.js colour input | Diamond tint. |
| rgbBoost | [2,2,2] | Vector3 or three numbers | Per-channel brilliance gain. |
| reflectivity | 0.46 | 0–1 | Exterior Fresnel contribution. |
| transmission | 0 | 0–1 | Direct refracted environment contribution. |
| absorption | 1 | >= 0 | Beer–Lambert attenuation multiplier. |
| environmentMap | — | THREE.Texture | Replaces the active environment texture. |
setDiamondRendererResolution(renderer, options)
Controls the renderer's drawing-buffer resolution, independently of the diamond material. It is useful when a viewer needs to let its users trade sharpness for GPU cost on lower-end devices.
import { setDiamondRendererResolution } from 'realistic-diamond-shader';
// Fill the browser viewport at a chosen pixel ratio.
setDiamondRendererResolution(renderer, {
mode: 'display',
width: window.innerWidth,
height: window.innerHeight,
scale: 1.25
});
// Or use a fixed drawing buffer, regardless of the display size.
setDiamondRendererResolution(renderer, {
mode: 'custom',
width: 1080,
height: 1080
});display uses width and height as CSS pixels and applies scale as the
renderer pixel ratio. custom renders at the exact supplied pixel dimensions
with a pixel ratio of 1. Values below 1 reduce work further; 1 or 1.25
is a sensible starting point for mobile. Apply a new setting only after a
user changes the control, not every frame.
updateDiamondCamera(material, camera)
Copies the current camera position into the shader. Call it once per material before each render. This is required for correct refraction while the camera or diamond moves.
setDiamondEnvironmentRotation(material, rotation?)
Sets the equirectangular environment orientation in degrees. The default order
is YXZ, matching standard Three.js viewer controls.
setDiamondEnvironmentRotation(material, { x: 0, y: 45, z: 0 });loadDiamondEnvironment(url, options?)
Loads an .exr with EXRLoader or an .hdr with RGBELoader, sets
EquirectangularReflectionMapping, and returns the texture. Set
{ type: 'exr' } or { type: 'hdr' } to override automatic detection.
Environment maps
The material samples the unfiltered equirectangular texture directly. Use a high-dynamic-range map designed for jewellery lighting, and keep ownership or distribution rights for every HDRI/EXR you use. Environment files are not included in this package.
Performance
The optimized ray-tracing mode traces one internal BVH path per diamond fragment. It retains the direct exit, the strongest internal exit, and the terminal reflected path, so the diamond keeps its internal reflections without sampling the HDRI at every bounce. It supports up to 12 bounces; start at 3 and increase only while the target device remains smooth. Limit device pixel ratio on phones, reuse geometries for repeated diamonds, and avoid rebuilding BVHs during animation.
For interactive viewers, render on demand rather than continuously. Choose a fixed device pixel ratio for the viewer session. Changing the WebGL drawing buffer size while the user is dragging can visibly clear the canvas on Safari, so apply resolution changes after interaction instead.
Compatibility and limitations
- The material is ESM-only and does not provide a CommonJS entry point.
- WebGL1 is not supported.
- WebGPU is not supported.
- Non-uniformly scaling a diamond mesh can produce incorrect normal and ray directions; use uniform scale or bake the scale into its geometry.
- This package contains no automatic gem classification. Select diamond meshes in your own model-loading code.
Development
npm ci
npm run check
npm test
npm run build
npm run test:packageSee CONTRIBUTING.md for contribution requirements and SECURITY.md for vulnerability reporting.
Licence
Licensed under Apache-2.0. See NOTICE for attribution.
Apache-2.0 permits commercial use, modification, and redistribution while requiring licence and attribution notices to remain with redistributed copies. It does not grant trademark rights.
