diff --git a/fluid.js b/fluid.js index 4d1e2dd..17528a9 100644 --- a/fluid.js +++ b/fluid.js @@ -1,558 +1,280 @@ "use strict"; - // ===== CONFIGURACIÓN DEL EFECTO ===== const config = { - TEXTURE_DOWNSAMPLE: 1, - DENSITY_DISSIPATION: 0.975, - VELOCITY_DISSIPATION: 0.98, - PRESSURE_DISSIPATION: 0.8, - PRESSURE_ITERATIONS: 20, - CURL: 30, - SPLAT_RADIUS: 0.003 + TEXTURE_DOWNSAMPLE: 1, + DENSITY_DISSIPATION: 0.975, + VELOCITY_DISSIPATION: 0.98, + PRESSURE_DISSIPATION: 0.8, + PRESSURE_ITERATIONS: 20, + CURL: 30, + SPLAT_RADIUS: 0.003 }; // ===== INICIALIZAR CANVAS ===== let canvas = document.getElementById("fluidCanvas"); -console.log("Canvas encontrado:", canvas); if (!canvas) { - console.error("ERROR: No se encontró el canvas con id 'fluidCanvas'"); + console.error("ERROR CRÍTICO: No se encontró en el HTML"); } + canvas.width = window.innerWidth; canvas.height = window.innerHeight; -// ===== CONTEXTO WEBGL ===== let pointers = []; -let splatStack = []; function getWebGLContext(canvas) { - let params = { - alpha: false, - depth: false, - stencil: false, - antialias: false - }; - let gl = canvas.getContext("webgl2", params); - let isWebGL2 = !!gl; - if (!isWebGL2) - gl = - canvas.getContext("webgl", params) || - canvas.getContext("experimental-webgl", params); - let halfFloat = gl.getExtension("OES_texture_half_float"); - let support_linear_float = gl.getExtension("OES_texture_half_float_linear"); - if (isWebGL2) { - gl.getExtension("EXT_color_buffer_float"); - support_linear_float = gl.getExtension("OES_texture_float_linear"); - } - gl.clearColor(0.0, 0.0, 0.0, 1.0); - let internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA; - let internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA; - let formatRG = isWebGL2 ? gl.RG : gl.RGBA; - let texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES; - return { - gl: gl, - ext: { - internalFormat: internalFormat, - internalFormatRG: internalFormatRG, - formatRG: formatRG, - texType: texType - }, - support_linear_float: support_linear_float - }; + let params = { + alpha: true, // ✅ TRANSPARENCIA ACTIVADA + depth: false, + stencil: false, + antialias: false + }; + + // ✅ SIN ESPACIOS DENTRO DE LAS COMILLAS + let gl = canvas.getContext("webgl2", params); + let isWebGL2 = !!gl; + + if (!isWebGL2) { + gl = canvas.getContext("webgl", params) || canvas.getContext("experimental-webgl", params); + } + + let halfFloat = gl.getExtension("OES_texture_half_float"); + let support_linear_float = gl.getExtension("OES_texture_half_float_linear"); + + if (isWebGL2) { + gl.getExtension("EXT_color_buffer_float"); + support_linear_float = gl.getExtension("OES_texture_float_linear"); + } + + // ✅ ÚLTIMO 0 = TRANSPARENCIA TOTAL DEL FONDO + gl.clearColor(0.0, 0.0, 0.0, 0.0); + + let internalFormat = isWebGL2 ? gl.RGBA16F : gl.RGBA; + let internalFormatRG = isWebGL2 ? gl.RG16F : gl.RGBA; + let formatRG = isWebGL2 ? gl.RG : gl.RGBA; + let texType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES; + + return { + gl: gl, + ext: { internalFormat, internalFormatRG, formatRG, texType }, + support_linear_float: support_linear_float + }; } -let _getWebGLContext = getWebGLContext(canvas); -let gl = _getWebGLContext.gl; -let ext = _getWebGLContext.ext; -let support_linear_float = _getWebGLContext.support_linear_float; +let _ctx = getWebGLContext(canvas); +let gl = _ctx.gl; +let ext = _ctx.ext; +let support_linear_float = _ctx.support_linear_float; // ===== POINTER ===== function pointerPrototype() { - this.id = -1; - this.x = 0; - this.y = 0; - this.dx = 0; - this.dy = 0; - this.down = false; - this.moved = false; - this.color = [30, 0, 300]; + this.id = -1; this.x = 0; this.y = 0; this.dx = 0; this.dy = 0; + this.down = false; this.moved = false; this.color = [0.8, 0.2, 0.6]; } pointers.push(new pointerPrototype()); -// ===== SHADERS ===== +// ===== SHADERS (Código WebGL estándar) ===== let GLProgram = (function () { - function GLProgram(vertexShader, fragmentShader) { - if (!(this instanceof GLProgram)) - throw new TypeError("Cannot call a class as a function"); - this.uniforms = {}; - this.program = gl.createProgram(); - gl.attachShader(this.program, vertexShader); - gl.attachShader(this.program, fragmentShader); - gl.linkProgram(this.program); - if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) - throw gl.getProgramInfoLog(this.program); - let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); - for (let i = 0; i < uniformCount; i++) { - let uniformName = gl.getActiveUniform(this.program, i).name; - this.uniforms[uniformName] = gl.getUniformLocation( - this.program, - uniformName - ); + function GLProgram(vertexShader, fragmentShader) { + this.uniforms = {}; + this.program = gl.createProgram(); + gl.attachShader(this.program, vertexShader); + gl.attachShader(this.program, fragmentShader); + gl.linkProgram(this.program); + if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) throw gl.getProgramInfoLog(this.program); + let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); + for (let i = 0; i < uniformCount; i++) { + let uniformName = gl.getActiveUniform(this.program, i).name; + this.uniforms[uniformName] = gl.getUniformLocation(this.program, uniformName); + } } - } - GLProgram.prototype.bind = function bind() { - gl.useProgram(this.program); - }; - return GLProgram; + GLProgram.prototype.bind = function () { gl.useProgram(this.program); }; + return GLProgram; })(); function compileShader(type, source) { - let shader = gl.createShader(type); - gl.shaderSource(shader, source); - gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) - throw gl.getShaderInfoLog(shader); - return shader; + let shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw gl.getShaderInfoLog(shader); + return shader; } -// ===== VERTEX SHADER ===== -let baseVertexShader = compileShader( - gl.VERTEX_SHADER, - "precision highp float; precision mediump sampler2D; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize; void main () { vUv = aPosition * 0.5 + 0.5; vL = vUv - vec2(texelSize.x, 0.0); vR = vUv + vec2(texelSize.x, 0.0); vT = vUv + vec2(0.0, texelSize.y); vB = vUv - vec2(0.0, texelSize.y); gl_Position = vec4(aPosition, 0.0, 1.0); }" -); - -// ===== FRAGMENT SHADERS ===== -let clearShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; uniform float value; void main () { gl_FragColor = value * texture2D(uTexture, vUv); }" -); - -let displayShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; void main () { gl_FragColor = texture2D(uTexture, vUv); }" -); - -let splatShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius; void main () { vec2 p = vUv - point.xy; p.x *= aspectRatio; vec3 splat = exp(-dot(p, p) / radius) * color; vec3 base = texture2D(uTarget, vUv).xyz; gl_FragColor = vec4(base + splat, 1.0); }" -); - -let advectionManualFilteringShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; vec4 bilerp (in sampler2D sam, in vec2 p) { vec4 st; st.xy = floor(p - 0.5) + 0.5; st.zw = st.xy + 1.0; vec4 uv = st * texelSize.xyxy; vec4 a = texture2D(sam, uv.xy); vec4 b = texture2D(sam, uv.zy); vec4 c = texture2D(sam, uv.xw); vec4 d = texture2D(sam, uv.zw); vec2 f = p - st.xy; return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); } void main () { vec2 coord = gl_FragCoord.xy - dt * texture2D(uVelocity, vUv).xy; gl_FragColor = dissipation * bilerp(uSource, coord); gl_FragColor.a = 1.0; }" -); - -let advectionShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; void main () { vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize; gl_FragColor = dissipation * texture2D(uSource, coord); }" -); - -let divergenceShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; vec2 sampleVelocity (in vec2 uv) { vec2 multiplier = vec2(1.0, 1.0); if (uv.x < 0.0) { uv.x = 0.0; multiplier.x = -1.0; } if (uv.x > 1.0) { uv.x = 1.0; multiplier.x = -1.0; } if (uv.y < 0.0) { uv.y = 0.0; multiplier.y = -1.0; } if (uv.y > 1.0) { uv.y = 1.0; multiplier.y = -1.0; } return multiplier * texture2D(uVelocity, uv).xy; } void main () { float L = sampleVelocity(vL).x; float R = sampleVelocity(vR).x; float T = sampleVelocity(vT).y; float B = sampleVelocity(vB).y; float div = 0.5 * (R - L + T - B); gl_FragColor = vec4(div, 0.0, 0.0, 1.0); }" -); - -let curlShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).y; float R = texture2D(uVelocity, vR).y; float T = texture2D(uVelocity, vT).x; float B = texture2D(uVelocity, vB).x; float vorticity = R - L - T + B; gl_FragColor = vec4(vorticity, 0.0, 0.0, 1.0); }" -); - -let vorticityShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt; void main () { float L = texture2D(uCurl, vL).y; float R = texture2D(uCurl, vR).y; float T = texture2D(uCurl, vT).x; float B = texture2D(uCurl, vB).x; float C = texture2D(uCurl, vUv).x; vec2 force = vec2(abs(T) - abs(B), abs(R) - abs(L)); force *= 1.0 / length(force + 0.00001) * curl * C; vec2 vel = texture2D(uVelocity, vUv).xy; gl_FragColor = vec4(vel + force * dt, 0.0, 1.0); }" -); - -let pressureShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; float C = texture2D(uPressure, vUv).x; float divergence = texture2D(uDivergence, vUv).x; float pressure = (L + R + B + T - divergence) * 0.25; gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0); }" -); - -let gradientSubtractShader = compileShader( - gl.FRAGMENT_SHADER, - "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity; vec2 boundary (in vec2 uv) { uv = min(max(uv, 0.0), 1.0); return uv; } void main () { float L = texture2D(uPressure, boundary(vL)).x; float R = texture2D(uPressure, boundary(vR)).x; float T = texture2D(uPressure, boundary(vT)).x; float B = texture2D(uPressure, boundary(vB)).x; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity.xy -= vec2(R - L, T - B); gl_FragColor = vec4(velocity, 0.0, 1.0); }" -); +let baseVertexShader = compileShader(gl.VERTEX_SHADER, "precision highp float; precision mediump sampler2D; attribute vec2 aPosition; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform vec2 texelSize; void main () { vUv = aPosition * 0.5 + 0.5; vL = vUv - vec2(texelSize.x, 0.0); vR = vUv + vec2(texelSize.x, 0.0); vT = vUv + vec2(0.0, texelSize.y); vB = vUv - vec2(0.0, texelSize.y); gl_Position = vec4(aPosition, 0.0, 1.0); }"); +let clearShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; uniform float value; void main () { gl_FragColor = value * texture2D(uTexture, vUv); }"); +let displayShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTexture; void main () { gl_FragColor = texture2D(uTexture, vUv); }"); +let splatShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uTarget; uniform float aspectRatio; uniform vec3 color; uniform vec2 point; uniform float radius; void main () { vec2 p = vUv - point.xy; p.x *= aspectRatio; vec3 splat = exp(-dot(p, p) / radius) * color; vec3 base = texture2D(uTarget, vUv).xyz; gl_FragColor = vec4(base + splat, 1.0); }"); +let advectionShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; uniform sampler2D uVelocity; uniform sampler2D uSource; uniform vec2 texelSize; uniform float dt; uniform float dissipation; void main () { vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize; gl_FragColor = dissipation * texture2D(uSource, coord); }"); +let divergenceShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).x; float R = texture2D(uVelocity, vR).x; float T = texture2D(uVelocity, vT).y; float B = texture2D(uVelocity, vB).y; float div = 0.5 * (R - L + T - B); gl_FragColor = vec4(div, 0.0, 0.0, 1.0); }"); +let curlShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; void main () { float L = texture2D(uVelocity, vL).y; float R = texture2D(uVelocity, vR).y; float T = texture2D(uVelocity, vT).x; float B = texture2D(uVelocity, vB).x; float vorticity = R - L - T + B; gl_FragColor = vec4(vorticity, 0.0, 0.0, 1.0); }"); +let vorticityShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uVelocity; uniform sampler2D uCurl; uniform float curl; uniform float dt; void main () { float L = texture2D(uCurl, vL).x; float R = texture2D(uCurl, vR).x; float T = texture2D(uCurl, vT).y; float B = texture2D(uCurl, vB).y; float C = texture2D(uCurl, vUv).x; vec2 force = vec2(abs(T) - abs(B), abs(R) - abs(L)); force *= 1.0 / length(force + 0.00001) * curl * C; vec2 vel = texture2D(uVelocity, vUv).xy; gl_FragColor = vec4(vel + force * dt, 0.0, 1.0); }"); +let pressureShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uDivergence; void main () { float L = texture2D(uPressure, vL).x; float R = texture2D(uPressure, vR).x; float T = texture2D(uPressure, vT).x; float B = texture2D(uPressure, vB).x; float C = texture2D(uPressure, vUv).x; float divergence = texture2D(uDivergence, vUv).x; float pressure = (L + R + B + T - divergence) * 0.25; gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0); }"); +let gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, "precision highp float; precision mediump sampler2D; varying vec2 vUv; varying vec2 vL; varying vec2 vR; varying vec2 vT; varying vec2 vB; uniform sampler2D uPressure; uniform sampler2D uVelocity; void main () { float L = texture2D(uPressure, vL).x; float R = texture2D(uPressure, vR).x; float T = texture2D(uPressure, vT).x; float B = texture2D(uPressure, vB).x; vec2 velocity = texture2D(uVelocity, vUv).xy; velocity.xy -= vec2(R - L, T - B); gl_FragColor = vec4(velocity, 0.0, 1.0); }"); // ===== FRAMEBUFFERS ===== -let textureWidth, textureHeight; -let density, velocity, divergence, curl, pressure; - +let textureWidth, textureHeight, density, velocity, divergence, curl, pressure; function initFramebuffers() { - textureWidth = gl.drawingBufferWidth >> config.TEXTURE_DOWNSAMPLE; - textureHeight = gl.drawingBufferHeight >> config.TEXTURE_DOWNSAMPLE; - let iFormat = ext.internalFormat; - let iFormatRG = ext.internalFormatRG; - let formatRG = ext.formatRG; - let texType = ext.texType; - density = createDoubleFBO( - 0, - textureWidth, - textureHeight, - iFormat, - gl.RGBA, - texType, - support_linear_float ? gl.LINEAR : gl.NEAREST - ); - velocity = createDoubleFBO( - 2, - textureWidth, - textureHeight, - iFormatRG, - formatRG, - texType, - support_linear_float ? gl.LINEAR : gl.NEAREST - ); - divergence = createFBO( - 4, - textureWidth, - textureHeight, - iFormatRG, - formatRG, - texType, - gl.NEAREST - ); - curl = createFBO( - 5, - textureWidth, - textureHeight, - iFormatRG, - formatRG, - texType, - gl.NEAREST - ); - pressure = createDoubleFBO( - 6, - textureWidth, - textureHeight, - iFormatRG, - formatRG, - texType, - gl.NEAREST - ); + textureWidth = gl.drawingBufferWidth >> config.TEXTURE_DOWNSAMPLE; + textureHeight = gl.drawingBufferHeight >> config.TEXTURE_DOWNSAMPLE; + let iFormat = ext.internalFormat, iFormatRG = ext.internalFormatRG, formatRG = ext.formatRG, texType = ext.texType; + density = createDoubleFBO(0, textureWidth, textureHeight, iFormat, gl.RGBA, texType, support_linear_float ? gl.LINEAR : gl.NEAREST); + velocity = createDoubleFBO(2, textureWidth, textureHeight, iFormatRG, formatRG, texType, support_linear_float ? gl.LINEAR : gl.NEAREST); + divergence = createFBO(4, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST); + curl = createFBO(5, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST); + pressure = createDoubleFBO(6, textureWidth, textureHeight, iFormatRG, formatRG, texType, gl.NEAREST); } - function createFBO(texId, w, h, internalFormat, format, type, param) { - gl.activeTexture(gl.TEXTURE0 + texId); - let texture = gl.createTexture(); - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null); - let fbo = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); - gl.framebufferTexture2D( - gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0, - gl.TEXTURE_2D, - texture, - 0 - ); - gl.viewport(0, 0, w, h); - gl.clear(gl.COLOR_BUFFER_BIT); - return [texture, fbo, texId]; + gl.activeTexture(gl.TEXTURE0 + texId); + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null); + let fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.viewport(0, 0, w, h); + gl.clear(gl.COLOR_BUFFER_BIT); + return [texture, fbo, texId]; } - function createDoubleFBO(texId, w, h, internalFormat, format, type, param) { - let fbo1 = createFBO(texId, w, h, internalFormat, format, type, param); - let fbo2 = createFBO(texId + 1, w, h, internalFormat, format, type, param); - return { - get first() { - return fbo1; - }, - get second() { - return fbo2; - }, - swap: function swap() { - let temp = fbo1; - fbo1 = fbo2; - fbo2 = temp; - } - }; + let fbo1 = createFBO(texId, w, h, internalFormat, format, type, param); + let fbo2 = createFBO(texId + 1, w, h, internalFormat, format, type, param); + return { + get first() { return fbo1; }, + get second() { return fbo2; }, + swap: function () { let temp = fbo1; fbo1 = fbo2; fbo2 = temp; } + }; } - initFramebuffers(); -// ===== PROGRAMS ===== +// ===== PROGRAMS & BLIT ===== let clearProgram = new GLProgram(baseVertexShader, clearShader); let displayProgram = new GLProgram(baseVertexShader, displayShader); let splatProgram = new GLProgram(baseVertexShader, splatShader); -let advectionProgram = new GLProgram( - baseVertexShader, - support_linear_float ? advectionShader : advectionManualFilteringShader -); +let advectionProgram = new GLProgram(baseVertexShader, advectionShader); let divergenceProgram = new GLProgram(baseVertexShader, divergenceShader); let curlProgram = new GLProgram(baseVertexShader, curlShader); let vorticityProgram = new GLProgram(baseVertexShader, vorticityShader); let pressureProgram = new GLProgram(baseVertexShader, pressureShader); -let gradienSubtractProgram = new GLProgram( - baseVertexShader, - gradientSubtractShader -); +let gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader); -// ===== BLIT ===== let blit = (function () { - gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); - gl.bufferData( - gl.ARRAY_BUFFER, - new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), - gl.STATIC_DRAW - ); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); - gl.bufferData( - gl.ELEMENT_ARRAY_BUFFER, - new Uint16Array([0, 1, 2, 0, 2, 3]), - gl.STATIC_DRAW - ); - gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(0); - return function (destination) { - gl.bindFramebuffer(gl.FRAMEBUFFER, destination); - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); - }; + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + return function (destination) { + gl.bindFramebuffer(gl.FRAMEBUFFER, destination); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + }; })(); -// ===== SPLAT ===== function splat(x, y, dx, dy, color) { - splatProgram.bind(); - gl.uniform1i(splatProgram.uniforms.uTarget, velocity.first[2]); - gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height); - gl.uniform2f( - splatProgram.uniforms.point, - x / canvas.width, - 1.0 - y / canvas.height - ); - gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0); - gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS); - blit(velocity.second[1]); - velocity.swap(); - gl.uniform1i(splatProgram.uniforms.uTarget, density.first[2]); - gl.uniform3f( - splatProgram.uniforms.color, - color[0] * 0.3, - color[1] * 0.3, - color[2] * 0.3 - ); - blit(density.second[1]); - density.swap(); + splatProgram.bind(); + gl.uniform1i(splatProgram.uniforms.uTarget, velocity.first[2]); + gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height); + gl.uniform2f(splatProgram.uniforms.point, x / canvas.width, 1.0 - y / canvas.height); + gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0); + gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS); + blit(velocity.second[1]); velocity.swap(); + gl.uniform1i(splatProgram.uniforms.uTarget, density.first[2]); + gl.uniform3f(splatProgram.uniforms.color, color[0] * 0.3, color[1] * 0.3, color[2] * 0.3); + blit(density.second[1]); density.swap(); } -// ===== RESIZE ===== -function resizeCanvas() { - (canvas.width !== canvas.clientWidth || - canvas.height !== canvas.clientHeight) && - ((canvas.width = canvas.clientWidth), - (canvas.height = canvas.clientHeight), - initFramebuffers()); -} - -// ===== COLORES DE LA MARCA CON TRANSICIÓN ===== -const brandColors = [ - [0.874, 0.741, 0.725], // Rosa (#DFBDB9) - [0.729, 0.071, 0.302], // Bordo (#BA124D) - [0.000, 0.247, 0.322] // Teal (#003F52) -]; - -let currentColor = brandColors[0]; -let targetColor = brandColors[1]; -let transitionProgress = 0; +// ===== COLORES DE LA MARCA ===== +const brandColors = [[0.874, 0.741, 0.725], [0.729, 0.071, 0.302], [0.000, 0.247, 0.322]]; +let currentColor = brandColors[0], targetColor = brandColors[1], transitionProgress = 0; const transitionSpeed = 0.008; +function lerpColor(a, b, t) { return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t]; } +let colorArr = currentColor, count = 0; -function lerpColor(colorA, colorB, t) { - return [ - colorA[0] + (colorB[0] - colorA[0]) * t, - colorA[1] + (colorB[1] - colorA[1]) * t, - colorA[2] + (colorB[2] - colorA[2]) * t - ]; -} - -let colorArr = currentColor; -let count = 0; - -// ===== EVENTOS CON COLORES DE MARCA ===== +// ===== EVENTOS ===== canvas.addEventListener("mousemove", function (e) { - console.log("Mouse moviéndose:", e.clientX, e.clientY); - count++; - if (count > 1) { - transitionProgress += transitionSpeed; - if (transitionProgress >= 1) { - transitionProgress = 0; - let currentIndex = brandColors.indexOf(currentColor); - let nextIndex = (currentIndex + 1) % brandColors.length; - currentColor = targetColor; - targetColor = brandColors[nextIndex]; - } - colorArr = lerpColor(currentColor, targetColor, transitionProgress); - count = 0; - } - - pointers[0].down = true; - pointers[0].color = colorArr; - pointers[0].moved = pointers[0].down; - pointers[0].dx = (e.offsetX - pointers[0].x) * 10.0; - pointers[0].dy = (e.offsetY - pointers[0].y) * 10.0; - pointers[0].x = e.offsetX; - pointers[0].y = e.offsetY; -}); - -canvas.addEventListener( - "touchmove", - function (e) { - e.preventDefault(); - let touches = e.targetTouches; count++; - if (count > 20) { - transitionProgress += transitionSpeed; - if (transitionProgress >= 1) { - transitionProgress = 0; - let currentIndex = brandColors.indexOf(currentColor); - let nextIndex = (currentIndex + 1) % brandColors.length; - currentColor = targetColor; - targetColor = brandColors[nextIndex]; - } - colorArr = lerpColor(currentColor, targetColor, transitionProgress); - count = 0; + if (count > 1) { + transitionProgress += transitionSpeed; + if (transitionProgress >= 1) { + transitionProgress = 0; + let currentIndex = brandColors.indexOf(currentColor); + currentColor = targetColor; + targetColor = brandColors[(currentIndex + 1) % brandColors.length]; + } + colorArr = lerpColor(currentColor, targetColor, transitionProgress); + count = 0; } - - for (let i = 0, len = touches.length; i < len; i++) { - if (i >= pointers.length) pointers.push(new pointerPrototype()); - pointers[i].id = touches[i].identifier; - pointers[i].down = true; - pointers[i].x = touches[i].pageX; - pointers[i].y = touches[i].pageY; - pointers[i].color = colorArr; - let pointer = pointers[i]; - pointer.moved = pointer.down; - pointer.dx = (touches[i].pageX - pointer.x) * 10.0; - pointer.dy = (touches[i].pageY - pointer.y) * 10.0; - pointer.x = touches[i].pageX; - pointer.y = touches[i].pageY; - } - }, - false -); + pointers[0].down = true; pointers[0].color = colorArr; pointers[0].moved = true; + pointers[0].dx = (e.offsetX - pointers[0].x) * 10.0; + pointers[0].dy = (e.offsetY - pointers[0].y) * 10.0; + pointers[0].x = e.offsetX; pointers[0].y = e.offsetY; +}); // ===== LOOP PRINCIPAL ===== let lastTime = Date.now(); - function update() { - resizeCanvas(); - let dt = Math.min((Date.now() - lastTime) / 1000, 0.016); - lastTime = Date.now(); - gl.viewport(0, 0, textureWidth, textureHeight); - - advectionProgram.bind(); - gl.uniform2f( - advectionProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); - gl.uniform1i(advectionProgram.uniforms.uSource, velocity.first[2]); - gl.uniform1f(advectionProgram.uniforms.dt, dt); - gl.uniform1f( - advectionProgram.uniforms.dissipation, - config.VELOCITY_DISSIPATION - ); - blit(velocity.second[1]); - velocity.swap(); - - gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); - gl.uniform1i(advectionProgram.uniforms.uSource, density.first[2]); - gl.uniform1f( - advectionProgram.uniforms.dissipation, - config.DENSITY_DISSIPATION - ); - blit(density.second[1]); - density.swap(); - - for (let i = 0, len = pointers.length; i < len; i++) { - let pointer = pointers[i]; - if (pointer.moved) { - splat(pointer.x, pointer.y, pointer.dx, pointer.dy, pointer.color); - pointer.moved = false; + if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) { + canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; + initFramebuffers(); } - } - - curlProgram.bind(); - gl.uniform2f( - curlProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.first[2]); - blit(curl[1]); - - vorticityProgram.bind(); - gl.uniform2f( - vorticityProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.first[2]); - gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]); - gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL); - gl.uniform1f(vorticityProgram.uniforms.dt, dt); - blit(velocity.second[1]); - velocity.swap(); - - divergenceProgram.bind(); - gl.uniform2f( - divergenceProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.first[2]); - blit(divergence[1]); - - clearProgram.bind(); - let pressureTexId = pressure.first[2]; - gl.activeTexture(gl.TEXTURE0 + pressureTexId); - gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); - gl.uniform1i(clearProgram.uniforms.uTexture, pressureTexId); - gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION); - blit(pressure.second[1]); - pressure.swap(); - - pressureProgram.bind(); - gl.uniform2f( - pressureProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]); - pressureTexId = pressure.first[2]; - gl.activeTexture(gl.TEXTURE0 + pressureTexId); - for (let _i = 0; _i < config.PRESSURE_ITERATIONS; _i++) { - gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); - gl.uniform1i(pressureProgram.uniforms.uPressure, pressureTexId); - blit(pressure.second[1]); - pressure.swap(); - } - - gradienSubtractProgram.bind(); - gl.uniform2f( - gradienSubtractProgram.uniforms.texelSize, - 1.0 / textureWidth, - 1.0 / textureHeight - ); - gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.first[2]); - gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.first[2]); - blit(velocity.second[1]); - velocity.swap(); - - gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); - displayProgram.bind(); - gl.uniform1i(displayProgram.uniforms.uTexture, density.first[2]); - blit(null); - - requestAnimationFrame(update); + let dt = Math.min((Date.now() - lastTime) / 1000, 0.016); + lastTime = Date.now(); + gl.viewport(0, 0, textureWidth, textureHeight); + + advectionProgram.bind(); + gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.first[2]); + gl.uniform1i(advectionProgram.uniforms.uSource, velocity.first[2]); + gl.uniform1f(advectionProgram.uniforms.dt, dt); + gl.uniform1f(advectionProgram.uniforms.dissipation, config.VELOCITY_DISSIPATION); + blit(velocity.second[1]); velocity.swap(); + gl.uniform1i(advectionProgram.uniforms.uSource, density.first[2]); + gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION); + blit(density.second[1]); density.swap(); + + for (let i = 0; i < pointers.length; i++) { + if (pointers[i].moved) { splat(pointers[i].x, pointers[i].y, pointers[i].dx, pointers[i].dy, pointers[i].color); pointers[i].moved = false; } + } + + curlProgram.bind(); gl.uniform2f(curlProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.first[2]); blit(curl[1]); + + vorticityProgram.bind(); gl.uniform2f(vorticityProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.first[2]); gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]); + gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL); gl.uniform1f(vorticityProgram.uniforms.dt, dt); + blit(velocity.second[1]); velocity.swap(); + + divergenceProgram.bind(); gl.uniform2f(divergenceProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.first[2]); blit(divergence[1]); + + clearProgram.bind(); + gl.activeTexture(gl.TEXTURE0 + pressure.first[2]); gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); + gl.uniform1i(clearProgram.uniforms.uTexture, pressure.first[2]); gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION); + blit(pressure.second[1]); pressure.swap(); + + pressureProgram.bind(); gl.uniform2f(pressureProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]); + for (let _i = 0; _i < config.PRESSURE_ITERATIONS; _i++) { + gl.activeTexture(gl.TEXTURE0 + pressure.first[2]); gl.bindTexture(gl.TEXTURE_2D, pressure.first[0]); + gl.uniform1i(pressureProgram.uniforms.uPressure, pressure.first[2]); + blit(pressure.second[1]); pressure.swap(); + } + + gradienSubtractProgram.bind(); gl.uniform2f(gradienSubtractProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight); + gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.first[2]); gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.first[2]); + blit(velocity.second[1]); velocity.swap(); + + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + displayProgram.bind(); gl.uniform1i(displayProgram.uniforms.uTexture, density.first[2]); + blit(null); + requestAnimationFrame(update); } - update(); -// ===== REDIMENSIONAR AL CAMBIAR TAMAÑO DE VENTANA ===== window.addEventListener("resize", function() { - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - initFramebuffers(); + canvas.width = window.innerWidth; canvas.height = window.innerHeight; + initFramebuffers(); }); \ No newline at end of file diff --git a/index.html b/index.html index 9d01c9b..173995f 100644 --- a/index.html +++ b/index.html @@ -666,7 +666,7 @@ - + diff --git a/styles.css b/styles.css index b8bfe2b..34291c2 100644 --- a/styles.css +++ b/styles.css @@ -34,21 +34,59 @@ body { /* ===== CANVAS FLUIDO ===== */ #fluidCanvas { - display: block; - width: 100%; - height: 100vh; - position: fixed; - top: 0; - left: 0; - z-index: 0; - pointer-events: auto; - background: var(--oscuro); + display: block; + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + z-index: 0; + pointer-events: none; /* ✅ No interfiere con clicks */ + background: transparent; } -/* Asegurar que el contenido esté por encima */ +/* ===== CONTENIDO SOBRE EL CANVAS ===== */ .hero, section { - position: relative; - z-index: 1; + position: relative; + z-index: 1; + background: transparent; /* ✅ Transparente para ver el humo */ +} + +/* Si necesitas fondos en secciones específicas, usa semi-transparente */ +.hero { + background: rgba(17, 9, 22, 0.3); /* Semi-transparente */ +} + +section { + background: rgba(17, 9, 22, 0.5); /* Semi-transparente */ +} + +/* Header siempre por encima */ +header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 50; + background: linear-gradient(180deg, rgba(17,9,22,.9) 0%, rgba(17,9,22,0) 100%); + pointer-events: auto; /* ✅ Header sí recibe clicks */ +} + +/* Botones y elementos interactivos siempre por encima */ +.btn, .menu-btn, .plan-cta, .tab, .acc-head, .faq-head, +.filter-tag, input, textarea, button, a { + position: relative; + z-index: 2; + pointer-events: auto; /* ✅ Todos los elementos interactivos funcionan */ +} + +/* Overlay y Sheet por encima de todo */ +.overlay { + z-index: 90; +} + +.sheet { + z-index: 100; } /* ===== HEADER ===== */