feature: add serial + flashy-terrain

This commit is contained in:
FMS-Cat
2021-03-27 18:21:25 +09:00
parent d1c5ea7b5d
commit f683f09e54
11 changed files with 487 additions and 14 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,79 @@
import { Entity } from '../heck/Entity';
import { Geometry } from '../heck/Geometry';
import { Material } from '../heck/Material';
import { Mesh } from '../heck/components/Mesh';
import { Quaternion, Vector3 } from '@fms-cat/experimental';
import { dummyRenderTarget, dummyRenderTargetFourDrawBuffers } from '../globals/dummyRenderTarget';
import { genPlane } from '../geometries/genPlane';
import { quadGeometry } from '../globals/quadGeometry';
import depthFrag from '../shaders/depth.frag';
import flashyTerrainFrag from '../shaders/flashy-terrain.frag';
import flashyTerrainVert from '../shaders/flashy-terrain.vert';
export class FlashyTerrain extends Entity {
public mesh: Mesh;
public constructor() {
super();
this.transform.position = new Vector3( [ 0.0, -4.0, 0.0 ] );
this.transform.rotation = Quaternion.fromAxisAngle(
new Vector3( [ 1.0, 0.0, 0.0 ] ),
-0.5 * Math.PI,
);
this.transform.scale = this.transform.scale.scale( 8.0 );
// -- geometry ---------------------------------------------------------------------------------
const plane = genPlane();
const geometry = new Geometry();
geometry.vao.bindVertexbuffer( plane.position, 0, 3 );
geometry.vao.bindIndexbuffer( plane.index );
geometry.count = plane.count;
geometry.mode = plane.mode;
geometry.indexType = plane.indexType;
// -- materials --------------------------------------------------------------------------------
const deferred = new Material(
flashyTerrainVert,
flashyTerrainFrag,
{
defines: [ 'DEFERRED 1' ],
initOptions: { geometry: quadGeometry, target: dummyRenderTargetFourDrawBuffers },
},
);
const depth = new Material(
flashyTerrainVert,
depthFrag,
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
);
const materials = { deferred, depth };
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept(
[
'../shaders/flashy-terrain.vert',
'../shaders/flashy-terrain.frag',
],
() => {
deferred.replaceShader( flashyTerrainVert, flashyTerrainFrag );
depth.replaceShader( flashyTerrainVert, depthFrag );
},
);
}
}
// -- mesh -------------------------------------------------------------------------------------
this.mesh = new Mesh( {
geometry,
materials,
name: process.env.DEV && 'FlashyTerrain/mesh',
} );
this.components.push( this.mesh );
}
}

113
src/entities/Serial.ts Normal file
View File

