refactor: even better automaton music integration

This commit is contained in:
FMS_Cat
2021-03-21 15:22:17 +09:00
parent fee60f1f9b
commit 4239eeea81
8 changed files with 201 additions and 160 deletions

View File

@@ -5,9 +5,10 @@ import musicVert from './shaders/music.vert';
import { gl, glCat } from './globals/canvas'; import { gl, glCat } from './globals/canvas';
import samplesOpus from './samples.opus'; import samplesOpus from './samples.opus';
import { randomTextureStatic } from './globals/randomTexture'; import { randomTextureStatic } from './globals/randomTexture';
import { automaton } from './globals/automaton'; import { auto, automaton } from './globals/automaton';
import { automatonMusicParamMap } from './automatonMusicParamMap'; import { Channel } from '@fms-cat/automaton';
import type { AutomatonWithGUI } from '@fms-cat/automaton-with-gui'; import { injectCodeToShader } from './utils/injectCodeToShader';
import { AutomatonWithGUI } from '@fms-cat/automaton-with-gui';
const discardFrag = '#version 300 es\nvoid main(){discard;}'; const discardFrag = '#version 300 es\nvoid main(){discard;}';
@@ -28,6 +29,8 @@ export class Music {
private __bufferPool: Pool<AudioBuffer>; private __bufferPool: Pool<AudioBuffer>;
private __prevBufferSource: AudioBufferSourceNode | null = null; private __prevBufferSource: AudioBufferSourceNode | null = null;
private __samples?: GLCatTexture; private __samples?: GLCatTexture;
private __automatonChannelList: Channel[];
private __automatonDefineString: string;
private __textureAutomaton: GLCatTexture; private __textureAutomaton: GLCatTexture;
constructor( glCat: GLCat, audio: AudioContext ) { constructor( glCat: GLCat, audio: AudioContext ) {
@@ -59,12 +62,18 @@ export class Music {
this.__transformFeedback.bindBuffer( 0, this.__bufferTransformFeedbacks[ 0 ] ); this.__transformFeedback.bindBuffer( 0, this.__bufferTransformFeedbacks[ 0 ] );
this.__transformFeedback.bindBuffer( 1, this.__bufferTransformFeedbacks[ 1 ] ); this.__transformFeedback.bindBuffer( 1, this.__bufferTransformFeedbacks[ 1 ] );
// == automaton? ===============================================================================
this.__automatonChannelList = [];
this.__automatonDefineString = '';
this.__updateAutomatonChannelList();
this.__textureAutomaton = glCat.createTexture(); this.__textureAutomaton = glCat.createTexture();
this.__textureAutomaton.textureFilter( gl.NEAREST ); this.__textureAutomaton.textureFilter( gl.NEAREST );
// == program ================================================================================== // == program ==================================================================================
this.__program = glCat.lazyProgram( this.__program = glCat.lazyProgram(
musicVert, injectCodeToShader( musicVert, this.__automatonDefineString ),
discardFrag, discardFrag,
{ transformFeedbackVaryings: [ 'outL', 'outR' ] }, { transformFeedbackVaryings: [ 'outL', 'outR' ] },
); );
@@ -85,15 +94,10 @@ export class Music {
this.deltaTime = 0.0; this.deltaTime = 0.0;
// == hot hot hot hot hot ====================================================================== // == hot hot hot hot hot ======================================================================
if ( process.env.DEV ) { if ( process.env.DEV && module.hot ) {
if ( module.hot ) { const recompileShader = async () => {
module.hot.accept(
[
'./shaders/music.vert',
],
async () => {
const program = await glCat.lazyProgramAsync( const program = await glCat.lazyProgramAsync(
musicVert, injectCodeToShader( musicVert, this.__automatonDefineString ),
discardFrag, discardFrag,
{ transformFeedbackVaryings: [ 'outL', 'outR' ] }, { transformFeedbackVaryings: [ 'outL', 'outR' ] },
).catch( ( error: any ) => { ).catch( ( error: any ) => {
@@ -105,9 +109,36 @@ export class Music {
this.__program.dispose( true ); this.__program.dispose( true );
this.__program = program; this.__program = program;
} }
};
module.hot.accept(
[ './shaders/music.vert' ],
() => {
recompileShader();
} }
); );
module.hot.accept(
[ './automaton.json' ],
() => {
this.__updateAutomatonChannelList();
recompileShader();
} }
);
( automaton as AutomatonWithGUI ).on( 'createChannel', ( { name } ) => {
if ( name.startsWith( 'Music/' ) ) {
this.__updateAutomatonChannelList();
recompileShader();
}
} );
( automaton as AutomatonWithGUI ).on( 'removeChannel', ( { name } ) => {
if ( name.startsWith( 'Music/' ) ) {
this.__updateAutomatonChannelList();
recompileShader();
}
} );
} }
} }
@@ -171,16 +202,24 @@ export class Music {
this.__samples = texture; this.__samples = texture;
} }
private __updateAutomatonChannelList(): void {
this.__automatonChannelList = [];
this.__automatonDefineString = '';
for ( const [ channelName, channel ] of automaton.mapNameToChannel.entries() ) {
if ( channelName.startsWith( 'Music/' ) ) {
const key = channelName.substring( 6 );
const index = this.__automatonChannelList.length;
this.__automatonDefineString += `const int AUTO_${key}=${index};`;
this.__automatonChannelList.push( channel );
}
}
}
private __updateAutomatonTexture(): void { private __updateAutomatonTexture(): void {
const buffer = new Float32Array( MUSIC_BUFFER_LENGTH * 256 ); const buffer = new Float32Array( MUSIC_BUFFER_LENGTH * 256 );
for ( const [ iChannel, channelName ] of automatonMusicParamMap.entries() ) { for ( const [ iChannel, channel ] of this.__automatonChannelList.entries() ) {
let channel = automaton.mapNameToChannel.get( channelName )!;
if ( process.env.DEV && !channel ) {
channel = ( automaton as AutomatonWithGUI ).createChannel( channelName );
}
for ( let iSample = 0; iSample < MUSIC_BUFFER_LENGTH; iSample ++ ) { for ( let iSample = 0; iSample < MUSIC_BUFFER_LENGTH; iSample ++ ) {
const t = this.time + iSample / this.audio.sampleRate; const t = this.time + iSample / this.audio.sampleRate;
buffer[ MUSIC_BUFFER_LENGTH * iChannel + iSample ] = channel.getValue( t ); buffer[ MUSIC_BUFFER_LENGTH * iChannel + iSample ] = channel.getValue( t );

View File

@@ -1 +1 @@
export { hermitePatch } from '@fms-cat/automaton-fxs'; export { sine, hermitePatch } from '@fms-cat/automaton-fxs';

View File

@@ -1 +1 @@
{"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]},{"nodes":[[0,0,0,0,0.1],[0.3747555183012014,1,-0.1,0,0.1],[1.3333333333333333,0,-0.1]]},{"nodes":[[],[0.6666666666666666,1,-0.5266666666666666]]},{"nodes":[[0,0,0,0,0.2355866843161978],[1.3333333333333333,1.2000000000000002,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261082,0.061764705882352944],[0.6666666666666666,0.4,-0.1]]},{"nodes":[[0,0,0,0,0.10333333333333335],[1,1]]}],"channels":[["Glitch/amp",{"items":[{},{"time":10.666666666666666,"length":1,"curve":0},{"time":256}]}],["PixelSorter/amp",{"items":[{},{"time":15.666666666666666,"length":1.666666666666666,"curve":1}]}],["Music/neuro/t",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":2},{"time":43.33333333333333,"length":1.3333333333333357,"curve":3},{"time":44.666666666666664,"length":0.6666666666666643,"curve":5,"speed":1.5000000000000056,"amp":0.5}]}],["Music/neuro/det",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":4},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.17000000000000004},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.5}]}],["Music/neuro/radius",{"items":[]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":true,"bpm":180,"beatOffset":0,"useBeatInGUI":true,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}} {"version":"4.1.1","resolution":100,"curves":[{"nodes":[[0,1,0,0,0.16140350877192983],[1,0,-0.47719298245614045]]},{"nodes":[[0,0,0,0,0.1],[0.3747555183012014,1,-0.1,0,0.1],[1.3333333333333333,0,-0.1]]},{"nodes":[[],[0.6666666666666666,1,-0.4560629631300563,-0.1852941176470587]]},{"nodes":[[0,0,0,0,0.2355866843161978],[1.3333333333333333,1.2000000000000002,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261082,0.061764705882352944],[0.6666666666666666,0.4,-0.1]]},{"nodes":[[0,0,0,0,0.10333333333333335],[1,1]]},{"nodes":[[0,0,0,0,0.1],[0.3333333333333333,1,-0.1,0,0.1],[0.6666666666666666,0,-0.1]]},{"nodes":[[0,0,0,0,0.12284595300261095,1.0980392156862744],[1.3333333333333333,0.8,-0.1]]},{"nodes":[[0,0,0,0,0.016158102050275177,0.4803921568627448],[0.02868275456174794,1,-0.017181893944471203,0.046509328639327226,0.07352361117377113,-0.1990196078431371],[0.25,0,-0.025439765782812148,0.6242474227849143]]},{"nodes":[[],[0.5,0.5,0,0,0.3072250626170559,0.32683448369273865],[1.3333333333333333,1.5]]},{"nodes":[[0,0.2,0,0,0.18674781789871728,2.609799325705191],[0.6666666666666666,0.1,-0.3546547911950566,0.44607843137254866]]},{"nodes":[[0,0.15115889256979576,0,0,0.1],[1.3333333333333333,0.7344636184844164,-0.8192982456140352,-0.01372549019607843]],"fxs":[{"def":"sine","params":{"amp":0.42000000000000004,"freq":9.43,"offset":0},"time":0.5833333333333333,"length":0.75},{"def":"hermitePatch","params":{},"time":0.5,"length":0.16666666666666663,"row":1}]}],"channels":[["Glitch/amp",{"items":[{},{"time":10.666666666666666,"length":1,"curve":0},{"time":256}]}],["PixelSorter/amp",{"items":[{},{"time":15.666666666666666,"length":1.666666666666666,"curve":1}]}],["Music/NEURO_TIME",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":2},{"time":43.33333333333333,"length":1.3333333333333357,"curve":3},{"time":44.666666666666664,"length":0.6666666666666643,"curve":5,"speed":2.280000000000004,"amp":0.5},{"time":46,"length":1.3333333333333286,"curve":9,"speed":1.0000000000000109},{"time":48,"length":0.6666666666666643,"curve":2,"speed":0.6666666666666642}]}],["Music/NEURO_DETUNE",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"curve":4},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.050000000000000024},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.08000000000000045},{"time":46,"length":1.3333333333333286,"value":0.40000000000000024},{"time":47.33333333333333,"length":0.6666666666666643,"curve":10,"amp":0.15588235294117658},{"time":48,"length":0.6666666666666643,"value":1.0431372549019606,"curve":4}]}],["Music/NEURO_DETUNE_PHASE",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"value":1},{"time":43.33333333333333,"length":1.3333333333333357,"value":0.0299999999999997},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.19000000000000006},{"time":46,"length":1.3333333333333286,"value":1},{"time":48,"length":0.6666666666666643,"value":0.0399999999999995}]}],["Music/NEURO_WUB_AMP",{"items":[{"time":42.664755294969126,"length":0.6685780383642026,"curve":6},{"time":43.33333333333333,"length":1.3333333333333357,"curve":7},{"time":44.666666666666664,"length":0.25,"curve":8},{"time":44.916666666666664,"length":0.25,"curve":8},{"time":45.166666666666664,"length":0.1666666666666643,"curve":8},{"time":46,"length":1.3333333333333286,"curve":11},{"time":48,"length":0.6685780383642026,"curve":6}]}],["Music/NEURO_WUB_FREQ",{"items":[{"time":42.666666666666664,"length":0.6666666666666643,"value":1},{"time":43.33333333333333,"length":1.3333333333333357,"value":1},{"time":44.666666666666664,"length":0.6666666666666643,"value":0.5},{"time":46,"length":1.3333333333333286,"value":2},{"time":48,"length":0.6666666666666643,"value":1}]}]],"labels":{"zero":0},"guiSettings":{"snapTimeActive":true,"snapTimeInterval":0.1,"snapValueActive":true,"snapValueInterval":0.1,"snapBeatActive":true,"bpm":180,"beatOffset":0,"useBeatInGUI":true,"minimizedPrecisionTime":3,"minimizedPrecisionValue":3}}

View File

@@ -1,6 +0,0 @@
export const automatonMusicParamMap = [
'Music/neuro/t',
'Music/neuro/det',
'Music/neuro/wubIntensity',
'Music/neuro/wubFreq',
];

View File

@@ -1,5 +1,6 @@
import { GLCatProgram, GLCatProgramLinkOptions, GLCatProgramUniformType, GLCatTexture, GLCatTextureCubemap } from '@fms-cat/glcat-ts'; import { GLCatProgram, GLCatProgramLinkOptions, GLCatProgramUniformType, GLCatTexture, GLCatTextureCubemap } from '@fms-cat/glcat-ts';
import { gl } from '../globals/canvas'; import { gl } from '../globals/canvas';
import { injectCodeToShader } from '../utils/injectCodeToShader';
import { SHADERPOOL } from './ShaderPool'; import { SHADERPOOL } from './ShaderPool';
export type MaterialTag = export type MaterialTag =
@@ -163,18 +164,14 @@ export class Material {
} }
protected __withDefines( code: string ): string { protected __withDefines( code: string ): string {
if ( !code.startsWith( '#version' ) ) { let inject = '';
return code;
}
const lines = code.split( '\n' );
Object.entries( this.__defines ).map( ( [ key, value ] ) => { Object.entries( this.__defines ).map( ( [ key, value ] ) => {
if ( value != null ) { if ( value != null ) {
lines.splice( 1, 0, `#define ${key} ${value}` ); inject += `#define ${key} ${value}\n`;
} }
} ); } );
return lines.join( '\n' ); return injectCodeToShader( code, inject );
} }
} }

View File

@@ -68,15 +68,15 @@ class EntityReplacer<T extends { entity: Entity }> {
// -- bake ----------------------------------------------------------------------------------------- // -- bake -----------------------------------------------------------------------------------------
const ibllut = new IBLLUT(); const ibllut = new IBLLUT();
dog.root.children.push( ibllut.entity ); // dog.root.children.push( ibllut.entity );
// -- "objects" ------------------------------------------------------------------------------------ // -- "objects" ------------------------------------------------------------------------------------
const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() ); // const replacerSphereParticles = new EntityReplacer( () => new SphereParticles() );
if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/SphereParticles', () => { // module.hot.accept( './entities/SphereParticles', () => {
replacerSphereParticles.replace(); // replacerSphereParticles.replace();
} ); // } );
} // }
// const replacerTrails = new EntityReplacer( () => new Trails() ); // const replacerTrails = new EntityReplacer( () => new Trails() );
// if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
@@ -85,12 +85,12 @@ if ( process.env.DEV && module.hot ) {
// } ); // } );
// } // }
const replacerRings = new EntityReplacer( () => new Rings() ); // const replacerRings = new EntityReplacer( () => new Rings() );
if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/Rings', () => { // module.hot.accept( './entities/Rings', () => {
replacerRings.replace(); // replacerRings.replace();
} ); // } );
} // }
// const replacerCube = new EntityReplacer( () => new Cube() ); // const replacerCube = new EntityReplacer( () => new Cube() );
// if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
@@ -99,19 +99,19 @@ if ( process.env.DEV && module.hot ) {
// } ); // } );
// } // }
const replacerFlickyParticles = new EntityReplacer( () => new FlickyParticles() ); // const replacerFlickyParticles = new EntityReplacer( () => new FlickyParticles() );
if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/FlickyParticles', () => { // module.hot.accept( './entities/FlickyParticles', () => {
replacerFlickyParticles.replace(); // replacerFlickyParticles.replace();
} ); // } );
} // }
const replacerRaymarcher = new EntityReplacer( () => new Raymarcher() ); // const replacerRaymarcher = new EntityReplacer( () => new Raymarcher() );
if ( process.env.DEV && module.hot ) { // if ( process.env.DEV && module.hot ) {
module.hot.accept( './entities/Raymarcher', () => { // module.hot.accept( './entities/Raymarcher', () => {
replacerRaymarcher.replace(); // replacerRaymarcher.replace();
} ); // } );
} // }
// -- things that is not an "object" --------------------------------------------------------------- // -- things that is not an "object" ---------------------------------------------------------------
const swapOptions = { const swapOptions = {
@@ -130,16 +130,16 @@ const swap = new Swap(
} ), } ),
); );
const light = new LightEntity( { // const light = new LightEntity( {
root: dog.root, // root: dog.root,
shadowMapFov: 90.0, // shadowMapFov: 90.0,
shadowMapNear: 1.0, // shadowMapNear: 1.0,
shadowMapFar: 20.0, // shadowMapFar: 20.0,
namePrefix: process.env.DEV && 'light1', // namePrefix: process.env.DEV && 'light1',
} ); // } );
light.color = [ 40.0, 40.0, 40.0 ]; // light.color = [ 40.0, 40.0, 40.0 ];
light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) ); // light.entity.transform.lookAt( new Vector3( [ -1.0, 2.0, 8.0 ] ) );
dog.root.children.push( light.entity ); // dog.root.children.push( light.entity );
// const light2 = new LightEntity( { // const light2 = new LightEntity( {
// root: dog.root, // root: dog.root,
@@ -152,83 +152,83 @@ dog.root.children.push( light.entity );
// light2.entity.transform.lookAt( new Vector3( [ -4.0, -2.0, 6.0 ] ) ); // light2.entity.transform.lookAt( new Vector3( [ -4.0, -2.0, 6.0 ] ) );
// dog.root.children.push( light2.entity ); // dog.root.children.push( light2.entity );
const cubemapCamera = new CubemapCameraEntity( { // const cubemapCamera = new CubemapCameraEntity( {
root: dog.root, // root: dog.root,
lights: [ // lights: [
light, // light,
// light2 // // light2
], // ],
} ); // } );
dog.root.children.push( cubemapCamera.entity ); // dog.root.children.push( cubemapCamera.entity );
const environmentMap = new EnvironmentMap( { // const environmentMap = new EnvironmentMap( {
cubemap: cubemapCamera.target, // cubemap: cubemapCamera.target,
} ); // } );
dog.root.children.push( environmentMap.entity ); // dog.root.children.push( environmentMap.entity );
const camera = new CameraEntity( { // const camera = new CameraEntity( {
root: dog.root, // root: dog.root,
target: swap.o, // target: swap.o,
lights: [ // lights: [
light, // light,
// light2 // // light2
], // ],
textureIBLLUT: ibllut.texture, // textureIBLLUT: ibllut.texture,
textureEnv: environmentMap.texture, // textureEnv: environmentMap.texture,
} ); // } );
camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ]; // camera.camera.clear = [ 0.0, 0.0, 0.0, 0.0 ];
camera.entity.components.unshift( new Lambda( { // camera.entity.components.unshift( new Lambda( {
onUpdate: ( event ) => { // onUpdate: ( event ) => {
const t1 = 0.02 * Math.sin( event.time ); // const t1 = 0.02 * Math.sin( event.time );
const s1 = Math.sin( t1 ); // const s1 = Math.sin( t1 );
const c1 = Math.cos( t1 ); // const c1 = Math.cos( t1 );
const t2 = 0.02 * Math.cos( event.time ); // const t2 = 0.02 * Math.cos( event.time );
const s2 = Math.sin( t2 ); // const s2 = Math.sin( t2 );
const c2 = Math.cos( t2 ); // const c2 = Math.cos( t2 );
const r = 5.0; // const r = 5.0;
camera.entity.transform.lookAt( new Vector3( [ // camera.entity.transform.lookAt( new Vector3( [
r * c1 * s2, // r * c1 * s2,
r * s1, // r * s1,
r * c1 * c2 // r * c1 * c2
] ) ); // ] ) );
}, // },
visible: false, // visible: false,
name: process.env.DEV && 'main/updateCamera', // name: process.env.DEV && 'main/updateCamera',
} ) ); // } ) );
dog.root.children.push( camera.entity ); // dog.root.children.push( camera.entity );
swap.swap(); // swap.swap();
const bloom = new Bloom( { // const bloom = new Bloom( {
input: swap.i, // input: swap.i,
target: swap.o // target: swap.o
} ); // } );
dog.root.children.push( bloom.entity ); // dog.root.children.push( bloom.entity );
swap.swap(); // swap.swap();
const glitch = new Glitch( { // const glitch = new Glitch( {
input: swap.i, // input: swap.i,
target: swap.o, // target: swap.o,
} ); // } );
dog.root.children.push( glitch.entity ); // dog.root.children.push( glitch.entity );
swap.swap(); // swap.swap();
const pixelSorter = new PixelSorter( { // const pixelSorter = new PixelSorter( {
input: swap.i, // input: swap.i,
target: swap.o, // target: swap.o,
} ); // } );
dog.root.children.push( pixelSorter.entity ); // dog.root.children.push( pixelSorter.entity );
swap.swap(); // swap.swap();
const post = new Post( { // const post = new Post( {
input: swap.i, // input: swap.i,
target: canvasRenderTarget // target: canvasRenderTarget
} ); // } );
dog.root.children.push( post.entity ); // dog.root.children.push( post.entity );
if ( process.env.DEV ) { // if ( process.env.DEV ) {
const rtInspector = new RTInspector( { // const rtInspector = new RTInspector( {
target: canvasRenderTarget // target: canvasRenderTarget
} ); // } );
dog.root.children.push( rtInspector.entity ); // dog.root.children.push( rtInspector.entity );
} // }

