Skip to content

Commit

Permalink
Update color gradient
Browse files Browse the repository at this point in the history
  • Loading branch information
bbazukun123 committed Jan 5, 2024
1 parent 7b959d1 commit 38a2b49
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 75 deletions.
78 changes: 44 additions & 34 deletions filters/color-gradient/src/ColorGradientFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -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<f32>',
},
uCounts: {
value: [
// Number of Stops
options.stops.length,
// Max Gradient Colors
options.maxColors,
],
type: 'vec2<f32>',
},

uColors: { value: new Float32Array(maxStops * 3), type: 'vec3<f32>', 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<f32>', size: maxStops },
},
},
});
Expand All @@ -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;
Expand All @@ -164,52 +174,52 @@ 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;
}

/**
* 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; }
}

43 changes: 24 additions & 19 deletions filters/color-gradient/src/color-gradient.frag
Original file line number Diff line number Diff line change
Expand Up @@ -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.;
Expand Down Expand Up @@ -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);

Expand All @@ -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];
}
}

Expand All @@ -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
}
Expand All @@ -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;
}
}
Expand All @@ -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 {
Expand Down
49 changes: 27 additions & 22 deletions filters/color-gradient/src/color-gradient.wgsl
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
struct ColorGradientUniforms {
uType: i32,
uAngle: f32,
uAlpha: f32,
uColors: array<vec3<f32>, 20>,
uOffsets: array<f32, 20>,
uAlphas: array<f32, 20>,
uNumStops: f32,
uMaxColors: f32,
uReplace: f32,
uOptions: vec4<f32>,
uCounts: vec2<f32>,
uColors: array<vec3<f32>, MAX_STOPS>,
uStops: array<vec4<f32>, MAX_STOPS>,
};

struct GlobalFilterUniforms {
Expand Down Expand Up @@ -121,30 +116,40 @@ fn mainFragment(
@location(0) uv : vec2<f32>,
@location(1) coord : vec2<f32>
) -> @location(0) vec4<f32> {
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<f32> = 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
}
Expand All @@ -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;
}
}
Expand All @@ -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<f32> = 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 {
Expand Down

0 comments on commit 38a2b49

Please sign in to comment.