feature: add PixelSorter

This commit is contained in:
FMS-Cat
2021-03-14 11:35:05 +09:00
parent 033ff1d945
commit ba9d4740ed
5 changed files with 231 additions and 2 deletions

View File

@@ -1 +1 @@
{"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]}],"channels":[["Glitch/amp",{"items":[{"length":1,"curve":0}]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":false,"bpm":140,"beatOffset":0,"useBeatInGUI":false,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}} {"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]}],"channels":[["Glitch/amp",{"items":[{"length":1,"curve":0}]}],["PixelSorter/amp",{"items":[{},{"time":1,"length":1,"curve":0}]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":false,"bpm":140,"beatOffset":0,"useBeatInGUI":false,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}}

125
src/entities/PixelSorter.ts Normal file
View File

@@ -0,0 +1,125 @@
import { Entity } from '../heck/Entity';
import { GLCatTexture } from '@fms-cat/glcat-ts';
import { Material } from '../heck/Material';
import { Quad } from '../heck/components/Quad';
import { RenderTarget } from '../heck/RenderTarget';
import pixelSorterIndexFrag from '../shaders/pixel-sorter-index.frag';
import pixelSorterFrag from '../shaders/pixel-sorter.frag';
import quadVert from '../shaders/quad.vert';
import { BufferRenderTarget } from '../heck/BufferRenderTarget';
import { Swap } from '@fms-cat/experimental';
import { DISPLAY } from '../heck/DISPLAY';
import { Automaton } from '@fms-cat/automaton';
export interface PixelSorterOptions {
input: GLCatTexture<WebGL2RenderingContext>;
target: RenderTarget;
automaton: Automaton;
}
export class PixelSorter {
public entity: Entity;
public swapBuffer: Swap<BufferRenderTarget>;
public constructor( options: PixelSorterOptions ) {
const { gl } = DISPLAY;
this.entity = new Entity();
this.entity.visible = false;
this.swapBuffer = new Swap(
new BufferRenderTarget( {
width: options.target.width,
height: options.target.height,
numBuffers: 2,
name: process.env.DEV && 'PixelSorter/swap0',
} ),
new BufferRenderTarget( {
width: options.target.width,
height: options.target.height,
numBuffers: 2,
name: process.env.DEV && 'PixelSorter/swap1',
} ),
);
// -- calc index -------------------------------------------------------------------------------
let mul = 1;
const indexMaterials: Material[] = [];
while ( mul < options.target.width ) {
const isFirst = mul === 1;
const material = new Material(
quadVert,
pixelSorterIndexFrag,
);
material.addUniform( 'mul', '1f', mul );
material.addUniformTexture(
'sampler0',
isFirst ? options.input : this.swapBuffer.o.getTexture( gl.COLOR_ATTACHMENT0 ),
);
material.addUniformTexture(
'sampler1',
this.swapBuffer.o.getTexture( gl.COLOR_ATTACHMENT1 ),
);
indexMaterials.push( material );
this.entity.components.push( new Quad( {
target: this.swapBuffer.i,
material,
name: process.env.DEV && `PixelSorter/quadIndex-${ mul }`,
} ) );
this.swapBuffer.swap();
mul *= 8;
}
// -- sort -------------------------------------------------------------------------------------
let dir = 1.0 / 64.0;
let comp = 1.0 / 64.0;
while ( dir < 1.0 ) {
const isLast = ( dir === 0.5 ) && ( comp === 1.0 / 64.0 );
const material = new Material(
quadVert,
pixelSorterFrag,
);
material.addUniform( 'dir', '1f', dir );
material.addUniform( 'comp', '1f', comp );
material.addUniformTexture(
'sampler0',
this.swapBuffer.o.getTexture( gl.COLOR_ATTACHMENT0 ),
);
material.addUniformTexture(
'sampler1',
this.swapBuffer.o.getTexture( gl.COLOR_ATTACHMENT1 ),
);
this.entity.components.push( new Quad( {
target: isLast ? options.target : this.swapBuffer.i,
material,
name: process.env.DEV && `PixelSorter/quad-${ dir }-${ comp }`,
} ) );
this.swapBuffer.swap();
if ( comp === 1.0 / 64.0 ) {
dir *= 2.0;
comp = dir;
} else {
comp /= 2.0;
}
}
// -- update uniform ---------------------------------------------------------------------------
options.automaton.auto( 'PixelSorter/amp', ( { value } ) => {
indexMaterials.map( ( material ) => {
material.addUniform( 'threshold', '1f', value );
} );
this.entity.active = 0.0 < value;
} );
}
}

View File

@@ -23,6 +23,7 @@ import { Rings } from './entities/Rings';
import { RTInspector } from './entities/RTInspector'; import { RTInspector } from './entities/RTInspector';
import { Component } from './heck/components/Component'; import { Component } from './heck/components/Component';
import { FlickyParticles } from './entities/FlickyParticles'; import { FlickyParticles } from './entities/FlickyParticles';
import { PixelSorter } from './entities/PixelSorter';
// == gl =========================================================================================== // == gl ===========================================================================================
const { canvas, glCat } = DISPLAY; const { canvas, glCat } = DISPLAY;
@@ -166,7 +167,7 @@ if ( process.env.DEV ) {
); );
} }
// == random texture =============================================================================== // == textures =====================================================================================
const randomTexture = new RandomTexture( const randomTexture = new RandomTexture(
glCat, glCat,
RANDOM_RESOLUTION[ 0 ], RANDOM_RESOLUTION[ 0 ],
@@ -316,6 +317,14 @@ const glitch = new Glitch( {
} ); } );
dog.root.children.push( glitch.entity ); dog.root.children.push( glitch.entity );
swap.swap();
const pixelSorter = new PixelSorter( {
input: swap.i.texture,
target: swap.o,
automaton,
} );
dog.root.children.push( pixelSorter.entity );
swap.swap(); swap.swap();
const post = new Post( { const post = new Post( {
input: swap.i.texture, input: swap.i.texture,

View File

@@ -0,0 +1,37 @@
#version 300 es
precision highp float;
const vec3 RGB = vec3( 0.299, 0.587, 0.114 );
in vec2 vUv;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec4 fragClamp;
uniform float threshold;
uniform float mul;
uniform vec2 resolution;
uniform sampler2D sampler0;
uniform sampler2D sampler1;
vec2 getValue( vec2 uv ) {
return ( ( uv.x < 0.0 ) || ( 1.0 < uv.x ) )
? vec2( 0.0 )
: ( mul == 1.0 )
? vec2( 1E9 * step( dot( texture( sampler0, uv ).xyz, RGB ), threshold ) )
: texture( sampler1, uv ).xy;
}
void main() {
vec2 uv = vUv;
fragColor = vec4( texture( sampler0, uv ).xyz, 1.0 );
fragClamp = vec4( getValue( uv ), 0.0, 1.0 );
for ( int i = 1; i < 8; i ++ ) {
vec2 uvc = uv - vec2( i, 0 ) / resolution * mul;
vec2 texc = getValue( uvc );
fragClamp.xy = min( fragClamp.xy, texc + mul * float( i ) );
}
}

View File

@@ -0,0 +1,58 @@
#version 300 es
precision highp float;
#define lofi(i,m) (floor((i)/(m))*(m))
const vec3 RGB = vec3( 0.299, 0.587, 0.114 );
in vec2 vUv;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec4 fragClamp;
uniform float dir;
uniform float comp;
uniform vec2 resolution;
uniform sampler2D sampler0;
uniform sampler2D sampler1;
float positiveOrHuge( float i ) {
return 0.0 < i ? i : 1E9;
}
void main() {
vec2 uv = vUv;
fragColor = texture( sampler0, uv );
fragClamp = texture( sampler1, uv );
if ( fragClamp.x < 0.5 ) {
return;
}
float index = fragClamp.x - 1.0;
float width = fragClamp.x + fragClamp.y - 1.0;
bool isCompRight = mod( index, 2.0 * comp * width ) < comp * width;
float offset = floor( ( isCompRight ? comp : -comp ) * width + 0.5 );
vec2 uvc = uv;
uvc.x += offset / resolution.x;
vec4 cColor = texture( sampler0, uvc );
vec4 cClamp = texture( sampler1, uvc );
if ( uvc.x < 0.0 || 1.0 < uvc.x ) {
return;
}
float vp = dot( fragColor.xyz, RGB );
float vc = dot( cColor.xyz, RGB );
bool shouldSwap = mod( index / ( 2.0 * dir * width ), 2.0 ) < 1.0;
shouldSwap = shouldSwap ^^ isCompRight;
shouldSwap = shouldSwap ^^ ( vc < vp );
if ( shouldSwap ) {
fragColor = cColor;
}
}