From 38a2b4948e1375246f8b1f273ddb1ed872157856 Mon Sep 17 00:00:00 2001 From: Baz Utsahajit Date: Fri, 5 Jan 2024 22:26:30 +0000 Subject: [PATCH] Update color gradient --- .../color-gradient/src/ColorGradientFilter.ts | 78 +++++++++++-------- .../color-gradient/src/color-gradient.frag | 43 +++++----- .../color-gradient/src/color-gradient.wgsl | 49 ++++++------ 3 files changed, 95 insertions(+), 75 deletions(-) diff --git a/filters/color-gradient/src/ColorGradientFilter.ts b/filters/color-gradient/src/ColorGradientFilter.ts index d321d38bc..ea889e4db 100644 --- a/filters/color-gradient/src/ColorGradientFilter.ts +++ b/filters/color-gradient/src/ColorGradientFilter.ts @@ -62,15 +62,10 @@ export class ColorGradientFilter extends Filter }; public uniforms: { - uType: number; - uAngle: number; - uAlpha: number; + uOptions: Float32Array; + uCounts: Float32Array; uColors: Float32Array; - uOffsets: Float32Array; - uAlphas: Float32Array; - uNumStops: number; - uMaxColors: number; - uReplace: number; + uStops: Float32Array; }; private _stops: ColorStop[] = []; @@ -124,17 +119,33 @@ export class ColorGradientFilter extends Filter glProgram, resources: { colorGradientUniforms: { - uType: { value: options.type, type: 'i32' }, - uAngle: { value: options.angle ?? ANGLE_OFFSET, type: 'f32' }, - uAlpha: { value: options.alpha, type: 'f32' }, - uReplace: { value: options.replace ? 1 : 0, type: 'f32' }, + uOptions: { + value: [ + // Gradient Type + options.type, + // Gradient Angle + options.angle ?? ANGLE_OFFSET, + // Master Alpha + options.alpha, + // Replace Base Color + options.replace ? 1 : 0, + ], + type: 'vec4', + }, + uCounts: { + value: [ + // Number of Stops + options.stops.length, + // Max Gradient Colors + options.maxColors, + ], + type: 'vec2', + }, uColors: { value: new Float32Array(maxStops * 3), type: 'vec3', size: maxStops }, - uOffsets: { value: new Float32Array(maxStops), type: 'f32', size: maxStops }, - uAlphas: { value: new Float32Array(maxStops * 3), type: 'f32', size: maxStops * 3 }, - uNumStops: { value: options.stops.length, type: 'f32' }, - uMaxColors: { value: options.maxColors, type: 'f32' }, + // We only need vec2, but we need to pad to eliminate the WGSL warning, TODO: @Mat ? + uStops: { value: new Float32Array(maxStops * 4), type: 'vec4', size: maxStops }, }, }, }); @@ -153,7 +164,6 @@ export class ColorGradientFilter extends Filter { const sortedStops = sortColorStops(stops); const color = new Color(); - const colors = new Float32Array(sortedStops.length * 3); let r; let g; let b; @@ -164,15 +174,15 @@ export class ColorGradientFilter extends Filter const indexStart = i * 3; [r, g, b] = color.toArray(); - colors[indexStart] = r; - colors[indexStart + 1] = g; - colors[indexStart + 2] = b; + this.uniforms.uColors[indexStart] = r; + this.uniforms.uColors[indexStart + 1] = g; + this.uniforms.uColors[indexStart + 2] = b; + + this.uniforms.uStops[i * 4] = sortedStops[i].offset; + this.uniforms.uStops[(i * 4) + 1] = sortedStops[i].alpha; } - this.uniforms.uColors = colors; - this.uniforms.uOffsets.set(sortedStops.map((s) => s.offset)); - this.uniforms.uAlphas.set(sortedStops.map((s) => s.alpha)); - this.uniforms.uNumStops = sortedStops.length; + this.uniforms.uCounts[0] = sortedStops.length; this._stops = sortedStops; } @@ -180,36 +190,36 @@ export class ColorGradientFilter extends Filter * The type of gradient * @default ColorGradientFilter.LINEAR */ - get type(): number { return this.uniforms.uType; } - set type(value: number) { this.uniforms.uType = value; } + get type(): number { return this.uniforms.uOptions[0]; } + set type(value: number) { this.uniforms.uOptions[0] = value; } /** * The angle of the gradient in degrees * @default 90 */ - get angle(): number { return this.uniforms.uAngle + ANGLE_OFFSET; } - set angle(value: number) { this.uniforms.uAngle = value - ANGLE_OFFSET; } + get angle(): number { return this.uniforms.uOptions[1] + ANGLE_OFFSET; } + set angle(value: number) { this.uniforms.uOptions[1] = value - ANGLE_OFFSET; } /** * The alpha value of the gradient (0-1) * @default 1 */ - get alpha(): number { return this.uniforms.uAlpha; } - set alpha(value: number) { this.uniforms.uAlpha = value; } + get alpha(): number { return this.uniforms.uOptions[2]; } + set alpha(value: number) { this.uniforms.uOptions[2] = value; } /** * The maximum number of colors to render (0 = no limit) * @default 0 */ - get maxColors(): number { return this.uniforms.uMaxColors; } - set maxColors(value: number) { this.uniforms.uMaxColors = value; } + get maxColors(): number { return this.uniforms.uCounts[1]; } + set maxColors(value: number) { this.uniforms.uCounts[1] = value; } /** * If true, the gradient will replace the existing color, otherwise it * will be multiplied with it * @default false */ - get replace(): boolean { return this.uniforms.uReplace > 0.5; } - set replace(value: boolean) { this.uniforms.uReplace = value ? 1 : 0; } + get replace(): boolean { return this.uniforms.uOptions[3] > 0.5; } + set replace(value: boolean) { this.uniforms.uOptions[3] = value ? 1 : 0; } } diff --git a/filters/color-gradient/src/color-gradient.frag b/filters/color-gradient/src/color-gradient.frag index b1061f03a..d0551c514 100644 --- a/filters/color-gradient/src/color-gradient.frag +++ b/filters/color-gradient/src/color-gradient.frag @@ -9,15 +9,10 @@ const int TYPE_CONIC = 2; const int MAX_STOPS = 32; uniform sampler2D uTexture; -uniform int uNumStops; -uniform float uAlphas[3*MAX_STOPS]; +uniform vec4 uOptions; +uniform vec2 uCounts; uniform vec3 uColors[MAX_STOPS]; -uniform float uOffsets[MAX_STOPS]; -uniform int uType; -uniform float uAngle; -uniform float uAlpha; -uniform int uMaxColors; -uniform bool uReplace; +uniform vec4 uStops[MAX_STOPS]; const float PI = 3.1415926538; const float PI_2 = PI*2.; @@ -65,6 +60,14 @@ float projectPosition(vec2 pos, int type, float angle) { } void main(void) { + int uType = int(uOptions[0]); + float uAngle = uOptions[1]; + float uAlpha = uOptions[2]; + float uReplace = uOptions[3]; + + int uNumStops = int(uCounts[0]); + float uMaxColors = uCounts[1]; + // current/original color vec4 currentColor = texture(uTexture, vTextureCoord); @@ -75,15 +78,17 @@ void main(void) { } // project position - float y = projectPosition(vFilterCoord, uType, radians(uAngle)); + float y = projectPosition(vFilterCoord, int(uType), radians(uAngle)); // check gradient bounds - float offsetMin = uOffsets[0]; + float offsetMin = uStops[0][0]; float offsetMax = 0.0; + int numStops = int(uNumStops); + for (int i = 0; i < MAX_STOPS; i++) { - if (i == uNumStops-1){ // last index - offsetMax = uOffsets[i]; + if (i == numStops-1){ // last index + offsetMax = uStops[i][0]; } } @@ -93,8 +98,8 @@ void main(void) { } // limit colors - if (uMaxColors > 0) { - float stepSize = 1./float(uMaxColors); + if (uMaxColors > 0.) { + float stepSize = 1./uMaxColors; float stepNumber = float(floor(y/stepSize)); y = stepSize * (stepNumber + 0.5);// offset by 0.5 to use color from middle of segment } @@ -104,12 +109,12 @@ void main(void) { ColorStop to; for (int i = 0; i < MAX_STOPS; i++) { - if (y >= uOffsets[i]) { - from = ColorStop(uOffsets[i], uColors[i], uAlphas[i]); - to = ColorStop(uOffsets[i+1], uColors[i+1], uAlphas[i+1]); + if (y >= uStops[i][0]) { + from = ColorStop(uStops[i][0], uColors[i], uStops[i][1]); + to = ColorStop(uStops[i+1][0], uColors[i+1], uStops[i+1][1]); } - if (i == uNumStops-1){ // last index + if (i == numStops-1){ // last index break; } } @@ -125,7 +130,7 @@ void main(void) { float gradientAlpha = uAlpha * currentColor.a; vec4 gradientColor = mix(colorFrom, colorTo, relativePercent) * gradientAlpha; - if (uReplace == false) { + if (uReplace < 0.5) { // mix resulting color with current color finalColor = gradientColor + currentColor*(1.-gradientColor.a); } else { diff --git a/filters/color-gradient/src/color-gradient.wgsl b/filters/color-gradient/src/color-gradient.wgsl index 6aea080e6..dd25bb37a 100644 --- a/filters/color-gradient/src/color-gradient.wgsl +++ b/filters/color-gradient/src/color-gradient.wgsl @@ -1,13 +1,8 @@ struct ColorGradientUniforms { - uType: i32, - uAngle: f32, - uAlpha: f32, - uColors: array, 20>, - uOffsets: array, - uAlphas: array, - uNumStops: f32, - uMaxColors: f32, - uReplace: f32, + uOptions: vec4, + uCounts: vec2, + uColors: array, MAX_STOPS>, + uStops: array, MAX_STOPS>, }; struct GlobalFilterUniforms { @@ -121,30 +116,40 @@ fn mainFragment( @location(0) uv : vec2, @location(1) coord : vec2 ) -> @location(0) vec4 { + let uType: i32 = i32(colorGradientUniforms.uOptions[0]); + let uAngle: f32 = colorGradientUniforms.uOptions[1]; + let uAlpha: f32 = colorGradientUniforms.uOptions[2]; + let uReplace: f32 = colorGradientUniforms.uOptions[3]; + + let uNumStops: i32 = i32(colorGradientUniforms.uCounts[0]); + let uMaxColors: f32 = colorGradientUniforms.uCounts[1]; + // current/original color var currentColor: vec4 = textureSample(uTexture, uSampler, uv); // skip calculations if gradient alpha is 0 - if (colorGradientUniforms.uAlpha == 0.0) { return currentColor; } + if (uAlpha == 0.0) { return currentColor; } // project position - var y: f32 = projectPosition(coord, colorGradientUniforms.uType, radians(colorGradientUniforms.uAngle)); + var y: f32 = projectPosition(coord, uType, radians(uAngle)); // check gradient bounds - var offsetMin: f32 = colorGradientUniforms.uOffsets[0]; + var offsetMin: f32 = colorGradientUniforms.uStops[0][0]; var offsetMax: f32 = 0.0; + let numStops: i32 = uNumStops; + for (var i: i32 = 0; i < MAX_STOPS; i = i + 1) { - if (i == colorGradientUniforms.uNumStops - 1) { // last index - offsetMax = colorGradientUniforms.uOffsets[i]; + if (i == numStops - 1) { // last index + offsetMax = colorGradientUniforms.uStops[i][0]; } } if (y < offsetMin || y > offsetMax) { return currentColor; } // limit colors - if (colorGradientUniforms.uMaxColors > 0) { - var stepSize: f32 = 1.0 / f32(colorGradientUniforms.uMaxColors); + if (uMaxColors > 0.0) { + var stepSize: f32 = 1.0 / uMaxColors; var stepNumber: f32 = floor(y / stepSize); y = stepSize * (stepNumber + 0.5); // offset by 0.5 to use color from middle of segment } @@ -154,12 +159,12 @@ fn mainFragment( var stopTo: ColorStop; for (var i: i32 = 0; i < MAX_STOPS; i = i + 1) { - if (y >= colorGradientUniforms.uOffsets[i]) { - stopFrom = ColorStop(colorGradientUniforms.uOffsets[i], colorGradientUniforms.uColors[i], colorGradientUniforms.uAlphas[i]); - stopTo = ColorStop(colorGradientUniforms.uOffsets[i + 1], colorGradientUniforms.uColors[i + 1], colorGradientUniforms.uAlphas[i + 1]); + if (y >= colorGradientUniforms.uStops[i][0]) { + stopFrom = ColorStop(colorGradientUniforms.uStops[i][0], colorGradientUniforms.uColors[i], colorGradientUniforms.uStops[i][1]); + stopTo = ColorStop(colorGradientUniforms.uStops[i + 1][0], colorGradientUniforms.uColors[i + 1], colorGradientUniforms.uStops[i + 1][1]); } - if (i == colorGradientUniforms.uNumStops - 1) { // last index + if (i == numStops - 1) { // last index break; } } @@ -172,10 +177,10 @@ fn mainFragment( var relativePos: f32 = y - stopFrom.offset; // position from 0 to [segmentHeight] var relativePercent: f32 = relativePos / segmentHeight; // position in percent between [from.offset] and [to.offset]. - var gradientAlpha: f32 = colorGradientUniforms.uAlpha * currentColor.a; + var gradientAlpha: f32 = uAlpha * currentColor.a; var gradientColor: vec4 = mix(colorFrom, colorTo, relativePercent) * gradientAlpha; - if (colorGradientUniforms.uReplace == false) { + if (uReplace < 0.5) { // mix resulting color with current color return gradientColor + currentColor * (1.0 - gradientColor.a); } else {