@@ -0,0 +1,113 @@
import { Blit } from '../heck/components/Blit';
import { BufferRenderTarget } from '../heck/BufferRenderTarget';
import { Entity } from '../heck/Entity';
import { Material } from '../heck/Material';
import { Quad } from '../heck/components/Quad';
import { RenderTarget } from '../heck/RenderTarget';
import { auto } from '../globals/automaton';
import { dummyRenderTarget } from '../globals/dummyRenderTarget';
import { gl } from '../globals/canvas';
import { quadGeometry } from '../globals/quadGeometry';
import { randomTexture } from '../globals/randomTexture';
import quadVert from '../shaders/quad.vert';
import serialDecodeFrag from '../shaders/serial-decode.frag';
import serialEncodeFrag from '../shaders/serial-encode.frag';
export interface SerialOptions {
input: BufferRenderTarget;
target: RenderTarget;
}
export class Serial extends Entity {
public constructor( options: SerialOptions ) {
super();
const entityBypass = new Entity();
entityBypass.visible = false;
this.children.push( entityBypass );
const entityMain = new Entity();
entityMain.active = false;
entityMain.visible = false;
this.children.push( entityMain );
// -- bypass -----------------------------------------------------------------------------------
entityBypass.components.push( new Blit( {
src: options.input,
dst: options.target,
name: 'Serial/blitBypass',
} ) );
// -- encode -----------------------------------------------------------------------------------
const bufferEncode = new BufferRenderTarget( {
width: 4096,
height: 240,
name: process.env.DEV && 'Serial/bufferEncode',
} );
const materialEncode = new Material(
quadVert,
serialEncodeFrag,
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
);
materialEncode.addUniformTexture( 'sampler0', options.input.texture );
materialEncode.addUniformTexture( 'samplerRandom', randomTexture.texture );
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept( '../shaders/serial-encode.frag', () => {
materialEncode.replaceShader( quadVert, serialEncodeFrag );
} );
}
}
entityMain.components.push( new Quad( {
target: bufferEncode,
material: materialEncode,
name: process.env.DEV && 'Serial/quadEncode',
} ) );
// -- decode -----------------------------------------------------------------------------------
const bufferDecode = new BufferRenderTarget( {
width: 320,
height: 240,
name: process.env.DEV && 'Serial/bufferDecode',
} );
const materialDecode = new Material(
quadVert,
serialDecodeFrag,
{ initOptions: { geometry: quadGeometry, target: dummyRenderTarget } },
);
materialDecode.addUniformTexture( 'sampler0', bufferEncode.texture );
materialDecode.addUniformTexture( 'samplerRandom', randomTexture.texture );
if ( process.env.DEV ) {
if ( module.hot ) {
module.hot.accept( '../shaders/serial-decode.frag', () => {
materialDecode.replaceShader( quadVert, serialDecodeFrag );
} );
}
}
entityMain.components.push( new Quad( {
target: bufferDecode,
material: materialDecode,
name: process.env.DEV && 'Serial/quadDecode',
} ) );
// -- blit to target -----------------------------------------------------------------------------
entityMain.components.push( new Blit( {
src: bufferDecode,
dst: options.target,
name: 'Serial/blitTarget',
filter: gl.LINEAR,
} ) );
// -- auto -------------------------------------------------------------------------------------
auto( 'Serial/enable', ( { uninit } ) => {
entityMain.active = !uninit;
entityBypass.active = !entityMain.active;
} );
}
}

View File

@@ -0,0 +1,56 @@
import { GLCatBuffer } from '@fms-cat/glcat-ts';
import { gl, glCat } from '../globals/canvas';
interface ResultGenPlane {
position: GLCatBuffer;
index: GLCatBuffer;
count: number;
mode: GLenum;
indexType: GLenum;
}
export function genPlane( options?: {
segment?: number;
} ): ResultGenPlane {
const segment = options?.segment ?? 33;
const segmentMinusOne = segment - 1;
const pos: number[] = [];
const ind: number[] = [];
for ( let iY = 0; iY < segmentMinusOne; iY ++ ) {
const y = iY / segmentMinusOne * 2.0 - 1.0;
for ( let iX = 0; iX < segmentMinusOne; iX ++ ) {
const x = iX / segmentMinusOne * 2.0 - 1.0;
const i = iX + iY * segment;
pos.push( x, y, 0 );
ind.push(
i, i + 1, i + segment + 1,
i, i + segment + 1, i + segment,
);
}
pos.push( 1, y, 0 );
}
for ( let iX = 0; iX < segment; iX ++ ) {
const x = iX / segmentMinusOne * 2.0 - 1.0;
pos.push( x, 1, 0 );
}
const position = glCat.createBuffer();
position.setVertexbuffer( new Float32Array( pos ) );
const index = glCat.createBuffer();
index.setIndexbuffer( new Uint32Array( ind ) );
return {
position,
index,
count: ind.length,
mode: gl.TRIANGLES,
indexType: gl.UNSIGNED_INT,
};
}

View File

@@ -10,6 +10,7 @@ import { CubemapCameraEntity } from './entities/CubemapCameraEntity';
import { Dog } from './heck/Dog'; import { Dog } from './heck/Dog';
import { Entity } from './heck/Entity'; import { Entity } from './heck/Entity';
import { EnvironmentMap } from './entities/EnvironmentMap'; import { EnvironmentMap } from './entities/EnvironmentMap';
import { FlashyTerrain } from './entities/FlashyTerrain';
import { FlickyParticles } from './entities/FlickyParticles'; import { FlickyParticles } from './entities/FlickyParticles';
import { Glitch } from './entities/Glitch'; import { Glitch } from './entities/Glitch';
import { IBLLUT } from './entities/IBLLUT'; import { IBLLUT } from './entities/IBLLUT';
@@ -20,6 +21,7 @@ import { Post } from './entities/Post';
import { RTInspector } from './entities/RTInspector'; import { RTInspector } from './entities/RTInspector';
import { Raymarcher } from './entities/Raymarcher'; import { Raymarcher } from './entities/Raymarcher';
import { Rings } from './entities/Rings'; import { Rings } from './entities/Rings';
import { Serial } from './entities/Serial';
import { SphereParticles } from './entities/SphereParticles'; import { SphereParticles } from './entities/SphereParticles';
import { SufferTexts } from './entities/SufferTexts'; import { SufferTexts } from './entities/SufferTexts';
import { Swap, Vector3 } from '@fms-cat/experimental'; import { Swap, Vector3 } from '@fms-cat/experimental';
@@ -98,10 +100,17 @@ if ( process.env.DEV && module.hot ) {
} ); } );
} }
const replacerSVGTest = new EntityReplacer( () => new Condition(), 'Condition' ); const replacerCondition = new EntityReplacer( () => new Condition(), 'Condition' );
if ( process.env.DEV && module.hot ) { if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/Condition', () => { module.hot.accept( './entities/Condition', () => {
replacerSVGTest.replace(); replacerCondition.replace();
} );
}
const replacerFlashyTerrain = new EntityReplacer( () => new FlashyTerrain(), 'FlashyTerrain' );
if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/FlashyTerrain', () => {
replacerFlashyTerrain.replace();
} ); } );
} }
@@ -245,9 +254,13 @@ const camera = new CameraEntity( {
camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ]; camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ];
camera.components.unshift( new Lambda( { camera.components.unshift( new Lambda( {
onUpdate: ( { time } ) => { onUpdate: ( { time } ) => {
const r = auto( 'Camera/r' ); const r = auto( 'Camera/rot/r' );
const t = auto( 'Camera/t' ); const t = auto( 'Camera/rot/t' );
const p = auto( 'Camera/p' ); const p = auto( 'Camera/rot/p' );
const x = auto( 'Camera/pos/x' );
const y = auto( 'Camera/pos/y' );
const z = auto( 'Camera/pos/z' );
const roll = auto( 'Camera/roll' );
const st = Math.sin( t ); const st = Math.sin( t );
const ct = Math.cos( t ); const ct = Math.cos( t );
@@ -261,17 +274,17 @@ camera.components.unshift( new Lambda( {
camera.transform.lookAt( camera.transform.lookAt(
new Vector3( [ new Vector3( [
r * ct * sp + wubPosAmp * Math.sin( wubPosTheta ), r * ct * sp + wubPosAmp * Math.sin( wubPosTheta ) + x,
r * st + wubPosAmp * Math.sin( 2.0 + wubPosTheta ), r * st + wubPosAmp * Math.sin( 2.0 + wubPosTheta ) + y,
r * ct * cp + wubPosAmp * Math.sin( 4.0 + wubPosTheta ), r * ct * cp + wubPosAmp * Math.sin( 4.0 + wubPosTheta ) + z,
] ), ] ),
new Vector3( [ new Vector3( [
wubTarAmp * Math.sin( wubTarTheta ), wubTarAmp * Math.sin( wubTarTheta ) + x,
wubTarAmp * Math.sin( 2.0 + wubTarTheta ), wubTarAmp * Math.sin( 2.0 + wubTarTheta ) + y,
wubTarAmp * Math.sin( 4.0 + wubTarTheta ), wubTarAmp * Math.sin( 4.0 + wubTarTheta ) + z,
] ), ] ),
undefined, undefined,
0.02 * Math.sin( 2.74 * time ), 0.02 * Math.sin( 2.74 * time ) + roll,
); );
}, },
name: process.env.DEV && 'main/updateCamera', name: process.env.DEV && 'main/updateCamera',
@@ -306,6 +319,13 @@ const pixelSorter = new PixelSorter( {
} ); } );
dog.root.children.push( pixelSorter ); dog.root.children.push( pixelSorter );
swap.swap();
const serial = new Serial( {
input: swap.i,
target: swap.o,
} );
dog.root.children.push( serial );
swap.swap(); swap.swap();
const post = new Post( { const post = new Post( {
input: swap.i, input: swap.i,

View File

@@ -0,0 +1,40 @@
#version 300 es
precision highp float;
const float TAU = 6.283185307;
in float vInstanceId;
in vec3 vNormal;
in vec4 vPosition;
in vec4 vPositionWithoutModel;
#ifdef DEFERRED
layout (location = 0) out vec4 fragPosition;
layout (location = 1) out vec4 fragNormal;
layout (location = 2) out vec4 fragColor;
layout (location = 3) out vec4 fragWTF;
#endif
uniform float time;
#pragma glslify: cyclicNoise = require( ./modules/cyclicNoise );
void main() {
float grid = max(
step( 0.996, cos( 16.0 * TAU * vPositionWithoutModel.x ) ),
step( 0.996, cos( 16.0 * TAU * vPositionWithoutModel.y ) )
);
vec2 cell = floor( 16.0 * vPositionWithoutModel.xy );
grid = max(
grid,
smoothstep( 0.2, 0.3, cyclicNoise( vec3( cell.xy, 4.0 * time ) ).x )
);
#ifdef DEFERRED
fragPosition = vPosition;
fragNormal = vec4( 0.0, 0.0, 1.0, 1.0 );
fragColor = vec4( grid * vec3( 2.0 ), 1.0 );
fragWTF = vec4( 0.0, 0.0, 0.0, 1 );
#endif
}

View File

@@ -0,0 +1,30 @@
#version 300 es
layout (location = 0) in vec3 position;
out vec4 vPositionWithoutModel;
out vec4 vPosition;
uniform float time;
uniform vec2 resolution;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform mat4 normalMatrix;
#pragma glslify: cyclicNoise = require( ./modules/cyclicNoise );
void main() {
vPositionWithoutModel = vec4( position, 1.0 );
float n = 0.5 + 0.5 * cyclicNoise( 2.0 * vPositionWithoutModel.xyz - vec3( 0.0, time, 0.0 ) ).x;
vPositionWithoutModel.z += 1.0 * n * exp( -2.0 * length( vPositionWithoutModel.xy ) );
vPosition = modelMatrix * vPositionWithoutModel;
vec4 outPos = projectionMatrix * viewMatrix * vPosition;
outPos.x *= resolution.y / resolution.x;
gl_Position = outPos;
vPosition.w = outPos.z / outPos.w;
}

View File

@@ -0,0 +1,5 @@
float fractSin( float s ) {
return fract( sin( s * 114.514 ) * 1919.810 );
}
#pragma glslify: export(fractSin)

View File

@@ -0,0 +1,49 @@
#version 300 es
precision highp float;
const int DECODE_ITER = 10;
const float INV_DECODE_ITER = 1.0 / float( DECODE_ITER );
const float DECODE_PERIOD = 2.0;
const float CHROMA_AMP = 0.4;
const float PI = 3.14159265;
const float TAU = PI * 2.0;
const vec2 CHROMA_FREQ = vec2( 227.5, 120.0 );
const mat3 YCBCR_TO_RGB = mat3( 1.0, 1.0, 1.0, 0.0, -0.344136, 1.772, 1.402, -0.714136, 0.0 );
#define saturate(i) clamp(i,0.,1.)
#define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a)))
#define lofi(i,m) (floor((i)/(m))*(m))
in vec2 vUv;
out vec4 fragColor;
uniform float time;
uniform vec2 resolution;
uniform sampler2D sampler0;
// https://www.shadertoy.com/view/3sKSzW
void main() {
// YCbCr
float y = 0.0;
vec2 cbcr = vec2( 0.0 );
// sample
vec2 sampleOffset = vec2( 1.0, 0.0 ) / CHROMA_FREQ.x * DECODE_PERIOD * INV_DECODE_ITER;
for ( int i = -DECODE_ITER / 2; i < DECODE_ITER / 2; i ++ ) {
vec2 uvt = vUv - float( i ) * sampleOffset;
float tex = texture( sampler0, uvt ).x * INV_DECODE_ITER;
y += tex;
float phase = TAU * dot( CHROMA_FREQ, uvt );
cbcr += tex * vec2( cos( phase ), sin( phase ) );
}
// back to rgb
vec3 col = YCBCR_TO_RGB * vec3(
saturate( 1.2 * ( linearstep( CHROMA_AMP, 1.0 - CHROMA_AMP, y ) - 0.5 ) + 0.5 ),
PI * cbcr / CHROMA_AMP
);
fragColor = vec4( col, 1.0 );
}

View File

@@ -0,0 +1,81 @@
#version 300 es
precision highp float;
const int LPF_ITER = 10;
const float INV_LPF_ITER = 1.0 / float( LPF_ITER );
const float LPF_WIDTH = 0.04;
const float CHROMA_AMP = 0.4;
const float DECODE_PERIOD = 2.0;
const float PI = 3.14159265;
const float TAU = PI * 2.0;
const float NOISE_CLUTCH = 0.1;
const vec2 CHROMA_FREQ = vec2( 227.5, 120.0 );
const mat3 RGB_TO_YCBCR = mat3( 0.299, -0.168736, 0.5, 0.587, -0.331264, -0.418688, 0.114, 0.5, -0.081312 );
#define saturate(i) clamp(i,0.,1.)
#define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a)))
#define lofi(i,m) (floor((i)/(m))*(m))
in vec2 vUv;
out vec4 fragColor;
uniform float time;
uniform vec2 resolution;
uniform sampler2D sampler0;
#pragma glslify: fractSin = require( ./modules/fractSin );
#pragma glslify: cyclicNoise = require( ./modules/cyclicNoise );
// https://www.shadertoy.com/view/3sKSzW
void main() {
vec2 uv = vUv;
// prepare random noises
vec3 noise = cyclicNoise( vec3( uv, time ) );
vec3 hnoise = cyclicNoise( vec3( vec2( 1.0, 320.0 ) * uv, 100.0 * fract( time ) ) );
// offsync noise
vec3 offsyncPos = vec3( vec2( 0.02, 1.0 ) * uv, 0.0 );
offsyncPos -= vec3( 0.03, 0.2, 1.0 ) * time;
vec3 offsyncNoise = cyclicNoise( offsyncPos );
float offsyncAmp = offsyncNoise.y * linearstep( 0.5, 1.0, offsyncNoise.x );
offsyncAmp *= 1.0 - uv.y;
float offsyncChromaSup = 1.0 - linearstep( 0.0, 0.1, abs( offsyncAmp ) );
float phase = TAU * dot( CHROMA_FREQ, uv );
uv.x += 0.2 * offsyncAmp;
phase += CHROMA_FREQ.x * 0.05 * offsyncAmp;
vec4 tex = saturate( texture( sampler0, uv ) );
vec3 ycbcr = RGB_TO_YCBCR * tex.xyz;
// chroma signal will be filtered using LPF, this time we're gonna use cheap LPF
ycbcr.yz *= 0.0;
for ( int i = 1; i < LPF_ITER; i ++ ) {
vec2 uvt = uv - vec2( INV_LPF_ITER * LPF_WIDTH * float( i ), 0.0 );
vec4 tex = saturate( texture( sampler0, uvt ) );
ycbcr.yz += INV_LPF_ITER * ( RGB_TO_YCBCR * tex.xyz ).yz;
}
float signal = ycbcr.x; // y as base level
signal = mix( CHROMA_AMP, 1.0 - CHROMA_AMP, signal );
signal += CHROMA_AMP * offsyncChromaSup * (
ycbcr.y * cos( phase ) +
ycbcr.z * sin( phase )
); // cb as cosine of subcarrier
// static noise
signal += 0.01 * ( fractSin( noise.x ) - 0.5 );
// high peak noise
float bump = exp( -2.0 * fract( 10.0 * hnoise.z * ( 1.0 * hnoise.x + uv.x ) ) );
signal += 4.0 * pow(
( 0.5 + 0.5 * hnoise.y ) * bump,
mix( 20.0, 8.0, linearstep( 0.0, 0.01, abs( offsyncAmp ) ) )
);
fragColor = vec4( signal, 0.0, 0.0, 1.0 );
}

View File

@@ -122,7 +122,7 @@ void main() {
) { ) {
dt = time - timing; dt = time - timing;
pos = 15.0 * randomSphere( seed ); pos = 10.0 * randomSphere( seed );
vel = 1.0 * randomSphere( seed ); vel = 1.0 * randomSphere( seed );