From 3ae890cb71cbe3b4200ba93772ff3323ca8b62e3 Mon Sep 17 00:00:00 2001 From: Julien Sulpis Date: Mon, 4 Aug 2025 21:57:09 +0200 Subject: [PATCH 01/15] fix: don't override the render target of an effect pass if it already exists --- lib/src/hooks/useCompositeEffectPass.ts | 2 +- lib/src/hooks/useCompositor.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/hooks/useCompositeEffectPass.ts b/lib/src/hooks/useCompositeEffectPass.ts index 81ddd13..8374b14 100644 --- a/lib/src/hooks/useCompositeEffectPass.ts +++ b/lib/src/hooks/useCompositeEffectPass.ts @@ -28,7 +28,7 @@ export function useCompositeEffectPass

> for (const [index, pass] of effectPasses.entries()) { pass.initialize(gl); - if (index < effectPasses.length - 1) { + if (index < effectPasses.length - 1 && pass.target == undefined) { pass.setTarget(createRenderTarget(gl)); } diff --git a/lib/src/hooks/useCompositor.ts b/lib/src/hooks/useCompositor.ts index b59722a..0aa467a 100644 --- a/lib/src/hooks/useCompositor.ts +++ b/lib/src/hooks/useCompositor.ts @@ -6,7 +6,6 @@ import type { CompositeEffectPass, EffectPass, RenderPass } from "../types"; * The compositor handles the combination of the render pass and the effects: * - initialize the gl context and create render targets for all effects * - provide each effect with its previousPass and inputPass to use in uniforms - * - fill the texture uniforms with the previous pass if they are not provided in uniforms * - detect the first texture uniform of each effect and, if it has no value provided, fill it with the previous pass * - render all passes in the correct order */ @@ -23,7 +22,10 @@ export function useCompositor( for (const [index, effect] of effects.entries()) { effect.initialize(gl); - effect.setTarget(index === effects.length - 1 ? null : createRenderTarget(gl)); + + if (index < effects.length - 1 && effect.target == undefined) { + effect.setTarget(createRenderTarget(gl)); + } if (isCompositeEffectPass(effect)) { const inputPass = previousPass; From 6cc0db99f66222704ede550c39a7a744778b1059 Mon Sep 17 00:00:00 2001 From: Julien Sulpis Date: Wed, 6 Aug 2025 22:43:15 +0200 Subject: [PATCH 02/15] feat: add srgb to linear rgb color space conversion for textures --- lib/src/core/texture.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/src/core/texture.ts b/lib/src/core/texture.ts index 39647a9..8765cda 100644 --- a/lib/src/core/texture.ts +++ b/lib/src/core/texture.ts @@ -1,6 +1,11 @@ export type TextureData = ArrayBufferView | null; -type BaseTextureParams = { +type MagFilter = "linear" | "nearest"; +type MinFilter = "linear" | "nearest" | "linear-mipmap-linear" | "nearest-mipmap-linear"; +type ColorSpace = "srgb" | "linear-rgb"; +type WrappingMode = "clamp-to-edge" | "repeat" | "mirrored-repeat"; + +export type BaseTextureParams = { /** * @default "linear-mipmap-linear" if generateMipmaps is true, "linear" otherwise */ @@ -37,6 +42,10 @@ type BaseTextureParams = { * @default WebGL2RenderingContext.RGBA */ internalFormat?: number; + /** + * @default "linear-rgb" + */ + colorSpace?: ColorSpace; /** * @default WebGL2RenderingContext.RGBA */ @@ -75,10 +84,6 @@ export type ImageTextureParams = BaseTextureParams & { export type TextureParams = DataTextureParams | ImageTextureParams; -type MagFilter = "linear" | "nearest"; -type MinFilter = "linear" | "nearest" | "linear-mipmap-linear" | "nearest-mipmap-linear"; -type WrappingMode = "clamp-to-edge" | "repeat" | "mirrored-repeat"; - const minFilterMap: Record = { linear: WebGL2RenderingContext.LINEAR, nearest: WebGL2RenderingContext.NEAREST, @@ -118,8 +123,11 @@ export function fillTexture( const { level = 0, flipY = true, - internalFormat = WebGL2RenderingContext.RGBA, format = WebGL2RenderingContext.RGBA, + colorSpace = "linear-rgb", + internalFormat = colorSpace === "srgb" + ? WebGL2RenderingContext.SRGB8_ALPHA8 + : WebGL2RenderingContext.RGBA, type = WebGL2RenderingContext.UNSIGNED_BYTE, generateMipmaps = true, anisotropy = navigator.hardwareConcurrency, // in most case between 4 and 12, depending on the hardware range From 504d6ba30bef719447ac4cc1f966cc8e448ae615 Mon Sep 17 00:00:00 2001 From: Julien Sulpis Date: Wed, 6 Aug 2025 22:49:35 +0200 Subject: [PATCH 03/15] feat: allow to scale the render target of an effect pass --- lib/src/core/renderTarget.ts | 7 +++--- lib/src/hooks/useEffectPass.ts | 37 ++++++++++++++++++++++++++++-- lib/src/hooks/useQuadRenderPass.ts | 5 ++-- lib/src/hooks/useRenderPass.ts | 6 ++--- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/src/core/renderTarget.ts b/lib/src/core/renderTarget.ts index dcf5f84..7cf214f 100644 --- a/lib/src/core/renderTarget.ts +++ b/lib/src/core/renderTarget.ts @@ -2,9 +2,11 @@ import type { RenderTarget } from "../types"; import type { DataTextureParams } from "./texture"; import { fillTexture } from "./texture"; +export type RenderTargetParams = Partial>; + export function createRenderTarget( gl: WebGL2RenderingContext, - params?: Omit, + params?: RenderTargetParams, ): RenderTarget { let _width = params?.width ?? gl.canvas.width; let _height = params?.height ?? gl.canvas.height; @@ -14,10 +16,10 @@ export function createRenderTarget( let _texture = gl.createTexture()!; fillTexture(gl, _texture, { data: null, - ...params, width: _width, height: _height, generateMipmaps: false, + ...params, }); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); @@ -38,7 +40,6 @@ export function createRenderTarget( }); gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); - gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, newTexture, 0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); diff --git a/lib/src/hooks/useEffectPass.ts b/lib/src/hooks/useEffectPass.ts index ed5641d..e743e6a 100644 --- a/lib/src/hooks/useEffectPass.ts +++ b/lib/src/hooks/useEffectPass.ts @@ -1,9 +1,42 @@ +import type { RenderTargetParams } from "../core/renderTarget"; +import { createRenderTarget } from "../core/renderTarget"; import type { EffectPass, EffectUniforms } from "../types"; import type { QuadPassOptions } from "./useQuadRenderPass"; import { useQuadRenderPass } from "./useQuadRenderPass"; +type EffectPassOptions = Omit, "target"> & { + /** + * Allow to scale the render target texture based on size given to the pass. + */ + resolutionScale?: number; + /** + * Params to create the render target of the pass. + */ + target?: RenderTargetParams | null; +}; + export function useEffectPass( - options: QuadPassOptions, + options: EffectPassOptions, ): EffectPass { - return useQuadRenderPass(undefined, options); + const { target, resolutionScale = 1 } = options; + + const renderPass = useQuadRenderPass(undefined, { ...options, target: null }); + + const renderPassSetSize = renderPass.setSize; + renderPass.setSize = function ({ width, height }) { + renderPassSetSize({ + width: Math.ceil(width * resolutionScale), + height: Math.ceil(height * resolutionScale), + }); + }; + + const renderPassInitialize = renderPass.initialize; + renderPass.initialize = function (gl) { + if (target != null && !("framebuffer" in target)) { + renderPass.setTarget(createRenderTarget(gl, target)); + } + renderPassInitialize(gl); + }; + + return renderPass; } diff --git a/lib/src/hooks/useQuadRenderPass.ts b/lib/src/hooks/useQuadRenderPass.ts index baf9ebf..79fc06d 100644 --- a/lib/src/hooks/useQuadRenderPass.ts +++ b/lib/src/hooks/useQuadRenderPass.ts @@ -42,13 +42,12 @@ export function useQuadRenderPass( } const quadVertexShaderSource = /*glsl*/ `#version 300 es - in vec2 aPosition; out vec2 vUv; void main() { - gl_Position = vec4(aPosition, 0.0, 1.0); - vUv = (aPosition + 1.0) / 2.0; + gl_Position = vec4(aPosition, 0.0, 1.0); + vUv = (aPosition + 1.0) / 2.0; } `; diff --git a/lib/src/hooks/useRenderPass.ts b/lib/src/hooks/useRenderPass.ts index fb8afff..b40e089 100644 --- a/lib/src/hooks/useRenderPass.ts +++ b/lib/src/hooks/useRenderPass.ts @@ -37,7 +37,7 @@ export function useRenderPass( transformFeedbackVaryings, }: RenderPassOptions, ): RenderPass { - /** + /* * INIT */ @@ -77,7 +77,7 @@ export function useRenderPass( initialize(gl); } - /** + /* * UPDATE */ @@ -96,7 +96,7 @@ export function useRenderPass( _target = target; } - /** + /* * RENDER */ From 0a2f8ddbdce671c100d177f097f8223cee531310 Mon Sep 17 00:00:00 2001 From: Julien Sulpis Date: Sat, 9 Aug 2025 15:26:25 +0200 Subject: [PATCH 04/15] feat: add bloom effect --- .../src/pages/post-processing/bloom.astro | 86 ++----- lib/playground/src/shaders/bloom.ts | 222 +++--------------- lib/src/effects/bloom.ts | 85 +++++++ lib/src/effects/glsl/combine.frag | 22 ++ lib/src/effects/glsl/downsample.frag | 34 +++ lib/src/effects/glsl/sample.vert | 12 + lib/src/effects/glsl/upsample.frag | 36 +++ lib/src/index.ts | 2 + 8 files changed, 239 insertions(+), 260 deletions(-) create mode 100644 lib/src/effects/bloom.ts create mode 100644 lib/src/effects/glsl/combine.frag create mode 100644 lib/src/effects/glsl/downsample.frag create mode 100644 lib/src/effects/glsl/sample.vert create mode 100644 lib/src/effects/glsl/upsample.frag diff --git a/lib/playground/src/pages/post-processing/bloom.astro b/lib/playground/src/pages/post-processing/bloom.astro index a1164bc..7e38224 100644 --- a/lib/playground/src/pages/post-processing/bloom.astro +++ b/lib/playground/src/pages/post-processing/bloom.astro @@ -3,78 +3,25 @@ import Layout from "../../layouts/Layout.astro"; ---