Skip to content

Commit

Permalink
Update Bevel Filter
Browse files Browse the repository at this point in the history
  • Loading branch information
bbazukun123 committed Jan 3, 2024
1 parent b451567 commit 5014a88
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 111 deletions.
232 changes: 138 additions & 94 deletions filters/bevel/src/BevelFilter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import { vertex } from '@tools/fragments';
import { vertex, wgslVertex } from '@tools/fragments';
import fragment from './bevel.frag';
import { Filter, DEG_TO_RAD, GlProgram } from 'pixi.js';
import source from './bevel.wgsl';
import { Filter, DEG_TO_RAD, GlProgram, ColorSource, GpuProgram, Color } from 'pixi.js';

export interface BevelFilterOptions
{
rotation: number,
thickness: number,
lightColor: number,
lightAlpha: number,
shadowColor: number,
shadowAlpha: number,
/**
* The angle of the light in degrees
* @default 45
*/
rotation?: number,
/**
* The thickness of the bevel
* @default 2
*/
thickness?: number,
/**
* The color value of the left & top bevel.
* @example [1.0, 1.0, 1.0] = 0xffffff
* @default 0xffffff
*/
lightColor?: ColorSource,
/**
* The alpha value of the left & top bevel.
* @default 0.7
*/
lightAlpha?: number,
/**
* The color value of the right & bottom bevel.
* @example [1.0, 1.0, 1.0] = 0xffffff
* @default 0x000000
*/
shadowColor?: ColorSource,
/**
* The alpha value of the right & bottom bevel.
* @default 0.7
*/
shadowAlpha?: number,
}

