Created
June 19, 2025 19:17
-
-
Save jaykdoe/a03a0ba334860d4cfe7018a003695851 to your computer and use it in GitHub Desktop.
Dithered Blob with Controls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"config":{"codeTheme":"OneDarkPro","pageThemeSyncCodeTheme":true,"openAlmightyConsole":false,"autoRun":false,"layout":"default","keepPreviousLogs":true,"codeFontSize":16},"title":"Dithered Blob with Controls","code":{"HTML":{"language":"html","content":"<script src=\"https://cdn.tailwindcss.com\"></script>\r\n<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap\" rel=\"stylesheet\">\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n\r\n<div id=\"ui-container\" class=\"fixed top-5 right-5 z-50 bg-gray-900/70 backdrop-blur-md p-6 rounded-xl shadow-2xl border border-gray-700/50 transition-all duration-300 ease-in-out w-72\">\r\n <h3 class=\"text-lg font-semibold text-gray-100 mb-5 tracking-wide uppercase\">Dithering Controls</h3>\r\n <div class=\"mb-5\">\r\n <label for=\"dither-pattern\" class=\"block text-sm font-medium text-gray-300 mb-2\">Dither Pattern</label>\r\n <select id=\"dither-pattern\" class=\"w-full bg-gray-800/80 border border-gray-700 text-gray-200 text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block p-2.5 placeholder-gray-400 shadow-sm appearance-none\">\r\n <option value=\"0\">Bayer Matrix (8x8)</option>\r\n <option value=\"1\">Halftone Dots</option>\r\n <option value=\"2\">Line Pattern</option>\r\n <option value=\"3\">Noise Dithering</option>\r\n <option value=\"4\">No Dithering</option>\r\n </select>\r\n </div>\r\n <div class=\"mb-3\">\r\n <label for=\"dither-scale\" class=\"block text-sm font-medium text-gray-300 mb-2\">Dither Scale</label>\r\n <select id=\"dither-scale\" class=\"w-full bg-gray-800/80 border border-gray-700 text-gray-200 text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block p-2.5 placeholder-gray-400 shadow-sm appearance-none\">\r\n <option value=\"1.0\">Fine</option>\r\n <option value=\"1.5\" selected>Medium</option>\r\n <option value=\"2.5\">Coarse</option>\r\n <option value=\"3.5\">Very Coarse</option>\r\n </select>\r\n </div>\r\n</div>"},"CSS":{"language":"css","content":"body {\r\n margin: 0;\r\n overflow: hidden;\r\n font-family: 'Inter', sans-serif;\r\n}\r\ncanvas {\r\n display: block;\r\n width: 100vw;\r\n height: 100vh;\r\n}\r\n::-webkit-scrollbar {\r\n width: 8px;\r\n}\r\n::-webkit-scrollbar-track {\r\n background: rgba(255, 255, 255, 0.1);\r\n border-radius: 10px;\r\n}\r\n::-webkit-scrollbar-thumb {\r\n background: rgba(255, 255, 255, 0.3);\r\n border-radius: 10px;\r\n}\r\n::-webkit-scrollbar-thumb:hover {\r\n background: rgba(255, 255, 255, 0.5);\r\n}\r\nselect {\r\n background-image: url(\"data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23DDDDDD%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E\");\r\n background-repeat: no-repeat;\r\n background-position: right 0.75rem top 50%;\r\n background-size: 0.65em auto;\r\n -webkit-appearance: none;\r\n -moz-appearance: none;\r\n appearance: none;\r\n}","resources":[]},"JS":{"language":"javascript","content":"import * as THREE from \"https://esm.sh/three\";\r\nimport { OrbitControls } from \"https://esm.sh/three/examples/jsm/controls/OrbitControls.js\";\r\nimport { EffectComposer } from \"https://esm.sh/three/examples/jsm/postprocessing/EffectComposer.js\";\r\nimport { RenderPass } from \"https://esm.sh/three/examples/jsm/postprocessing/RenderPass.js\";\r\nimport { UnrealBloomPass } from \"https://esm.sh/three/examples/jsm/postprocessing/UnrealBloomPass.js\";\r\nimport { FilmPass } from \"https://esm.sh/three/examples/jsm/postprocessing/FilmPass.js\";\r\n\r\nconst DITHER_MOTION_SPEED = 2.0;\r\nconst DITHER_MOTION_AMPLITUDE = 1.5;\r\nconst BLOB_BASE_RADIUS = 2.0;\r\nconst BLOB_NOISE_FREQUENCY_VERTEX = 0.75;\r\nconst BLOB_NOISE_AMPLITUDE_VERTEX = 0.65;\r\nconst BLOB_NOISE_SPEED_VERTEX = 0.08;\r\nconst PARTICLE_COUNT = 1200;\r\nconst STAR_COUNT = 3000;\r\n\r\nconst scene = new THREE.Scene();\r\nscene.background = new THREE.Color(0x000000);\r\nscene.fog = new THREE.FogExp2(0x000000, 0.025);\r\n\r\nconst camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);\r\ncamera.position.set(0, 0, 5.0);\r\n\r\nconst renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: \"high-performance\" });\r\nrenderer.setSize(window.innerWidth, window.innerHeight);\r\nrenderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));\r\nrenderer.outputColorSpace = THREE.SRGBColorSpace;\r\ndocument.body.appendChild(renderer.domElement);\r\n\r\nconst composer = new EffectComposer(renderer);\r\nconst renderPass = new RenderPass(scene, camera);\r\ncomposer.addPass(renderPass);\r\n\r\nconst bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.45, 0.55, 0.75);\r\ncomposer.addPass(bloomPass);\r\n\r\nconst filmPass = new FilmPass(0.20, 0.15, 648, false);\r\ncomposer.addPass(filmPass);\r\n\r\nconst controls = new OrbitControls(camera, renderer.domElement);\r\ncontrols.enableDamping = true;\r\ncontrols.dampingFactor = 0.04;\r\ncontrols.rotateSpeed = 0.20;\r\ncontrols.minDistance = 2.0;\r\ncontrols.maxDistance = 12;\r\ncontrols.enablePan = false;\r\ncontrols.autoRotate = false;\r\n\r\nconst ambientLight = new THREE.AmbientLight(0x606070, 0.6);\r\nscene.add(ambientLight);\r\n\r\nconst pointLight1 = new THREE.PointLight(0xffddaa, 0.9, 60);\r\npointLight1.position.set(5, 5, 5);\r\nscene.add(pointLight1);\r\n\r\nconst pointLight2 = new THREE.PointLight(0xaaccff, 0.6, 60);\r\npointLight2.position.set(-5, -3, -4);\r\nscene.add(pointLight2);\r\n\r\nconst pointLight3 = new THREE.PointLight(0xff8844, 0.75, 60);\r\npointLight3.position.set(0, -5, 3);\r\nscene.add(pointLight3);\r\n\r\nconst starGeometry = new THREE.BufferGeometry();\r\nconst starPositions = [];\r\nconst starColors = [];\r\nconst starSizes = [];\r\n\r\nfor (let i = 0; i < STAR_COUNT; i++) {\r\n const x = THREE.MathUtils.randFloatSpread(200);\r\n const y = THREE.MathUtils.randFloatSpread(200);\r\n const z = THREE.MathUtils.randFloatSpread(200);\r\n starPositions.push(x, y, z);\r\n const color = new THREE.Color();\r\n color.setHSL(THREE.MathUtils.randFloat(0.5, 0.7), 0.2, THREE.MathUtils.randFloat(0.3, 0.6));\r\n starColors.push(color.r, color.g, color.b);\r\n starSizes.push(THREE.MathUtils.randFloat(0.5, 1.5));\r\n}\r\nstarGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));\r\nstarGeometry.setAttribute('color', new THREE.Float32BufferAttribute(starColors, 3));\r\nstarGeometry.setAttribute('size', new THREE.Float32BufferAttribute(starSizes, 1));\r\n\r\nconst starMaterial = new THREE.ShaderMaterial({\r\n uniforms: {\r\n uTime: { value: 0.0 },\r\n },\r\n vertexShader: `\r\n uniform float uTime;\r\n attribute float size;\r\n varying vec3 vColor;\r\n varying float vAlpha;\r\n void main() {\r\n vColor = color;\r\n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\r\n gl_PointSize = size * (100.0 / -mvPosition.z) * (sin(position.x * 0.1 + uTime * 0.3) * 0.2 + 0.8);\r\n vAlpha = clamp(1.0 - (-mvPosition.z / 150.0), 0.1, 0.8);\r\n gl_Position = projectionMatrix * mvPosition;\r\n }\r\n `,\r\n fragmentShader: `\r\n uniform float uTime;\r\n varying vec3 vColor;\r\n varying float vAlpha;\r\n void main() {\r\n float dist = length(gl_PointCoord - vec2(0.5));\r\n if (dist > 0.5) discard;\r\n gl_FragColor = vec4(vColor, vAlpha * (0.6 + 0.4 * sin(uTime * 2.0 + gl_FragCoord.x * 0.5)));\r\n }\r\n `,\r\n transparent: true,\r\n blending: THREE.AdditiveBlending,\r\n depthWrite: false,\r\n vertexColors: true\r\n});\r\nconst stars = new THREE.Points(starGeometry, starMaterial);\r\nscene.add(stars);\r\n\r\nconst ditherPatternsFunction = `\r\n const float bayerMatrix[64] = float[64](\r\n 0.0/64.0, 32.0/64.0, 8.0/64.0, 40.0/64.0, 2.0/64.0, 34.0/64.0, 10.0/64.0, 42.0/64.0,\r\n 48.0/64.0, 16.0/64.0, 56.0/64.0, 24.0/64.0, 50.0/64.0, 18.0/64.0, 58.0/64.0, 26.0/64.0,\r\n 12.0/64.0, 44.0/64.0, 4.0/64.0, 36.0/64.0, 14.0/64.0, 46.0/64.0, 6.0/64.0, 38.0/64.0,\r\n 60.0/64.0, 28.0/64.0, 52.0/64.0, 20.0/64.0, 62.0/64.0, 30.0/64.0, 54.0/64.0, 22.0/64.0,\r\n 3.0/64.0, 35.0/64.0, 11.0/64.0, 43.0/64.0, 1.0/64.0, 33.0/64.0, 9.0/64.0, 41.0/64.0,\r\n 51.0/64.0, 19.0/64.0, 59.0/64.0, 27.0/64.0, 49.0/64.0, 17.0/64.0, 57.0/64.0, 25.0/64.0,\r\n 15.0/64.0, 47.0/64.0, 7.0/64.0, 39.0/64.0, 13.0/64.0, 45.0/64.0, 5.0/64.0, 37.0/64.0,\r\n 63.0/64.0, 31.0/64.0, 55.0/64.0, 23.0/64.0, 61.0/64.0, 29.0/64.0, 53.0/64.0, 21.0/64.0\r\n );\r\n\r\n float getBayerValue(vec2 coord) {\r\n int x = int(mod(coord.x, 8.0));\r\n int y = int(mod(coord.y, 8.0));\r\n return bayerMatrix[y * 8 + x];\r\n }\r\n\r\n float getHalftoneValue(vec2 coord, float time) {\r\n vec2 c = vec2(0.5);\r\n coord = mod(coord * 0.1 + vec2(sin(time*0.1)*0.02, cos(time*0.1)*0.02), 1.0);\r\n float d = distance(coord, c);\r\n return smoothstep(0.28, 0.29, d);\r\n }\r\n\r\n float getLinePatternValue(vec2 coord, float time) {\r\n float lw = 0.35 + sin(time*0.15)*0.1;\r\n float p1 = mod(coord.x*0.15+sin(coord.y*0.04+time*0.08)*0.6,1.0);\r\n float p2 = mod(coord.y*0.15+cos(coord.x*0.04+time*0.12)*0.6,1.0);\r\n return max(smoothstep(0.0,lw,p1)*smoothstep(1.0,1.0-lw,p1), smoothstep(0.0,lw,p2)*smoothstep(1.0,1.0-lw,p2));\r\n }\r\n\r\n float getNoiseDitheringValue(vec2 coord, float time) {\r\n return fract(sin(dot(coord + time * 0.05, vec2(12.9898, 78.233))) * 43758.5453);\r\n }\r\n\r\n vec3 ditherMonochrome(vec3 color, vec2 baseScreenPos, float colorLevels, float time,\r\n float motionSpeed, float motionAmplitude, int patternType) {\r\n float luminance = dot(color, vec3(0.299, 0.587, 0.114));\r\n luminance = pow(luminance, 1.2); \r\n luminance = (luminance - 0.5) * 6.0 + 0.5; \r\n luminance = clamp(luminance, 0.0, 1.0);\r\n\r\n vec2 ditherScreenPos = baseScreenPos;\r\n ditherScreenPos.x += sin(time * motionSpeed * 0.75 + baseScreenPos.y * 0.08) * motionAmplitude;\r\n ditherScreenPos.y += cos(time * motionSpeed * 0.55 + baseScreenPos.x * 0.08) * motionAmplitude;\r\n\r\n float threshold = 0.5; \r\n\r\n if (patternType == 0) { \r\n threshold = getBayerValue(ditherScreenPos);\r\n } else if (patternType == 1) { \r\n threshold = getHalftoneValue(ditherScreenPos, time);\r\n } else if (patternType == 2) { \r\n threshold = getLinePatternValue(ditherScreenPos, time);\r\n } else if (patternType == 3) { \r\n threshold = getNoiseDitheringValue(ditherScreenPos, time);\r\n } else if (patternType == 4) { \r\n threshold = 0.5;\r\n }\r\n\r\n float ditheredValue = (luminance < threshold) ? 0.0 : 1.0;\r\n return vec3(ditheredValue);\r\n }\r\n`;\r\n\r\nconst glslRandFunction = `\r\n float rand(vec3 co){ return fract(sin(dot(co, vec3(12.9898,78.233,53.543))) * 43758.5453); }\r\n float snoise(vec3 p) {\r\n vec3 ip = floor(p); vec3 fp = fract(p); fp = fp*fp*(3.0-2.0*fp);\r\n float v000=rand(ip+vec3(0,0,0)); float v100=rand(ip+vec3(1,0,0)); float v010=rand(ip+vec3(0,1,0)); float v110=rand(ip+vec3(1,1,0));\r\n float v001=rand(ip+vec3(0,0,1)); float v101=rand(ip+vec3(1,0,1)); float v011=rand(ip+vec3(0,1,1)); float v111=rand(ip+vec3(1,1,1));\r\n return mix(mix(mix(v000,v100,fp.x),mix(v010,v110,fp.x),fp.y), mix(mix(v001,v101,fp.x),mix(v011,v111,fp.x),fp.y),fp.z);\r\n }\r\n`;\r\n\r\nconst blobMaterial = new THREE.ShaderMaterial({\r\n uniforms: {\r\n uTime: { value: 0 },\r\n ditherScale: { value: 1.5 },\r\n colorLevels: { value: 2.0 },\r\n uDitherMotionSpeed: { value: DITHER_MOTION_SPEED },\r\n uDitherMotionAmplitude: { value: DITHER_MOTION_AMPLITUDE },\r\n uBaseColor: { value: new THREE.Color(0xffffff) },\r\n uFresnelPower: { value: 2.5 },\r\n uVertexNoiseFrequency: { value: BLOB_NOISE_FREQUENCY_VERTEX },\r\n uVertexNoiseAmplitude: { value: BLOB_NOISE_AMPLITUDE_VERTEX },\r\n uVertexNoiseSpeed: { value: BLOB_NOISE_SPEED_VERTEX },\r\n uSurfaceNoiseFrequency: { value: 2.8 },\r\n uSurfaceNoiseAmplitude: { value: 0.22 },\r\n uDitherPattern: { value: 0 },\r\n uCoreBrightness: { value: 0.2 }\r\n },\r\n vertexShader: `\r\n uniform float uTime;\r\n uniform float uVertexNoiseFrequency;\r\n uniform float uVertexNoiseAmplitude;\r\n uniform float uVertexNoiseSpeed;\r\n varying vec3 vNormal;\r\n varying vec3 vViewPosition;\r\n varying vec3 vWorldPosition;\r\n ${glslRandFunction}\r\n void main() {\r\n vec3 pos = position;\r\n float displacement = snoise(pos * uVertexNoiseFrequency + uTime * uVertexNoiseSpeed) * uVertexNoiseAmplitude;\r\n displacement += snoise(pos * uVertexNoiseFrequency * 2.2 + uTime * uVertexNoiseSpeed * 1.4) * (uVertexNoiseAmplitude * 0.45);\r\n pos += normal * displacement;\r\n vec3 offset = vec3(0.01, 0.01, 0.01);\r\n float ddx_noise_orig = snoise((position + offset.xyy) * uVertexNoiseFrequency + uTime * uVertexNoiseSpeed) * uVertexNoiseAmplitude;\r\n ddx_noise_orig += snoise((position + offset.xyy) * uVertexNoiseFrequency * 2.2 + uTime * uVertexNoiseSpeed * 1.4) * (uVertexNoiseAmplitude * 0.45);\r\n vec3 p_ddx = (position + offset.xyy) + normal * ddx_noise_orig;\r\n float ddy_noise_orig = snoise((position + offset.yxy) * uVertexNoiseFrequency + uTime * uVertexNoiseSpeed) * uVertexNoiseAmplitude;\r\n ddy_noise_orig += snoise((position + offset.yxy) * uVertexNoiseFrequency * 2.2 + uTime * uVertexNoiseSpeed * 1.4) * (uVertexNoiseAmplitude * 0.45);\r\n vec3 p_ddy = (position + offset.yxy) + normal * ddy_noise_orig;\r\n vec3 tangent = normalize(p_ddx - pos);\r\n vec3 bitangent = normalize(p_ddy - pos);\r\n vec3 displacedNormal = normalize(cross(tangent, bitangent));\r\n if (length(displacedNormal) < 0.1) { displacedNormal = normal; }\r\n vNormal = normalize(normalMatrix * displacedNormal);\r\n vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);\r\n vViewPosition = -mvPosition.xyz;\r\n vWorldPosition = (modelMatrix * vec4(pos, 1.0)).xyz;\r\n gl_Position = projectionMatrix * mvPosition;\r\n }\r\n `,\r\n fragmentShader: `\r\n uniform float uTime;\r\n uniform float ditherScale;\r\n uniform float colorLevels;\r\n uniform float uDitherMotionSpeed;\r\n uniform float uDitherMotionAmplitude;\r\n uniform vec3 uBaseColor;\r\n uniform float uFresnelPower;\r\n uniform float uSurfaceNoiseFrequency;\r\n uniform float uSurfaceNoiseAmplitude;\r\n uniform int uDitherPattern;\r\n uniform float uCoreBrightness;\r\n varying vec3 vNormal;\r\n varying vec3 vViewPosition;\r\n varying vec3 vWorldPosition;\r\n ${ditherPatternsFunction}\r\n ${glslRandFunction}\r\n void main() {\r\n vec3 normal = normalize(vNormal);\r\n vec3 viewDir = normalize(vViewPosition);\r\n float fresnel = pow(1.0 - abs(dot(viewDir, normal)), uFresnelPower);\r\n fresnel = smoothstep(0.0, 1.0, fresnel) * 0.6 + 0.4;\r\n float rim = pow(1.0 - abs(dot(viewDir, normal)), 10.0);\r\n fresnel += rim * 0.25;\r\n float surfaceNoise1 = snoise(vWorldPosition * uSurfaceNoiseFrequency + vec3(uTime * 0.1, uTime * 0.06, uTime * 0.08));\r\n float surfaceNoise2 = snoise(vWorldPosition * uSurfaceNoiseFrequency * 2.7 + vec3(uTime * 0.15, uTime * 0.1, uTime * -0.04)) * 0.45;\r\n float surfaceNoise = (surfaceNoise1 + surfaceNoise2) * 0.5 + 0.5;\r\n surfaceNoise = surfaceNoise * uSurfaceNoiseAmplitude + (1.0 - uSurfaceNoiseAmplitude * 0.6);\r\n float coreGlow = pow(max(0.0, dot(viewDir, normal)), 2.0) * uCoreBrightness;\r\n float intensity = (fresnel + coreGlow) * surfaceNoise;\r\n intensity = clamp(intensity, 0.02, 1.0);\r\n vec3 finalColor = uBaseColor * intensity;\r\n vec2 screenPos = gl_FragCoord.xy / ditherScale;\r\n vec3 ditheredOutput = ditherMonochrome(finalColor, screenPos, colorLevels, uTime, uDitherMotionSpeed, uDitherMotionAmplitude, uDitherPattern);\r\n gl_FragColor = vec4(ditheredOutput, 1.0);\r\n }\r\n `,\r\n});\r\n\r\nconst blobGeometry = new THREE.SphereGeometry(BLOB_BASE_RADIUS, 128, 128);\r\nconst morphingBlob = new THREE.Mesh(blobGeometry, blobMaterial);\r\nscene.add(morphingBlob);\r\n\r\nconst particleGeometry = new THREE.BufferGeometry();\r\nconst particlePositions = new Float32Array(PARTICLE_COUNT * 3);\r\nconst particleSizes = new Float32Array(PARTICLE_COUNT);\r\nconst particleSpeeds = new Float32Array(PARTICLE_COUNT);\r\n\r\nfor (let i = 0; i < PARTICLE_COUNT; i++) {\r\n const radius = BLOB_BASE_RADIUS * 2.5 + Math.random() * BLOB_BASE_RADIUS * 4;\r\n const theta = Math.random() * Math.PI * 2;\r\n const phi = Math.acos(2 * Math.random() - 1);\r\n particlePositions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);\r\n particlePositions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);\r\n particlePositions[i * 3 + 2] = radius * Math.cos(phi);\r\n particleSizes[i] = Math.random() * 0.06 + 0.015;\r\n particleSpeeds[i] = Math.random() * 0.2 + 0.1;\r\n}\r\nparticleGeometry.setAttribute('position', new THREE.BufferAttribute(particlePositions, 3));\r\nparticleGeometry.setAttribute('size', new THREE.BufferAttribute(particleSizes, 1));\r\nparticleGeometry.setAttribute('speed', new THREE.BufferAttribute(particleSpeeds, 1));\r\n\r\nconst particleMaterial = new THREE.ShaderMaterial({\r\n uniforms: {\r\n uTime: { value: 0 },\r\n uColor: { value: new THREE.Color(0xddddff) },\r\n uBlobBaseRadius: { value: BLOB_BASE_RADIUS }\r\n },\r\n vertexShader: `\r\n uniform float uTime;\r\n uniform float uBlobBaseRadius;\r\n attribute float size;\r\n attribute float speed;\r\n varying float vDistance;\r\n varying float vParticleAlpha;\r\n void main() {\r\n vec3 pos = position;\r\n float waveX = sin(uTime * (speed * 0.8) + position.y * 0.15) * 0.12;\r\n float waveY = cos(uTime * (speed * 1.0) + position.z * 0.20) * 0.12;\r\n float waveZ = sin(uTime * (speed * 0.9) + position.x * 0.18) * 0.12;\r\n pos += vec3(waveX, waveY, waveZ);\r\n vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);\r\n vDistance = length(mvPosition.xyz);\r\n gl_PointSize = size * (400.0 / -mvPosition.z);\r\n vParticleAlpha = smoothstep(uBlobBaseRadius * 6.0, uBlobBaseRadius * 2.0, vDistance);\r\n gl_Position = projectionMatrix * mvPosition;\r\n }\r\n `,\r\n fragmentShader: `\r\n uniform float uTime;\r\n uniform vec3 uColor;\r\n varying float vDistance;\r\n varying float vParticleAlpha;\r\n\r\n void main() {\r\n float dist = length(gl_PointCoord - vec2(0.5));\r\n if (dist > 0.5) discard;\r\n float pulse = 0.4 + 0.6 * abs(sin(uTime * (1.0 + mod(vDistance, 1.0) * 0.5) + vDistance * 0.2));\r\n float finalAlpha = (1.0 - dist * 2.0) * pulse * vParticleAlpha;\r\n finalAlpha = clamp(finalAlpha, 0.0, 0.5);\r\n gl_FragColor = vec4(uColor, finalAlpha * 0.4);\r\n }\r\n `,\r\n transparent: true,\r\n blending: THREE.AdditiveBlending,\r\n depthWrite: false\r\n});\r\nconst particleSystem = new THREE.Points(particleGeometry, particleMaterial);\r\nscene.add(particleSystem);\r\n\r\nconst ditherPatternSelect = document.getElementById('dither-pattern');\r\nconst ditherScaleSelect = document.getElementById('dither-scale');\r\nconst uiContainer = document.getElementById('ui-container');\r\n\r\nditherPatternSelect.addEventListener('change', (e) => {\r\n blobMaterial.uniforms.uDitherPattern.value = parseInt(e.target.value);\r\n});\r\nditherScaleSelect.addEventListener('change', (e) => {\r\n blobMaterial.uniforms.ditherScale.value = parseFloat(e.target.value);\r\n});\r\n\r\nlet uiTimeout;\r\nconst uiAutoHideDelay = 3000;\r\nconst resetUITimeout = () => {\r\n clearTimeout(uiTimeout);\r\n uiContainer.classList.remove('opacity-0', 'translate-x-12');\r\n uiContainer.classList.add('opacity-100', 'translate-x-0');\r\n uiTimeout = setTimeout(() => {\r\n uiContainer.classList.remove('opacity-100', 'translate-x-0');\r\n uiContainer.classList.add('opacity-0', 'translate-x-12');\r\n }, uiAutoHideDelay);\r\n};\r\ndocument.addEventListener('mousemove', resetUITimeout);\r\ndocument.addEventListener('click', resetUITimeout);\r\ndocument.addEventListener('touchstart', resetUITimeout);\r\nresetUITimeout();\r\n\r\nwindow.addEventListener('resize', () => {\r\n camera.aspect = window.innerWidth / window.innerHeight;\r\n camera.updateProjectionMatrix();\r\n renderer.setSize(window.innerWidth, window.innerHeight);\r\n composer.setSize(window.innerWidth, window.innerHeight);\r\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));\r\n bloomPass.resolution.set(window.innerWidth, window.innerHeight);\r\n});\r\n\r\ncamera.lookAt(scene.position);\r\n\r\nconst clock = new THREE.Clock();\r\nfunction animate() {\r\n requestAnimationFrame(animate);\r\n const elapsedTime = clock.getElapsedTime();\r\n\r\n blobMaterial.uniforms.uTime.value = elapsedTime;\r\n particleMaterial.uniforms.uTime.value = elapsedTime;\r\n starMaterial.uniforms.uTime.value = elapsedTime;\r\n\r\n morphingBlob.rotation.x += 0.0004;\r\n morphingBlob.rotation.y += 0.0007;\r\n\r\n pointLight1.position.x = Math.sin(elapsedTime * 0.32) * 6;\r\n pointLight1.position.z = Math.cos(elapsedTime * 0.32) * 6;\r\n pointLight2.position.y = Math.sin(elapsedTime * 0.18) * 4;\r\n pointLight2.position.x = Math.cos(elapsedTime * 0.25) * -5;\r\n pointLight3.position.z = Math.cos(elapsedTime * 0.40) * 5;\r\n pointLight3.position.y = Math.sin(elapsedTime * 0.35) * -4;\r\n\r\n controls.update();\r\n composer.render();\r\n}\r\n\r\nwindow.onload = () => {\r\n animate();\r\n};","resources":[]},"VUE":{"language":"vue2","content":"","resources":[]}}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment