diff --git a/packages/console/app/src/component/light-rays.tsx b/packages/console/app/src/component/light-rays.tsx index 565deef790..b1c79158cf 100644 --- a/packages/console/app/src/component/light-rays.tsx +++ b/packages/console/app/src/component/light-rays.tsx @@ -1,5 +1,4 @@ import { createSignal, createEffect, onMount, onCleanup, Show, For, Accessor, Setter } from "solid-js" -import { Renderer, Program, Triangle, Mesh } from "ogl" import "./light-rays.css" export type RaysOrigin = @@ -34,9 +33,9 @@ export interface LightRaysConfig { export const defaultConfig: LightRaysConfig = { raysOrigin: "top-center", raysColor: "#ffffff", - raysSpeed: 0.3, + raysSpeed: 1.0, lightSpread: 1.15, - rayLength: 2.75, + rayLength: 4.0, sourceWidth: 0.1, pulsating: true, pulsatingMin: 0.9, @@ -86,37 +85,237 @@ const getAnchorAndDir = ( } } -type Vec2 = [number, number] -type Vec3 = [number, number, number] +interface UniformData { + iTime: number + iResolution: [number, number] + rayPos: [number, number] + rayDir: [number, number] + raysColor: [number, number, number] + raysSpeed: number + lightSpread: number + rayLength: number + sourceWidth: number + pulsating: number + pulsatingMin: number + pulsatingMax: number + fadeDistance: number + saturation: number + mousePos: [number, number] + mouseInfluence: number + noiseAmount: number + distortion: number +} -interface Uniforms { - iTime: { value: number } - iResolution: { value: Vec2 } - rayPos: { value: Vec2 } - rayDir: { value: Vec2 } - raysColor: { value: Vec3 } - raysSpeed: { value: number } - lightSpread: { value: number } - rayLength: { value: number } - sourceWidth: { value: number } - pulsating: { value: number } - pulsatingMin: { value: number } - pulsatingMax: { value: number } - fadeDistance: { value: number } - saturation: { value: number } - mousePos: { value: Vec2 } - mouseInfluence: { value: number } - noiseAmount: { value: number } - distortion: { value: number } +const WGSL_SHADER = ` +struct Uniforms { + iTime: f32, + _pad0: f32, + iResolution: vec2, + rayPos: vec2, + rayDir: vec2, + raysColor: vec3, + raysSpeed: f32, + lightSpread: f32, + rayLength: f32, + sourceWidth: f32, + pulsating: f32, + pulsatingMin: f32, + pulsatingMax: f32, + fadeDistance: f32, + saturation: f32, + mousePos: vec2, + mouseInfluence: f32, + noiseAmount: f32, + distortion: f32, + _pad1: f32, + _pad2: f32, + _pad3: f32, +}; + +@group(0) @binding(0) var uniforms: Uniforms; + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) vUv: vec2, +}; + +@vertex +fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { + // Full-screen triangle + var positions = array, 3>( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0) + ); + + var output: VertexOutput; + let pos = positions[vertexIndex]; + output.position = vec4(pos, 0.0, 1.0); + output.vUv = pos * 0.5 + 0.5; + return output; +} + +fn noise(st: vec2) -> f32 { + return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453123); +} + +fn rayStrength(raySource: vec2, rayRefDirection: vec2, coord: vec2, + seedA: f32, seedB: f32, speed: f32) -> f32 { + let sourceToCoord = coord - raySource; + let dirNorm = normalize(sourceToCoord); + let cosAngle = dot(dirNorm, rayRefDirection); + + let distortedAngle = cosAngle + uniforms.distortion * sin(uniforms.iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2; + + let spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(uniforms.lightSpread, 0.001)); + + let distance = length(sourceToCoord); + let maxDistance = uniforms.iResolution.x * uniforms.rayLength; + let lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0); + + let fadeFalloff = clamp((uniforms.iResolution.x * uniforms.fadeDistance - distance) / (uniforms.iResolution.x * uniforms.fadeDistance), 0.5, 1.0); + let pulseCenter = (uniforms.pulsatingMin + uniforms.pulsatingMax) * 0.5; + let pulseAmplitude = (uniforms.pulsatingMax - uniforms.pulsatingMin) * 0.5; + var pulse: f32; + if (uniforms.pulsating > 0.5) { + pulse = pulseCenter + pulseAmplitude * sin(uniforms.iTime * speed * 3.0); + } else { + pulse = 1.0; + } + + let baseStrength = clamp( + (0.45 + 0.15 * sin(distortedAngle * seedA + uniforms.iTime * speed)) + + (0.3 + 0.2 * cos(-distortedAngle * seedB + uniforms.iTime * speed)), + 0.0, 1.0 + ); + + return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse; +} + +@fragment +fn fragmentMain(@builtin(position) fragCoord: vec4, @location(0) vUv: vec2) -> @location(0) vec4 { + let coord = vec2(fragCoord.x, fragCoord.y); + + let normalizedX = (coord.x / uniforms.iResolution.x) - 0.5; + let widthOffset = -normalizedX * uniforms.sourceWidth * uniforms.iResolution.x; + + let perpDir = vec2(-uniforms.rayDir.y, uniforms.rayDir.x); + let adjustedRayPos = uniforms.rayPos + perpDir * widthOffset; + + var finalRayDir = uniforms.rayDir; + if (uniforms.mouseInfluence > 0.0) { + let mouseScreenPos = uniforms.mousePos * uniforms.iResolution; + let mouseDirection = normalize(mouseScreenPos - adjustedRayPos); + finalRayDir = normalize(mix(uniforms.rayDir, mouseDirection, uniforms.mouseInfluence)); + } + + let rays1 = vec4(1.0) * + rayStrength(adjustedRayPos, finalRayDir, coord, 36.2214, 21.11349, + 1.5 * uniforms.raysSpeed); + let rays2 = vec4(1.0) * + rayStrength(adjustedRayPos, finalRayDir, coord, 22.3991, 18.0234, + 1.1 * uniforms.raysSpeed); + + var fragColor = rays1 * 0.5 + rays2 * 0.4; + + if (uniforms.noiseAmount > 0.0) { + let n = noise(coord * 0.01 + uniforms.iTime * 0.1); + fragColor = vec4(fragColor.rgb * (1.0 - uniforms.noiseAmount + uniforms.noiseAmount * n), fragColor.a); + } + + let brightness = 1.0 - (coord.y / uniforms.iResolution.y); + fragColor.x = fragColor.x * (0.1 + brightness * 0.8); + fragColor.y = fragColor.y * (0.3 + brightness * 0.6); + fragColor.z = fragColor.z * (0.5 + brightness * 0.5); + + if (uniforms.saturation != 1.0) { + let gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114)); + fragColor = vec4(mix(vec3(gray), fragColor.rgb, uniforms.saturation), fragColor.a); + } + + fragColor = vec4(fragColor.rgb * uniforms.raysColor, fragColor.a); + + return fragColor; +} +` + +const UNIFORM_BUFFER_SIZE = 96 + +function createUniformBuffer(data: UniformData): Float32Array { + const buffer = new Float32Array(24) + buffer[0] = data.iTime + buffer[1] = 0 + buffer[2] = data.iResolution[0] + buffer[3] = data.iResolution[1] + buffer[4] = data.rayPos[0] + buffer[5] = data.rayPos[1] + buffer[6] = data.rayDir[0] + buffer[7] = data.rayDir[1] + buffer[8] = data.raysColor[0] + buffer[9] = data.raysColor[1] + buffer[10] = data.raysColor[2] + buffer[11] = data.raysSpeed + buffer[12] = data.lightSpread + buffer[13] = data.rayLength + buffer[14] = data.sourceWidth + buffer[15] = data.pulsating + buffer[16] = data.pulsatingMin + buffer[17] = data.pulsatingMax + buffer[18] = data.fadeDistance + buffer[19] = data.saturation + buffer[20] = data.mousePos[0] + buffer[21] = data.mousePos[1] + buffer[22] = data.mouseInfluence + buffer[23] = data.noiseAmount + return buffer +} + +const UNIFORM_BUFFER_SIZE_CORRECTED = 112 + +function createUniformBufferCorrected(data: UniformData): Float32Array { + const buffer = new Float32Array(28) + buffer[0] = data.iTime + buffer[1] = 0 + buffer[2] = data.iResolution[0] + buffer[3] = data.iResolution[1] + buffer[4] = data.rayPos[0] + buffer[5] = data.rayPos[1] + buffer[6] = data.rayDir[0] + buffer[7] = data.rayDir[1] + buffer[8] = data.raysColor[0] + buffer[9] = data.raysColor[1] + buffer[10] = data.raysColor[2] + buffer[11] = data.raysSpeed + buffer[12] = data.lightSpread + buffer[13] = data.rayLength + buffer[14] = data.sourceWidth + buffer[15] = data.pulsating + buffer[16] = data.pulsatingMin + buffer[17] = data.pulsatingMax + buffer[18] = data.fadeDistance + buffer[19] = data.saturation + buffer[20] = data.mousePos[0] + buffer[21] = data.mousePos[1] + buffer[22] = data.mouseInfluence + buffer[23] = data.noiseAmount + buffer[24] = data.distortion + buffer[25] = 0 + buffer[26] = 0 + buffer[27] = 0 + return buffer } export default function LightRays(props: LightRaysProps) { let containerRef: HTMLDivElement | undefined - let uniformsRef: Uniforms | null = null - let rendererRef: Renderer | null = null - let meshRef: Mesh | null = null + let canvasRef: HTMLCanvasElement | null = null + let deviceRef: GPUDevice | null = null + let contextRef: GPUCanvasContext | null = null + let pipelineRef: GPURenderPipeline | null = null + let uniformBufferRef: GPUBuffer | null = null + let bindGroupRef: GPUBindGroup | null = null let animationIdRef: number | null = null let cleanupFunctionRef: (() => void) | null = null + let uniformDataRef: UniformData | null = null const mouseRef = { x: 0.5, y: 0.5 } const smoothMouseRef = { x: 0.5, y: 0.5 } @@ -153,7 +352,7 @@ export default function LightRays(props: LightRaysProps) { cleanupFunctionRef = null } - const initializeWebGL = async () => { + const initializeWebGPU = async () => { if (!containerRef) { return } @@ -164,199 +363,167 @@ export default function LightRays(props: LightRaysProps) { return } - const renderer = new Renderer({ - dpr: Math.min(window.devicePixelRatio, 2), - alpha: true, - }) - rendererRef = renderer + if (!navigator.gpu) { + console.warn("WebGPU is not supported in this browser") + return + } - const gl = renderer.gl - gl.canvas.style.width = "100%" - gl.canvas.style.height = "100%" + const adapter = await navigator.gpu.requestAdapter() + if (!adapter) { + console.warn("Failed to get WebGPU adapter") + return + } + + const device = await adapter.requestDevice() + deviceRef = device + + const canvas = document.createElement("canvas") + canvas.style.width = "100%" + canvas.style.height = "100%" + canvasRef = canvas while (containerRef.firstChild) { containerRef.removeChild(containerRef.firstChild) } - containerRef.appendChild(gl.canvas) + containerRef.appendChild(canvas) - const vert = ` -attribute vec2 position; -varying vec2 vUv; -void main() { - vUv = position * 0.5 + 0.5; - gl_Position = vec4(position, 0.0, 1.0); -}` - - const frag = `precision highp float; - -uniform float iTime; -uniform vec2 iResolution; - -uniform vec2 rayPos; -uniform vec2 rayDir; -uniform vec3 raysColor; -uniform float raysSpeed; -uniform float lightSpread; -uniform float rayLength; -uniform float sourceWidth; -uniform float pulsating; -uniform float pulsatingMin; -uniform float pulsatingMax; -uniform float fadeDistance; -uniform float saturation; -uniform vec2 mousePos; -uniform float mouseInfluence; -uniform float noiseAmount; -uniform float distortion; - -varying vec2 vUv; - -float noise(vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); -} - -float rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord, - float seedA, float seedB, float speed) { - vec2 sourceToCoord = coord - raySource; - vec2 dirNorm = normalize(sourceToCoord); - float cosAngle = dot(dirNorm, rayRefDirection); - - float distortedAngle = cosAngle + distortion * sin(iTime * 2.0 + length(sourceToCoord) * 0.01) * 0.2; - - float spreadFactor = pow(max(distortedAngle, 0.0), 1.0 / max(lightSpread, 0.001)); - - float distance = length(sourceToCoord); - float maxDistance = iResolution.x * rayLength; - float lengthFalloff = clamp((maxDistance - distance) / maxDistance, 0.0, 1.0); - - float fadeFalloff = clamp((iResolution.x * fadeDistance - distance) / (iResolution.x * fadeDistance), 0.5, 1.0); - float pulseCenter = (pulsatingMin + pulsatingMax) * 0.5; - float pulseAmplitude = (pulsatingMax - pulsatingMin) * 0.5; - float pulse = pulsating > 0.5 ? (pulseCenter + pulseAmplitude * sin(iTime * speed * 3.0)) : 1.0; - - float baseStrength = clamp( - (0.45 + 0.15 * sin(distortedAngle * seedA + iTime * speed)) + - (0.3 + 0.2 * cos(-distortedAngle * seedB + iTime * speed)), - 0.0, 1.0 - ); - - return baseStrength * lengthFalloff * fadeFalloff * spreadFactor * pulse; -} - -void mainImage(out vec4 fragColor, in vec2 fragCoord) { - vec2 coord = vec2(fragCoord.x, iResolution.y - fragCoord.y); - - // Calculate source position offset based on sourceWidth - // Negative offset makes rays spread wider (source moves opposite to pixel position) - float normalizedX = (coord.x / iResolution.x) - 0.5; // -0.5 to 0.5 - float widthOffset = -normalizedX * sourceWidth * iResolution.x; - - // Perpendicular to ray direction for width offset - vec2 perpDir = vec2(-rayDir.y, rayDir.x); - vec2 adjustedRayPos = rayPos + perpDir * widthOffset; - - vec2 finalRayDir = rayDir; - if (mouseInfluence > 0.0) { - vec2 mouseScreenPos = mousePos * iResolution.xy; - vec2 mouseDirection = normalize(mouseScreenPos - adjustedRayPos); - finalRayDir = normalize(mix(rayDir, mouseDirection, mouseInfluence)); - } - - vec4 rays1 = vec4(1.0) * - rayStrength(adjustedRayPos, finalRayDir, coord, 36.2214, 21.11349, - 1.5 * raysSpeed); - vec4 rays2 = vec4(1.0) * - rayStrength(adjustedRayPos, finalRayDir, coord, 22.3991, 18.0234, - 1.1 * raysSpeed); - - fragColor = rays1 * 0.5 + rays2 * 0.4; - - if (noiseAmount > 0.0) { - float n = noise(coord * 0.01 + iTime * 0.1); - fragColor.rgb *= (1.0 - noiseAmount + noiseAmount * n); - } - - float brightness = 1.0 - (coord.y / iResolution.y); - fragColor.x *= 0.1 + brightness * 0.8; - fragColor.y *= 0.3 + brightness * 0.6; - fragColor.z *= 0.5 + brightness * 0.5; - - if (saturation != 1.0) { - float gray = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114)); - fragColor.rgb = mix(vec3(gray), fragColor.rgb, saturation); - } - - fragColor.rgb *= raysColor; -} - -void main() { - vec4 color; - mainImage(color, gl_FragCoord.xy); - gl_FragColor = color; -}` - - const uniforms: Uniforms = { - iTime: { value: 0 }, - iResolution: { value: [1, 1] }, - - rayPos: { value: [0, 0] }, - rayDir: { value: [0, 1] }, - - raysColor: { value: hexToRgb(config.raysColor) }, - raysSpeed: { value: config.raysSpeed }, - lightSpread: { value: config.lightSpread }, - rayLength: { value: config.rayLength }, - sourceWidth: { value: config.sourceWidth }, - pulsating: { value: config.pulsating ? 1.0 : 0.0 }, - pulsatingMin: { value: config.pulsatingMin }, - pulsatingMax: { value: config.pulsatingMax }, - fadeDistance: { value: config.fadeDistance }, - saturation: { value: config.saturation }, - mousePos: { value: [0.5, 0.5] }, - mouseInfluence: { value: config.mouseInfluence }, - noiseAmount: { value: config.noiseAmount }, - distortion: { value: config.distortion }, + const context = canvas.getContext("webgpu") + if (!context) { + console.warn("Failed to get WebGPU context") + return } - uniformsRef = uniforms + contextRef = context - const geometry = new Triangle(gl) - const program = new Program(gl, { - vertex: vert, - fragment: frag, - uniforms, + const presentationFormat = navigator.gpu.getPreferredCanvasFormat() + context.configure({ + device, + format: presentationFormat, + alphaMode: "premultiplied", }) - const mesh = new Mesh(gl, { geometry, program }) - meshRef = mesh + + const shaderModule = device.createShaderModule({ + code: WGSL_SHADER, + }) + + const uniformBuffer = device.createBuffer({ + size: UNIFORM_BUFFER_SIZE_CORRECTED, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }) + uniformBufferRef = uniformBuffer + + const bindGroupLayout = device.createBindGroupLayout({ + entries: [ + { + binding: 0, + visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, + buffer: { type: "uniform" }, + }, + ], + }) + + const bindGroup = device.createBindGroup({ + layout: bindGroupLayout, + entries: [ + { + binding: 0, + resource: { buffer: uniformBuffer }, + }, + ], + }) + bindGroupRef = bindGroup + + const pipelineLayout = device.createPipelineLayout({ + bindGroupLayouts: [bindGroupLayout], + }) + + const pipeline = device.createRenderPipeline({ + layout: pipelineLayout, + vertex: { + module: shaderModule, + entryPoint: "vertexMain", + }, + fragment: { + module: shaderModule, + entryPoint: "fragmentMain", + targets: [ + { + format: presentationFormat, + blend: { + color: { + srcFactor: "src-alpha", + dstFactor: "one-minus-src-alpha", + operation: "add", + }, + alpha: { + srcFactor: "one", + dstFactor: "one-minus-src-alpha", + operation: "add", + }, + }, + }, + ], + }, + primitive: { + topology: "triangle-list", + }, + }) + pipelineRef = pipeline + + const { clientWidth: wCSS, clientHeight: hCSS } = containerRef + const dpr = Math.min(window.devicePixelRatio, 2) + const w = wCSS * dpr + const h = hCSS * dpr + const { anchor, dir } = getAnchorAndDir(config.raysOrigin, w, h) + + uniformDataRef = { + iTime: 0, + iResolution: [w, h], + rayPos: anchor, + rayDir: dir, + raysColor: hexToRgb(config.raysColor), + raysSpeed: config.raysSpeed, + lightSpread: config.lightSpread, + rayLength: config.rayLength, + sourceWidth: config.sourceWidth, + pulsating: config.pulsating ? 1.0 : 0.0, + pulsatingMin: config.pulsatingMin, + pulsatingMax: config.pulsatingMax, + fadeDistance: config.fadeDistance, + saturation: config.saturation, + mousePos: [0.5, 0.5], + mouseInfluence: config.mouseInfluence, + noiseAmount: config.noiseAmount, + distortion: config.distortion, + } const updatePlacement = () => { - if (!containerRef || !renderer) { + if (!containerRef || !canvasRef || !uniformDataRef) { return } - renderer.dpr = Math.min(window.devicePixelRatio, 2) - + const dpr = Math.min(window.devicePixelRatio, 2) const { clientWidth: wCSS, clientHeight: hCSS } = containerRef - renderer.setSize(wCSS, hCSS) + const w = Math.floor(wCSS * dpr) + const h = Math.floor(hCSS * dpr) - const dpr = renderer.dpr - const w = wCSS * dpr - const h = hCSS * dpr + canvasRef.width = w + canvasRef.height = h - uniforms.iResolution.value = [w, h] + uniformDataRef.iResolution = [w, h] const currentConfig = props.config() const { anchor, dir } = getAnchorAndDir(currentConfig.raysOrigin, w, h) - uniforms.rayPos.value = anchor - uniforms.rayDir.value = dir + uniformDataRef.rayPos = anchor + uniformDataRef.rayDir = dir } const loop = (t: number) => { - if (!rendererRef || !uniformsRef || !meshRef) { + if (!deviceRef || !contextRef || !pipelineRef || !uniformBufferRef || !bindGroupRef || !uniformDataRef) { return } const currentConfig = props.config() - uniforms.iTime.value = t * 0.001 + uniformDataRef.iTime = t * 0.001 if (currentConfig.followMouse && currentConfig.mouseInfluence > 0.0) { const smoothing = 0.92 @@ -364,14 +531,38 @@ void main() { smoothMouseRef.x = smoothMouseRef.x * smoothing + mouseRef.x * (1 - smoothing) smoothMouseRef.y = smoothMouseRef.y * smoothing + mouseRef.y * (1 - smoothing) - uniforms.mousePos.value = [smoothMouseRef.x, smoothMouseRef.y] + uniformDataRef.mousePos = [smoothMouseRef.x, smoothMouseRef.y] } try { - renderer.render({ scene: mesh }) + const uniformData = createUniformBufferCorrected(uniformDataRef) + deviceRef.queue.writeBuffer(uniformBufferRef, 0, uniformData) + + const commandEncoder = deviceRef.createCommandEncoder() + + const textureView = contextRef.getCurrentTexture().createView() + + const renderPass = commandEncoder.beginRenderPass({ + colorAttachments: [ + { + view: textureView, + clearValue: { r: 0, g: 0, b: 0, a: 0 }, + loadOp: "clear", + storeOp: "store", + }, + ], + }) + + renderPass.setPipeline(pipelineRef) + renderPass.setBindGroup(0, bindGroupRef) + renderPass.draw(3) + renderPass.end() + + deviceRef.queue.submit([commandEncoder.finish()]) + animationIdRef = requestAnimationFrame(loop) } catch (error) { - console.warn("WebGL rendering error:", error) + console.warn("WebGPU rendering error:", error) return } } @@ -388,29 +579,29 @@ void main() { window.removeEventListener("resize", updatePlacement) - if (renderer) { - try { - const canvas = renderer.gl.canvas - const loseContextExt = renderer.gl.getExtension("WEBGL_lose_context") - if (loseContextExt) { - loseContextExt.loseContext() - } - - if (canvas && canvas.parentNode) { - canvas.parentNode.removeChild(canvas) - } - } catch (error) { - console.warn("Error during WebGL cleanup:", error) - } + if (uniformBufferRef) { + uniformBufferRef.destroy() + uniformBufferRef = null } - rendererRef = null - uniformsRef = null - meshRef = null + if (deviceRef) { + deviceRef.destroy() + deviceRef = null + } + + if (canvasRef && canvasRef.parentNode) { + canvasRef.parentNode.removeChild(canvasRef) + } + + canvasRef = null + contextRef = null + pipelineRef = null + bindGroupRef = null + uniformDataRef = null } } - initializeWebGL() + initializeWebGPU() onCleanup(() => { if (cleanupFunctionRef) { @@ -421,33 +612,31 @@ void main() { }) createEffect(() => { - if (!uniformsRef || !containerRef || !rendererRef) { + if (!uniformDataRef || !containerRef) { return } const config = props.config() - const u = uniformsRef - const renderer = rendererRef - u.raysColor.value = hexToRgb(config.raysColor) - u.raysSpeed.value = config.raysSpeed - u.lightSpread.value = config.lightSpread - u.rayLength.value = config.rayLength - u.sourceWidth.value = config.sourceWidth - u.pulsating.value = config.pulsating ? 1.0 : 0.0 - u.pulsatingMin.value = config.pulsatingMin - u.pulsatingMax.value = config.pulsatingMax - u.fadeDistance.value = config.fadeDistance - u.saturation.value = config.saturation - u.mouseInfluence.value = config.mouseInfluence - u.noiseAmount.value = config.noiseAmount - u.distortion.value = config.distortion + uniformDataRef.raysColor = hexToRgb(config.raysColor) + uniformDataRef.raysSpeed = config.raysSpeed + uniformDataRef.lightSpread = config.lightSpread + uniformDataRef.rayLength = config.rayLength + uniformDataRef.sourceWidth = config.sourceWidth + uniformDataRef.pulsating = config.pulsating ? 1.0 : 0.0 + uniformDataRef.pulsatingMin = config.pulsatingMin + uniformDataRef.pulsatingMax = config.pulsatingMax + uniformDataRef.fadeDistance = config.fadeDistance + uniformDataRef.saturation = config.saturation + uniformDataRef.mouseInfluence = config.mouseInfluence + uniformDataRef.noiseAmount = config.noiseAmount + uniformDataRef.distortion = config.distortion + const dpr = Math.min(window.devicePixelRatio, 2) const { clientWidth: wCSS, clientHeight: hCSS } = containerRef - const dpr = renderer.dpr const { anchor, dir } = getAnchorAndDir(config.raysOrigin, wCSS * dpr, hCSS * dpr) - u.rayPos.value = anchor - u.rayDir.value = dir + uniformDataRef.rayPos = anchor + uniformDataRef.rayDir = dir }) createEffect(() => { @@ -457,7 +646,7 @@ void main() { } const handleMouseMove = (e: MouseEvent) => { - if (!containerRef || !rendererRef) { + if (!containerRef) { return } const rect = containerRef.getBoundingClientRect()