import { useEffect, useRef } from "react"; import { generateRandomRGBABytes } from "./randomTextureForShader"; const buffer_1 = `#version 300 es precision mediump float; uniform vec3 iResolution; // viewport resolution (in pixels) uniform float iTime; // shader playback time (in seconds) uniform float iTimeDelta; // render time (in seconds) uniform float iFrameRate; // shader frame rate uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform sampler2D iChannel0; uniform sampler2D iChannel1; uniform sampler2D iChannel2; uniform sampler2D iChannel3; out vec4 FragColor; /* Main render. Temporal AA for a smooth image. Temporal accumulation is disabled while moving the view to prevent ghosting. */ #define ITERATIONS 200 //Increase for less grainy result #define TEMPORAL_AA const vec3 MainColor = vec3(1.0); //noise code by iq float noise( in vec3 x ) { vec3 p = floor(x); vec3 f = fract(x); f = f*f*(3.0-2.0*f); vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy; vec2 rg = textureLod( iChannel0, (uv+ 0.5)/256.0, 0.0 ).yx; return -1.0+2.0*mix( rg.x, rg.y, f.z ); } float saturate(float x) { return clamp(x, 0.0, 1.0); } vec3 saturate(vec3 x) { return clamp(x, vec3(0.0), vec3(1.0)); } float rand(vec2 coord) { return saturate(fract(sin(dot(coord, vec2(12.9898, 78.223))) * 43758.5453)); } float pcurve( float x, float a, float b ) { float k = pow(a+b,a+b) / (pow(a,a)*pow(b,b)); return k * pow( x, a ) * pow( 1.0-x, b ); } const float pi = 3.14159265; float atan2(float y, float x) { if (x > 0.0) { return atan(y / x); } else if (x == 0.0) { if (y > 0.0) { return pi / 2.0; } else if (y < 0.0) { return -(pi / 2.0); } else { return 0.0; } } else //(x < 0.0) { if (y >= 0.0) { return atan(y / x) + pi; } else { return atan(y / x) - pi; } } } float sdTorus(vec3 p, vec2 t) { vec2 q = vec2(length(p.xz) - t.x, p.y); return length(q)-t.y; } float sdSphere(vec3 p, float r) { return length(p)-r; } void Haze(inout vec3 color, vec3 pos, float alpha) { vec2 t = vec2(1.0, 0.01); float torusDist = length(sdTorus(pos + vec3(0.0, -0.05, 0.0), t)); float bloomDisc = 1.0 / (pow(torusDist, 2.0) + 0.001); vec3 col = MainColor; bloomDisc *= length(pos) < 0.5 ? 0.0 : 1.0; color += col * bloomDisc * (2.9 / float(ITERATIONS)) * (1.0 - alpha * 1.0); } void GasDisc(inout vec3 color, inout float alpha, vec3 pos) { float discRadius = 3.2; float discWidth = 5.3; float discInner = discRadius - discWidth * 0.5; float discOuter = discRadius + discWidth * 0.5; vec3 origin = vec3(0.0, 0.0, 0.0); float mouseZ = iMouse.y / iResolution.y; vec3 discNormal = normalize(vec3(0.0, 1.0, 0.0)); float discThickness = 0.1; float distFromCenter = distance(pos, origin); float distFromDisc = dot(discNormal, pos - origin); float radialGradient = 1.0 - saturate((distFromCenter - discInner) / discWidth * 0.5); float coverage = pcurve(radialGradient, 4.0, 0.9); discThickness *= radialGradient; coverage *= saturate(1.0 - abs(distFromDisc) / discThickness); vec3 dustColorLit = MainColor; vec3 dustColorDark = vec3(0.0, 0.0, 0.0); float dustGlow = 1.0 / (pow(1.0 - radialGradient, 2.0) * 290.0 + 0.002); vec3 dustColor = dustColorLit * dustGlow * 8.2; coverage = saturate(coverage * 0.7); float fade = pow((abs(distFromCenter - discInner) + 0.4), 4.0) * 0.04; float bloomFactor = 1.0 / (pow(distFromDisc, 2.0) * 40.0 + fade + 0.00002); vec3 b = dustColorLit * pow(bloomFactor, 1.5); b *= mix(vec3(1.7, 1.1, 1.0), vec3(0.5, 0.6, 1.0), vec3(pow(radialGradient, 2.0))); b *= mix(vec3(1.7, 0.5, 0.1), vec3(1.0), vec3(pow(radialGradient, 0.5))); dustColor = mix(dustColor, b * 150.0, saturate(1.0 - coverage * 1.0)); coverage = saturate(coverage + bloomFactor * bloomFactor * 0.1); if (coverage < 0.01) { return; } vec3 radialCoords; radialCoords.x = distFromCenter * 1.5 + 0.55; radialCoords.y = atan2(-pos.x, -pos.z) * 1.5; radialCoords.z = distFromDisc * 1.5; radialCoords *= 0.95; float speed = 0.06; float noise1 = 1.0; vec3 rc = radialCoords + 0.0; rc.y += iTime * speed; noise1 *= noise(rc * 3.0) * 0.5 + 0.5; rc.y -= iTime * speed; noise1 *= noise(rc * 6.0) * 0.5 + 0.5; rc.y += iTime * speed; noise1 *= noise(rc * 12.0) * 0.5 + 0.5; rc.y -= iTime * speed; noise1 *= noise(rc * 24.0) * 0.5 + 0.5; rc.y += iTime * speed; float noise2 = 2.0; rc = radialCoords + 30.0; noise2 *= noise(rc * 3.0) * 0.5 + 0.5; rc.y += iTime * speed; noise2 *= noise(rc * 6.0) * 0.5 + 0.5; rc.y -= iTime * speed; noise2 *= noise(rc * 12.0) * 0.5 + 0.5; rc.y += iTime * speed; noise2 *= noise(rc * 24.0) * 0.5 + 0.5; rc.y -= iTime * speed; noise2 *= noise(rc * 48.0) * 0.5 + 0.5; rc.y += iTime * speed; noise2 *= noise(rc * 92.0) * 0.5 + 0.5; rc.y -= iTime * speed; dustColor *= noise1 * 0.998 + 0.002; coverage *= noise2; radialCoords.y += iTime * speed * 0.5; dustColor *= pow(texture(iChannel1, radialCoords.yx * vec2(0.15, 0.27)).rgb, vec3(2.0)) * 4.0; coverage = saturate(coverage * 1200.0 / float(ITERATIONS)); dustColor = max(vec3(0.0), dustColor); coverage *= pcurve(radialGradient, 4.0, 0.9); color = (1.0 - alpha) * dustColor * coverage + color; alpha = (1.0 - alpha) * coverage + alpha; } vec3 rotate(vec3 p, float x, float y, float z) { mat3 matx = mat3(1.0, 0.0, 0.0, 0.0, cos(x), sin(x), 0.0, -sin(x), cos(x)); mat3 maty = mat3(cos(y), 0.0, -sin(y), 0.0, 1.0, 0.0, sin(y), 0.0, cos(y)); mat3 matz = mat3(cos(z), sin(z), 0.0, -sin(z), cos(z), 0.0, 0.0, 0.0, 1.0); p = matx * p; p = matz * p; p = maty * p; return p; } void RotateCamera(inout vec3 eyevec, inout vec3 eyepos) { float mousePosY = iMouse.y / iResolution.y; float mousePosX = iMouse.x / iResolution.x; vec3 angle = vec3(mousePosY * 0.05 + 0.05, 1.0 + mousePosX * 1.0, -0.45); eyevec = rotate(eyevec, angle.x, angle.y, angle.z); eyepos = rotate(eyepos, angle.x, angle.y, angle.z); } void WarpSpace(inout vec3 eyevec, inout vec3 raypos) { vec3 origin = vec3(0.0, 0.0, 0.0); float singularityDist = distance(raypos, origin); float warpFactor = 1.0 / (pow(singularityDist, 2.0) + 0.000001); vec3 singularityVector = normalize(origin - raypos); float warpAmount = 5.0; eyevec = normalize(eyevec + singularityVector * warpFactor * warpAmount / float(ITERATIONS)); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord.xy / iResolution.xy; float aspect = iResolution.x / iResolution.y; vec2 uveye = uv; #ifdef TEMPORAL_AA uveye.x += (rand(uv + sin(iTime * 1.0)) / iResolution.x) * (iMouse.z > 1.0 ? 0.0 : 1.0); uveye.y += (rand(uv + 1.0 + sin(iTime * 1.0)) / iResolution.y) * (iMouse.z > 1.0 ? 0.0 : 1.0); #endif vec3 eyevec = normalize(vec3((uveye * 2.0 - 1.0) * vec2(aspect, 1.0), 6.0)); vec3 eyepos = vec3(0.0, -0.0, -10.0); vec2 mousepos = iMouse.xy / iResolution.xy; if (mousepos.x == 0.0) { mousepos.x = 0.35; } eyepos.x += mousepos.x * 3.0 - 1.5; const float far = 15.0; RotateCamera(eyevec, eyepos); vec3 color = vec3(0.0, 0.0, 0.0); float dither = rand(uv #ifdef TEMPORAL_AA + sin(iTime * 1.0) * (iMouse.z > 1.0 ? 0.0 : 1.0) #endif ) * 2.0; float alpha = 0.0; vec3 raypos = eyepos + eyevec * dither * far / float(ITERATIONS); for (int i = 0; i < ITERATIONS; i++) { WarpSpace(eyevec, raypos); raypos += eyevec * far / float(ITERATIONS); GasDisc(color, alpha, raypos); Haze(color, raypos, alpha); } color *= 0.0001; #ifdef TEMPORAL_AA const float p = 1.0; vec3 previous = pow(texture(iChannel2, uv).rgb, vec3(1.0 / p)); color = pow(color, vec3(1.0 / p)); float blendWeight = 0.9 * (iMouse.z > 1.0 ? 0.0 : 1.0); color = mix(color, previous, blendWeight); color = pow(color, vec3(p)); #endif fragColor = vec4(saturate(color), 1.0); } void main() { // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } `; const buffer_2 = `#version 300 es precision mediump float; uniform vec3 iResolution; // viewport resolution (in pixels) uniform float iTime; // shader playback time (in seconds) uniform float iTimeDelta; // render time (in seconds) uniform float iFrameRate; // shader frame rate uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform sampler2D iChannel0; uniform sampler2D iChannel1; uniform sampler2D iChannel2; uniform sampler2D iChannel3; out vec4 FragColor; //First bloom pass, mipmap tree thing vec3 ColorFetch(vec2 coord) { return texture(iChannel0, coord).rgb; } vec3 Grab1(vec2 coord, const float octave, const vec2 offset) { float scale = exp2(octave); coord += offset; coord *= scale; if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { return vec3(0.0); } vec3 color = ColorFetch(coord); return color; } vec3 Grab4(vec2 coord, const float octave, const vec2 offset) { float scale = exp2(octave); coord += offset; coord *= scale; if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { return vec3(0.0); } vec3 color = vec3(0.0); float weights = 0.0; const int oversampling = 4; for (int i = 0; i < oversampling; i++) { for (int j = 0; j < oversampling; j++) { vec2 off = (vec2(i, j) / iResolution.xy + vec2(0.0) / iResolution.xy) * scale / float(oversampling); color += ColorFetch(coord + off); weights += 1.0; } } color /= weights; return color; } vec3 Grab8(vec2 coord, const float octave, const vec2 offset) { float scale = exp2(octave); coord += offset; coord *= scale; if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { return vec3(0.0); } vec3 color = vec3(0.0); float weights = 0.0; const int oversampling = 8; for (int i = 0; i < oversampling; i++) { for (int j = 0; j < oversampling; j++) { vec2 off = (vec2(i, j) / iResolution.xy + vec2(0.0) / iResolution.xy) * scale / float(oversampling); color += ColorFetch(coord + off); weights += 1.0; } } color /= weights; return color; } vec3 Grab16(vec2 coord, const float octave, const vec2 offset) { float scale = exp2(octave); coord += offset; coord *= scale; if (coord.x < 0.0 || coord.x > 1.0 || coord.y < 0.0 || coord.y > 1.0) { return vec3(0.0); } vec3 color = vec3(0.0); float weights = 0.0; const int oversampling = 16; for (int i = 0; i < oversampling; i++) { for (int j = 0; j < oversampling; j++) { vec2 off = (vec2(i, j) / iResolution.xy + vec2(0.0) / iResolution.xy) * scale / float(oversampling); color += ColorFetch(coord + off); weights += 1.0; } } color /= weights; return color; } vec2 CalcOffset(float octave) { vec2 offset = vec2(0.0); vec2 padding = vec2(10.0) / iResolution.xy; offset.x = -min(1.0, floor(octave / 3.0)) * (0.25 + padding.x); offset.y = -(1.0 - (1.0 / exp2(octave))) - padding.y * octave; offset.y += min(1.0, floor(octave / 3.0)) * 0.35; return offset; } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord.xy / iResolution.xy; vec3 color = vec3(0.0); /* Create a mipmap tree thingy with padding to prevent leaking bloom Since there's no mipmaps for the previous buffer and the reduction process has to be done in one pass, oversampling is required for a proper result */ color += Grab1(uv, 1.0, vec2(0.0, 0.0) ); color += Grab4(uv, 2.0, vec2(CalcOffset(1.0)) ); color += Grab8(uv, 3.0, vec2(CalcOffset(2.0)) ); color += Grab16(uv, 4.0, vec2(CalcOffset(3.0)) ); color += Grab16(uv, 5.0, vec2(CalcOffset(4.0)) ); color += Grab16(uv, 6.0, vec2(CalcOffset(5.0)) ); color += Grab16(uv, 7.0, vec2(CalcOffset(6.0)) ); color += Grab16(uv, 8.0, vec2(CalcOffset(7.0)) ); fragColor = vec4(color, 1.0); } void main() { // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } `; const buffer_3 = `#version 300 es precision mediump float; uniform vec3 iResolution; // viewport resolution (in pixels) uniform float iTime; // shader playback time (in seconds) uniform float iTimeDelta; // render time (in seconds) uniform float iFrameRate; // shader frame rate uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform sampler2D iChannel0; uniform sampler2D iChannel1; uniform sampler2D iChannel2; uniform sampler2D iChannel3; out vec4 FragColor; //Horizontal gaussian blur leveraging hardware filtering for fewer texture lookups. vec3 ColorFetch(vec2 coord) { return texture(iChannel0, coord).rgb; } float weights[5]; float offsets[5]; void mainImage( out vec4 fragColor, in vec2 fragCoord ) { weights[0] = 0.19638062; weights[1] = 0.29675293; weights[2] = 0.09442139; weights[3] = 0.01037598; weights[4] = 0.00025940; offsets[0] = 0.00000000; offsets[1] = 1.41176471; offsets[2] = 3.29411765; offsets[3] = 5.17647059; offsets[4] = 7.05882353; vec2 uv = fragCoord.xy / iResolution.xy; vec3 color = vec3(0.0); float weightSum = 0.0; if (uv.x < 0.52) { color += ColorFetch(uv) * weights[0]; weightSum += weights[0]; for(int i = 1; i < 5; i++) { vec2 offset = vec2(offsets[i]) / iResolution.xy; color += ColorFetch(uv + offset * vec2(0.5, 0.0)) * weights[i]; color += ColorFetch(uv - offset * vec2(0.5, 0.0)) * weights[i]; weightSum += weights[i] * 2.0; } color /= weightSum; } fragColor = vec4(color,1.0); } void main() { // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } `; const buffer_4 = `#version 300 es precision mediump float; uniform vec3 iResolution; // viewport resolution (in pixels) uniform float iTime; // shader playback time (in seconds) uniform float iTimeDelta; // render time (in seconds) uniform float iFrameRate; // shader frame rate uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform sampler2D iChannel0; uniform sampler2D iChannel1; uniform sampler2D iChannel2; uniform sampler2D iChannel3; out vec4 FragColor; //Vertical gaussian blur leveraging hardware filtering for fewer texture lookups. vec3 ColorFetch(vec2 coord) { return texture(iChannel0, coord).rgb; } float weights[5]; float offsets[5]; void mainImage( out vec4 fragColor, in vec2 fragCoord ) { weights[0] = 0.19638062; weights[1] = 0.29675293; weights[2] = 0.09442139; weights[3] = 0.01037598; weights[4] = 0.00025940; offsets[0] = 0.00000000; offsets[1] = 1.41176471; offsets[2] = 3.29411765; offsets[3] = 5.17647059; offsets[4] = 7.05882353; vec2 uv = fragCoord.xy / iResolution.xy; vec3 color = vec3(0.0); float weightSum = 0.0; if (uv.x < 0.52) { color += ColorFetch(uv) * weights[0]; weightSum += weights[0]; for(int i = 1; i < 5; i++) { vec2 offset = vec2(offsets[i]) / iResolution.xy; color += ColorFetch(uv + offset * vec2(0.0, 0.5)) * weights[i]; color += ColorFetch(uv - offset * vec2(0.0, 0.5)) * weights[i]; weightSum += weights[i] * 2.0; } color /= weightSum; } fragColor = vec4(color,1.0); } void main() { // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } `; const image_1 = `#version 300 es precision mediump float; uniform vec3 iResolution; // viewport resolution (in pixels) uniform float iTime; // shader playback time (in seconds) uniform float iTimeDelta; // render time (in seconds) uniform float iFrameRate; // shader frame rate uniform int iFrame; // shader playback frame uniform float iChannelTime[4]; // channel playback time (in seconds) uniform vec3 iChannelResolution[4]; // channel resolution (in pixels) uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click uniform sampler2D iChannel0; uniform sampler2D iChannel1; uniform sampler2D iChannel2; uniform sampler2D iChannel3; out vec4 FragColor; vec3 saturate(vec3 x) { return clamp(x, vec3(0.0), vec3(1.0)); } vec4 cubic(float x) { float x2 = x * x; float x3 = x2 * x; vec4 w; w.x = -x3 + 3.0*x2 - 3.0*x + 1.0; w.y = 3.0*x3 - 6.0*x2 + 4.0; w.z = -3.0*x3 + 3.0*x2 + 3.0*x + 1.0; w.w = x3; return w / 6.0; } vec4 BicubicTexture(in sampler2D tex, in vec2 coord) { vec2 resolution = iResolution.xy; coord *= resolution; float fx = fract(coord.x); float fy = fract(coord.y); coord.x -= fx; coord.y -= fy; fx -= 0.5; fy -= 0.5; vec4 xcubic = cubic(fx); vec4 ycubic = cubic(fy); vec4 c = vec4(coord.x - 0.5, coord.x + 1.5, coord.y - 0.5, coord.y + 1.5); vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x + ycubic.y, ycubic.z + ycubic.w); vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s; vec4 sample0 = texture(tex, vec2(offset.x, offset.z) / resolution); vec4 sample1 = texture(tex, vec2(offset.y, offset.z) / resolution); vec4 sample2 = texture(tex, vec2(offset.x, offset.w) / resolution); vec4 sample3 = texture(tex, vec2(offset.y, offset.w) / resolution); float sx = s.x / (s.x + s.y); float sy = s.z / (s.z + s.w); return mix( mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); } vec3 ColorFetch(vec2 coord) { return texture(iChannel0, coord).rgb; } vec3 BloomFetch(vec2 coord) { return BicubicTexture(iChannel3, coord).rgb; } vec3 Grab(vec2 coord, const float octave, const vec2 offset) { float scale = exp2(octave); coord /= scale; coord -= offset; return BloomFetch(coord); } vec2 CalcOffset(float octave) { vec2 offset = vec2(0.0); vec2 padding = vec2(10.0) / iResolution.xy; offset.x = -min(1.0, floor(octave / 3.0)) * (0.25 + padding.x); offset.y = -(1.0 - (1.0 / exp2(octave))) - padding.y * octave; offset.y += min(1.0, floor(octave / 3.0)) * 0.35; return offset; } vec3 GetBloom(vec2 coord) { vec3 bloom = vec3(0.0); //Reconstruct bloom from multiple blurred images bloom += Grab(coord, 1.0, vec2(CalcOffset(0.0))) * 1.0; bloom += Grab(coord, 2.0, vec2(CalcOffset(1.0))) * 1.5; bloom += Grab(coord, 3.0, vec2(CalcOffset(2.0))) * 1.0; bloom += Grab(coord, 4.0, vec2(CalcOffset(3.0))) * 1.5; bloom += Grab(coord, 5.0, vec2(CalcOffset(4.0))) * 1.8; bloom += Grab(coord, 6.0, vec2(CalcOffset(5.0))) * 1.0; bloom += Grab(coord, 7.0, vec2(CalcOffset(6.0))) * 1.0; bloom += Grab(coord, 8.0, vec2(CalcOffset(7.0))) * 1.0; return bloom; } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = fragCoord.xy / iResolution.xy; vec3 color = ColorFetch(uv); color += GetBloom(uv) * 0.08; color *= 200.0; //Tonemapping and color grading color = pow(color, vec3(1.5)); color = color / (1.0 + color); color = pow(color, vec3(1.0 / 1.5)); color = mix(color, color * color * (3.0 - 2.0 * color), vec3(1.0)); color = pow(color, vec3(1.3, 1.20, 1.0)); color = saturate(color * 1.01); color = pow(color, vec3(0.7 / 2.2)); fragColor = vec4(color, 1.0); } void main() { // gl_FragCoord.xy is pixel coordinates (1..width, 1..height) mainImage(FragColor, gl_FragCoord.xy); } `; function buildProgram( ctx: WebGL2RenderingContext, fragShader: WebGLShader, vertexShader: WebGLShader ) { const program = ctx.createProgram(); if (!program) return null; ctx.attachShader(program, fragShader); ctx.attachShader(program, vertexShader); ctx.linkProgram(program); const status = ctx.getProgramParameter(program, ctx.LINK_STATUS); if (status) { return program; } console.error(ctx.getProgramInfoLog(program)); ctx.deleteProgram(program); return null; } function buildShader( ctx: WebGL2RenderingContext, type: number, source: string ) { const shader = ctx.createShader(type); if (!shader) return null; ctx.shaderSource(shader, source); ctx.compileShader(shader); const status = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); if (status) { return shader; } console.error(ctx.getShaderInfoLog(shader)); ctx.deleteShader(shader); return null; } export function ShaderBackground() { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const gl = canvas.getContext("webgl2"); if (!gl) return; gl.viewport(0, 0, canvas.width, canvas.height); gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); const buffer_1_shader = buildShader(gl, gl.FRAGMENT_SHADER, buffer_1); const buffer_2_shader = buildShader(gl, gl.FRAGMENT_SHADER, buffer_2); const buffer_3_shader = buildShader(gl, gl.FRAGMENT_SHADER, buffer_3); const buffer_4_shader = buildShader(gl, gl.FRAGMENT_SHADER, buffer_4); const fragmentShader = buildShader(gl, gl.FRAGMENT_SHADER, image_1); const vertexShader = buildShader( gl, gl.VERTEX_SHADER, `#version 300 es precision mediump float; in vec2 a_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); }` ); if ( !buffer_1_shader || !buffer_2_shader || !buffer_3_shader || !buffer_4_shader || !fragmentShader || !vertexShader ) return; const buffer_1_program = buildProgram(gl, buffer_1_shader, vertexShader); const buffer_2_program = buildProgram(gl, buffer_2_shader, vertexShader); const buffer_3_program = buildProgram(gl, buffer_3_shader, vertexShader); const buffer_4_program = buildProgram(gl, buffer_4_shader, vertexShader); const finalProgram = buildProgram(gl, fragmentShader, vertexShader); if ( !buffer_1_program || !buffer_2_program || !buffer_3_program || !buffer_4_program || !finalProgram ) return; const posBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW ); { gl.useProgram(buffer_1_program); const posAttrLocation = gl.getAttribLocation( buffer_1_program, "a_position" ); gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(posAttrLocation); } { gl.useProgram(buffer_2_program); const posAttrLocation = gl.getAttribLocation( buffer_2_program, "a_position" ); gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(posAttrLocation); } { gl.useProgram(buffer_3_program); const posAttrLocation = gl.getAttribLocation( buffer_3_program, "a_position" ); gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(posAttrLocation); } { gl.useProgram(buffer_4_program); const posAttrLocation = gl.getAttribLocation( buffer_4_program, "a_position" ); gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(posAttrLocation); } { gl.useProgram(finalProgram); const posAttrLocation = gl.getAttribLocation(finalProgram, "a_position"); gl.vertexAttribPointer(posAttrLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(posAttrLocation); } function setResolution( program: WebGLProgram, canvas: HTMLCanvasElement, gl: WebGL2RenderingContext ) { const loc = gl.getUniformLocation(program, "iResolution"); if (loc === null) { console.warn( "iResolution uniform not found (maybe not used by shader)." ); } else { const w = canvas.width; const h = canvas.height; gl.uniform3fv(loc, [w, h, 1]); } } function setTime( program: WebGLProgram, now: number, gl: WebGL2RenderingContext ) { const loc = gl.getUniformLocation(program, "iTime"); if (loc === null) { console.warn( "iTime uniform not found (maybe not used by shader).", program ); } else { gl.uniform1f(loc, now); } } const frameBufferTexture = gl.createTexture(); if (!frameBufferTexture) return; gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); const frameBufferTexture2 = gl.createTexture(); if (!frameBufferTexture2) return; gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); const frameBuffer = gl.createFramebuffer(); if (!frameBuffer) return; gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, frameBuffer); gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture, 0 ); const status = gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER); switch (status) { case gl.FRAMEBUFFER_COMPLETE: console.log("Framebuffer is complete"); break; case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: console.log("Framebuffer is incomplete attachment"); break; case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: console.log("Framebuffer is incomplete missing attachment"); break; case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: console.log("Framebuffer is incomplete dimensions"); break; case gl.FRAMEBUFFER_UNSUPPORTED: console.log("Framebuffer is unsupported"); break; default: console.log("Framebuffer is unknown"); } const ichannel0_location_buffer_1 = gl.getUniformLocation( buffer_1_program as WebGLProgram, "iChannel0" ); const ichannel0_location_buffer_2 = gl.getUniformLocation( buffer_2_program as WebGLProgram, "iChannel0" ); const ichannel0_location_buffer_3 = gl.getUniformLocation( buffer_3_program as WebGLProgram, "iChannel0" ); const ichannel0_location_buffer_4 = gl.getUniformLocation( buffer_4_program as WebGLProgram, "iChannel0" ); const ichannel0_location_final = gl.getUniformLocation( finalProgram as WebGLProgram, "iChannel0" ); function render( gl: WebGL2RenderingContext, canvas: HTMLCanvasElement, now: number ) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, frameBuffer); //Clear with Clear color gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(buffer_1_program); setResolution(buffer_1_program as WebGLProgram, canvas, gl); setTime(buffer_1_program as WebGLProgram, now, gl); const bytes = generateRandomRGBABytes(canvas.width, canvas.height); const tex = gl.createTexture(); if (!tex) throw new Error("Failed to create texture"); // Activate texture unit 0 and bind the texture gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, tex); // Texture parameters suitable for data textures gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 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); // Upload pixel data as RGBA UNSIGNED_BYTE gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, bytes ); // Set sampler uniform to texture unit 0 gl.uniform1i(ichannel0_location_buffer_1, 0); // RENDER 1 gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture, 0 ); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.useProgram(buffer_2_program); setResolution(buffer_2_program as WebGLProgram, canvas, gl); // RENDER 2 gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture); gl.uniform1i(ichannel0_location_buffer_2, 1); gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture2, 0 ); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.useProgram(buffer_3_program); setResolution(buffer_3_program as WebGLProgram, canvas, gl); // RENDER 3 gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2); gl.uniform1i(ichannel0_location_buffer_3, 1); gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture, 0 ); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.useProgram(buffer_4_program); setResolution(buffer_4_program as WebGLProgram, canvas, gl); // RENDER 4 gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture); gl.uniform1i(ichannel0_location_buffer_4, 1); gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture2, 0 ); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.useProgram(finalProgram); setResolution(finalProgram as WebGLProgram, canvas, gl); // FINAL RENDER gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, frameBufferTexture2); gl.uniform1i(ichannel0_location_final, 1); gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, frameBufferTexture, 0 ); gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.drawArrays(gl.TRIANGLES, 0, 6); } const started = Date.now(); function update(now: number) { const time = (now - started) / 1000; render(gl as WebGL2RenderingContext, canvas as HTMLCanvasElement, time); requestAnimationFrame(update); } requestAnimationFrame(update); }, []); return ( ); }