Spherical Harmonics Shader (Three.js Coordinates)
This time using the equations / coordinate system used in THREE.SphericalHarmonics3
by Matt
HTML
<base href="https://rawcdn.githack.com/mrdoob/three.js/r152/examples/" />
<script async src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"lil-gui": "https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.esm.min.js"
}
}
</script>
<template id="vertex-shader">
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</template>
<template id="fragment-shader">
#define WHITE vec3(1., 1., 1.)
// Reference: https://www.shadertoy.com/view/lsfXWH
uniform vec3 colorPos;
uniform vec3 colorNeg;
uniform float f0_0;
uniform float f1_0;
uniform float f1_1;
uniform float f1_2;
uniform float f2_0;
uniform float f2_1;
uniform float f2_2;
uniform float f2_3;
uniform float f2_4;
varying vec3 vPosition;
vec3 color_ramp(float x) {
float step0 = -1.0;
float step1 = 0.0;
float step2 = 1.0;
vec3 color;
color = mix(colorNeg, WHITE, smoothstep(step0, step1, x));
color = mix(color, colorPos, smoothstep(step1, step2, x));
return color;
}
float SH3(vec3 normal, float[9] coeff) {
float target;
float x = normal.x, y = normal.y, z = normal.z;
// band 0
target += coeff[ 0 ] * 0.282095;
// band 1
target += coeff[ 1 ] * 0.488603 * y;
target += coeff[ 2 ] * 0.488603 * z;
target += coeff[ 3 ] * 0.488603 * x;
// band 2
target += coeff[ 4 ] * 1.092548 * ( x * y );
target += coeff[ 5 ] * 1.092548 * ( y * z );
target += coeff[ 6 ] * 0.315392 * ( 3.0 * z * z - 1.0 );
target += coeff[ 7 ] * 1.092548 * ( x * z );
target += coeff[ 8 ] * 0.546274 * ( x * x - y * y );
return target;
}
void main() {
float factor = 0.;
float[9] coeff = float[9](f0_0, f1_0, f1_1, f1_2, f2_0, f2_1, f2_2, f2_3,...
CSS
canvas {
position: fixed;
inset: 0;
}
JavaScript
/**
* Reference: https://www.shadertoy.com/view/lsfXWH
*/
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GUI } from 'lil-gui'
let scene, camera, renderer
let mesh, orbitControls, clock
let params
init().then(animate)
async function init() {
params = {
colorPos: "#ff0000",
colorNeg: "#0000ff",
l0: [0],
l1: [0,0,0],
l2: [0,0,0,0,0],
}
scene = new THREE.Scene()
scene.background = new THREE.Color('black')
camera = new THREE.PerspectiveCamera(75, undefined, 0.1, 1000)
camera.position.set(1, 2, 3)
clock = new THREE.Clock()
const directionalLight = new THREE.DirectionalLight('white')
directionalLight.position.set(2, 3, 4)
scene.add(directionalLight)
const ambientLight = new THREE.AmbientLight('white', 0.3)
scene.add(ambientLight)
renderer = new THREE.WebGLRenderer({ antialias: true })
document.body.appendChild(renderer.domElement)
orbitControls = new OrbitControls(camera, renderer.domElement)
const grid = new THREE.GridHelper()
grid.position.y = -1
scene.add(grid)
const axes = new THREE.AxesHelper(2)
//scene.add(axes)
const vertexShader = document.querySelector("#vertex-shader").content.textContent
const fragmentShader = document.querySelector("#fragment-shader").content.textContent
const uniforms = {
colorPos: { value: new THREE.Color(params.colorPos) },
colorNeg: { value: new THREE.Color(params.colorNeg) },
f0_0: { value: params.l0[0] },
f1_0: { value: params.l1[0] },
f1_1: { value: params.l1[1] },
f1_2: { value: params.l1[2] },
f2_0: { value: params.l2[0] },
f2_1: { value: params.l2[1] },
f2_2: { value: params.l2[2] },
f2_3: { value: params.l2[3] },
f2_4: { value: params.l2[4] },
}
const geometry = new THREE.SphereGeometry(1, 128, 64)
const material = new THREE.ShaderMaterial({
uniforms,
vertexShader,
fragmentShader,
})
mesh = new THREE.Mesh(geometry,...