/**
Expand All @@ -23,136 +50,153 @@ export interface BevelFilterOptions
*/
export class BevelFilter extends Filter
{
private _thickness = 2;
private _angle = 0;
/** Default values for options. */
public static readonly DEFAULT_OPTIONS: BevelFilterOptions = {
rotation: 45,
thickness: 2,
lightColor: 0xffffff,
lightAlpha: 0.7,
shadowColor: 0x000000,
shadowAlpha: 0.7,
};

/**
* @param {object} [options] - The optional parameters of the filter.
* @param {number} [options.rotation = 45] - The angle of the light in degrees.
* @param {number} [options.thickness = 2] - The tickness of the bevel.
* @param {number} [options.lightColor = 0xffffff] - Color of the light.
* @param {number} [options.lightAlpha = 0.7] - Alpha of the light.
* @param {number} [options.shadowColor = 0x000000] - Color of the shadow.
* @param {number} [options.shadowAlpha = 0.7] - Alpha of the shadow.
*/
constructor(options?: Partial<BevelFilterOptions>)
public uniforms: {
uLightColor: Float32Array;
uLightAlpha: number;
uShadowColor: Float32Array;
uShadowAlpha: number;
uTransform: Float32Array;
};

private _thickness!: number;
private _rotation!: number;
private _LightColor: Color;
private _shadowColor: Color;

constructor(options?: BevelFilterOptions)
{
options = { ...BevelFilter.DEFAULT_OPTIONS, ...options };

const rotation = (options.rotation ?? 45) * DEG_TO_RAD;

const gpuProgram = new GpuProgram({
vertex: {
source: wgslVertex,
entryPoint: 'mainVertex',
},
fragment: {
source,
entryPoint: 'mainFragment',
},
});

const glProgram = new GlProgram({
vertex,
fragment,
name: 'bevel-filter',
});

super({
gpuProgram,
glProgram,
resources: {},
resources: {
bevelUniforms: {
uLightColor: { value: new Float32Array(3), type: 'vec3<f32>' },
uLightAlpha: { value: options.lightAlpha, type: 'f32' },
uShadowColor: { value: new Float32Array(3), type: 'vec3<f32>' },
uShadowAlpha: { value: options.shadowAlpha, type: 'f32' },
uTransform: { value: new Float32Array(2), type: 'vec2<f32>' },
}
},
// Workaround: https://github.com/pixijs/filters/issues/230
// applies correctly only if there is at least a single-pixel padding with alpha=0 around an image
// To solve this problem, a padding of 1 put on the filter should suffice
padding: 1,
});

// this.uniforms.lightColor = new Float32Array(3);
// this.uniforms.shadowColor = new Float32Array(3);

Object.assign(this, {
rotation: 45,
thickness: 2,
lightColor: 0xffffff,
lightAlpha: 0.7,
shadowColor: 0x000000,
shadowAlpha: 0.7,
}, options);

// Workaround: https://github.com/pixijs/filters/issues/230
// applies correctly only if there is at least a single-pixel padding with alpha=0 around an image
// To solve this problem, a padding of 1 put on the filter should suffice
this.padding = 1;
}
this.uniforms = this.resources.bevelUniforms.uniforms;

/**
* Update the transform matrix of offset angle.
* @private
*/
private _updateTransform()
{
// this.uniforms.transformX = this._thickness * Math.cos(this._angle);
// this.uniforms.transformY = this._thickness * Math.sin(this._angle);
this._LightColor = new Color();
this._shadowColor = new Color();

Object.assign(this, options, { rotation });
}

/**
* The angle of the light in degrees.
* The angle of the light in degrees
* @default 45
*/
get rotation(): number
{
return this._angle / DEG_TO_RAD;
}
get rotation(): number { return this._rotation / DEG_TO_RAD; }
set rotation(value: number)
{
this._angle = value * DEG_TO_RAD;
this._rotation = value * DEG_TO_RAD;
this._updateTransform();
}

/**
* The tickness of the bevel.
* The thickness of the bevel
* @default 2
*/
get thickness(): number
{
return this._thickness;
}
get thickness(): number { return this._thickness; }
set thickness(value: number)
{
this._thickness = value;
this._updateTransform();
}

/**
* Color of the light.
* The color value of the left & top bevel.
* @example [1.0, 1.0, 1.0] = 0xffffff
* @default 0xffffff
*/
// get lightColor(): number
// {
// return utils.rgb2hex(this.uniforms.lightColor);
// }
// set lightColor(value: number)
// {
// utils.hex2rgb(value, this.uniforms.lightColor);
// }
get lightColor(): ColorSource { return this._LightColor.value as ColorSource; }
set lightColor(value: ColorSource)
{
this._LightColor.setValue(value);
const [r, g, b] = this._LightColor.toArray();

this.uniforms.uLightColor[0] = r;
this.uniforms.uLightColor[1] = g;
this.uniforms.uLightColor[2] = b;
}

/**
* Alpha of the light.
* The alpha value of the left & top bevel.
* @default 0.7
*/
// get lightAlpha(): number
// {
// return this.uniforms.lightAlpha;
// }
// set lightAlpha(value: number)
// {
// this.uniforms.lightAlpha = value;
// }
get lightAlpha(): number { return this.uniforms.uLightAlpha; }
set lightAlpha(value: number) { this.uniforms.uLightAlpha = value; }

/**
* Color of the shadow.
* The color value of the right & bottom bevel.
* @default 0xffffff
*/
get shadowColor(): ColorSource { return this._shadowColor.value as ColorSource; }
set shadowColor(value: ColorSource)
{
this._shadowColor.setValue(value);
const [r, g, b] = this._shadowColor.toArray();

this.uniforms.uShadowColor[0] = r;
this.uniforms.uShadowColor[1] = g;
this.uniforms.uShadowColor[2] = b;
}

/**
* The color value of the right & bottom bevel.
* @example [1.0, 1.0, 1.0] = 0xffffff
* @default 0x000000
*/
// get shadowColor(): number
// {
// return utils.rgb2hex(this.uniforms.shadowColor);
// }
// set shadowColor(value: number)
// {
// utils.hex2rgb(value, this.uniforms.shadowColor);
// }
get shadowAlpha(): number { return this.uniforms.uShadowAlpha; }
set shadowAlpha(value: number) { this.uniforms.uShadowAlpha = value; }

/**
* Alpha of the shadow.
* @default 0.7
* Update the transform matrix of offset angle.
* @private
*/
// get shadowAlpha(): number
// {
// return this.uniforms.shadowAlpha;
// }
// set shadowAlpha(value: number)
// {
// this.uniforms.shadowAlpha = value;
// }
private _updateTransform()
{
this.uniforms.uTransform[0] = this.thickness * Math.cos(this._rotation);
this.uniforms.uTransform[1] = this.thickness * Math.sin(this._rotation);
}
}
32 changes: 16 additions & 16 deletions filters/bevel/src/bevel.frag
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
precision mediump float;
precision highp float;
in vec2 vTextureCoord;
out vec4 finalColor;

varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec4 filterArea;
uniform vec2 uTransform;
uniform vec3 uLightColor;
uniform float uLightAlpha;
uniform vec3 uShadowColor;
uniform float uShadowAlpha;

uniform float transformX;
uniform float transformY;
uniform vec3 lightColor;
uniform float lightAlpha;
uniform vec3 shadowColor;
uniform float shadowAlpha;
uniform vec4 uInputSize;

void main(void) {
vec2 transform = vec2(1.0 / filterArea) * vec2(transformX, transformY);
vec4 color = texture2D(uSampler, vTextureCoord);
float light = texture2D(uSampler, vTextureCoord - transform).a;
float shadow = texture2D(uSampler, vTextureCoord + transform).a;
vec2 transform = vec2(1.0 / uInputSize) * vec2(uTransform.x, uTransform.y);
vec4 color = texture(uSampler, vTextureCoord);
float light = texture(uSampler, vTextureCoord - transform).a;
float shadow = texture(uSampler, vTextureCoord + transform).a;

color.rgb = mix(color.rgb, lightColor, clamp((color.a - light) * lightAlpha, 0.0, 1.0));
color.rgb = mix(color.rgb, shadowColor, clamp((color.a - shadow) * shadowAlpha, 0.0, 1.0));
gl_FragColor = vec4(color.rgb * color.a, color.a);
color.rgb = mix(color.rgb, uLightColor, clamp((color.a - light) * uLightAlpha, 0.0, 1.0));
color.rgb = mix(color.rgb, uShadowColor, clamp((color.a - shadow) * uShadowAlpha, 0.0, 1.0));
finalColor = vec4(color.rgb * color.a, color.a);
}
40 changes: 40 additions & 0 deletions filters/bevel/src/bevel.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
struct BevelUniforms {
uLightColor: vec3<f32>,
uLightAlpha: f32,
uShadowColor: vec3<f32>,
uShadowAlpha: f32,
uTransform: vec2<f32>,
};

struct GlobalFilterUniforms {
uInputSize:vec4<f32>,
uInputPixel:vec4<f32>,
uInputClamp:vec4<f32>,
uOutputFrame:vec4<f32>,
uGlobalFrame:vec4<f32>,
uOutputTexture:vec4<f32>,
};

@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;

@group(0) @binding(1) var uSampler: texture_2d<f32>;
@group(1) @binding(0) var<uniform> bevelUniforms : BevelUniforms;

@fragment
fn mainFragment(
@builtin(position) position: vec4<f32>,
@location(0) uv : vec2<f32>
) -> @location(0) vec4<f32> {
let transform = vec2<f32>(1.0 / gfu.uInputSize.xy) * vec2<f32>(bevelUniforms.uTransform.x, bevelUniforms.uTransform.y);
var color: vec4<f32> = textureSample(uSampler, uSampler, uv);
let lightSample: f32 = textureSample(uSampler, uSampler, uv - transform).a;
let shadowSample: f32 = textureSample(uSampler, uSampler, uv + transform).a;

let light = vec4<f32>(bevelUniforms.uLightColor, bevelUniforms.uLightAlpha);
let shadow = vec4<f32>(bevelUniforms.uShadowColor, bevelUniforms.uShadowAlpha);

color = vec4<f32>(mix(color.rgb, light.rgb, clamp((color.a - lightSample) * light.a, 0.0, 1.0)), color.a);
color = vec4<f32>(mix(color.rgb, shadow.rgb, clamp((color.a - shadowSample) * shadow.a, 0.0, 1.0)), color.a);

return vec4<f32>(color.rgb * color.a, color.a);
}
2 changes: 1 addition & 1 deletion tools/demo/src/filters/bevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function ()
oncreate(folder)
{
folder.add(this, 'rotation', 0, 360);
folder.add(this, 'thickness', 0, 5);
folder.add(this, 'thickness', 0, 10);
folder.addColor(this, 'lightColor');
folder.add(this, 'lightAlpha', 0, 1);
folder.addColor(this, 'shadowColor');
Expand Down
1 change: 1 addition & 0 deletions tools/demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const main = async () =>
filters.colorMap.call(app);
filters.colorGradient.call(app);
filters.bulgePinch.call(app);
filters.bevel.call(app);
// filters.kawaseBlur.call(app);

// TODO: Re-enable this in place of the above once v8 conversion is complete
Expand Down

0 comments on commit 5014a88

Please sign in to comment.