View File

@@ -380,15 +380,16 @@ vec2 mainAudio( vec4 time ) {
if ( inRange( time.w, SECTION_NEURO, SECTION_PORTER_FUCKING_ROBINSON ) ) { if ( inRange( time.w, SECTION_NEURO, SECTION_PORTER_FUCKING_ROBINSON ) ) {
vec2 sum = vec2( 0.0 ); vec2 sum = vec2( 0.0 );
float t = auto( 0 ); float t = auto( AUTO_NEURO_TIME );
float det = 0.01 * auto( 1 ); float det = 0.01 * auto( AUTO_NEURO_DETUNE );
float wubIntensity = auto( 2 ); float detPhase = auto( AUTO_NEURO_DETUNE_PHASE );
float wubFreq = auto( 3 ); float wubIntensity = auto( AUTO_NEURO_WUB_AMP );
float wubFreq = auto( AUTO_NEURO_WUB_FREQ );
for ( int i = 0; i < 5; i ++ ) { for ( int i = 0; i < 5; i ++ ) {
float fi = float( i ); float fi = float( i );
float tt = t + det * fi * sin( 0.7 * fi + 1.0 * t ); float tt = t + det * fi * sin( fi * detPhase + 1.0 * t );
float radius = 0.2 + 0.1 * wubIntensity * fbm( 0.1 * vec2( tri( n2f( -36.0 ) * wubFreq * tt ) ) ).x; float radius = 0.2 + 0.1 * wubIntensity * fbm( 0.1 * vec2( tri( n2f( -36.0 ) * wubFreq * tt ) ) ).x;

View File

@@ -0,0 +1,10 @@
/**
* Assuming the first line is #version...
* @param code The original shader code
* @param inject The code you want to inject
*/
export function injectCodeToShader( code: string, inject: string ): string {
const lines = code.split( '\n' );
lines.splice( 1, 0, inject );
return lines.join( '\n' );
}