mirror of
https://github.com/storytold/spark.git
synced 2026-10-09 00:09:53 +00:00
Splat shader effects (#141)
* Added particle flow transition to examples * Added particle flow transition to examples * Added splat-shader-effects to examples
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Spark • GLSL Shaders</title>
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
canvas { touch-action: none; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "/examples/js/vendor/three/build/three.module.js",
|
||||
"lil-gui": "/examples/js/vendor/lil-gui/dist/lil-gui.esm.js",
|
||||
"@sparkjsdev/spark": "/dist/spark.module.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import * as THREE from "three";
|
||||
import { SparkControls, SplatMesh, dyno } from "@sparkjsdev/spark";
|
||||
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
|
||||
import GUI from "lil-gui";
|
||||
|
||||
// Scene setup
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
// Cat model setup
|
||||
const splatURL = await getAssetFileURL("cat.spz");
|
||||
const cat = new SplatMesh({ url: splatURL });
|
||||
cat.quaternion.set(1, 0, 0, 0);
|
||||
cat.position.set(0, 0, -1.5);
|
||||
cat.scale.set(.5, .5, .5);
|
||||
scene.add(cat);
|
||||
|
||||
const animateT = dyno.dynoFloat(0);
|
||||
|
||||
// Effect parameters and GUI
|
||||
const effectParams = {
|
||||
effect: "Disintegrate",
|
||||
intensity: 0.8
|
||||
};
|
||||
|
||||
const gui = new GUI({ title: "Fractal Effects" });
|
||||
gui.add(effectParams, "effect", ["Electronic", "Deep Meditation", "Waves", "Disintegrate", "Flare"])
|
||||
.name("Effect Type")
|
||||
.onChange(() => cat.updateGenerator());
|
||||
gui.add(effectParams, "intensity", 0, 1, 0.01)
|
||||
.name("Intensity")
|
||||
.onChange(() => cat.updateGenerator());
|
||||
|
||||
// Shader modifier setup
|
||||
cat.objectModifier = dyno.dynoBlock(
|
||||
{ gsplat: dyno.Gsplat },
|
||||
{ gsplat: dyno.Gsplat },
|
||||
({ gsplat }) => {
|
||||
const d = new dyno.Dyno({
|
||||
inTypes: {
|
||||
gsplat: dyno.Gsplat,
|
||||
t: "float",
|
||||
effectType: "int",
|
||||
intensity: "float"
|
||||
},
|
||||
outTypes: { gsplat: dyno.Gsplat },
|
||||
globals: () => [
|
||||
dyno.unindent(`
|
||||
vec3 hash(vec3 p) {
|
||||
return fract(sin(p*123.456)*123.456);
|
||||
}
|
||||
|
||||
mat2 rot(float a) {
|
||||
float s = sin(a), c = cos(a);
|
||||
return mat2(c, -s, s, c);
|
||||
}
|
||||
|
||||
vec3 headMovement(vec3 pos, float t) {
|
||||
pos.xy *= rot(smoothstep(-1., -2., pos.y) * .2 * sin(t*2.));
|
||||
return pos;
|
||||
}
|
||||
|
||||
vec3 breathAnimation(vec3 pos, float t) {
|
||||
float b = sin(t*1.5);
|
||||
pos.yz *= rot(smoothstep(-1., -3., pos.y) * .15 * -b);
|
||||
pos.z += .3;
|
||||
pos.y += 1.2;
|
||||
pos *= 1. + exp(-3. * length(pos)) * b;
|
||||
pos.z -= .3;
|
||||
pos.y -= 1.2;
|
||||
return pos;
|
||||
}
|
||||
|
||||
vec4 fractal1(vec3 pos, float t, float intensity) {
|
||||
float m = 100.;
|
||||
vec3 p = pos * .1;
|
||||
p.y += .5;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
p = abs(p) / clamp(abs(p.x * p.y), 0.3, 3.) - 1.;
|
||||
p.xy *= rot(radians(90.));
|
||||
if (i > 1) m = min(m, length(p.xy) + step(.3, fract(p.z * .5 + t * .5 + float(i) * .2)));
|
||||
}
|
||||
m = step(m, 0.5) * 1.3 * intensity;
|
||||
return vec4(-pos.y * .3, 0.5, 0.7, .3) * intensity + m;
|
||||
}
|
||||
|
||||
vec4 fractal2(vec3 center, vec3 scales, vec4 rgba, float t, float intensity) {
|
||||
vec3 pos = center;
|
||||
float splatSize = length(scales);
|
||||
float pattern = exp(-50. * splatSize);
|
||||
vec3 p = pos * .65;
|
||||
pos.y += 2.;
|
||||
float c = 0.;
|
||||
float l, l2 = length(p);
|
||||
float m = 100.;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
p.xyz = abs(p.xyz) / dot(p.xyz, p.xyz) - .8;
|
||||
l = length(p.xyz);
|
||||
c += exp(-1. * abs(l - l2) * (1. + sin(t * 1.5 + pos.y)));
|
||||
l2 = length(p.xyz);
|
||||
m = min(m, length(p.xyz));
|
||||
}
|
||||
|
||||
c = smoothstep(0.3, 0.5, m + sin(t * 1.5 + pos.y * .5)) + c * .1;
|
||||
return vec4(vec3(length(rgba.rgb)) * vec3(c, c*c, c*c*c) * intensity,
|
||||
rgba.a * exp(-20. * splatSize) * m * intensity);
|
||||
}
|
||||
|
||||
vec4 sin3D(vec3 p, float t) {
|
||||
float m = exp(-2. * length(sin(p * 5. + t * 3.))) * 5.;
|
||||
return vec4(m) + .3;
|
||||
}
|
||||
|
||||
vec4 disintegrate(vec3 pos, float t, float intensity) {
|
||||
vec3 p = pos + (hash(pos) * 2. - 1.) * intensity;
|
||||
float tt = smoothstep(-1., 0.5, -sin(t + -pos.y * .5));
|
||||
p.xz *= rot(tt * 2. + p.y * 2. * tt);
|
||||
return vec4(mix(p, pos, tt), tt);
|
||||
}
|
||||
|
||||
vec4 flare(vec3 pos, float t) {
|
||||
vec3 p = vec3(0., -1.5, 0.);
|
||||
float tt = smoothstep(-1., .5, sin(t + hash(pos).x));
|
||||
tt = tt * tt;
|
||||
p.x += sin(t * 2.) * tt;
|
||||
p.z += sin(t * 2.) * tt;
|
||||
p.y += sin(t) * tt;
|
||||
return vec4(mix(pos, p, tt), tt);
|
||||
}
|
||||
`)
|
||||
],
|
||||
statements: ({ inputs, outputs }) => dyno.unindentLines(`
|
||||
${outputs.gsplat} = ${inputs.gsplat};
|
||||
|
||||
vec3 localPos = ${inputs.gsplat}.center;
|
||||
vec3 splatScales = ${inputs.gsplat}.scales;
|
||||
vec4 splatColor = ${inputs.gsplat}.rgba;
|
||||
|
||||
if (${inputs.effectType} == 1) {
|
||||
${outputs.gsplat}.center = headMovement(localPos, ${inputs.t});
|
||||
vec4 effect1 = fractal1(localPos, ${inputs.t}, ${inputs.intensity});
|
||||
${outputs.gsplat}.rgba.rgba = mix(splatColor, splatColor*effect1, ${inputs.intensity});
|
||||
}
|
||||
else if (${inputs.effectType} == 2) {
|
||||
vec4 effectColor = fractal2(localPos, splatScales, splatColor, ${inputs.t}, ${inputs.intensity});
|
||||
${outputs.gsplat}.rgba.rgba = mix(splatColor, effectColor, ${inputs.intensity});
|
||||
${outputs.gsplat}.center = breathAnimation(localPos, ${inputs.t});
|
||||
}
|
||||
else if (${inputs.effectType} == 3) {
|
||||
vec4 effect = sin3D(localPos, ${inputs.t});
|
||||
${outputs.gsplat}.rgba.rgba = mix(splatColor, splatColor*effect, ${inputs.intensity});
|
||||
vec3 pos = localPos;
|
||||
pos.y += 1.;
|
||||
pos *= (1. + effect.x * .05 * ${inputs.intensity});
|
||||
pos.y -= 1.;
|
||||
${outputs.gsplat}.center = pos;
|
||||
}
|
||||
else if (${inputs.effectType} == 5) {
|
||||
vec4 e = disintegrate(localPos, ${inputs.t}, ${inputs.intensity});
|
||||
${outputs.gsplat}.center = e.xyz;
|
||||
${outputs.gsplat}.scales = mix(vec3(.01, .01, .01), ${inputs.gsplat}.scales, e.w);
|
||||
}
|
||||
else if (${inputs.effectType} == 4) {
|
||||
vec4 e = flare(localPos, ${inputs.t});
|
||||
${outputs.gsplat}.center = e.xyz;
|
||||
${outputs.gsplat}.rgba.rgb = mix(splatColor.rgb, vec3(1.), abs(e.w));
|
||||
${outputs.gsplat}.rgba.a = mix(splatColor.a, 0.3, abs(e.w));
|
||||
}
|
||||
`),
|
||||
});
|
||||
|
||||
// Map effect names to type integers
|
||||
const effectTypeMap = {
|
||||
"Electronic": 1,
|
||||
"Deep Meditation": 2,
|
||||
"Waves": 3,
|
||||
"Flare": 4,
|
||||
"Disintegrate": 5
|
||||
};
|
||||
|
||||
const effectType = effectTypeMap[effectParams.effect] || 1;
|
||||
|
||||
gsplat = d.apply({
|
||||
gsplat,
|
||||
t: animateT,
|
||||
effectType: dyno.dynoInt(effectType),
|
||||
intensity: dyno.dynoFloat(effectParams.intensity)
|
||||
}).gsplat;
|
||||
|
||||
return { gsplat };
|
||||
}
|
||||
);
|
||||
|
||||
cat.updateGenerator();
|
||||
|
||||
// Animation loop
|
||||
const controls = new SparkControls({ canvas: renderer.domElement });
|
||||
renderer.setAnimationLoop(function animate(time) {
|
||||
animateT.value = time / 1000;
|
||||
cat.updateVersion();
|
||||
cat.position.set(0, -.7, -2.5);
|
||||
cat.rotation.set(Math.PI, time / 2000, 0);
|
||||
controls.update(camera);
|
||||
renderer.render(scene, camera);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user