├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── README.md ├── package.json ├── src ├── aefun ├── config.json ├── entities │ ├── Background.ts │ ├── BigWords.ts │ ├── Bloom.ts │ ├── CameraEntity.ts │ ├── Consooru.ts │ ├── ErrorLayer.ts │ ├── FXAA.ts │ ├── GPUParticles.ts │ ├── Glitch.ts │ ├── HotPlane.ts │ ├── Invert.ts │ ├── LightEntity.ts │ ├── Plane.ts │ ├── Post.ts │ ├── Raymarcher.ts │ ├── SphereParticles.ts │ ├── Trails.ts │ ├── UIParticles.ts │ └── Waku.ts ├── geometries │ └── genOctahedron.ts ├── heck │ ├── BufferRenderTarget.ts │ ├── CanvasRenderTarget.ts │ ├── DISPLAY.ts │ ├── Dog.ts │ ├── Entity.ts │ ├── Geometry.ts │ ├── InstancedGeometry.ts │ ├── Material.ts │ ├── RenderTarget.ts │ ├── ShaderPool.ts │ ├── Transform.ts │ └── components │ │ ├── Camera.ts │ │ ├── Component.ts │ │ ├── DrawLambda.ts │ │ ├── Lambda.ts │ │ ├── LogTransform.ts │ │ ├── Mesh.ts │ │ ├── PerspectiveCamera.ts │ │ └── Quad.ts ├── html │ └── index.html ├── images │ ├── brdf-lut.png │ ├── char5x5.png │ ├── luxo.png │ └── missing.png ├── main.ts ├── shaders │ ├── -distFunc.glsl │ ├── -prng.glsl │ ├── -simplex4d.glsl │ ├── ao.frag │ ├── background.frag │ ├── background.vert │ ├── bigword.frag │ ├── bloom-blur.frag │ ├── bloom-post.frag │ ├── bloom-pre.frag │ ├── consooru-compute.frag │ ├── consooru-render.frag │ ├── consooru-render.vert │ ├── errorlayer.frag │ ├── fuck.frag │ ├── fxaa.frag │ ├── glitch.frag │ ├── hotplane.frag │ ├── index.ts │ ├── init.frag │ ├── invert.frag │ ├── normal.frag │ ├── object.vert │ ├── pos-to-depth.frag │ ├── post.frag │ ├── quad.vert │ ├── raymarcher.frag │ ├── return.frag │ ├── shading.frag │ ├── shadow-blur.frag │ ├── sphere-particles-compute.frag │ ├── sphere-particles-render.frag │ ├── sphere-particles-render.vert │ ├── texture.frag │ ├── trails-compute.frag │ ├── trails-render.frag │ ├── trails-render.vert │ ├── ui-particles-compute.frag │ ├── ui-particles-render.frag │ ├── ui-particles-render.vert │ └── waku.frag ├── styles │ └── main.scss ├── types │ ├── FontFace.d.ts │ ├── FontFaceSet.d.ts │ ├── frag.d.ts │ └── vert.d.ts └── utils │ ├── EventEmittable.ts │ ├── EventManager.ts │ ├── MidiManager.ts │ ├── RandomSphereTexture.ts │ ├── RandomTexture.ts │ ├── ScreenCaptureTexture.ts │ ├── WebCameraTexture.ts │ ├── createFontSpritesheetSDF.ts │ ├── createImageSDF.ts │ ├── createSDF.ts │ ├── doQuadRender.ts │ ├── edt.ts │ ├── loadImage.ts │ ├── loadImageTexture.ts │ ├── matchAll.ts │ └── stringHash.ts ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/** 2 | /node_modules/** 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "root": true, 3 | 4 | "plugins": [ "@typescript-eslint" ], 5 | 6 | "env": { 7 | "es6": true, 8 | "browser": true, 9 | "commonjs": true 10 | }, 11 | 12 | "parser": "@typescript-eslint/parser", 13 | 14 | "parserOptions": { 15 | "sourceType": "module", 16 | "ecmaVersion": 2017 17 | }, 18 | 19 | "extends": [ 20 | "eslint:recommended", 21 | "plugin:@typescript-eslint/eslint-recommended", 22 | "plugin:@typescript-eslint/recommended" 23 | ], 24 | 25 | "globals": { 26 | "process": true // since gulp has envify 27 | }, 28 | 29 | "rules": { 30 | // basics 31 | "@typescript-eslint/indent": [ "error", 2, { // indentation should be 2 spaces 32 | "flatTernaryExpressions": true, // ternary should be performed in flat 33 | "MemberExpression": 0 // member chain should be performed in flat 34 | } ], // it forces 2 spaces indentation 35 | "linebreak-style": [ "error", "unix" ], // fuck you, CRLF 36 | "quotes": [ "error", "single" ], // quotes must be single 37 | "eqeqeq": [ "error", "smart" ], // fuck you, `==` 38 | "max-len": [ "error", { // don't be too long, code 39 | "code": 100, 40 | "ignoreComments": true, // comments are okay 41 | "ignoreStrings": true, // strings are okay 42 | "ignoreTemplateLiterals": true, // templates are also okay 43 | "ignoreRegExpLiterals": true, // regexs are also okay too 44 | } ], 45 | "sort-imports": [ "error" ], // imports have to be ordered 46 | "eol-last": [ "error", "always" ], // eof newline is cool 47 | 48 | // variables 49 | "@typescript-eslint/no-unused-vars": [ "warn" ], // draw yellow line under unused vars 50 | "no-undef": [ "warn" ], // draws yellow line under undefined vars 51 | "no-var": [ "error" ], // fuck you, var 52 | "prefer-const": [ "error" ], // const is better than let 53 | 54 | // omittables 55 | "semi": [ "error", "always" ], // semicolon is required 56 | "curly": [ "error" ], // it kills `if (foo) bar++;` 57 | "arrow-parens": [ "error", "always" ], // it kills `arg => { func(); }` 58 | 59 | // force spacing (I prefer super sparse code!) 60 | "array-bracket-spacing": [ "error", "always" ], // it kills `[val1, val2]` 61 | "arrow-spacing": [ "error", { "before": true, "after": true } ], // it kills `( arg )=>{ func(); }` 62 | "block-spacing": [ "error", "always" ], // it kills `if ( cond ) {func();}` 63 | "comma-spacing": [ "error", { "before": false, "after": true } ], // it kills `func( arg1,arg2 )` 64 | "computed-property-spacing": [ "error", "always" ], // it kills `arr[i]` 65 | "key-spacing": [ "error", { "beforeColon": false, "afterColon": true } ], // it kills `{ key:val }` 66 | "keyword-spacing": [ "error", { "before": true, "after": true } ], // it kills `}else{` 67 | "object-curly-spacing": [ "error", "always" ], // it kills `{key: val}` 68 | "semi-spacing": [ "error", { "before": false, "after": true } ], // it kills `func1();func2();` 69 | "space-before-blocks": [ "error", "always" ], // it kills `if (cond){` 70 | "space-in-parens": [ "error", "always" ], // it kills `func (arg)` 71 | "space-infix-ops": [ "error" ], // it kills val1+val2 72 | "space-unary-ops": [ "error", { "words": true, "nonwords": false, "overrides": { "++": true, "--": true } } ], // it kills `val++` 73 | "spaced-comment": [ "error", "always" ], // it kills `//this is comment` 74 | 75 | // ban spacing 76 | "func-call-spacing": [ "error", "never" ], // no-trailing-spaces. yea. 77 | "no-trailing-spaces": [ "error" ], // no-trailing-spaces. yea. 78 | "no-whitespace-before-property": [ "error" ], // it kills `obj .key` 79 | "space-before-function-paren": [ "error", { "anonymous": "never", "named": "never", "asyncArrow": "always" } ], // it kills `func ()` 80 | 81 | // others 82 | "no-eval": [ "off" ], // we need to go the evil way 83 | "no-implied-eval": [ "warn" ], // ok don't 84 | "no-console": [ "error", { allow: [ "info", "warn", "error" ] } ], // don't forget to remove `console.log` ! 85 | 86 | // typescript-specifics 87 | "@typescript-eslint/no-explicit-any": [ "off" ], // yea 88 | "@typescript-eslint/no-inferrable-types": [ "off" ], // it's ok 89 | "@typescript-eslint/no-non-null-assertion": [ "off" ], // bang is sometimes required 90 | "@typescript-eslint/no-empty-interface": [ "off" ], // we need to perform mixins 91 | "@typescript-eslint/explicit-function-return-type": [ "error", { "allowExpressions": true } ], // return type is required 92 | "@typescript-eslint/explicit-member-accessibility": [ "error" ], // `public` / `private` for members and methods are required 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | **/*.log 4 | pdg-icons.* -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch chrome", 6 | "type": "chrome", 7 | "request": "launch", 8 | "url": "http://localhost:8080/index.html", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///./*": "${webRoot}/*", 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitlens.currentLine.enabled": false 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vj20200217 2 | 3 | ![Imgur](https://i.imgur.com/s4nHXDC.png) 4 | 5 | ``` 6 | yarn dev 7 | ``` 8 | 9 | Use along with [ChromaCoder](https://marketplace.visualstudio.com/items?itemName=fms-cat.chromacoder) 10 | 11 | ### Current MIDI mappings 12 | 13 | All of these mappings are defined in shaders (`uniform float midiCC[ 128 ]`). 14 | You might want to connect a midi controller. 15 | 16 | - `CC13`: Noise Amp 17 | - `CC14`: Noise Freq 18 | - `CC15`: Trails Gen Rate 19 | - `CC16`: Spheres Gen Rate 20 | - `CC17`: UIs Gen Rate 21 | - `CC18`: UIs Variety 22 | - `CC29`: Camera RotX 23 | - `CC30`: Camera RotY 24 | - `CC31`: Camera Radius 25 | - `CC49`: PostFX Barrel 26 | - `CC77`: XFD? 27 | - `CC78`: Metaball Positions 28 | - `CC79`: Metaball Radius 29 | - `CC83`: Fadeout 30 | - `CC84`: Glitch 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vj20200217", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "webpack-dev-server --hot --mode development", 8 | "lint": "eslint \"src/**/*.{ts,tsx}\"", 9 | "build-dev": "webpack --mode development", 10 | "build-prod": "webpack --mode production", 11 | "build": "yarn build-dev && yarn build-prod" 12 | }, 13 | "devDependencies": { 14 | "@fms-cat/experimental": "^0.2.0", 15 | "@fms-cat/glcat-ts": "^0.10.0", 16 | "@types/webmidi": "^2.0.4", 17 | "@types/webpack-env": "^1.15.1", 18 | "@typescript-eslint/eslint-plugin": "^2.19.0", 19 | "@typescript-eslint/parser": "^2.19.0", 20 | "css-loader": "^3.4.2", 21 | "eslint": "^6.8.0", 22 | "fibers": "^4.0.2", 23 | "fork-ts-checker-webpack-plugin": "^4.0.3", 24 | "glslify-loader": "^2.0.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "raw-loader": "^4.0.0", 27 | "sass": "^1.25.0", 28 | "sass-loader": "^8.0.2", 29 | "style-loader": "^1.1.3", 30 | "ts-loader": "^6.2.1", 31 | "typescript": "^3.7.5", 32 | "url-loader": "^3.0.0", 33 | "webpack": "^4.41.5", 34 | "webpack-cli": "^3.3.10", 35 | "webpack-dev-server": "^3.10.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/aefun: -------------------------------------------------------------------------------- 1 | 2 | // == loop here ==================================================================================== 3 | function update(): void { 4 | const checkboxActive = $( '#active' ); 5 | 6 | if ( checkboxActive && !checkboxActive.checked ) { 7 | setTimeout( update, 100 ); 8 | return; 9 | } 10 | 11 | requestAnimationFrame( update ); 12 | 13 | // -- update some bunch of shit ------------------------------------------------------------------ 14 | clock.update(); 15 | screenCaptureTexture.update(); 16 | randomTexture.update(); 17 | 18 | // -- let's render this -------------------------------------------------------------------------- 19 | glCat.useProgram( programRender ); 20 | gl.bindFramebuffer( gl.FRAMEBUFFER, fbFeedbackSwap.i.raw ); 21 | 22 | programRender.attribute( 'p', vboQuad, 2 ); 23 | programRender.uniform4f( 'pos', -1.0, -1.0, 1.0, 1.0 ); 24 | 25 | programRender.uniform1f( 'time', clock.time ); 26 | programRender.uniform1f( 'frame', totalFrame ); 27 | programRender.uniform2f( 'resolution', width, height ); 28 | 29 | programRender.uniformTexture( 'samplerScreen', screenCaptureTexture.texture.raw, 0 ); 30 | programRender.uniformTexture( 'samplerRandomStatic', randomTextureStatic.texture.raw, 1 ); 31 | programRender.uniformTexture( 'samplerRandom', randomTexture.texture.raw, 2 ); 32 | programRender.uniformTexture( 'samplerFeedback', fbFeedbackSwap.o.texture!.raw, 3 ); 33 | 34 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 35 | fbFeedbackSwap.swap(); 36 | 37 | // == bloom ====================================================================================== 38 | glCat.useProgram( programBloomPre ); 39 | gl.bindFramebuffer( gl.FRAMEBUFFER, fbPostSwap.i.raw ); 40 | 41 | programBloomPre.attribute( 'p', vboQuad, 2 ); 42 | programBloomPre.uniform4f( 'pos', -1.0, -1.0, 0.0, 0.0 ); 43 | 44 | programBloomPre.uniformTexture( 'sampler0', fbFeedbackSwap.o.texture!.raw, 0 ); 45 | 46 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 47 | fbPostSwap.swap(); 48 | 49 | for ( let i = 0; i < 5; i ++ ) { 50 | glCat.useProgram( programReturn ); 51 | gl.bindFramebuffer( gl.FRAMEBUFFER, fbPostSwap.i.raw ); 52 | 53 | programReturn.attribute( 'p', vboQuad, 2 ); 54 | 55 | programReturn.uniformTexture( 'sampler0', fbPostSwap.o.texture!.raw, 0 ); 56 | 57 | programReturn.uniform4f( 'pos', -1.0, -1.0, 1.0, 1.0 ); 58 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 59 | programReturn.uniform4f( 'pos', 0.0, 0.0, 1.0, 1.0 ); 60 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 61 | fbPostSwap.swap(); 62 | } 63 | 64 | for ( let i = 0; i < 2; i ++ ) { 65 | glCat.useProgram( programBloomBlur ); 66 | gl.bindFramebuffer( gl.FRAMEBUFFER, fbPostSwap.i.raw ); 67 | 68 | programBloomBlur.attribute( 'p', vboQuad, 2 ); 69 | programBloomBlur.uniform1i( 'isVert', i ); 70 | programBloomBlur.uniform4f( 'pos', -1.0, -1.0, 1.0, 1.0 ); 71 | programBloomBlur.uniform2f( 'resolution', width, height ); 72 | 73 | programBloomBlur.uniformTexture( 'sampler0', fbPostSwap.o.texture!.raw, 0 ); 74 | 75 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 76 | fbPostSwap.swap(); 77 | } 78 | 79 | // -- return ------------------------------------------------------------------------------------- 80 | glCat.useProgram( programBloomPost ); 81 | gl.bindFramebuffer( gl.FRAMEBUFFER, null ); 82 | 83 | programBloomPost.attribute( 'p', vboQuad, 2 ); 84 | programBloomPost.uniform4f( 'pos', -1.0, -1.0, 1.0, 1.0 ); 85 | 86 | programBloomPost.uniformTexture( 'samplerDry', fbFeedbackSwap.o.texture!.raw, 0 ); 87 | programBloomPost.uniformTexture( 'samplerWet', fbPostSwap.o.texture!.raw, 1 ); 88 | 89 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 90 | 91 | // -- return ------------------------------------------------------------------------------------- 92 | // glCat.useProgram( programReturn ); 93 | // gl.bindFramebuffer( gl.FRAMEBUFFER, null ); 94 | 95 | // programReturn.attribute( 'p', vboQuad, 2 ); 96 | // programReturn.uniform4f( 'pos', -1.0, -1.0, 1.0, 1.0 ); 97 | 98 | // programReturn.uniformTexture( 'sampler0', fbPostSwap.o.texture!.raw, 0 ); 99 | 100 | // gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 101 | 102 | // -- frame -------------------------------------------------------------------------------------- 103 | totalFrame ++; 104 | } 105 | 106 | update(); 107 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": [ 1920, 1080 ], 3 | "randomReso": [ 32, 32 ], 4 | "screenReso": [ 1920, 1080 ], 5 | "randomStaticReso": [ 1024, 1024 ], 6 | "aoRatio": 0.5 7 | } 8 | -------------------------------------------------------------------------------- /src/entities/Background.ts: -------------------------------------------------------------------------------- 1 | import { Mesh, MeshCull } from '../heck/components/Mesh'; 2 | import { Quaternion, Vector3 } from '@fms-cat/experimental'; 3 | import { Entity } from '../heck/Entity'; 4 | import { Geometry } from '../heck/Geometry'; 5 | import { Lambda } from '../heck/components/Lambda'; 6 | import { Material } from '../heck/Material'; 7 | import { genOctahedron } from '../geometries/genOctahedron'; 8 | 9 | export class Background { 10 | private __entity: Entity; 11 | 12 | public get entity(): Entity { 13 | return this.__entity; 14 | } 15 | 16 | public constructor() { 17 | this.__entity = new Entity(); 18 | 19 | const mesh = new Mesh( 20 | this.__createGeometry(), 21 | this.__createMaterial() 22 | ); 23 | mesh.cull = MeshCull.None; 24 | this.__entity.components.push( mesh ); 25 | 26 | this.__entity.components.push( new Lambda( ( event ) => { 27 | this.__entity.transform.rotation = Quaternion.fromAxisAngle( 28 | new Vector3( [ 1.0, 0.7, 0.2 ] ).normalized, 29 | 0.1 * event.time 30 | ); 31 | } ) ); 32 | } 33 | 34 | private __createGeometry(): Geometry { 35 | const octahedron = genOctahedron( { div: 6, radius: -10.0 } ); 36 | 37 | const geometry = new Geometry(); 38 | 39 | geometry.addAttribute( 'position', octahedron.position ); 40 | geometry.addAttribute( 'normal', octahedron.normal ); 41 | geometry.setIndex( octahedron.index ); 42 | 43 | geometry.count = octahedron.count; 44 | geometry.mode = octahedron.mode; 45 | 46 | return geometry; 47 | } 48 | 49 | private __createMaterial(): Material { 50 | const material = new Material( 51 | require( '../shaders/background.vert' ).default, 52 | require( '../shaders/background.frag' ).default 53 | ); 54 | 55 | if ( module.hot ) { 56 | module.hot.accept( '../shaders/background.vert', () => { 57 | material.cueShader( 58 | require( '../shaders/background.vert' ).default, 59 | require( '../shaders/background.frag' ).default 60 | ); 61 | } ); 62 | 63 | module.hot.accept( '../shaders/background.frag', () => { 64 | material.cueShader( 65 | require( '../shaders/background.vert' ).default, 66 | require( '../shaders/background.frag' ).default 67 | ); 68 | } ); 69 | } 70 | 71 | return material; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/entities/BigWords.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { Entity } from '../heck/Entity'; 4 | import { Material } from '../heck/Material'; 5 | import { Quad } from '../heck/components/Quad'; 6 | import { RenderTarget } from '../heck/RenderTarget'; 7 | import { Shaders } from '../shaders'; 8 | 9 | export class BigWords { 10 | private __canvas: HTMLCanvasElement; 11 | private __context: CanvasRenderingContext2D; 12 | 13 | private __entity: Entity; 14 | 15 | private __texture: GLCatTexture; 16 | 17 | public words: Set = new Set(); 18 | 19 | public get entity(): Entity { 20 | return this.__entity; 21 | } 22 | 23 | public constructor( options: { 24 | target: RenderTarget; 25 | width: number; 26 | height: number; 27 | } ) { 28 | const { target, width, height } = options; 29 | 30 | this.__entity = new Entity(); 31 | 32 | // -- canvas ----------------------------------------------------------------------------------- 33 | this.__canvas = document.createElement( 'canvas' ); 34 | this.__canvas.width = width; 35 | this.__canvas.height = height; 36 | 37 | this.__context = this.__canvas.getContext( '2d' )!; 38 | 39 | // -- quad ------------------------------------------------------------------------------------- 40 | this.__texture = DISPLAY.glCat.createTexture(); 41 | this.__texture.setZeroTexture(); 42 | 43 | const material = new Material( 44 | Shaders.quadVert, 45 | require( '../shaders/bigword.frag' ).default 46 | ); 47 | material.blend = [ GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA ]; 48 | material.addUniformTexture( 'sampler0', this.__texture ); 49 | 50 | if ( module.hot ) { 51 | module.hot.accept( '../shaders/bigword.frag', () => { 52 | material.cueShader( 53 | Shaders.quadVert, 54 | require( '../shaders/bigword.frag' ).default 55 | ); 56 | } ); 57 | } 58 | 59 | const quad = new Quad( { 60 | material, 61 | target 62 | } ); 63 | this.__entity.components.push( quad ); 64 | } 65 | 66 | public draw(): void { 67 | const { width, height } = this.__canvas; 68 | 69 | this.__context.clearRect( 0, 0, width, height ); 70 | 71 | this.__context.textAlign = 'left'; 72 | this.__context.textBaseline = 'middle'; 73 | this.__context.font = `100 ${ height * 0.6 }px "Helvetica Now Display"`; 74 | this.__context.fillStyle = '#ffffff'; 75 | this.__context.fillText( this.__getRandomWord(), -0.05 * width, 0.17 * height ); 76 | 77 | this.__context.textAlign = 'right'; 78 | this.__context.textBaseline = 'middle'; 79 | this.__context.font = `700 ${ height * 0.6 }px "Helvetica Now Display"`; 80 | this.__context.fillStyle = '#ffffff'; 81 | this.__context.fillText( this.__getRandomWord(), 1.05 * width, 0.8 * height ); 82 | 83 | this.__texture.setTexture( this.__canvas ); 84 | } 85 | 86 | private __getRandomWord(): string { 87 | const arr = Array.from( this.words ); 88 | 89 | if ( arr.length === 0 ) { return 'New Text'; } 90 | return arr[ Math.floor( Math.random() * arr.length ) ]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/entities/Bloom.ts: -------------------------------------------------------------------------------- 1 | import { BufferRenderTarget } from '../heck/BufferRenderTarget'; 2 | import { Entity } from '../heck/Entity'; 3 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 4 | import { Material } from '../heck/Material'; 5 | import { Quad } from '../heck/components/Quad'; 6 | import { RenderTarget } from '../heck/RenderTarget'; 7 | import { Shaders } from '../shaders'; 8 | import { Swap } from '@fms-cat/experimental'; 9 | 10 | export interface BloomOptions { 11 | input: GLCatTexture; 12 | target: RenderTarget; 13 | } 14 | 15 | export class Bloom { 16 | private __entity: Entity; 17 | 18 | public get entity(): Entity { 19 | return this.__entity; 20 | } 21 | 22 | public constructor( options: BloomOptions ) { 23 | this.__entity = new Entity(); 24 | 25 | const swap = new Swap( 26 | new BufferRenderTarget( { width: options.target.width, height: options.target.height } ), 27 | new BufferRenderTarget( { width: options.target.width, height: options.target.height } ) 28 | ); 29 | 30 | // -- pre ---------------------------------------------------------------------------------------- 31 | const materialBloomPre = new Material( 32 | Shaders.quadVert, 33 | Shaders.bloomPreFrag 34 | ); 35 | materialBloomPre.addUniformTexture( 'sampler0', options.input ); 36 | 37 | this.__entity.components.push( new Quad( { 38 | target: swap.o, 39 | material: materialBloomPre, 40 | range: [ -1.0, -1.0, 0.0, 0.0 ] 41 | } ) ); 42 | 43 | swap.swap(); 44 | 45 | // -- dup ---------------------------------------------------------------------------------------- 46 | for ( let i = 0; i < 6; i ++ ) { 47 | const material = new Material( 48 | Shaders.quadVert, 49 | Shaders.returnFrag 50 | ); 51 | material.addUniformTexture( 'sampler0', swap.i.texture ); 52 | 53 | this.__entity.components.push( new Quad( { 54 | target: swap.o, 55 | material, 56 | range: i === 0 ? [ -1.0, -1.0, 1.0, 1.0 ] : [ 0.0, 0.0, 1.0, 1.0 ] 57 | } ) ); 58 | 59 | swap.swap(); 60 | } 61 | 62 | // -- blur --------------------------------------------------------------------------------------- 63 | for ( let i = 0; i < 2; i ++ ) { 64 | const material = new Material( 65 | Shaders.quadVert, 66 | Shaders.bloomBlurFrag 67 | ); 68 | material.addUniform( 'isVert', '1i', i ); 69 | material.addUniformTexture( 'sampler0', swap.i.texture ); 70 | 71 | this.__entity.components.push( new Quad( { 72 | target: swap.o, 73 | material 74 | } ) ); 75 | 76 | swap.swap(); 77 | } 78 | 79 | // -- post --------------------------------------------------------------------------------------- 80 | const materialBloomPost = new Material( 81 | Shaders.quadVert, 82 | Shaders.bloomPostFrag 83 | ); 84 | materialBloomPost.addUniformTexture( 'samplerDry', options.input ); 85 | materialBloomPost.addUniformTexture( 'samplerWet', swap.i.texture ); 86 | 87 | this.__entity.components.push( new Quad( { 88 | target: options.target, 89 | material: materialBloomPost 90 | } ) ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/entities/CameraEntity.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { BufferRenderTarget } from '../heck/BufferRenderTarget'; 3 | import CONFIG from '../config.json'; 4 | import { DISPLAY } from '../heck/DISPLAY'; 5 | import { Entity } from '../heck/Entity'; 6 | import { Lambda } from '../heck/components/Lambda'; 7 | import { LightEntity } from './LightEntity'; 8 | import { Material } from '../heck/Material'; 9 | import { PerspectiveCamera } from '../heck/components/PerspectiveCamera'; 10 | import { Quad } from '../heck/components/Quad'; 11 | import { RenderTarget } from '../heck/RenderTarget'; 12 | import { Shaders } from '../shaders'; 13 | import { loadImageTexture } from '../utils/loadImageTexture'; 14 | 15 | const textureEnv = DISPLAY.glCat.createTexture(); 16 | textureEnv.setZeroTexture(); 17 | loadImageTexture( { 18 | texture: textureEnv, 19 | url: require( '../images/luxo.png' ).default 20 | } ); 21 | 22 | const textureBRDFLUT = DISPLAY.glCat.createTexture(); 23 | textureBRDFLUT.setZeroTexture(); 24 | loadImageTexture( { 25 | texture: textureBRDFLUT, 26 | url: require( '../images/brdf-lut.png' ).default 27 | } ); 28 | 29 | export interface CameraEntityOptions { 30 | root: Entity; 31 | target: RenderTarget; 32 | lights: LightEntity[]; 33 | textureRandom: GLCatTexture; 34 | } 35 | 36 | export class CameraEntity { 37 | private __root: Entity; 38 | 39 | public get root(): Entity { 40 | return this.__root; 41 | } 42 | 43 | private __camera: PerspectiveCamera; 44 | 45 | public get camera(): PerspectiveCamera { 46 | return this.__camera; 47 | } 48 | 49 | private __entity: Entity; 50 | 51 | public get entity(): Entity { 52 | return this.__entity; 53 | } 54 | 55 | public constructor( options: CameraEntityOptions ) { 56 | this.__root = options.root; 57 | 58 | this.__entity = new Entity(); 59 | 60 | const cameraTarget = new BufferRenderTarget( { 61 | width: options.target.width, 62 | height: options.target.height, 63 | numBuffers: 4 64 | } ); 65 | this.__camera = new PerspectiveCamera( { 66 | scene: this.__root, 67 | renderTarget: cameraTarget, 68 | near: 0.1, 69 | far: 20.0 70 | } ); 71 | this.__entity.components.push( this.__camera ); 72 | 73 | const aoTarget = new BufferRenderTarget( { 74 | width: CONFIG.aoRatio * options.target.width, 75 | height: CONFIG.aoRatio * options.target.height 76 | } ); 77 | 78 | const aoMaterial = new Material( 79 | Shaders.quadVert, 80 | require( '../shaders/ao.frag' ).default 81 | ); 82 | 83 | this.__entity.components.push( new Lambda( () => { 84 | const cameraView = this.__entity.transform.matrix.inverse!; 85 | 86 | aoMaterial.addUniformVector( 87 | 'cameraPV', 88 | 'Matrix4fv', 89 | this.camera.projectionMatrix.multiply( 90 | cameraView 91 | ).elements 92 | ); 93 | } ) ); 94 | 95 | for ( let i = 0; i < 2; i ++ ) { // it doesn't need 2 and 3 96 | aoMaterial.addUniformTexture( 97 | 'sampler' + i, 98 | cameraTarget.getTexture( GL.COLOR_ATTACHMENT0 + i ) 99 | ); 100 | } 101 | 102 | aoMaterial.addUniformTexture( 'samplerRandom', options.textureRandom ); 103 | 104 | if ( module.hot ) { 105 | module.hot.accept( '../shaders/ao.frag', () => { 106 | aoMaterial.cueShader( 107 | Shaders.quadVert, 108 | require( '../shaders/ao.frag' ).default 109 | ); 110 | } ); 111 | } 112 | 113 | const aoQuad = new Quad( { 114 | material: aoMaterial, 115 | target: aoTarget 116 | } ); 117 | this.__entity.components.push( aoQuad ); 118 | 119 | const shadingMaterials = options.lights.map( ( light, iLight ) => { 120 | const shadingMaterial = new Material( 121 | Shaders.quadVert, 122 | require( '../shaders/shading.frag' ).default, 123 | { 124 | IS_FIRST_LIGHT: iLight === 0 ? 'true' : undefined 125 | } 126 | ); 127 | 128 | this.__entity.components.push( new Lambda( () => { 129 | const cameraView = this.__entity.transform.matrix.inverse!; 130 | 131 | shadingMaterial.addUniformVector( 132 | 'cameraView', 133 | 'Matrix4fv', 134 | cameraView.elements 135 | ); 136 | 137 | shadingMaterial.addUniformVector( 138 | 'cameraPV', 139 | 'Matrix4fv', 140 | this.camera.projectionMatrix.multiply( 141 | cameraView 142 | ).elements 143 | ); 144 | 145 | shadingMaterial.addUniform( 146 | 'lightNearFar', 147 | '2f', 148 | light.camera.near, 149 | light.camera.far 150 | ); 151 | 152 | shadingMaterial.addUniform( 153 | 'cameraNearFar', 154 | '2f', 155 | this.camera.near, 156 | this.camera.far 157 | ); 158 | 159 | shadingMaterial.addUniform( 160 | 'cameraPos', 161 | '3f', 162 | ...this.__entity.transform.position.elements 163 | ); 164 | 165 | shadingMaterial.addUniform( 166 | 'lightPos', 167 | '3f', 168 | ...light.entity.transform.position.elements 169 | ); 170 | 171 | shadingMaterial.addUniform( 172 | 'lightColor', 173 | '3f', 174 | ...light.color 175 | ); 176 | 177 | shadingMaterial.addUniformVector( 178 | 'lightPV', 179 | 'Matrix4fv', 180 | light.camera.projectionMatrix.multiply( 181 | light.entity.transform.matrix.inverse! 182 | ).elements 183 | ); 184 | } ) ); 185 | 186 | for ( let i = 0; i < 4; i ++ ) { 187 | shadingMaterial.addUniformTexture( 188 | 'sampler' + i, 189 | cameraTarget.getTexture( GL.COLOR_ATTACHMENT0 + i ) 190 | ); 191 | } 192 | 193 | shadingMaterial.blend = [ GL.ONE, GL.ONE ]; 194 | shadingMaterial.addUniformTexture( 'samplerAo', aoTarget.texture ); 195 | shadingMaterial.addUniformTexture( 'samplerShadow', light.shadowMap.texture ); 196 | shadingMaterial.addUniformTexture( 'samplerBRDFLUT', textureBRDFLUT ); 197 | shadingMaterial.addUniformTexture( 'samplerEnv', textureEnv ); 198 | shadingMaterial.addUniformTexture( 'samplerRandom', options.textureRandom ); 199 | 200 | const shadingQuad = new Quad( { 201 | material: shadingMaterial, 202 | target: options.target 203 | } ); 204 | shadingQuad.clear = iLight === 0 ? [] : false; 205 | this.__entity.components.push( shadingQuad ); 206 | 207 | return shadingMaterial; 208 | } ); 209 | 210 | if ( module.hot ) { 211 | module.hot.accept( '../shaders/shading.frag', () => { 212 | shadingMaterials.forEach( ( material ) => { 213 | material.cueShader( 214 | Shaders.quadVert, 215 | require( '../shaders/shading.frag' ).default 216 | ); 217 | } ); 218 | } ); 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/entities/ErrorLayer.ts: -------------------------------------------------------------------------------- 1 | import { DISPLAY } from '../heck/DISPLAY'; 2 | import { Entity } from '../heck/Entity'; 3 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 4 | import { Lambda } from '../heck/components/Lambda'; 5 | import { Material } from '../heck/Material'; 6 | import { Plane } from './Plane'; 7 | import { Shaders } from '../shaders'; 8 | import { Vector3 } from '@fms-cat/experimental'; 9 | 10 | export class ErrorLayer { 11 | private __entity: Entity; 12 | 13 | public get entity(): Entity { 14 | return this.__entity; 15 | } 16 | 17 | private __plane: Plane; 18 | 19 | public get plane(): Plane { 20 | return this.__plane; 21 | } 22 | 23 | private __isResolved = true; 24 | private __errorTime: number = 0.0; 25 | private __resolvedTime: number = 100.0; 26 | 27 | private __message: string = ''; 28 | private __canvas: HTMLCanvasElement; 29 | private __context: CanvasRenderingContext2D; 30 | private __texture: GLCatTexture; 31 | 32 | public constructor() { 33 | this.__entity = new Entity(); 34 | this.__entity.transform.position = new Vector3( [ 0.0, 0.0, 1.4 ] ); 35 | this.__entity.transform.scale = new Vector3( [ 2.0, 1.0, 1.0 ] ); 36 | 37 | this.__canvas = document.createElement( 'canvas' ); 38 | this.__canvas.width = 2048; 39 | this.__canvas.height = 1024; 40 | document.body.appendChild( this.__canvas ); 41 | 42 | this.__context = this.__canvas.getContext( '2d' )!; 43 | 44 | this.__texture = DISPLAY.glCat.createTexture(); 45 | this.__texture.setTexture( this.__canvas ); 46 | 47 | this.__entity.components.push( new Lambda( ( event ) => { 48 | this.__draw( event.deltaTime ); 49 | } ) ); 50 | 51 | const material = new Material( 52 | Shaders.objectVert, 53 | require( '../shaders/errorlayer.frag' ).default 54 | ); 55 | 56 | if ( module.hot ) { 57 | module.hot.accept( '../shaders/errorlayer.frag', () => { 58 | material.cueShader( 59 | Shaders.objectVert, 60 | require( '../shaders/errorlayer.frag' ).default 61 | ); 62 | } ); 63 | } 64 | 65 | this.__plane = new Plane( { material } ); 66 | this.__entity.children.push( this.__plane.entity ); 67 | this.__plane.material.addUniformTexture( 'sampler0', this.__texture ); 68 | } 69 | 70 | public setText( message: any ): void { 71 | if ( this.__isResolved ) { 72 | this.__isResolved = false; 73 | this.__errorTime = 0.0; 74 | this.__resolvedTime = 0.0; 75 | } 76 | 77 | this.__message = message?.toString(); 78 | } 79 | 80 | public resolve(): void { 81 | this.__isResolved = true; 82 | } 83 | 84 | private __draw( deltaTime: number ): void { 85 | this.__context.clearRect( 0, 0, this.__canvas.width, this.__canvas.height ); 86 | 87 | this.__errorTime += deltaTime; 88 | 89 | if ( this.__isResolved ) { 90 | this.__resolvedTime += deltaTime; 91 | } 92 | 93 | this.__context.textAlign = 'center'; 94 | this.__context.textBaseline = 'middle'; 95 | this.__context.font = '900 128px Yu Mincho'; 96 | this.__context.fillStyle = '#ffffff'; 97 | this.__context.fillText( '警告', 1024, 102.4 ); 98 | 99 | this.__context.textAlign = 'left'; 100 | this.__context.font = '500 40px "Exo", monospace'; 101 | this.__context.fillStyle = '#ffffff'; 102 | const lines = this.__message.split( '\n' ); 103 | lines.forEach( ( line, i ) => { 104 | this.__context.fillText( line, 64, 256 + i * 64 ); 105 | } ); 106 | 107 | this.__texture.setTexture( this.__canvas ); 108 | this.__plane.material.addUniform( 'errorTime', '1f', this.__errorTime ); 109 | this.__plane.material.addUniform( 'resolvedTime', '1f', this.__resolvedTime ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/entities/FXAA.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../heck/Entity'; 2 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 3 | import { Material } from '../heck/Material'; 4 | import { Quad } from '../heck/components/Quad'; 5 | import { RenderTarget } from '../heck/RenderTarget'; 6 | import { Shaders } from '../shaders'; 7 | 8 | export interface FXAAOptions { 9 | input: GLCatTexture; 10 | target: RenderTarget; 11 | } 12 | 13 | export class FXAA { 14 | private __entity: Entity; 15 | 16 | public get entity(): Entity { 17 | return this.__entity; 18 | } 19 | 20 | public constructor( options: FXAAOptions ) { 21 | this.__entity = new Entity(); 22 | 23 | // -- fxaa ------------------------------------------------------------------------------------- 24 | const materialBloomPre = new Material( 25 | Shaders.quadVert, 26 | require( '../shaders/fxaa.frag' ).default 27 | ); 28 | materialBloomPre.addUniformTexture( 'sampler0', options.input ); 29 | 30 | this.__entity.components.push( new Quad( { 31 | target: options.target, 32 | material: materialBloomPre 33 | } ) ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/entities/GPUParticles.ts: -------------------------------------------------------------------------------- 1 | import { BufferRenderTarget, BufferRenderTargetOptions } from '../heck/BufferRenderTarget'; 2 | import { Entity } from '../heck/Entity'; 3 | import { Geometry } from '../heck/Geometry'; 4 | import { Lambda } from '../heck/components/Lambda'; 5 | import { Material } from '../heck/Material'; 6 | import { Mesh } from '../heck/components/Mesh'; 7 | import { Quad } from '../heck/components/Quad'; 8 | import { Swap } from '@fms-cat/experimental'; 9 | 10 | export interface GPUParticlesOptions { 11 | materialCompute: Material; 12 | geometryRender: Geometry; 13 | materialRender: Material; 14 | computeWidth: number; 15 | computeHeight: number; 16 | computeNumBuffers: number; 17 | } 18 | 19 | export class GPUParticles { 20 | private __entity: Entity; 21 | 22 | public get entity(): Entity { 23 | return this.__entity; 24 | } 25 | 26 | private __swapCompute: Swap; 27 | 28 | private __quadCompute: Quad; 29 | 30 | private __meshRender: Mesh; 31 | 32 | public get meshRender(): Mesh { 33 | return this.__meshRender; 34 | } 35 | 36 | public get materialCompute(): Material { 37 | return this.__quadCompute.material; 38 | } 39 | 40 | public get materialRender(): Material { 41 | return this.__meshRender.material; 42 | } 43 | 44 | public constructor( options: GPUParticlesOptions ) { 45 | this.__entity = new Entity(); 46 | 47 | const brtOptions: BufferRenderTargetOptions = { 48 | width: options.computeWidth, 49 | height: options.computeHeight, 50 | numBuffers: options.computeNumBuffers 51 | }; 52 | 53 | this.__swapCompute = new Swap( 54 | new BufferRenderTarget( brtOptions ), 55 | new BufferRenderTarget( brtOptions ) 56 | ); 57 | 58 | // -- swapper ---------------------------------------------------------------------------------- 59 | this.__entity.components.push( new Lambda( () => { 60 | this.__swapCompute.swap(); 61 | 62 | this.materialCompute.addUniformTexture( 'samplerCompute', this.__swapCompute.i.texture ); 63 | this.__quadCompute.target = this.__swapCompute.o; 64 | this.materialRender.addUniformTexture( 'samplerCompute', this.__swapCompute.o.texture ); 65 | } ) ); 66 | 67 | // -- compute ---------------------------------------------------------------------------------- 68 | this.__quadCompute = new Quad( { 69 | target: this.__swapCompute.o, 70 | material: options.materialCompute 71 | } ); 72 | this.__entity.components.push( this.__quadCompute ); 73 | 74 | // -- render ----------------------------------------------------------------------------------- 75 | this.__meshRender = new Mesh( 76 | options.geometryRender, 77 | options.materialRender 78 | ); 79 | options.materialRender.addUniform( 80 | 'resolutionCompute', 81 | '2f', 82 | options.computeWidth, 83 | options.computeHeight 84 | ); 85 | this.__entity.components.push( this.__meshRender ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/entities/Glitch.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../heck/Entity'; 2 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 3 | import { Material } from '../heck/Material'; 4 | import { Quad } from '../heck/components/Quad'; 5 | import { RenderTarget } from '../heck/RenderTarget'; 6 | import { Shaders } from '../shaders'; 7 | 8 | export interface GlitchOptions { 9 | input: GLCatTexture; 10 | target: RenderTarget; 11 | } 12 | 13 | export class Glitch { 14 | private __entity: Entity; 15 | 16 | public get entity(): Entity { 17 | return this.__entity; 18 | } 19 | 20 | private __material: Material; 21 | 22 | public get material(): Material { 23 | return this.__material; 24 | } 25 | 26 | public constructor( options: GlitchOptions ) { 27 | this.__entity = new Entity(); 28 | 29 | // -- quad ------------------------------------------------------------------------------------- 30 | this.__material = new Material( 31 | Shaders.quadVert, 32 | require( '../shaders/glitch.frag' ).default 33 | ); 34 | this.__material.addUniformTexture( 'sampler0', options.input ); 35 | 36 | if ( module.hot ) { 37 | module.hot.accept( '../shaders/glitch.frag', () => { 38 | this.__material.cueShader( 39 | Shaders.quadVert, 40 | require( '../shaders/glitch.frag' ).default 41 | ); 42 | } ); 43 | } 44 | 45 | this.__entity.components.push( new Quad( { 46 | target: options.target, 47 | material: this.__material 48 | } ) ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/entities/HotPlane.ts: -------------------------------------------------------------------------------- 1 | import { Mesh, MeshCull } from '../heck/components/Mesh'; 2 | import { TRIANGLE_STRIP_QUAD_3D, TRIANGLE_STRIP_QUAD_NORMAL, TRIANGLE_STRIP_QUAD_UV, Vector3 } from '@fms-cat/experimental'; 3 | import { DISPLAY } from '../heck/DISPLAY'; 4 | import { Entity } from '../heck/Entity'; 5 | import { GL } from '@fms-cat/glcat-ts'; 6 | import { Geometry } from '../heck/Geometry'; 7 | import { Material } from '../heck/Material'; 8 | import { Shaders } from '../shaders'; 9 | 10 | export class HotPlane { 11 | private __mesh: Mesh; 12 | private __geometry: Geometry; 13 | 14 | private __material: Material; 15 | 16 | public get material(): Material { 17 | return this.__material; 18 | } 19 | 20 | private __entity: Entity; 21 | 22 | public get entity(): Entity { 23 | return this.__entity; 24 | } 25 | 26 | public constructor() { 27 | this.__entity = new Entity(); 28 | this.__entity.transform.position = new Vector3( [ 0.0, 0.0, 1.0 ] ); 29 | this.__entity.transform.scale = new Vector3( [ 16.0, 9.0, 1.0 ] ).scale( 0.15 ); 30 | 31 | this.__geometry = this.__createGeoemtry(); 32 | this.__material = this.__createMaterial(); 33 | 34 | this.__mesh = new Mesh( this.__geometry, this.__material ); 35 | this.__mesh.cull = MeshCull.None; 36 | this.__entity.components.push( this.__mesh ); 37 | } 38 | 39 | protected __createGeoemtry(): Geometry { 40 | const geometry = new Geometry(); 41 | 42 | const bufferPos = DISPLAY.glCat.createBuffer(); 43 | bufferPos.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_3D ) ); 44 | geometry.addAttribute( 'position', { 45 | buffer: bufferPos, 46 | size: 3, 47 | type: GL.FLOAT 48 | } ); 49 | 50 | const bufferNor = DISPLAY.glCat.createBuffer(); 51 | bufferNor.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_NORMAL ) ); 52 | geometry.addAttribute( 'normal', { 53 | buffer: bufferNor, 54 | size: 3, 55 | type: GL.FLOAT 56 | } ); 57 | 58 | const bufferUv = DISPLAY.glCat.createBuffer(); 59 | bufferUv.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_UV ) ); 60 | geometry.addAttribute( 'uv', { 61 | buffer: bufferUv, 62 | size: 2, 63 | type: GL.FLOAT 64 | } ); 65 | 66 | geometry.count = 4; 67 | geometry.mode = DISPLAY.gl.TRIANGLE_STRIP; 68 | 69 | return geometry; 70 | } 71 | 72 | protected __createMaterial(): Material { 73 | const material = new Material( 74 | Shaders.objectVert, 75 | require( '../shaders/hotplane.frag' ).default 76 | ); 77 | 78 | if ( module.hot ) { 79 | module.hot.accept( '../shaders/hotplane.frag', () => { 80 | material.cueShader( 81 | Shaders.objectVert, 82 | require( '../shaders/hotplane.frag' ).default 83 | ); 84 | } ); 85 | } 86 | 87 | return material; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/entities/Invert.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../heck/Entity'; 2 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 3 | import { Material } from '../heck/Material'; 4 | import { Quad } from '../heck/components/Quad'; 5 | import { RenderTarget } from '../heck/RenderTarget'; 6 | import { Shaders } from '../shaders'; 7 | 8 | export class Invert { 9 | private __entity: Entity; 10 | 11 | public get entity(): Entity { 12 | return this.__entity; 13 | } 14 | 15 | private __material: Material; 16 | 17 | public get material(): Material { 18 | return this.__material; 19 | } 20 | 21 | public constructor( input: GLCatTexture, target: RenderTarget ) { 22 | this.__entity = new Entity(); 23 | 24 | this.__material = new Material( 25 | Shaders.quadVert, 26 | Shaders.invertFrag 27 | ); 28 | this.__material.addUniformTexture( 'sampler0', input ); 29 | 30 | const quad = new Quad( { 31 | target: target, 32 | material: this.__material 33 | } ); 34 | this.__entity.components.push( quad ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/entities/LightEntity.ts: -------------------------------------------------------------------------------- 1 | import { BufferRenderTarget } from '../heck/BufferRenderTarget'; 2 | import { Entity } from '../heck/Entity'; 3 | import { Lambda } from '../heck/components/Lambda'; 4 | import { Material } from '../heck/Material'; 5 | import { PerspectiveCamera } from '../heck/components/PerspectiveCamera'; 6 | import { Quad } from '../heck/components/Quad'; 7 | import { Shaders } from '../shaders'; 8 | import { Swap } from '@fms-cat/experimental'; 9 | 10 | export interface LightEntityOptions { 11 | root: Entity; 12 | shadowMapFov?: number; 13 | shadowMapNear?: number; 14 | shadowMapFar?: number; 15 | shadowMapWidth?: number; 16 | shadowMapHeight?: number; 17 | } 18 | 19 | export class LightEntity { 20 | public color: [ number, number, number ] = [ 1.0, 1.0, 1.0 ]; 21 | 22 | private __root: Entity; 23 | 24 | public get root(): Entity { 25 | return this.__root; 26 | } 27 | 28 | private __shadowMapCamera: PerspectiveCamera; 29 | 30 | public get camera(): PerspectiveCamera { 31 | return this.__shadowMapCamera; 32 | } 33 | 34 | private __shadowMap: BufferRenderTarget; 35 | 36 | public get shadowMap(): BufferRenderTarget { 37 | return this.__shadowMap; 38 | } 39 | 40 | private __entity: Entity; 41 | 42 | public get entity(): Entity { 43 | return this.__entity; 44 | } 45 | 46 | public constructor( options: LightEntityOptions ) { 47 | this.__root = options.root; 48 | 49 | this.__entity = new Entity(); 50 | 51 | const swapOptions = { 52 | width: options.shadowMapWidth || 1024, 53 | height: options.shadowMapHeight || 1024 54 | }; 55 | 56 | const swap = new Swap( 57 | new BufferRenderTarget( swapOptions ), 58 | new BufferRenderTarget( swapOptions ) 59 | ); 60 | 61 | // -- camera ----------------------------------------------------------------------------------- 62 | const fov = options.shadowMapFov || 45.0; 63 | const near = options.shadowMapNear || 0.1; 64 | const far = options.shadowMapFar || 100.0; 65 | 66 | this.__shadowMapCamera = new PerspectiveCamera( { 67 | fov, 68 | near, 69 | far, 70 | renderTarget: swap.o, 71 | scene: this.__root 72 | } ); 73 | this.__shadowMapCamera.clear = [ 1.0, 1.0, 1.0, 1.0 ]; 74 | this.__entity.components.push( this.__shadowMapCamera ); 75 | 76 | this.__shadowMap = new BufferRenderTarget( { 77 | width: options.shadowMapWidth || 1024, 78 | height: options.shadowMapHeight || 1024 79 | } ); 80 | 81 | swap.swap(); 82 | 83 | // -- convert ---------------------------------------------------------------------------------- 84 | const materialConvert = new Material( 85 | Shaders.quadVert, 86 | Shaders.posToDepthFrag 87 | ); 88 | 89 | materialConvert.addUniformTexture( 'sampler0', swap.i.texture ); 90 | 91 | this.__entity.components.push( new Lambda( () => { 92 | materialConvert.addUniform( 'cameraPos', '3f', ...this.entity.transform.position.elements ); 93 | materialConvert.addUniform( 'cameraNearFar', '2f', this.camera.near, this.camera.far ); 94 | } ) ); 95 | 96 | this.__entity.components.push( new Quad( { 97 | target: swap.o, 98 | material: materialConvert 99 | } ) ); 100 | 101 | swap.swap(); 102 | 103 | // -- blur --------------------------------------------------------------------------------------- 104 | for ( let i = 0; i < 2; i ++ ) { 105 | const material = new Material( 106 | Shaders.quadVert, 107 | Shaders.shadowBlurFrag 108 | ); 109 | material.addUniform( 'isVert', '1i', i ); 110 | material.addUniformTexture( 'sampler0', swap.i.texture ); 111 | 112 | this.__entity.components.push( new Quad( { 113 | target: i === 0 ? swap.o : this.__shadowMap, 114 | material 115 | } ) ); 116 | 117 | swap.swap(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/entities/Plane.ts: -------------------------------------------------------------------------------- 1 | import { Mesh, MeshCull } from '../heck/components/Mesh'; 2 | import { TRIANGLE_STRIP_QUAD_3D, TRIANGLE_STRIP_QUAD_NORMAL, TRIANGLE_STRIP_QUAD_UV } from '@fms-cat/experimental'; 3 | import { DISPLAY } from '../heck/DISPLAY'; 4 | import { Entity } from '../heck/Entity'; 5 | import { GL } from '@fms-cat/glcat-ts'; 6 | import { Geometry } from '../heck/Geometry'; 7 | import { Material } from '../heck/Material'; 8 | 9 | export class Plane { 10 | private __mesh: Mesh; 11 | private __geometry: Geometry; 12 | 13 | private __material: Material; 14 | 15 | public get material(): Material { 16 | return this.__material; 17 | } 18 | 19 | private __entity: Entity; 20 | 21 | public get entity(): Entity { 22 | return this.__entity; 23 | } 24 | 25 | public constructor( options: { 26 | material: Material; 27 | } ) { 28 | this.__entity = new Entity(); 29 | 30 | this.__geometry = this.__createGeoemtry(); 31 | this.__material = options.material; 32 | 33 | this.__mesh = new Mesh( this.__geometry, this.__material ); 34 | this.__mesh.cull = MeshCull.None; 35 | this.__entity.components.push( this.__mesh ); 36 | } 37 | 38 | protected __createGeoemtry(): Geometry { 39 | const geometry = new Geometry(); 40 | 41 | const bufferPos = DISPLAY.glCat.createBuffer(); 42 | bufferPos.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_3D ) ); 43 | geometry.addAttribute( 'position', { 44 | buffer: bufferPos, 45 | size: 3, 46 | type: GL.FLOAT 47 | } ); 48 | 49 | const bufferNor = DISPLAY.glCat.createBuffer(); 50 | bufferNor.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_NORMAL ) ); 51 | geometry.addAttribute( 'normal', { 52 | buffer: bufferNor, 53 | size: 3, 54 | type: GL.FLOAT 55 | } ); 56 | 57 | const bufferUv = DISPLAY.glCat.createBuffer(); 58 | bufferUv.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD_UV ) ); 59 | geometry.addAttribute( 'uv', { 60 | buffer: bufferUv, 61 | size: 2, 62 | type: GL.FLOAT 63 | } ); 64 | 65 | geometry.count = 4; 66 | geometry.mode = DISPLAY.gl.TRIANGLE_STRIP; 67 | 68 | return geometry; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/entities/Post.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../heck/Entity'; 2 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 3 | import { Material } from '../heck/Material'; 4 | import { Quad } from '../heck/components/Quad'; 5 | import { RenderTarget } from '../heck/RenderTarget'; 6 | import { Shaders } from '../shaders'; 7 | 8 | export interface PostOptions { 9 | input: GLCatTexture; 10 | target: RenderTarget; 11 | } 12 | 13 | export class Post { 14 | private __entity: Entity; 15 | 16 | public get entity(): Entity { 17 | return this.__entity; 18 | } 19 | 20 | public constructor( options: PostOptions ) { 21 | this.__entity = new Entity(); 22 | 23 | // -- post ------------------------------------------------------------------------------------- 24 | const material = new Material( 25 | Shaders.quadVert, 26 | require( '../shaders/post.frag' ).default 27 | ); 28 | material.addUniformTexture( 'sampler0', options.input ); 29 | 30 | if ( module.hot ) { 31 | module.hot.accept( '../shaders/post.frag', () => { 32 | material.cueShader( 33 | Shaders.quadVert, 34 | require( '../shaders/post.frag' ).default 35 | ); 36 | } ); 37 | } 38 | 39 | this.__entity.components.push( new Quad( { 40 | target: options.target, 41 | material 42 | } ) ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/entities/Raymarcher.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { Mesh, MeshCull } from '../heck/components/Mesh'; 3 | import { TRIANGLE_STRIP_QUAD, Vector3 } from '@fms-cat/experimental'; 4 | import { DISPLAY } from '../heck/DISPLAY'; 5 | import { DrawLambda } from '../heck/components/DrawLambda'; 6 | import { Entity } from '../heck/Entity'; 7 | import { Geometry } from '../heck/Geometry'; 8 | import { Material } from '../heck/Material'; 9 | import { Shaders } from '../shaders'; 10 | 11 | export class Raymarcher { 12 | private __mesh: Mesh; 13 | private __geometry: Geometry; 14 | 15 | private __material: Material; 16 | 17 | public get material(): Material { 18 | return this.__material; 19 | } 20 | 21 | private __entity: Entity; 22 | 23 | public get entity(): Entity { 24 | return this.__entity; 25 | } 26 | 27 | public constructor( options: { 28 | textureRandom: GLCatTexture; 29 | textureRandomStatic: GLCatTexture; 30 | } ) { 31 | this.__entity = new Entity(); 32 | this.__entity.transform.position = new Vector3( [ 0.0, 0.0, 0.3 ] ); 33 | this.__entity.transform.scale = new Vector3( [ 16.0, 9.0, 1.0 ] ).scale( 0.15 ); 34 | 35 | this.__geometry = this.__createGeoemtry(); 36 | this.__material = this.__createMaterial(); 37 | 38 | this.__material.addUniform( 'range', '4f', -1.0, -1.0, 1.0, 1.0 ); 39 | 40 | this.__material.addUniformTexture( 'samplerRandom', options.textureRandom ); 41 | this.__material.addUniformTexture( 'samplerRandomStatic', options.textureRandomStatic ); 42 | 43 | this.__entity.components.push( new DrawLambda( ( event ) => { 44 | this.__material.addUniform( 45 | 'cameraNearFar', 46 | '2f', 47 | event.camera.near, 48 | event.camera.far 49 | ); 50 | 51 | this.__material.addUniformVector( 52 | 'inversePV', 53 | 'Matrix4fv', 54 | event.projectionMatrix.multiply( event.viewMatrix ).inverse!.elements 55 | ); 56 | } ) ); 57 | 58 | this.__mesh = new Mesh( this.__geometry, this.__material ); 59 | this.__mesh.cull = MeshCull.None; 60 | this.__entity.components.push( this.__mesh ); 61 | } 62 | 63 | protected __createGeoemtry(): Geometry { 64 | const geometry = new Geometry(); 65 | 66 | const bufferPos = DISPLAY.glCat.createBuffer(); 67 | bufferPos.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD ) ); 68 | geometry.addAttribute( 'p', { 69 | buffer: bufferPos, 70 | size: 2, 71 | type: GL.FLOAT 72 | } ); 73 | 74 | geometry.count = 4; 75 | geometry.mode = GL.TRIANGLE_STRIP; 76 | 77 | return geometry; 78 | } 79 | 80 | protected __createMaterial(): Material { 81 | const material = new Material( 82 | Shaders.quadVert, 83 | require( '../shaders/raymarcher.frag' ).default 84 | ); 85 | 86 | if ( module.hot ) { 87 | module.hot.accept( '../shaders/raymarcher.frag', () => { 88 | material.cueShader( 89 | Shaders.quadVert, 90 | require( '../shaders/raymarcher.frag' ).default 91 | ); 92 | } ); 93 | } 94 | 95 | return material; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/entities/SphereParticles.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { Entity } from '../heck/Entity'; 4 | import { GPUParticles } from './GPUParticles'; 5 | import { Geometry } from '../heck/Geometry'; 6 | import { InstancedGeometry } from '../heck/InstancedGeometry'; 7 | import { Material } from '../heck/Material'; 8 | import { Shaders } from '../shaders'; 9 | import { genOctahedron } from '../geometries/genOctahedron'; 10 | 11 | export interface SphereParticlesOptions { 12 | particlesSqrt: number; 13 | textureRandom: GLCatTexture; 14 | textureRandomStatic: GLCatTexture; 15 | } 16 | 17 | export class SphereParticles { 18 | private static __ppp = 2; 19 | 20 | public get entity(): Entity { 21 | return this.__gpuParticles.entity; 22 | } 23 | 24 | private __gpuParticles: GPUParticles; 25 | 26 | public get materialCompute(): Material { 27 | return this.__gpuParticles.materialCompute; 28 | } 29 | 30 | public get materialRender(): Material { 31 | return this.__gpuParticles.materialRender; 32 | } 33 | 34 | public constructor( options: SphereParticlesOptions ) { 35 | this.__gpuParticles = new GPUParticles( { 36 | materialCompute: this.__createMaterialCompute( options ), 37 | geometryRender: this.__createGeometryRender( options ), 38 | materialRender: this.__createMaterialRender( options ), 39 | computeWidth: SphereParticles.__ppp * options.particlesSqrt, 40 | computeHeight: options.particlesSqrt, 41 | computeNumBuffers: 1 42 | } ); 43 | } 44 | 45 | private __createMaterialCompute( options: SphereParticlesOptions ): Material { 46 | const { particlesSqrt } = options; 47 | const particles = particlesSqrt * particlesSqrt; 48 | 49 | const material = new Material( 50 | Shaders.quadVert, 51 | require( '../shaders/sphere-particles-compute.frag' ).default 52 | ); 53 | material.addUniform( 'particlesSqrt', '1f', particlesSqrt ); 54 | material.addUniform( 'particles', '1f', particles ); 55 | material.addUniform( 'ppp', '1f', SphereParticles.__ppp ); 56 | material.addUniformTexture( 'samplerRandom', options.textureRandom ); 57 | 58 | if ( module.hot ) { 59 | module.hot.accept( '../shaders/sphere-particles-compute.frag', () => { 60 | material.cueShader( 61 | Shaders.quadVert, 62 | require( '../shaders/sphere-particles-compute.frag' ).default 63 | ); 64 | } ); 65 | } 66 | 67 | return material; 68 | } 69 | 70 | private __createGeometryRender( options: SphereParticlesOptions ): Geometry { 71 | const { particlesSqrt } = options; 72 | const particles = particlesSqrt * particlesSqrt; 73 | 74 | const octahedron = genOctahedron( { radius: 1.0, div: 1 } ); 75 | 76 | const geometry = new InstancedGeometry(); 77 | 78 | geometry.addAttribute( 'position', octahedron.position ); 79 | geometry.addAttribute( 'normal', octahedron.normal ); 80 | geometry.setIndex( octahedron.index ); 81 | 82 | const bufferComputeUV = DISPLAY.glCat.createBuffer(); 83 | bufferComputeUV.setVertexbuffer( ( () => { 84 | const ret = new Float32Array( particles * 2 ); 85 | for ( let iy = 0; iy < particlesSqrt; iy ++ ) { 86 | for ( let ix = 0; ix < particlesSqrt; ix ++ ) { 87 | const i = ix + iy * particlesSqrt; 88 | const s = ( SphereParticles.__ppp * ix + 0.5 ) 89 | / ( SphereParticles.__ppp * particlesSqrt ); 90 | const t = ( iy + 0.5 ) 91 | / ( particlesSqrt ); 92 | ret[ i * 2 + 0 ] = s; 93 | ret[ i * 2 + 1 ] = t; 94 | } 95 | } 96 | return ret; 97 | } )() ); 98 | 99 | geometry.addAttribute( 'computeUV', { 100 | buffer: bufferComputeUV, 101 | size: 2, 102 | divisor: 1, 103 | type: GL.FLOAT 104 | } ); 105 | 106 | geometry.count = octahedron.count; 107 | geometry.mode = octahedron.mode; 108 | geometry.primcount = options.particlesSqrt * options.particlesSqrt; 109 | 110 | return geometry; 111 | } 112 | 113 | private __createMaterialRender( options: SphereParticlesOptions ): Material { 114 | const material = new Material( 115 | require( '../shaders/sphere-particles-render.vert' ).default, 116 | require( '../shaders/sphere-particles-render.frag' ).default, 117 | { 118 | 'USE_CLIP': 'true', 119 | 'USE_VERTEX_COLOR': 'true' 120 | } 121 | ); 122 | material.addUniform( 'colorVar', '1f', 0.1 ); 123 | material.addUniform( 'ppp', '1f', SphereParticles.__ppp ); 124 | material.addUniformTexture( 'samplerRandomStatic', options.textureRandomStatic ); 125 | 126 | if ( module.hot ) { 127 | module.hot.accept( '../shaders/sphere-particles-render.vert', () => { 128 | material.cueShader( 129 | require( '../shaders/sphere-particles-render.vert' ).default, 130 | require( '../shaders/sphere-particles-render.frag' ).default 131 | ); 132 | } ); 133 | } 134 | 135 | if ( module.hot ) { 136 | module.hot.accept( '../shaders/sphere-particles-render.frag', () => { 137 | material.cueShader( 138 | require( '../shaders/sphere-particles-render.vert' ).default, 139 | require( '../shaders/sphere-particles-render.frag' ).default 140 | ); 141 | } ); 142 | } 143 | 144 | return material; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/entities/Trails.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { Entity } from '../heck/Entity'; 4 | import { GPUParticles } from './GPUParticles'; 5 | import { Geometry } from '../heck/Geometry'; 6 | import { InstancedGeometry } from '../heck/InstancedGeometry'; 7 | import { Material } from '../heck/Material'; 8 | import { Shaders } from '../shaders'; 9 | 10 | export interface TrailsOptions { 11 | trails: number; 12 | trailLength: number; 13 | textureRandom: GLCatTexture; 14 | textureRandomStatic: GLCatTexture; 15 | } 16 | 17 | export class Trails { 18 | private static __ppp = 2; 19 | 20 | public get entity(): Entity { 21 | return this.__gpuParticles.entity; 22 | } 23 | 24 | private __gpuParticles: GPUParticles; 25 | 26 | public get materialCompute(): Material { 27 | return this.__gpuParticles.materialCompute; 28 | } 29 | 30 | public get materialRender(): Material { 31 | return this.__gpuParticles.materialRender; 32 | } 33 | 34 | public constructor( options: TrailsOptions ) { 35 | this.__gpuParticles = new GPUParticles( { 36 | materialCompute: this.__createMaterialCompute( options ), 37 | geometryRender: this.__createGeometryRender( options ), 38 | materialRender: this.__createMaterialRender( options ), 39 | computeWidth: Trails.__ppp * options.trailLength, 40 | computeHeight: options.trails, 41 | computeNumBuffers: 1 42 | } ); 43 | } 44 | 45 | private __createMaterialCompute( options: TrailsOptions ): Material { 46 | const material = new Material( 47 | Shaders.quadVert, 48 | require( '../shaders/trails-compute.frag' ).default 49 | ); 50 | material.addUniform( 'trails', '1f', options.trails ); 51 | material.addUniform( 'trailLength', '1f', options.trailLength ); 52 | material.addUniform( 'ppp', '1f', Trails.__ppp ); 53 | material.addUniformTexture( 'samplerRandom', options.textureRandom ); 54 | 55 | if ( module.hot ) { 56 | module.hot.accept( '../shaders/trails-compute.frag', () => { 57 | material.cueShader( 58 | Shaders.quadVert, 59 | require( '../shaders/trails-compute.frag' ).default 60 | ); 61 | } ); 62 | } 63 | 64 | return material; 65 | } 66 | 67 | private __createGeometryRender( options: TrailsOptions ): Geometry { 68 | const geometry = new InstancedGeometry(); 69 | 70 | const bufferComputeU = DISPLAY.glCat.createBuffer(); 71 | bufferComputeU.setVertexbuffer( ( () => { 72 | const ret = new Float32Array( options.trailLength * 3 ); 73 | for ( let i = 0; i < options.trailLength; i ++ ) { 74 | const u = ( Trails.__ppp * i + 0.5 ) / ( Trails.__ppp * options.trailLength ); 75 | ret[ i * 3 + 0 ] = u; 76 | ret[ i * 3 + 1 ] = u; 77 | ret[ i * 3 + 2 ] = u; 78 | } 79 | return ret; 80 | } )() ); 81 | 82 | geometry.addAttribute( 'computeU', { 83 | buffer: bufferComputeU, 84 | size: 1, 85 | type: GL.FLOAT 86 | } ); 87 | 88 | const bufferComputeV = DISPLAY.glCat.createBuffer(); 89 | bufferComputeV.setVertexbuffer( ( () => { 90 | const ret = new Float32Array( options.trails ); 91 | for ( let i = 0; i < options.trails; i ++ ) { 92 | ret[ i ] = ( i + 0.5 ) / options.trails; 93 | } 94 | return ret; 95 | } )() ); 96 | 97 | geometry.addAttribute( 'computeV', { 98 | buffer: bufferComputeV, 99 | size: 1, 100 | divisor: 1, 101 | type: GL.FLOAT 102 | } ); 103 | 104 | const bufferTriIndex = DISPLAY.glCat.createBuffer(); 105 | bufferTriIndex.setVertexbuffer( ( () => { 106 | const ret = new Float32Array( 3 * options.trailLength ); 107 | for ( let i = 0; i < options.trailLength; i ++ ) { 108 | ret[ i * 3 + 0 ] = 0; 109 | ret[ i * 3 + 1 ] = 1; 110 | ret[ i * 3 + 2 ] = 2; 111 | } 112 | return ret; 113 | } )() ); 114 | 115 | geometry.addAttribute( 'triIndex', { 116 | buffer: bufferTriIndex, 117 | size: 1, 118 | type: GL.FLOAT 119 | } ); 120 | 121 | const indexBuffer = DISPLAY.glCat.createBuffer(); 122 | indexBuffer.setIndexbuffer( ( () => { 123 | const ret = new Uint16Array( ( options.trailLength - 1 ) * 18 ); 124 | for ( let i = 0; i < options.trailLength - 1; i ++ ) { 125 | for ( let j = 0; j < 3; j ++ ) { 126 | const jn = ( j + 1 ) % 3; 127 | ret[ i * 18 + j * 6 + 0 ] = i * 3 + j; 128 | ret[ i * 18 + j * 6 + 1 ] = i * 3 + 3 + j; 129 | ret[ i * 18 + j * 6 + 2 ] = i * 3 + 3 + jn; 130 | ret[ i * 18 + j * 6 + 3 ] = i * 3 + j; 131 | ret[ i * 18 + j * 6 + 4 ] = i * 3 + 3 + jn; 132 | ret[ i * 18 + j * 6 + 5 ] = i * 3 + jn; 133 | } 134 | } 135 | return ret; 136 | } )() ); 137 | 138 | geometry.setIndex( { 139 | buffer: indexBuffer, 140 | type: GL.UNSIGNED_SHORT 141 | } ); 142 | 143 | geometry.count = ( options.trailLength - 1 ) * 18; 144 | geometry.primcount = options.trails; 145 | geometry.mode = GL.TRIANGLES; 146 | 147 | return geometry; 148 | } 149 | 150 | private __createMaterialRender( options: TrailsOptions ): Material { 151 | const material = new Material( 152 | require( '../shaders/trails-render.vert' ).default, 153 | require( '../shaders/trails-render.frag' ).default, 154 | { 155 | 'USE_CLIP': 'true', 156 | 'USE_VERTEX_COLOR': 'true' 157 | } 158 | ); 159 | material.addUniform( 'colorVar', '1f', 0.1 ); 160 | material.addUniform( 'ppp', '1f', Trails.__ppp ); 161 | material.addUniformTexture( 'samplerRandomStatic', options.textureRandomStatic ); 162 | 163 | if ( module.hot ) { 164 | module.hot.accept( '../shaders/trails-render.vert', () => { 165 | material.cueShader( 166 | require( '../shaders/trails-render.vert' ).default, 167 | require( '../shaders/trails-render.frag' ).default 168 | ); 169 | } ); 170 | } 171 | 172 | if ( module.hot ) { 173 | module.hot.accept( '../shaders/trails-render.frag', () => { 174 | material.cueShader( 175 | require( '../shaders/trails-render.vert' ).default, 176 | require( '../shaders/trails-render.frag' ).default 177 | ); 178 | } ); 179 | } 180 | 181 | return material; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/entities/UIParticles.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { EVENTMAN } from '../utils/EventManager'; 4 | import { Entity } from '../heck/Entity'; 5 | import { GPUParticles } from './GPUParticles'; 6 | import { Geometry } from '../heck/Geometry'; 7 | import { InstancedGeometry } from '../heck/InstancedGeometry'; 8 | import { Material } from '../heck/Material'; 9 | import { MeshCull } from '../heck/components/Mesh'; 10 | import { Shaders } from '../shaders'; 11 | import { TRIANGLE_STRIP_QUAD } from '@fms-cat/experimental'; 12 | import { createFontSpritesheetSDF } from '../utils/createFontSpritesheetSDF'; 13 | import { loadImageTexture } from '../utils/loadImageTexture'; 14 | 15 | const textureMissing = DISPLAY.glCat.createTexture(); 16 | textureMissing.setZeroTexture(); 17 | 18 | const textureChar = DISPLAY.glCat.createTexture(); 19 | textureChar.setZeroTexture(); 20 | 21 | async function prepareTextures(): Promise { 22 | await loadImageTexture( { 23 | texture: textureMissing, 24 | url: require( '../images/missing.png' ).default 25 | } ); 26 | 27 | await createFontSpritesheetSDF( { 28 | texture: textureChar, 29 | charSize: [ 64, 64 ], 30 | font: '700 48px "Exo"', 31 | } ); 32 | } 33 | prepareTextures(); 34 | EVENTMAN.on( 'regenerate', () => prepareTextures() ); 35 | 36 | export interface UIParticlesOptions { 37 | particlesSqrt: number; 38 | textureRandom: GLCatTexture; 39 | textureRandomStatic: GLCatTexture; 40 | } 41 | 42 | export class UIParticles { 43 | private static __ppp = 2; 44 | 45 | public get entity(): Entity { 46 | return this.__gpuParticles.entity; 47 | } 48 | 49 | private __gpuParticles: GPUParticles; 50 | 51 | public get materialCompute(): Material { 52 | return this.__gpuParticles.materialCompute; 53 | } 54 | 55 | public get materialRender(): Material { 56 | return this.__gpuParticles.materialRender; 57 | } 58 | 59 | public constructor( options: UIParticlesOptions ) { 60 | this.__gpuParticles = new GPUParticles( { 61 | materialCompute: this.__createMaterialCompute( options ), 62 | geometryRender: this.__createGeometryRender( options ), 63 | materialRender: this.__createMaterialRender( options ), 64 | computeWidth: UIParticles.__ppp * options.particlesSqrt, 65 | computeHeight: options.particlesSqrt, 66 | computeNumBuffers: 1 67 | } ); 68 | this.__gpuParticles.meshRender.cull = MeshCull.None; 69 | } 70 | 71 | private __createMaterialCompute( options: UIParticlesOptions ): Material { 72 | const material = new Material( 73 | Shaders.quadVert, 74 | require( '../shaders/ui-particles-compute.frag' ).default 75 | ); 76 | material.addUniform( 'particlesSqrt', '1f', options.particlesSqrt ); 77 | material.addUniform( 'particles', '1f', options.particlesSqrt * options.particlesSqrt ); 78 | material.addUniform( 'ppp', '1f', UIParticles.__ppp ); 79 | material.addUniformTexture( 'samplerRandom', options.textureRandom ); 80 | 81 | if ( module.hot ) { 82 | module.hot.accept( '../shaders/ui-particles-compute.frag', () => { 83 | material.cueShader( 84 | Shaders.quadVert, 85 | require( '../shaders/ui-particles-compute.frag' ).default 86 | ); 87 | } ); 88 | } 89 | 90 | return material; 91 | } 92 | 93 | private __createGeometryRender( options: UIParticlesOptions ): Geometry { 94 | const geometry = new InstancedGeometry(); 95 | 96 | const bufferComputeUV = DISPLAY.glCat.createBuffer(); 97 | bufferComputeUV.setVertexbuffer( ( () => { 98 | const ret = new Float32Array( options.particlesSqrt * options.particlesSqrt * 2 ); 99 | for ( let iy = 0; iy < options.particlesSqrt; iy ++ ) { 100 | for ( let ix = 0; ix < options.particlesSqrt; ix ++ ) { 101 | const i = ix + iy * options.particlesSqrt; 102 | const s = ( UIParticles.__ppp * ix + 0.5 ) 103 | / ( UIParticles.__ppp * options.particlesSqrt ); 104 | const t = ( iy + 0.5 ) 105 | / ( options.particlesSqrt ); 106 | ret[ i * 2 + 0 ] = s; 107 | ret[ i * 2 + 1 ] = t; 108 | } 109 | } 110 | return ret; 111 | } )() ); 112 | 113 | geometry.addAttribute( 'computeUV', { 114 | buffer: bufferComputeUV, 115 | size: 2, 116 | divisor: 1, 117 | type: GL.FLOAT 118 | } ); 119 | 120 | const bufferP = DISPLAY.glCat.createBuffer(); 121 | bufferP.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD ) ); 122 | 123 | geometry.addAttribute( 'p', { 124 | buffer: bufferP, 125 | size: 2, 126 | type: GL.FLOAT 127 | } ); 128 | 129 | geometry.count = 4; 130 | geometry.primcount = options.particlesSqrt * options.particlesSqrt; 131 | geometry.mode = GL.TRIANGLE_STRIP; 132 | 133 | return geometry; 134 | } 135 | 136 | private __createMaterialRender( options: UIParticlesOptions ): Material { 137 | const material = new Material( 138 | require( '../shaders/ui-particles-render.vert' ).default, 139 | require( '../shaders/ui-particles-render.frag' ).default, 140 | { 141 | 'USE_CLIP': 'true', 142 | 'USE_VERTEX_COLOR': 'true' 143 | } 144 | ); 145 | material.addUniform( 'ppp', '1f', UIParticles.__ppp ); 146 | material.addUniformTexture( 'samplerRandomStatic', options.textureRandomStatic ); 147 | material.addUniformTexture( 'samplerChar', textureChar ); 148 | material.addUniformTexture( 'samplerDoublequoteRandom', textureMissing ); 149 | 150 | if ( module.hot ) { 151 | module.hot.accept( '../shaders/ui-particles-render.vert', () => { 152 | material.cueShader( 153 | require( '../shaders/ui-particles-render.vert' ).default, 154 | require( '../shaders/ui-particles-render.frag' ).default 155 | ); 156 | } ); 157 | 158 | module.hot.accept( '../shaders/ui-particles-render.frag', () => { 159 | material.cueShader( 160 | require( '../shaders/ui-particles-render.vert' ).default, 161 | require( '../shaders/ui-particles-render.frag' ).default 162 | ); 163 | } ); 164 | } 165 | 166 | return material; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/entities/Waku.ts: -------------------------------------------------------------------------------- 1 | import { Entity } from '../heck/Entity'; 2 | import { Material } from '../heck/Material'; 3 | import { Plane } from './Plane'; 4 | import { Shaders } from '../shaders'; 5 | import { Vector3 } from '@fms-cat/experimental'; 6 | 7 | export class Waku { 8 | private __entity: Entity; 9 | 10 | public get entity(): Entity { 11 | return this.__entity; 12 | } 13 | 14 | public constructor() { 15 | this.__entity = new Entity(); 16 | 17 | const materialTop = new Material( 18 | Shaders.objectVert, 19 | require( '../shaders/waku.frag' ).default 20 | ); 21 | 22 | const planeTop = new Plane( { material: materialTop } ); 23 | planeTop.material.addUniform( 'uvScale', '2f', 2.5, 0.05 ); 24 | planeTop.entity.transform.scale = new Vector3( [ 2.5, 0.05, 1.0 ] ); 25 | planeTop.entity.transform.position = new Vector3( [ 0.0, 1.5, 1.0 ] ); 26 | this.__entity.children.push( planeTop.entity ); 27 | 28 | const materialBotttom = new Material( 29 | Shaders.objectVert, 30 | require( '../shaders/waku.frag' ).default 31 | ); 32 | 33 | const planeBottom = new Plane( { material: materialBotttom } ); 34 | planeBottom.material.addUniform( 'uvScale', '2f', -2.5, -0.05 ); 35 | planeBottom.entity.transform.scale = new Vector3( [ 2.5, 0.05, 1.0 ] ); 36 | planeBottom.entity.transform.position = new Vector3( [ 0.0, -1.5, 1.0 ] ); 37 | this.__entity.children.push( planeBottom.entity ); 38 | 39 | if ( module.hot ) { 40 | module.hot.accept( '../shaders/waku.frag', () => { 41 | materialTop.cueShader( 42 | Shaders.objectVert, 43 | require( '../shaders/waku.frag' ).default 44 | ); 45 | 46 | materialBotttom.cueShader( 47 | Shaders.objectVert, 48 | require( '../shaders/waku.frag' ).default 49 | ); 50 | } ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/geometries/genOctahedron.ts: -------------------------------------------------------------------------------- 1 | import { GeometryAttribute, GeometryIndex } from '../heck/Geometry'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { GL } from '@fms-cat/glcat-ts'; 4 | 5 | interface ResultGenOctahedron { 6 | position: GeometryAttribute; 7 | normal: GeometryAttribute; 8 | index: GeometryIndex; 9 | count: number; 10 | mode: GLenum; 11 | } 12 | 13 | export function genOctahedron( options: { 14 | radius?: number; 15 | div?: number; 16 | } = {} ): ResultGenOctahedron { 17 | const radius = options.radius ?? 1.0; 18 | const div = options.div ?? 1; 19 | 20 | const pos = []; 21 | const nor = []; 22 | const ind = []; 23 | 24 | for ( let ii = 0; ii < 2; ii ++ ) { // side 25 | for ( let ip = 0; ip < 4; ip ++ ) { // plane 26 | for ( let iy = 0; iy < div + 1; iy ++ ) { 27 | for ( let ix = 0; ix < iy + 1; ix ++ ) { 28 | const lat0 = ( ii * 2.0 + iy / ( div + 1 ) ) * Math.PI / 2.0; 29 | const lat1 = ( ii * 2.0 + ( iy + 1 ) / ( div + 1 ) ) * Math.PI / 2.0; 30 | 31 | const lon0 = ( ii * 2.0 - 1.0 ) * ( ( ix - 1 ) / Math.max( 1, iy ) + ip ) * Math.PI / 2.0; 32 | const lon1 = ( ii * 2.0 - 1.0 ) * ( ix / ( iy + 1 ) + ip ) * Math.PI / 2.0; 33 | const lon2 = ( ii * 2.0 - 1.0 ) * ( ix / Math.max( 1, iy ) + ip ) * Math.PI / 2.0; 34 | const lon3 = ( ii * 2.0 - 1.0 ) * ( ( ix + 1 ) / ( iy + 1 ) + ip ) * Math.PI / 2.0; 35 | 36 | if ( ix !== 0 ) { 37 | ind.push( 38 | pos.length / 3, 39 | pos.length / 3 + 1, 40 | pos.length / 3 + 2 41 | ); 42 | 43 | const x1 = radius * Math.sin( lat0 ) * Math.cos( lon0 ); 44 | const y1 = radius * Math.cos( lat0 ); 45 | const z1 = radius * Math.sin( lat0 ) * Math.sin( lon0 ); 46 | 47 | const x2 = radius * Math.sin( lat1 ) * Math.cos( lon1 ); 48 | const y2 = radius * Math.cos( lat1 ); 49 | const z2 = radius * Math.sin( lat1 ) * Math.sin( lon1 ); 50 | 51 | const x3 = radius * Math.sin( lat0 ) * Math.cos( lon2 ); 52 | const y3 = radius * Math.cos( lat0 ); 53 | const z3 = radius * Math.sin( lat0 ) * Math.sin( lon2 ); 54 | 55 | pos.push( 56 | x1, y1, z1, 57 | x2, y2, z2, 58 | x3, y3, z3 59 | ); 60 | 61 | { 62 | const x = x1 + x2 + x3; 63 | const y = y1 + y2 + y3; 64 | const z = z1 + z2 + z3; 65 | const l = Math.sqrt( x * x + y * y + z * z ); 66 | 67 | for ( let i = 0; i < 3; i ++ ) { 68 | nor.push( 69 | x / l, 70 | y / l, 71 | z / l 72 | ); 73 | } 74 | } 75 | } 76 | 77 | { 78 | ind.push( 79 | pos.length / 3, 80 | pos.length / 3 + 1, 81 | pos.length / 3 + 2 82 | ); 83 | 84 | const x1 = radius * Math.sin( lat0 ) * Math.cos( lon2 ); 85 | const y1 = radius * Math.cos( lat0 ); 86 | const z1 = radius * Math.sin( lat0 ) * Math.sin( lon2 ); 87 | 88 | const x2 = radius * Math.sin( lat1 ) * Math.cos( lon1 ); 89 | const y2 = radius * Math.cos( lat1 ); 90 | const z2 = radius * Math.sin( lat1 ) * Math.sin( lon1 ); 91 | 92 | const x3 = radius * Math.sin( lat1 ) * Math.cos( lon3 ); 93 | const y3 = radius * Math.cos( lat1 ); 94 | const z3 = radius * Math.sin( lat1 ) * Math.sin( lon3 ); 95 | 96 | pos.push( 97 | x1, y1, z1, 98 | x2, y2, z2, 99 | x3, y3, z3 100 | ); 101 | 102 | { 103 | const x = x1 + x2 + x3; 104 | const y = y1 + y2 + y3; 105 | const z = z1 + z2 + z3; 106 | const l = Math.sqrt( x * x + y * y + z * z ); 107 | 108 | for ( let i = 0; i < 3; i ++ ) { 109 | nor.push( 110 | x / l, 111 | y / l, 112 | z / l 113 | ); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | const position: GeometryAttribute = { 123 | buffer: DISPLAY.glCat.createBuffer(), 124 | type: GL.FLOAT, 125 | size: 3 126 | }; 127 | position.buffer.setVertexbuffer( new Float32Array( pos ) ); 128 | 129 | const normal: GeometryAttribute = { 130 | buffer: DISPLAY.glCat.createBuffer(), 131 | type: GL.FLOAT, 132 | size: 3 133 | }; 134 | normal.buffer.setVertexbuffer( new Float32Array( nor ) ); 135 | 136 | const index: GeometryIndex = { 137 | buffer: DISPLAY.glCat.createBuffer(), 138 | type: GL.UNSIGNED_SHORT 139 | }; 140 | index.buffer.setIndexbuffer( new Uint16Array( ind ) ); 141 | 142 | return { 143 | position, 144 | normal, 145 | index, 146 | count: ind.length, 147 | mode: GL.TRIANGLES 148 | }; 149 | } 150 | -------------------------------------------------------------------------------- /src/heck/BufferRenderTarget.ts: -------------------------------------------------------------------------------- 1 | import { GLCatFramebuffer, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from './DISPLAY'; 3 | import { RenderTarget } from './RenderTarget'; 4 | 5 | export interface BufferRenderTargetOptions { 6 | width: number; 7 | height: number; 8 | numBuffers?: number; 9 | isFloat?: boolean; 10 | } 11 | 12 | export class BufferRenderTarget extends RenderTarget { 13 | private readonly __framebuffer: GLCatFramebuffer; 14 | 15 | public get framebuffer(): GLCatFramebuffer { 16 | return this.__framebuffer; 17 | } 18 | 19 | private __width: number; 20 | 21 | public get width(): number { 22 | return this.__width; 23 | } 24 | 25 | private __height: number; 26 | 27 | public get height(): number { 28 | return this.__height; 29 | } 30 | 31 | private __numBuffers: number; 32 | 33 | public get numBuffers(): number { 34 | return this.__numBuffers; 35 | } 36 | 37 | public constructor( options: BufferRenderTargetOptions ) { 38 | super(); 39 | 40 | this.__framebuffer = DISPLAY.glCat.lazyDrawbuffers( 41 | options.width, 42 | options.height, 43 | options.numBuffers || 1, 44 | options.isFloat || true 45 | ); 46 | 47 | this.__width = options.width; 48 | this.__height = options.height; 49 | this.__numBuffers = options.numBuffers || 1; 50 | } 51 | 52 | public get texture(): GLCatTexture { 53 | return this.__framebuffer.texture!; 54 | } 55 | 56 | public getTexture( attachment: number ): GLCatTexture | null { 57 | return this.__framebuffer.getTexture( attachment ); 58 | } 59 | 60 | public bind(): void { 61 | const { gl, glCat } = DISPLAY; 62 | gl.bindFramebuffer( gl.FRAMEBUFFER, this.__framebuffer.raw ); 63 | glCat.drawBuffers( this.__numBuffers ); 64 | gl.viewport( 0, 0, this.width, this.height ); 65 | } 66 | 67 | public dispose(): void { 68 | this.__framebuffer.dispose(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/heck/CanvasRenderTarget.ts: -------------------------------------------------------------------------------- 1 | import { DISPLAY } from './DISPLAY'; 2 | import { RenderTarget } from './RenderTarget'; 3 | 4 | export class CanvasRenderTarget extends RenderTarget { 5 | public get width(): number { 6 | return DISPLAY.canvas.width; 7 | } 8 | 9 | public get height(): number { 10 | return DISPLAY.canvas.height; 11 | } 12 | 13 | public bind(): void { 14 | const { gl, glCat } = DISPLAY; 15 | gl.bindFramebuffer( gl.FRAMEBUFFER, null ); 16 | glCat.drawBuffers( [ gl.BACK ] ); 17 | gl.viewport( 0, 0, this.width, this.height ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/heck/DISPLAY.ts: -------------------------------------------------------------------------------- 1 | import CONFIG from '../config.json'; 2 | import { GLCat } from '@fms-cat/glcat-ts'; 3 | 4 | const canvas = document.querySelector( '#canvas' )!; 5 | canvas.width = CONFIG.resolution[ 0 ]; 6 | canvas.height = CONFIG.resolution[ 1 ]; 7 | 8 | const gl = canvas.getContext( 'webgl' )!; 9 | gl.lineWidth( 1 ); 10 | 11 | const glCat = new GLCat( gl ); 12 | 13 | glCat.getExtension( 'EXT_frag_depth', true ); 14 | glCat.getExtension( 'OES_standard_derivatives', true ); 15 | glCat.getExtension( 'OES_texture_float', true ); 16 | glCat.getExtension( 'OES_texture_float_linear', true ); 17 | 18 | export const DISPLAY = { 19 | canvas, 20 | gl, 21 | glCat 22 | }; 23 | -------------------------------------------------------------------------------- /src/heck/Dog.ts: -------------------------------------------------------------------------------- 1 | import { Clock, ClockRealtime } from '@fms-cat/experimental'; 2 | import { Entity } from './Entity'; 3 | import { Transform } from './Transform'; 4 | 5 | export interface DogOptions { 6 | clock: Clock; 7 | root: Entity; 8 | } 9 | 10 | const defaultDogOptions: DogOptions = { 11 | clock: new ClockRealtime(), 12 | root: new Entity() 13 | }; 14 | 15 | /** 16 | * And what a WONDERFUL Dog they are!! 17 | */ 18 | export class Dog { 19 | public root: Entity; 20 | public clock: Clock = new Clock(); 21 | public active: boolean = true; 22 | 23 | private __frameCount: number = 0; 24 | 25 | public constructor( options: DogOptions = defaultDogOptions ) { 26 | this.root = options.root; 27 | this.clock = options.clock; 28 | 29 | const update = (): void => { 30 | if ( this.active ) { 31 | this.clock.update(); 32 | this.root.update( { 33 | frameCount: this.__frameCount ++, 34 | time: this.clock.time, 35 | deltaTime: this.clock.deltaTime, 36 | globalTransform: new Transform(), 37 | parent: null 38 | } ); 39 | } 40 | 41 | requestAnimationFrame( update ); 42 | }; 43 | update(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/heck/Entity.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from './components/Camera'; 2 | import { Component } from './components/Component'; 3 | import { Matrix4 } from '@fms-cat/experimental'; 4 | import { RenderTarget } from './RenderTarget'; 5 | import { Transform } from './Transform'; 6 | 7 | export interface EntityUpdateEvent { 8 | frameCount: number; 9 | time: number; 10 | deltaTime: number; 11 | globalTransform: Transform; 12 | parent: Entity | null; 13 | } 14 | 15 | export interface EntityDrawEvent { 16 | frameCount: number; 17 | time: number; 18 | renderTarget: RenderTarget; 19 | globalTransform: Transform; 20 | viewMatrix: Matrix4; 21 | projectionMatrix: Matrix4; 22 | camera: Camera; 23 | } 24 | 25 | export class Entity { 26 | public readonly transform = new Transform(); 27 | 28 | public active = true; 29 | public visible = true; 30 | 31 | public children: Entity[] = []; 32 | public components: Component[] = []; 33 | 34 | public update( event: EntityUpdateEvent ): void { 35 | if ( !this.active ) { return; } 36 | 37 | const globalTransform = event.globalTransform.multiply( this.transform ); 38 | 39 | this.components.forEach( ( component ) => { 40 | component.update( { 41 | frameCount: event.frameCount, 42 | time: event.time, 43 | deltaTime: event.deltaTime, 44 | globalTransform, 45 | entity: this 46 | } ); 47 | } ); 48 | 49 | this.children.forEach( ( child ) => { 50 | child.update( { 51 | frameCount: event.frameCount, 52 | time: event.time, 53 | deltaTime: event.deltaTime, 54 | globalTransform, 55 | parent: this 56 | } ); 57 | } ); 58 | } 59 | 60 | public draw( event: EntityDrawEvent ): void { 61 | if ( !this.visible ) { return; } 62 | 63 | const globalTransform = event.globalTransform.multiply( this.transform ); 64 | 65 | this.components.forEach( ( component ) => { 66 | component.draw( { 67 | frameCount: event.frameCount, 68 | time: event.time, 69 | renderTarget: event.renderTarget, 70 | globalTransform, 71 | camera: event.camera, 72 | viewMatrix: event.viewMatrix, 73 | projectionMatrix: event.projectionMatrix, 74 | entity: this 75 | } ); 76 | } ); 77 | 78 | this.children.forEach( ( child ) => { 79 | child.draw( { 80 | frameCount: event.frameCount, 81 | time: event.time, 82 | renderTarget: event.renderTarget, 83 | globalTransform, 84 | viewMatrix: event.viewMatrix, 85 | projectionMatrix: event.projectionMatrix, 86 | camera: event.camera 87 | } ); 88 | } ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/heck/Geometry.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatBuffer } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from './DISPLAY'; 3 | import { Material } from './Material'; 4 | 5 | export interface GeometryAttribute { 6 | buffer: GLCatBuffer; 7 | size: number; 8 | divisor?: number; 9 | type: GLenum; 10 | stride?: number; 11 | offset?: number; 12 | } 13 | 14 | export interface GeometryIndex { 15 | buffer: GLCatBuffer; 16 | type: GLenum; 17 | } 18 | 19 | export class Geometry { 20 | protected static __typeSizeMap = { 21 | [ GL.UNSIGNED_BYTE ]: 1, 22 | [ GL.UNSIGNED_SHORT ]: 2, 23 | [ GL.UNSIGNED_INT ]: 4 24 | }; 25 | 26 | protected __attributes: { 27 | [ name: string ]: GeometryAttribute; 28 | } = {}; 29 | protected __index: GeometryIndex | null = null; 30 | 31 | public mode: GLenum = GL.TRIANGLES; 32 | public first = 0; 33 | public count = 0; 34 | public primcount: number | null = null; 35 | 36 | public addAttribute( name: string, attribute: GeometryAttribute ): void { 37 | this.__attributes[ name ] = attribute; 38 | } 39 | 40 | public removeAttribute( name: string, alsoDisposeBuffer = true ): void { 41 | if ( alsoDisposeBuffer ) { 42 | this.__attributes[ name ].buffer.dispose(); 43 | } 44 | 45 | delete this.__attributes[ name ]; 46 | } 47 | 48 | public setIndex( index: GeometryIndex | null ): void { 49 | this.__index = index; 50 | } 51 | 52 | public assignBuffers( material: Material ): void { 53 | const program = material.program; 54 | 55 | Object.entries( this.__attributes ).forEach( ( [ name, attr ] ) => { 56 | program.attribute( 57 | name, 58 | attr.buffer, 59 | attr.size, 60 | attr.divisor, 61 | attr.type, 62 | attr.stride, 63 | attr.offset 64 | ); 65 | } ); 66 | } 67 | 68 | public draw(): void { 69 | const gl = DISPLAY.glCat.renderingContext; 70 | 71 | if ( this.count === 0 ) { 72 | console.warn( 'You attempt to draw a geometry that count is 0' ); 73 | return; 74 | } 75 | 76 | if ( this.__index ) { 77 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, this.__index.buffer.raw ); 78 | gl.drawElements( 79 | this.mode, 80 | this.count, 81 | this.__index.type, 82 | this.first * Geometry.__typeSizeMap[ this.__index.type ] 83 | ); 84 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); 85 | } else { 86 | gl.drawArrays( this.mode, this.first, this.count ); 87 | } 88 | } 89 | 90 | public disposeBuffers(): void { 91 | Object.values( this.__attributes ).forEach( ( attr ) => { 92 | attr.buffer.dispose(); 93 | } ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/heck/InstancedGeometry.ts: -------------------------------------------------------------------------------- 1 | import { DISPLAY } from './DISPLAY'; 2 | import { Geometry } from './Geometry'; 3 | 4 | export class InstancedGeometry extends Geometry { 5 | public primcount: number = 0; 6 | 7 | public draw(): void { 8 | const { gl, glCat } = DISPLAY; 9 | const ext = glCat.getExtension( 'ANGLE_instanced_arrays', true ); 10 | 11 | if ( this.count === 0 ) { 12 | console.warn( 'You attempt to draw an instanced geometry that count is 0' ); 13 | return; 14 | } 15 | 16 | if ( this.primcount === 0 ) { 17 | console.warn( 'You attempt to draw an instanced geometry that primcount is 0' ); 18 | return; 19 | } 20 | 21 | if ( this.__index ) { 22 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, this.__index.buffer.raw ); 23 | ext.drawElementsInstancedANGLE( 24 | this.mode, 25 | this.count, 26 | this.__index.type, 27 | this.first * InstancedGeometry.__typeSizeMap[ this.__index.type ], 28 | this.primcount 29 | ); 30 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); 31 | } else { 32 | ext.drawArraysInstancedANGLE( this.mode, this.first, this.count, this.primcount ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/heck/Material.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatProgram, GLCatProgramUniformType, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from './DISPLAY'; 3 | import { EVENTMAN } from '../utils/EventManager'; 4 | import { SHADERPOOL } from './ShaderPool'; 5 | import { matchAll } from '../utils/matchAll'; 6 | 7 | export class Material { 8 | private static __cueMaterials: Set = new Set(); 9 | 10 | protected __defines: { 11 | [ name: string ]: ( string | undefined ); 12 | }; 13 | 14 | protected __uniforms: { 15 | [ name: string ]: { 16 | type: GLCatProgramUniformType; 17 | value: number[]; 18 | }; 19 | } = {}; 20 | 21 | protected __uniformVectors: { 22 | [ name: string ]: { 23 | type: GLCatProgramUniformType; 24 | value: Float32List | Int32List; 25 | }; 26 | } = {}; 27 | 28 | protected __uniformTextures: { 29 | [ name: string ]: { 30 | texture: GLCatTexture | null; 31 | }; 32 | } = {}; 33 | 34 | private __vert: string; 35 | 36 | public get vert(): string { 37 | return this.__vert; 38 | } 39 | 40 | public get vertWithDefines(): string { 41 | return this.__definesString + this.vert; 42 | } 43 | 44 | private __frag: string; 45 | 46 | public get frag(): string { 47 | return this.__frag; 48 | } 49 | 50 | public get fragWithDefines(): string { 51 | return this.__definesString + this.frag; 52 | } 53 | 54 | private __vertCue: string | null = null; 55 | 56 | private __fragCue: string | null = null; 57 | 58 | public get program(): GLCatProgram { 59 | return SHADERPOOL.getProgram( 60 | this.vertWithDefines, 61 | this.fragWithDefines, 62 | this 63 | ); 64 | } 65 | 66 | public blend: [ GLenum, GLenum ] = [ GL.ONE, GL.ZERO ]; 67 | 68 | public constructor( 69 | vert: string, 70 | frag: string, 71 | defines?: { [ key: string ]: ( string | undefined ) } 72 | ) { 73 | this.__vert = vert; 74 | this.__frag = frag; 75 | this.__defines = defines || {}; 76 | } 77 | 78 | public addUniform( name: string, type: GLCatProgramUniformType, ...value: number[] ): void { 79 | this.__uniforms[ name ] = { type, value }; 80 | } 81 | 82 | public addUniformVector( 83 | name: string, 84 | type: GLCatProgramUniformType, 85 | value: Float32List | Int32List 86 | ): void { 87 | this.__uniformVectors[ name ] = { type, value }; 88 | } 89 | 90 | public addUniformTexture( name: string, texture: GLCatTexture | null ): void { 91 | this.__uniformTextures[ name ] = { texture }; 92 | } 93 | 94 | public setUniforms(): void { 95 | const program = this.program; 96 | 97 | Object.entries( this.__uniforms ).forEach( ( [ name, { type, value } ] ) => { 98 | program.uniform( name, type, ...value ); 99 | } ); 100 | 101 | Object.entries( this.__uniformVectors ).forEach( ( [ name, { type, value } ] ) => { 102 | program.uniformVector( name, type, value ); 103 | } ); 104 | 105 | Object.entries( this.__uniformTextures ).forEach( ( [ name, { texture } ] ) => { 106 | program.uniformTexture( name, texture ); 107 | } ); 108 | } 109 | 110 | public setBlendMode(): void { 111 | const { gl } = DISPLAY; 112 | 113 | gl.blendFunc( ...this.blend ); 114 | } 115 | 116 | public async cueShader( 117 | vert: string, 118 | frag: string 119 | ): Promise { 120 | if ( this.__vertCue && this.__fragCue ) { 121 | SHADERPOOL.discardProgram( 122 | this.__definesString + this.__vertCue, 123 | this.__definesString + this.__fragCue, 124 | this 125 | ); 126 | } 127 | 128 | const d = performance.now(); 129 | 130 | const program = await SHADERPOOL.getProgramAsync( 131 | this.__definesString + vert, 132 | this.__definesString + frag, 133 | this 134 | ).catch( ( e ) => { 135 | console.error( e ); 136 | EVENTMAN.emitError( e ); 137 | // throw e; 138 | } ); 139 | 140 | if ( !program ) { 141 | this.__vertCue = null; 142 | this.__fragCue = null; 143 | Material.__cueMaterials.delete( this ); 144 | return; 145 | } else if ( this.program === program ) { 146 | this.__vertCue = null; 147 | this.__fragCue = null; 148 | Material.__cueMaterials.delete( this ); 149 | } else { 150 | this.__vertCue = vert; 151 | this.__fragCue = frag; 152 | Material.__cueMaterials.add( this ); 153 | } 154 | 155 | EVENTMAN.emitInfo( `Compiled a shader in ${ ( performance.now() - d ).toFixed( 3 ) }ms` ); 156 | } 157 | 158 | public applyCueProgram(): void { 159 | if ( !this.__vertCue || !this.__fragCue ) { 160 | console.warn( 'Attempt to apply a cue program but there was no cue' ); 161 | return; 162 | } 163 | 164 | const prevVert = this.vertWithDefines; 165 | const prevFrag = this.fragWithDefines; 166 | 167 | this.__vert = this.__vertCue; 168 | this.__frag = this.__fragCue; 169 | 170 | SHADERPOOL.discardProgram( prevVert, prevFrag, this ); 171 | 172 | this.__vertCue = null; 173 | this.__fragCue = null; 174 | 175 | EVENTMAN.emitInfo( 'Applied a shader' ); 176 | 177 | const str = this.__vert + this.__frag; 178 | const regexResult = matchAll( str, /(^|\s+)([a-zA-Z][a-zA-Z0-9_]+)/gm ); 179 | EVENTMAN.emitWords( regexResult.map( ( r ) => r[ 2 ] ) ); 180 | } 181 | 182 | public static applyCuePrograms(): void { 183 | Material.__cueMaterials.forEach( ( material ) => { 184 | material.applyCueProgram(); 185 | } ); 186 | Material.__cueMaterials.clear(); 187 | } 188 | 189 | protected get __definesString(): string { 190 | return Object.entries( this.__defines ).map( ( [ key, value ] ) => ( 191 | value ? `#define ${key} ${value}\n` : '' 192 | ) ).join( '' ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/heck/RenderTarget.ts: -------------------------------------------------------------------------------- 1 | export abstract class RenderTarget { 2 | public abstract get width(): number; 3 | 4 | public abstract get height(): number; 5 | 6 | public abstract bind(): void; 7 | } 8 | -------------------------------------------------------------------------------- /src/heck/ShaderPool.ts: -------------------------------------------------------------------------------- 1 | import { DISPLAY } from './DISPLAY'; 2 | import { GLCatProgram } from '@fms-cat/glcat-ts'; 3 | import { Material } from './Material'; 4 | 5 | export class ShaderPool { 6 | private __programMap: Map = new Map(); 7 | 8 | private __ongoingPromises: Map> = new Map(); 9 | 10 | private __programUsersMap: Map> = new Map(); 11 | 12 | public getProgram( vert: string, frag: string, user: TUser ): GLCatProgram { 13 | let program = this.__programMap.get( vert + frag ); 14 | if ( !program ) { 15 | program = DISPLAY.glCat.lazyProgram( vert, frag ); 16 | this.__programMap.set( vert + frag, program ); 17 | } 18 | 19 | this.__setUser( program, user ); 20 | 21 | return program; 22 | } 23 | 24 | public async getProgramAsync( vert: string, frag: string, user: TUser ): Promise { 25 | let program = this.__programMap.get( vert + frag ); 26 | if ( !program ) { 27 | let promise = this.__ongoingPromises.get( vert + frag ); 28 | if ( !promise ) { 29 | promise = DISPLAY.glCat.lazyProgramAsync( vert, frag ); 30 | promise.then( ( program ) => { 31 | this.__programMap.set( vert + frag, program ); 32 | this.__ongoingPromises.delete( vert + frag ); 33 | } ); 34 | this.__ongoingPromises.set( vert + frag, promise ); 35 | } 36 | 37 | program = await promise; 38 | } 39 | 40 | this.__setUser( program, user ); 41 | 42 | return program; 43 | } 44 | 45 | public discardProgram( 46 | vert: string, 47 | frag: string, 48 | user: TUser 49 | ): void { 50 | const program = this.__programMap.get( vert + frag )!; 51 | 52 | this.__deleteUser( program, user ); 53 | 54 | if ( this.__countUsers( program ) === 0 ) { 55 | program.dispose( true ); 56 | this.__programMap.delete( vert + frag ); 57 | } 58 | } 59 | 60 | private __setUser( program: GLCatProgram, user: TUser ): void { 61 | let users = this.__programUsersMap.get( program ); 62 | if ( !users ) { 63 | users = new Set(); 64 | this.__programUsersMap.set( program, users ); 65 | } 66 | 67 | if ( !users.has( user ) ) { 68 | users.add( user ); 69 | } 70 | } 71 | 72 | private __deleteUser( program: GLCatProgram, user: TUser ): void { 73 | const users = this.__programUsersMap.get( program )!; 74 | 75 | if ( !users.has( user ) ) { 76 | throw new Error( 'Attempt to delete an user of the program but the specified user is not an owner' ); 77 | } 78 | users.delete( user ); 79 | } 80 | 81 | private __countUsers( program: GLCatProgram ): number { 82 | const users = this.__programUsersMap.get( program )!; 83 | return users.size; 84 | } 85 | } 86 | 87 | export const SHADERPOOL = new ShaderPool(); 88 | -------------------------------------------------------------------------------- /src/heck/Transform.ts: -------------------------------------------------------------------------------- 1 | import { Matrix4, Quaternion, Vector3 } from '@fms-cat/experimental'; 2 | 3 | export class Transform { 4 | protected __position: Vector3 = Vector3.zero; 5 | 6 | public get position(): Vector3 { 7 | return this.__position; 8 | } 9 | 10 | public set position( vector: Vector3 ) { 11 | this.__position = vector; 12 | 13 | this.__matrix = Matrix4.compose( this.__position, this.__rotation, this.__scale ); 14 | } 15 | 16 | protected __rotation: Quaternion = Quaternion.identity; 17 | 18 | public get rotation(): Quaternion { 19 | return this.__rotation; 20 | } 21 | 22 | public set rotation( quaternion: Quaternion ) { 23 | this.__rotation = quaternion; 24 | 25 | this.__matrix = Matrix4.compose( this.__position, this.__rotation, this.__scale ); 26 | } 27 | 28 | protected __scale: Vector3 = Vector3.one; 29 | 30 | public get scale(): Vector3 { 31 | return this.__scale; 32 | } 33 | 34 | public set scale( vector: Vector3 ) { 35 | this.__scale = vector; 36 | 37 | this.__matrix = Matrix4.compose( this.__position, this.__rotation, this.__scale ); 38 | } 39 | 40 | protected __matrix: Matrix4 = Matrix4.identity; 41 | 42 | public get matrix(): Matrix4 { 43 | return this.__matrix; 44 | } 45 | 46 | public set matrix( matrix: Matrix4 ) { 47 | this.__matrix = matrix; 48 | 49 | const decomposed = this.__matrix.decompose(); 50 | this.__position = decomposed.position; 51 | this.__rotation = decomposed.rotation; 52 | this.__scale = decomposed.scale; 53 | } 54 | 55 | public lookAt( position: Vector3, target?: Vector3, up?: Vector3, roll?: number ): void { 56 | this.matrix = Matrix4.lookAt( position, target, up, roll ); 57 | } 58 | 59 | public multiply( transform: Transform ): Transform { 60 | const result = new Transform(); 61 | result.matrix = this.matrix.multiply( transform.matrix ); 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/heck/components/Camera.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentUpdateEvent } from './Component'; 2 | import { DISPLAY } from '../DISPLAY'; 3 | import { Entity } from '../Entity'; 4 | import { Matrix4 } from '@fms-cat/experimental'; 5 | import { RenderTarget } from '../RenderTarget'; 6 | import { Transform } from '../Transform'; 7 | 8 | export interface CameraOptions { 9 | renderTarget?: RenderTarget; 10 | projectionMatrix: Matrix4; 11 | scene?: Entity; 12 | clear?: Array | false; 13 | } 14 | 15 | export abstract class Camera extends Component { 16 | protected __projectionMatrix: Matrix4; 17 | 18 | public get projectionMatrix(): Matrix4 { 19 | return this.__projectionMatrix; 20 | } 21 | 22 | public renderTarget?: RenderTarget; 23 | 24 | public scene?: Entity; 25 | 26 | public clear: Array | false = []; 27 | 28 | public abstract get near(): number; 29 | 30 | public abstract get far(): number; 31 | 32 | public constructor( options: CameraOptions ) { 33 | super(); 34 | 35 | this.renderTarget = options.renderTarget; 36 | this.scene = options.scene; 37 | this.__projectionMatrix = options.projectionMatrix; 38 | if ( options.clear !== undefined ) { this.clear = options.clear; } 39 | } 40 | 41 | protected __updateImpl( event: ComponentUpdateEvent ): void { 42 | if ( !this.renderTarget ) { 43 | throw new Error( 'You must assign a renderTarget to the Camera' ); 44 | } 45 | 46 | if ( !this.scene ) { 47 | throw new Error( 'You must assign a scene to the Camera' ); 48 | } 49 | 50 | const viewMatrix = event.globalTransform.matrix.inverse!; 51 | 52 | this.renderTarget.bind(); 53 | 54 | if ( this.clear ) { 55 | DISPLAY.glCat.clear( ...this.clear ); 56 | } 57 | 58 | this.scene.draw( { 59 | frameCount: event.frameCount, 60 | time: event.time, 61 | renderTarget: this.renderTarget, 62 | globalTransform: new Transform(), 63 | viewMatrix, 64 | projectionMatrix: this.__projectionMatrix, 65 | camera: this 66 | } ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/heck/components/Component.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from './Camera'; 2 | import { Entity } from '../Entity'; 3 | import { Matrix4 } from '@fms-cat/experimental'; 4 | import { RenderTarget } from '../RenderTarget'; 5 | import { Transform } from '../Transform'; 6 | 7 | export interface ComponentUpdateEvent { 8 | frameCount: number; 9 | time: number; 10 | deltaTime: number; 11 | globalTransform: Transform; 12 | entity: Entity; 13 | } 14 | 15 | export interface ComponentDrawEvent { 16 | frameCount: number; 17 | time: number; 18 | camera: Camera; 19 | renderTarget: RenderTarget; 20 | globalTransform: Transform; 21 | viewMatrix: Matrix4; 22 | projectionMatrix: Matrix4; 23 | entity: Entity; 24 | } 25 | 26 | export class Component { 27 | protected __lastUpdateFrame = 0; 28 | 29 | public active = true; 30 | public visible = true; 31 | 32 | public update( event: ComponentUpdateEvent ): void { 33 | if ( !this.active ) { return; } 34 | if ( this.__lastUpdateFrame === event.frameCount ) { return; } 35 | this.__lastUpdateFrame = event.frameCount; 36 | this.__updateImpl( event ); 37 | } 38 | 39 | protected __updateImpl( event: ComponentUpdateEvent ): void { // eslint-disable-line 40 | // do nothing 41 | } 42 | 43 | public draw( event: ComponentDrawEvent ): void { 44 | if ( !this.visible ) { return; } 45 | this.__drawImpl( event ); 46 | } 47 | 48 | protected __drawImpl( event: ComponentDrawEvent ): void { // eslint-disable-line 49 | // do nothing 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/heck/components/DrawLambda.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentDrawEvent } from './Component'; 2 | 3 | export class DrawLambda extends Component { 4 | public onDraw?: ( event: ComponentDrawEvent ) => void; 5 | 6 | public constructor( onDraw?: ( event: ComponentDrawEvent ) => void ) { 7 | super(); 8 | this.onDraw = onDraw; 9 | } 10 | 11 | protected __drawImpl( event: ComponentDrawEvent ): void { 12 | this.onDraw && this.onDraw( event ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/heck/components/Lambda.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentUpdateEvent } from './Component'; 2 | 3 | export class Lambda extends Component { 4 | public onUpdate?: ( event: ComponentUpdateEvent ) => void; 5 | 6 | public constructor( onUpdate?: ( event: ComponentUpdateEvent ) => void ) { 7 | super(); 8 | this.onUpdate = onUpdate; 9 | } 10 | 11 | protected __updateImpl( event: ComponentUpdateEvent ): void { 12 | this.onUpdate && this.onUpdate( event ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/heck/components/LogTransform.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentUpdateEvent } from './Component'; 2 | 3 | export class LogTransform extends Component { 4 | protected __updateImpl( event: ComponentUpdateEvent ): void { 5 | console.info( ` 6 | Position: ${ event.globalTransform.position } 7 | Rotation: ${ event.globalTransform.rotation } 8 | Scale: ${ event.globalTransform.scale } 9 | Matrix: ${ event.globalTransform.matrix } 10 | ` ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/heck/components/Mesh.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentDrawEvent } from './Component'; 2 | import { DISPLAY } from '../DISPLAY'; 3 | import { GL } from '@fms-cat/glcat-ts'; 4 | import { Geometry } from '../Geometry'; 5 | import { MIDIMAN } from '../../utils/MidiManager'; 6 | import { Material } from '../Material'; 7 | 8 | export enum MeshCull { 9 | None, 10 | Front, 11 | Back, 12 | Both 13 | } 14 | 15 | const meshCullMap = { 16 | [ MeshCull.Front ]: GL.FRONT, 17 | [ MeshCull.Back ]: GL.BACK, 18 | [ MeshCull.Both ]: GL.FRONT_AND_BACK 19 | }; 20 | 21 | export class Mesh extends Component { 22 | public geometry: Geometry; 23 | public material: Material; 24 | 25 | public cull: MeshCull = MeshCull.Back; 26 | 27 | public constructor( geometry: Geometry, material: Material ) { 28 | super(); 29 | 30 | this.geometry = geometry; 31 | this.material = material; 32 | } 33 | 34 | protected __drawImpl( event: ComponentDrawEvent ): void { 35 | const glCat = DISPLAY.glCat; 36 | const gl = glCat.renderingContext; 37 | 38 | const program = this.material.program; 39 | 40 | glCat.useProgram( program ); 41 | this.material.setBlendMode(); 42 | 43 | if ( this.cull === MeshCull.None ) { 44 | gl.disable( gl.CULL_FACE ); 45 | } else { 46 | gl.enable( gl.CULL_FACE ); 47 | gl.cullFace( meshCullMap[ this.cull ] ); 48 | } 49 | 50 | this.geometry.assignBuffers( this.material ); 51 | 52 | this.material.setUniforms(); 53 | 54 | program.uniform1f( 'time', event.time ); 55 | program.uniform1f( 'frameCount', event.frameCount ); 56 | program.uniform2f( 'resolution', event.renderTarget.width, event.renderTarget.height ); 57 | program.uniform1fv( 'midiCC', MIDIMAN.ccValues ); 58 | 59 | program.uniformMatrix4fv( 'normalMatrix', event.globalTransform.matrix.inverse!.transpose.elements ); 60 | program.uniformMatrix4fv( 'modelMatrix', event.globalTransform.matrix.elements ); 61 | program.uniformMatrix4fv( 'viewMatrix', event.viewMatrix.elements ); 62 | program.uniformMatrix4fv( 'projectionMatrix', event.projectionMatrix.elements ); 63 | 64 | this.geometry.draw(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/heck/components/PerspectiveCamera.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from './Camera'; 2 | import { Entity } from '../Entity'; 3 | import { Matrix4 } from '@fms-cat/experimental'; 4 | import { RenderTarget } from '../RenderTarget'; 5 | 6 | export interface PerspectiveCameraOptions { 7 | renderTarget?: RenderTarget; 8 | near?: number; 9 | far?: number; 10 | fov?: number; 11 | scene?: Entity; 12 | clear?: Array | false; 13 | } 14 | 15 | export class PerspectiveCamera extends Camera { 16 | private __near: number; 17 | 18 | public get near(): number { 19 | return this.__near; 20 | } 21 | 22 | private __far: number; 23 | 24 | public get far(): number { 25 | return this.__far; 26 | } 27 | 28 | public constructor( options: PerspectiveCameraOptions ) { 29 | const projectionMatrix = Matrix4.perspective( 30 | options.fov || 45.0, 31 | options.near || 0.01, 32 | options.far || 100.0 33 | ); 34 | 35 | super( { 36 | projectionMatrix, 37 | renderTarget: options.renderTarget, 38 | scene: options.scene, 39 | clear: options.clear 40 | } ); 41 | 42 | this.__near = options.near || 0.01; 43 | this.__far = options.far || 100.0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/heck/components/Quad.ts: -------------------------------------------------------------------------------- 1 | import { Component, ComponentUpdateEvent } from './Component'; 2 | import { DISPLAY } from '../DISPLAY'; 3 | import { GL } from '@fms-cat/glcat-ts'; 4 | import { Geometry } from '../Geometry'; 5 | import { MIDIMAN } from '../../utils/MidiManager'; 6 | import { Material } from '../Material'; 7 | import { RenderTarget } from '../RenderTarget'; 8 | import { TRIANGLE_STRIP_QUAD } from '@fms-cat/experimental'; 9 | 10 | const quadBuffer = DISPLAY.glCat.createBuffer(); 11 | quadBuffer.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD ) ); 12 | 13 | const quadGeometry = new Geometry(); 14 | quadGeometry.addAttribute( 'p', { 15 | buffer: quadBuffer, 16 | size: 2, 17 | type: GL.FLOAT 18 | } ); 19 | 20 | quadGeometry.count = 4; 21 | quadGeometry.mode = GL.TRIANGLE_STRIP; 22 | 23 | export interface QuadOptions { 24 | material: Material; 25 | target: RenderTarget; 26 | range?: [ number, number, number, number ]; 27 | clear?: Array | false; 28 | } 29 | 30 | /** 31 | * Renders a fullscreen quad. 32 | */ 33 | export class Quad extends Component { 34 | public material: Material; 35 | public target: RenderTarget; 36 | public range: [ number, number, number, number ] = [ -1.0, -1.0, 1.0, 1.0 ]; 37 | public clear: Array | false = false; 38 | 39 | public constructor( options: QuadOptions ) { 40 | super(); 41 | 42 | this.material = options.material; 43 | this.target = options.target; 44 | if ( options.range !== undefined ) { this.range = options.range; } 45 | if ( options.clear !== undefined ) { this.clear = options.clear; } 46 | } 47 | 48 | protected __updateImpl( event: ComponentUpdateEvent ): void { 49 | const { glCat } = DISPLAY; 50 | 51 | glCat.useProgram( this.material.program ); 52 | this.target.bind(); 53 | this.material.setBlendMode(); 54 | 55 | if ( this.clear ) { 56 | DISPLAY.glCat.clear( ...this.clear ); 57 | } 58 | 59 | quadGeometry.assignBuffers( this.material ); 60 | 61 | this.material.setUniforms(); 62 | 63 | const program = this.material.program; 64 | 65 | program.uniform1f( 'time', event.time ); 66 | program.uniform1f( 'deltaTime', event.deltaTime ); 67 | program.uniform1f( 'frameCount', event.frameCount ); 68 | program.uniform1fv( 'midiCC', MIDIMAN.ccValues ); 69 | program.uniform2f( 'resolution', this.target.width, this.target.height ); 70 | program.uniform4f( 'range', ...this.range ); 71 | 72 | quadGeometry.draw(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 | Active 12 |
13 |
14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /src/images/brdf-lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/vj20200217/a17e0521542bafd69a5866b15d64c5c01474cf48/src/images/brdf-lut.png -------------------------------------------------------------------------------- /src/images/char5x5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/vj20200217/a17e0521542bafd69a5866b15d64c5c01474cf48/src/images/char5x5.png -------------------------------------------------------------------------------- /src/images/luxo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/vj20200217/a17e0521542bafd69a5866b15d64c5c01474cf48/src/images/luxo.png -------------------------------------------------------------------------------- /src/images/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/vj20200217/a17e0521542bafd69a5866b15d64c5c01474cf48/src/images/missing.png -------------------------------------------------------------------------------- /src/shaders/-distFunc.glsl: -------------------------------------------------------------------------------- 1 | float fractSin( float i ) { 2 | return fract( sin( i ) * 1846.42 ); 3 | } 4 | 5 | // https://www.iquilezles.org/www/articles/smin/smin.htm 6 | float smin( float a, float b, float k ) { 7 | float h = max( k - abs( a - b ), 0.0 ) / k; 8 | return min( a, b ) - h * h * h * k * ( 1.0 / 6.0 ); 9 | } 10 | 11 | mat2 rot2d( float t ) { 12 | float c = cos( t ); 13 | float s = sin( t ); 14 | return mat2( c, -s, s, c ); 15 | } 16 | 17 | vec3 ifs( vec3 p, vec3 r, vec3 t ) { 18 | vec3 s = t; 19 | 20 | for ( int i = 0; i < 5; i ++ ) { 21 | p = abs( p ) - abs( s ) * pow( 0.5, float( i ) ); 22 | 23 | s.yz = rot2d( r.x ) * s.yz; 24 | s.zx = rot2d( r.y ) * s.zx; 25 | s.xy = rot2d( r.z ) * s.xy; 26 | 27 | p.xy = p.x < p.y ? p.yx : p.xy; 28 | p.yz = p.y < p.z ? p.zy : p.yz; 29 | p.xz = p.x < p.z ? p.zx : p.xz; 30 | } 31 | 32 | return p; 33 | } 34 | 35 | float box( vec3 p, vec3 d ) { 36 | vec3 absp = abs( p ); 37 | return max( ( absp.x - d.x ), max( ( absp.y - d.y ), ( absp.z - d.z ) ) ); 38 | } 39 | 40 | float distFunc( vec3 p, float time, float midiCC[ 128 ] ) { 41 | float dist = 1E9; 42 | for ( int i = 0; i < 5; i ++ ) { 43 | float fi = float( i ); 44 | vec3 trans = 1.0 * midiCC[ 78 ] * vec3( 45 | sin( 6.0 * fractSin( fi * 2.874 ) + time * ( 1.0 + fractSin( fi * 2.271 ) ) ), 46 | sin( 6.0 * fractSin( fi * 4.512 ) + time * ( 1.0 + fractSin( fi * 1.271 ) ) ), 47 | sin( 6.0 * fractSin( fi * 3.112 ) + time * ( 1.0 + fractSin( fi * 3.271 ) ) ) 48 | ); 49 | dist = smin( dist, length( p - trans ) - 1.0 * midiCC[ 79 ], 1.0 ); 50 | } 51 | return dist; 52 | } 53 | 54 | #pragma glslify: export(distFunc) 55 | -------------------------------------------------------------------------------- /src/shaders/-prng.glsl: -------------------------------------------------------------------------------- 1 | float GPURnd(inout vec4 n) 2 | { 3 | // Based on the post http://gpgpu.org/forums/viewtopic.php?t=2591&sid=17051481b9f78fb49fba5b98a5e0f1f3 4 | // (The page no longer exists as of March 17th, 2015. Please let me know if you see why this code works.) 5 | const vec4 q = vec4( 1225.0, 1585.0, 2457.0, 2098.0); 6 | const vec4 r = vec4( 1112.0, 367.0, 92.0, 265.0); 7 | const vec4 a = vec4( 3423.0, 2646.0, 1707.0, 1999.0); 8 | const vec4 m = vec4(4194287.0, 4194277.0, 4194191.0, 4194167.0); 9 | 10 | vec4 beta = floor(n / q); 11 | vec4 p = a * (n - beta * q) - beta * r; 12 | beta = (sign(-p) + vec4(1.0)) * vec4(0.5) * m; 13 | n = (p + beta); 14 | 15 | return fract(dot(n / m, vec4(1.0, -1.0, 1.0, -1.0))); 16 | } 17 | 18 | #pragma glslify: export(GPURnd) 19 | -------------------------------------------------------------------------------- /src/shaders/-simplex4d.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D/3D/4D simplex 3 | // noise functions. 4 | // Author : Ian McEwan, Ashima Arts. 5 | // Maintainer : ijm 6 | // Lastmod : 20110822 (ijm) 7 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // 11 | 12 | vec4 mod289(vec4 x) { 13 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 14 | 15 | float mod289(float x) { 16 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 17 | 18 | vec4 permute(vec4 x) { 19 | return mod289(((x*34.0)+1.0)*x); 20 | } 21 | 22 | float permute(float x) { 23 | return mod289(((x*34.0)+1.0)*x); 24 | } 25 | 26 | vec4 taylorInvSqrt(vec4 r) 27 | { 28 | return 1.79284291400159 - 0.85373472095314 * r; 29 | } 30 | 31 | float taylorInvSqrt(float r) 32 | { 33 | return 1.79284291400159 - 0.85373472095314 * r; 34 | } 35 | 36 | vec4 grad4(float j, vec4 ip) 37 | { 38 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 39 | vec4 p,s; 40 | 41 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 42 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 43 | s = vec4(lessThan(p, vec4(0.0))); 44 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 45 | 46 | return p; 47 | } 48 | 49 | // (sqrt(5) - 1)/4 = F4, used once below 50 | #define F4 0.309016994374947451 51 | 52 | float snoise(vec4 v) 53 | { 54 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 55 | 0.276393202250021, // 2 * G4 56 | 0.414589803375032, // 3 * G4 57 | -0.447213595499958); // -1 + 4 * G4 58 | 59 | // First corner 60 | vec4 i = floor(v + dot(v, vec4(F4)) ); 61 | vec4 x0 = v - i + dot(i, C.xxxx); 62 | 63 | // Other corners 64 | 65 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 66 | vec4 i0; 67 | vec3 isX = step( x0.yzw, x0.xxx ); 68 | vec3 isYZ = step( x0.zww, x0.yyz ); 69 | // i0.x = dot( isX, vec3( 1.0 ) ); 70 | i0.x = isX.x + isX.y + isX.z; 71 | i0.yzw = 1.0 - isX; 72 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 73 | i0.y += isYZ.x + isYZ.y; 74 | i0.zw += 1.0 - isYZ.xy; 75 | i0.z += isYZ.z; 76 | i0.w += 1.0 - isYZ.z; 77 | 78 | // i0 now contains the unique values 0,1,2,3 in each channel 79 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 80 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 81 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 82 | 83 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 84 | // x1 = x0 - i1 + 1.0 * C.xxxx 85 | // x2 = x0 - i2 + 2.0 * C.xxxx 86 | // x3 = x0 - i3 + 3.0 * C.xxxx 87 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 88 | vec4 x1 = x0 - i1 + C.xxxx; 89 | vec4 x2 = x0 - i2 + C.yyyy; 90 | vec4 x3 = x0 - i3 + C.zzzz; 91 | vec4 x4 = x0 + C.wwww; 92 | 93 | // Permutations 94 | i = mod289(i); 95 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 96 | vec4 j1 = permute( permute( permute( permute ( 97 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 98 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 99 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 100 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 101 | 102 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 103 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 104 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 105 | 106 | vec4 p0 = grad4(j0, ip); 107 | vec4 p1 = grad4(j1.x, ip); 108 | vec4 p2 = grad4(j1.y, ip); 109 | vec4 p3 = grad4(j1.z, ip); 110 | vec4 p4 = grad4(j1.w, ip); 111 | 112 | // Normalise gradients 113 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 114 | p0 *= norm.x; 115 | p1 *= norm.y; 116 | p2 *= norm.z; 117 | p3 *= norm.w; 118 | p4 *= taylorInvSqrt(dot(p4,p4)); 119 | 120 | // Mix contributions from the five corners 121 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 122 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 123 | m0 = m0 * m0; 124 | m1 = m1 * m1; 125 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 126 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 127 | 128 | } 129 | 130 | #pragma glslify: export(snoise) 131 | -------------------------------------------------------------------------------- /src/shaders/ao.frag: -------------------------------------------------------------------------------- 1 | #define AO_ITER 8 2 | #define AO_BIAS 0.0 3 | #define AO_RADIUS 0.5 4 | 5 | #define PI 3.14159265359 6 | #define TAU 6.28318530718 7 | #define EPSILON 1E-3 8 | 9 | #define saturate(x) clamp(x,0.,1.) 10 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 11 | 12 | precision highp float; 13 | 14 | varying vec2 vUv; 15 | 16 | uniform vec2 resolution; 17 | uniform mat4 cameraPV; 18 | uniform float midiCC[ 128 ]; 19 | 20 | uniform sampler2D sampler0; // position.xyz, depth 21 | uniform sampler2D sampler1; // normal.xyz 22 | uniform sampler2D samplerRandom; 23 | 24 | // == commons ====================================================================================== 25 | #pragma glslify: prng = require( ./-prng ); 26 | 27 | vec3 randomDirection( inout vec4 seed ) { 28 | float phi16_ = TAU * prng( seed ); 29 | float theta = acos( -1.0 + 2.0 * prng( seed ) ); 30 | 31 | return vec3( 32 | sin( theta ) * sin( phi16_ ), 33 | cos( theta ), 34 | sin( theta ) * cos( phi16_ ) 35 | ); 36 | } 37 | 38 | // == features ===================================================================================== 39 | float ambientOcclusion( vec2 uv, vec3 position, vec3 normal ) { 40 | float ao = 0.0; 41 | 42 | vec4 seed = texture2D( samplerRandom, uv ); 43 | prng( seed ); 44 | 45 | for ( int i = 0; i < AO_ITER; i ++ ) { 46 | vec3 dir = randomDirection( seed ) * prng( seed ); 47 | if ( dot( dir, normal ) < 0.0 ) { dir = -dir; } 48 | 49 | vec4 screenPos = cameraPV * vec4( position + dir * AO_RADIUS, 1.0 ); 50 | screenPos.x *= resolution.y / resolution.x; 51 | vec2 screenUv = screenPos.xy / screenPos.w * 0.5 + 0.5; 52 | vec4 s0 = texture2D( sampler0, screenUv ); 53 | 54 | vec3 dDir = s0.xyz - position; 55 | if ( length( dDir ) < 1E-2 ) { 56 | ao += 1.0; 57 | } else { 58 | float dNor = dot( normalize( normal ), normalize( dDir ) ); 59 | ao += 1.0 - saturate( dNor - AO_BIAS ) / ( length( dDir ) + 1.0 ); 60 | } 61 | } 62 | 63 | ao = ao / float( AO_ITER ); 64 | return ao; 65 | } 66 | 67 | // == main procedure =============================================================================== 68 | void main() { 69 | vec4 tex0 = texture2D( sampler0, vUv ); 70 | vec4 tex1 = texture2D( sampler1, vUv ); 71 | 72 | vec3 position = tex0.xyz; 73 | vec3 normal = tex1.xyz; 74 | 75 | float ao = ambientOcclusion( vUv, position, normal ); 76 | gl_FragColor = vec4( vec3( ao ), 1.0 ); 77 | } 78 | -------------------------------------------------------------------------------- /src/shaders/background.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define MTL_UNLIT 1 4 | #define MTL_PBR 2 5 | #define MTL_GRADIENT 3 6 | 7 | #extension GL_EXT_draw_buffers : enable 8 | 9 | varying vec4 vPosition; 10 | varying vec3 vNormal; 11 | 12 | void main() { 13 | vec3 color = 0.5 - 0.5 * vNormal; 14 | // color = color.yyy * vec3( 0.2, 0.3, 0.4 ); 15 | 16 | gl_FragData[ 0 ] = vPosition; 17 | gl_FragData[ 1 ] = vec4( -vNormal, 1.0 ); 18 | gl_FragData[ 2 ] = vec4( color, 1.0 ); 19 | gl_FragData[ 3 ] = vec4( 0.0, 0.0, 0.0, MTL_GRADIENT ); 20 | } 21 | -------------------------------------------------------------------------------- /src/shaders/background.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute vec3 normal; 3 | attribute vec2 uv; 4 | 5 | varying vec4 vPosition; 6 | varying vec3 vNormal; 7 | varying vec2 vUv; 8 | 9 | #ifdef USE_VERTEX_COLOR 10 | attribute vec4 color; 11 | varying vec4 vColor; 12 | #endif 13 | 14 | uniform vec2 resolution; 15 | 16 | uniform mat4 projectionMatrix; 17 | uniform mat4 viewMatrix; 18 | uniform mat4 modelMatrix; 19 | uniform mat4 normalMatrix; 20 | 21 | // ------ 22 | 23 | void main() { 24 | vNormal = normalize( ( normalMatrix * vec4( normal, 1.0 ) ).xyz ); 25 | 26 | vPosition = modelMatrix * vec4( position, 1.0 ); 27 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 28 | outPos.x *= resolution.y / resolution.x; 29 | gl_Position = outPos; 30 | 31 | vPosition.w = outPos.z / outPos.w; 32 | 33 | #ifdef USE_VERTEX_COLOR 34 | vColor = color; 35 | #endif 36 | 37 | vUv = uv; 38 | } 39 | -------------------------------------------------------------------------------- /src/shaders/bigword.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D sampler0; 5 | 6 | void main() { 7 | vec2 uv = vUv; 8 | uv.y = 1.0 - uv.y; 9 | gl_FragColor = texture2D( sampler0, uv ); 10 | } 11 | -------------------------------------------------------------------------------- /src/shaders/bloom-blur.frag: -------------------------------------------------------------------------------- 1 | #define saturate(i) clamp(i,0.,1.) 2 | #define PI 3.14159265 3 | #define SAMPLES 30 4 | #define MUL_THR 1E-4 5 | 6 | // ------ 7 | 8 | precision highp float; 9 | 10 | varying vec2 vUv; 11 | uniform vec2 resolution; 12 | uniform bool isVert; 13 | uniform sampler2D sampler0; 14 | 15 | float gaussian( float _x, float _v ) { 16 | return 1.0 / sqrt( 2.0 * PI * _v ) * exp( - _x * _x / 2.0 / _v ); 17 | } 18 | 19 | void main() { 20 | vec2 halfTexel = 1.0 / resolution; // * 0.5; 21 | 22 | float lv = ceil( -log( 1.0 - min( vUv.x, vUv.y ) ) / log( 2.0 ) ); 23 | float p = pow( 0.5, lv ); 24 | vec2 clampMin = floor( vUv / p ) * p + halfTexel; 25 | vec2 clampMax = clampMin + p - 2.0 * halfTexel; 26 | 27 | vec2 bv = halfTexel * ( isVert ? vec2( 0.0, 1.0 ) : vec2( 1.0, 0.0 ) ); 28 | vec4 sum = vec4( 0.0 ); 29 | 30 | sum += 0.2270270270 * texture2D( sampler0, vUv ); 31 | vec2 suv = clamp( vUv - bv * 1.3846153846, clampMin, clampMax ); 32 | sum += 0.3162162162 * texture2D( sampler0, suv ); 33 | suv = clamp( vUv + bv * 1.3846153846, clampMin, clampMax ); 34 | sum += 0.3162162162 * texture2D( sampler0, suv ); 35 | suv = clamp( vUv - bv * 3.2307692308, clampMin, clampMax ); 36 | sum += 0.0702702703 * texture2D( sampler0, suv ); 37 | suv = clamp( vUv + bv * 3.2307692308, clampMin, clampMax ); 38 | sum += 0.0702702703 * texture2D( sampler0, suv ); 39 | 40 | gl_FragColor = vec4( sum.xyz, 1.0 ); 41 | } 42 | -------------------------------------------------------------------------------- /src/shaders/bloom-post.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D samplerDry; 5 | uniform sampler2D samplerWet; 6 | 7 | void main() { 8 | gl_FragColor = texture2D( samplerDry, vUv ); 9 | for ( int i = 0; i < 5; i ++ ) { 10 | float fuck = pow( 0.5, float( i ) ); 11 | vec2 suv = mix( vec2( 1.0 - fuck ), vec2( 1.0 - 0.5 * fuck ), vUv ); 12 | gl_FragColor += texture2D( samplerWet, suv ); 13 | } 14 | gl_FragColor.xyz = max( vec3( 0.0 ), gl_FragColor.xyz ); 15 | } 16 | -------------------------------------------------------------------------------- /src/shaders/bloom-pre.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D sampler0; 5 | 6 | void main() { 7 | gl_FragColor = vec4( 8 | max( vec3( 0.0 ), ( texture2D( sampler0, vUv ).xyz - 1.0 ) * 1.0 ), 9 | 1.0 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/shaders/consooru-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 1.0 2 | 3 | #define HUGE 9E16 4 | #define PI 3.14159265 5 | #define TAU 6.283185307 6 | #define V vec3(0.,1.,-1.) 7 | #define saturate(i) clamp(i,0.,1.) 8 | #define lofi(i,m) (floor((i)/(m))*(m)) 9 | #define lofir(i,m) (floor((i)/(m)+.5)*(m)) 10 | 11 | // ------ 12 | 13 | precision highp float; 14 | 15 | uniform float time; 16 | uniform float beat; 17 | 18 | uniform float linebreakTime; 19 | 20 | uniform float particlesSqrt; 21 | uniform float ppp; 22 | 23 | uniform float totalFrame; 24 | uniform bool init; 25 | uniform float deltaTime; 26 | uniform vec2 resolution; 27 | uniform vec4 newChar; // code, mode, position, head 28 | 29 | uniform sampler2D samplerCompute; 30 | uniform sampler2D samplerRandom; 31 | 32 | uniform float noiseScale; 33 | uniform float noisePhase; 34 | // uniform float velScale; 35 | // uniform float genRate; 36 | 37 | // ------ 38 | 39 | vec2 uvInvT( vec2 _uv ) { 40 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 41 | } 42 | 43 | // ------ 44 | 45 | mat2 rotate2D( float _t ) { 46 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 47 | } 48 | 49 | float fractSin( float i ) { 50 | return fract( sin( i ) * 1846.42 ); 51 | } 52 | 53 | vec4 sampleRandom( vec2 _uv ) { 54 | return texture2D( samplerRandom, _uv ); 55 | } 56 | 57 | #pragma glslify: prng = require( ./-prng ); 58 | #pragma glslify: noise = require( ./-simplex4d ); 59 | 60 | vec3 randomSphere( inout vec4 seed ) { 61 | vec3 v; 62 | for ( int i = 0; i < 10; i ++ ) { 63 | v = vec3( 64 | prng( seed ), 65 | prng( seed ), 66 | prng( seed ) 67 | ) * 2.0 - 1.0; 68 | if ( length( v ) < 1.0 ) { break; } 69 | } 70 | return v; 71 | } 72 | 73 | vec2 randomCircle( inout vec4 seed ) { 74 | vec2 v; 75 | for ( int i = 0; i < 10; i ++ ) { 76 | v = vec2( 77 | prng( seed ), 78 | prng( seed ) 79 | ) * 2.0 - 1.0; 80 | if ( length( v ) < 1.0 ) { break; } 81 | } 82 | return v; 83 | } 84 | 85 | vec3 randomBox( inout vec4 seed ) { 86 | vec3 v; 87 | v = vec3( 88 | prng( seed ), 89 | prng( seed ), 90 | prng( seed ) 91 | ) * 2.0 - 1.0; 92 | return v; 93 | } 94 | 95 | // ------ 96 | 97 | void main() { 98 | vec2 uv = gl_FragCoord.xy / resolution; 99 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 100 | float pixId = mod( gl_FragCoord.x, ppp ); 101 | vec2 dpix = vec2( 1.0 ) / resolution; 102 | 103 | float dt = deltaTime; 104 | 105 | // == prepare some vars ========================================================================== 106 | vec4 seed = texture2D( samplerRandom, puv ); 107 | prng( seed ); 108 | 109 | vec4 tex0 = texture2D( samplerCompute, puv ); 110 | vec4 tex1 = texture2D( samplerCompute, puv + dpix * vec2( 1.0, 0.0 ) ); 111 | 112 | vec3 pos = tex0.xyz; 113 | float lifetime = tex0.w; 114 | int code = int( tex1.x ); 115 | int mode = int( tex1.y ); 116 | vec2 dice = tex1.zw; 117 | 118 | float id = ( floor( puv.x * particlesSqrt ) / particlesSqrt + floor( puv.y * particlesSqrt ) ) / particlesSqrt; 119 | 120 | // == initialize particles ======================================================================= 121 | int newCharCode = int( newChar.x ); 122 | if ( 123 | newCharCode != 0 && newCharCode != 10 && abs( newChar.w - id ) < 1E-4 124 | ) { 125 | pos.xyz = vec3( 0.4 * newChar.z, 0.0, 0.0 ); 126 | lifetime = 0.0; 127 | code = newCharCode; 128 | mode = int( newChar.y ); 129 | dice = vec2( prng( seed ), prng( seed ) ); 130 | } else { 131 | // do nothing 132 | // if you want to remove init frag from the particle, do at here 133 | } 134 | 135 | // == update particles =========================================================================== 136 | pos.z += 5.0 * ( 1.0 + dice.x ) * smoothstep( 5.0, 6.0, lifetime ) * dt; 137 | pos.y += 5.0 * sin( PI * min( linebreakTime / 0.2, 1.0 ) ) * dt; 138 | lifetime += dt / PARTICLE_LIFE_LENGTH; 139 | 140 | gl_FragColor = ( 141 | pixId < 1.0 ? vec4( pos, lifetime ) : 142 | vec4( code, mode, dice ) 143 | ); 144 | } 145 | -------------------------------------------------------------------------------- /src/shaders/consooru-render.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.14159265 2 | #define TAU 6.28318531 3 | #define saturate(i) clamp(i,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | #define lofi(i,m) (floor((i)/(m))*(m)) 6 | #define xor(a,b) (mix((a),1.0-(a),(b))) 7 | 8 | #define PIXIV_BLUE vec3( 0.0, 0.311, 0.957 ) 9 | #define FADE_10 vec3( 0.914 ) 10 | #define FADE_60 vec3( 0.105 ) 11 | 12 | #define MTL_UNLIT 1 13 | #define MTL_PBR 2 14 | 15 | #extension GL_EXT_draw_buffers : enable 16 | #extension GL_OES_standard_derivatives : enable 17 | 18 | precision highp float; 19 | 20 | // == varings / uniforms =========================================================================== 21 | varying vec4 vPosition; 22 | varying vec3 vNormal; 23 | varying vec4 vColor; 24 | varying float vLifetime; 25 | varying vec2 vUv; 26 | varying vec2 vCharPos; 27 | varying vec4 vDice; 28 | varying float vCode; 29 | varying float vMode; 30 | 31 | uniform float time; 32 | uniform vec2 resolution; 33 | uniform sampler2D samplerChar; 34 | 35 | // == common ======================================================================================= 36 | mat2 rotate2D( float _t ) { 37 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 38 | } 39 | 40 | vec2 uvInvT( vec2 uv ) { 41 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * uv; 42 | } 43 | 44 | // == main procedure =============================================================================== 45 | void main() { 46 | int mode = int( vMode + 0.5 ); 47 | 48 | vec3 color = vec3( 0.0 ); 49 | 50 | float anim = exp( -10.0 * vLifetime ); 51 | 52 | vec2 uv = vUv; 53 | 54 | float code = vCode + 0.5; 55 | 56 | vec2 charUv = uvInvT( vUv ); 57 | charUv *= 0.0625; 58 | charUv.xy += lofi( 59 | vec2( fract( code * 0.0625 ), code * 0.0625 * 0.0625 ), 60 | 0.0625 61 | ); 62 | 63 | float tex = texture2D( samplerChar, charUv ).x; 64 | float shape = step( tex, 0.0 ); 65 | 66 | if ( shape < 0.5 ) { discard; } 67 | 68 | gl_FragData[ 0 ] = vPosition; 69 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 70 | gl_FragData[ 2 ] = vec4( vColor.xyz, 1.0 ); 71 | gl_FragData[ 3 ] = vec4( vec3( 0.0 ), MTL_UNLIT ); 72 | } 73 | -------------------------------------------------------------------------------- /src/shaders/consooru-render.vert: -------------------------------------------------------------------------------- 1 | #define HUGE 9E16 2 | #define PI 3.141592654 3 | #define TAU 6.283185307 4 | #define V vec3(0.,1.,-1.) 5 | #define saturate(i) clamp(i,0.,1.) 6 | #define lofi(i,m) (floor((i)/(m))*(m)) 7 | #define lofir(i,m) (floor((i+0.5)/(m))*(m)) 8 | 9 | #define MODE_NONE 0 10 | #define MODE_E 1 11 | #define MODE_W 2 12 | #define MODE_I 3 13 | #define MODE_V 4 14 | 15 | // ------------------------------------------------------------------------------------------------- 16 | 17 | attribute vec2 computeUV; 18 | attribute vec2 p; 19 | 20 | varying vec4 vPosition; 21 | varying vec3 vNormal; 22 | varying vec4 vColor; 23 | varying vec2 vUv; 24 | varying vec4 vDice; 25 | varying vec2 vCharPos; 26 | varying float vCode; 27 | varying float vMode; 28 | varying float vLifetime; 29 | 30 | uniform vec2 resolution; 31 | uniform vec2 resolutionCompute; 32 | uniform float ppp; 33 | 34 | uniform mat4 projectionMatrix; 35 | uniform mat4 viewMatrix; 36 | uniform mat4 modelMatrix; 37 | 38 | uniform bool isShadow; 39 | 40 | uniform float trailShaker; 41 | uniform float colorVar; 42 | uniform float colorOffset; 43 | 44 | uniform sampler2D samplerCompute; 45 | uniform sampler2D samplerRandomStatic; 46 | 47 | // ------------------------------------------------------------------------------------------------- 48 | 49 | vec3 catColor( float _p ) { 50 | return 0.5 + 0.5 * vec3( 51 | cos( _p ), 52 | cos( _p + PI / 3.0 * 4.0 ), 53 | cos( _p + PI / 3.0 * 2.0 ) 54 | ); 55 | } 56 | 57 | vec4 random( vec2 _uv ) { 58 | return texture2D( samplerRandomStatic, _uv ); 59 | } 60 | 61 | mat2 rotate2D( float _t ) { 62 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 63 | } 64 | 65 | // ------------------------------------------------------------------------------------------------- 66 | 67 | void main() { 68 | vec2 puv = vec2( computeUV ); 69 | vec2 dppix = vec2( 1.0 ) / resolutionCompute; 70 | 71 | // == fetch texture ============================================================================== 72 | vec4 tex0 = texture2D( samplerCompute, puv ); 73 | vec4 tex1 = texture2D( samplerCompute, puv + dppix * vec2( 1.0, 0.0 ) ); 74 | 75 | // == assign varying variables =================================================================== 76 | vDice = random( tex1.xy * 182.92 ); 77 | 78 | vNormal = normalize( ( modelMatrix * vec4( 0.0, 0.0, 1.0, 0.0 ) ).xyz ); 79 | 80 | vColor.xyz = vec3( 1.0 ); 81 | 82 | vUv = 0.5 + 0.5 * p; 83 | 84 | vLifetime = tex0.w; 85 | 86 | vCode = tex1.x; 87 | 88 | vMode = tex1.y; 89 | int mode = int( vMode ); 90 | 91 | // == mode ======================================================================================= 92 | if ( mode == MODE_E ) { 93 | vColor.xyz = vec3( 1.4, 0.1, 0.4 ); 94 | 95 | } else if ( mode == MODE_W ) { 96 | vColor.xyz = vec3( 1.5, 0.7, 0.1 ); 97 | 98 | } else if ( mode == MODE_I ) { 99 | vColor.xyz = vec3( 0.1, 0.4, 1.1 ); 100 | 101 | } else if ( mode == MODE_V ) { 102 | vColor.xyz = vec3( 0.6, 0.3, 1.0 ); 103 | 104 | } 105 | 106 | // == compute size and direction ================================================================= 107 | vPosition = vec4( tex0.xyz, 1.0 ); 108 | vCharPos = vPosition.xy; 109 | 110 | vec2 size; 111 | 112 | size = vec2( 0.4 ); 113 | size *= 1.0 - exp( -20.0 * vLifetime ); 114 | 115 | vPosition.xy += p * size; 116 | 117 | // == transformation tweak ======================================================================= 118 | vPosition.xyz *= 0.15; 119 | vPosition.xyz += vec3( -2.4, -1.3, 1.2 ); 120 | 121 | // == send the vertex position =================================================================== 122 | vPosition = modelMatrix * vPosition; 123 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 124 | outPos.x *= resolution.y / resolution.x; 125 | gl_Position = outPos; 126 | 127 | vPosition.w = outPos.z / outPos.w; 128 | 129 | // gl_PointSize = resolution.y * size / outPos.z; 130 | } 131 | -------------------------------------------------------------------------------- /src/shaders/errorlayer.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.141592654 2 | #define TAU 6.283185307 3 | #define saturate(i) clamp(i,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | #define lofi(i,m) (floor((i)/(m))*(m)) 6 | 7 | #define MTL_UNLIT 1 8 | #define MTL_PBR 2 9 | 10 | #extension GL_EXT_draw_buffers : enable 11 | 12 | precision highp float; 13 | 14 | // == varings / uniforms =========================================================================== 15 | varying vec4 vPosition; 16 | varying vec3 vNormal; 17 | varying vec2 vUv; 18 | 19 | uniform float time; 20 | uniform float errorTime; 21 | uniform float resolvedTime; 22 | 23 | uniform sampler2D sampler0; 24 | 25 | // == common ======================================================================================= 26 | mat2 rotate2D( float _t ) { 27 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 28 | } 29 | 30 | vec3 catColor( float t ) { 31 | return 0.5 + 0.5 * cos( t + vec3( 0.0, 4.0, 2.0 ) * PI / 3.0 ); 32 | } 33 | 34 | float fractSin( float v ) { 35 | return fract( 17.351 * sin( 27.119 * v ) ); 36 | } 37 | 38 | float rgb2gray( vec3 c ) { 39 | return 0.299 * c.x + 0.587 * c.y + 0.114 * c.z; 40 | } 41 | 42 | // == glitch ======================================================================================= 43 | vec2 displace( vec2 uv, float threshold ) { 44 | float seed = fractSin( lofi( uv.y, 0.125 ) + fractSin( lofi( uv.x, 0.25 ) ) ); 45 | if ( seed < threshold ) { return vec2( 0.0 ); } 46 | 47 | vec2 d = vec2( 0.0 ); 48 | seed = fractSin( seed ); 49 | d.x = seed - 0.5; 50 | 51 | return d; 52 | } 53 | 54 | // == fetch ======================================================================================== 55 | vec4 fetch( vec2 uv ) { 56 | vec2 uvt = saturate( uv ); 57 | float anix = abs( uvt.x - 0.5 ) + 0.5 * exp( -10.0 * errorTime ); 58 | if ( 0.5 < anix ) { return vec4( 0.0 ); } 59 | 60 | float b = 0.0; 61 | 62 | // -- slasher ------------------------------------------------------------------------------------ 63 | anix = abs( uvt.x - 0.5 ) + 0.5 * exp( -10.0 * ( errorTime - 0.2 ) ); 64 | 65 | b += step( 0.0, sin( 100.0 * ( 2.0 * uvt.x + uvt.y ) + 10.0 * time ) ) 66 | * step( anix, 0.48 ) 67 | * step( 0.08, abs( uvt.x - 0.5 ) ) 68 | * step( abs( uvt.y - 0.1 ), 0.06 ); 69 | 70 | // -- hr ----------------------------------------------------------------------------------------- 71 | anix = abs( uvt.x - 0.5 ) + 0.5 * exp( -10.0 * ( errorTime - 0.3 ) ); 72 | 73 | b += step( abs( uvt.y - 0.20 ), 0.002 ) 74 | * step( anix, 0.48 ); 75 | 76 | // -- canvas ------------------------------------------------------------------------------------- 77 | b += texture2D( sampler0, uvt ).r * ( 78 | errorTime < 0.4 ? 0.0 : 79 | errorTime < 0.6 ? step( 0.0, sin( 160.0 * errorTime ) ) : 80 | 1.0 81 | ); 82 | 83 | // -- check --------------------------------------------------------------------------------------- 84 | float animCheck = ( 1.0 - exp( -10.0 * resolvedTime ) ); 85 | vec2 uvtc = rotate2D( -PI / 4.0 ) * ( ( uvt - 0.5 ) * vec2( 2.0, 1.0 ) ); 86 | float thicc = 0.05 * animCheck; 87 | b += ( 88 | step( abs( uvtc.x - 0.15 ), thicc ) * step( abs( uvtc.y + 0.05 ), 0.25 + thicc ) 89 | + step( abs( uvtc.x + 0.0 ), 0.15 + thicc ) * step( abs( uvtc.y - 0.2 ), thicc ) 90 | ); 91 | 92 | // -- wipe --------------------------------------------------------------------------------------- 93 | float wipet = abs( uv.x - 0.5 ); 94 | b += step( wipet, smoothstep( 0.0, 0.5, resolvedTime - 0.5 ) ); 95 | if ( 0.5 < step( wipet, smoothstep( 0.0, 0.5, resolvedTime - 0.6 ) ) ) { 96 | return vec4( 0.0 ); 97 | } 98 | 99 | return vec4( mix( 100 | vec3( 0.5, 0.01, 0.01 ), 101 | vec3( 1.0 ), 102 | saturate( b ) 103 | ), 1.0 ); 104 | } 105 | 106 | // == main procedure =============================================================================== 107 | void main() { 108 | vec2 uv = vUv.xy; 109 | uv.y = 1.0 - uv.y; 110 | 111 | vec2 d = vec2( 0.0 ); 112 | for ( int i = 0; i < 3; i ++ ) { 113 | float p = pow( 2.4, float( i ) ); 114 | float thr = 1.05 - 0.1 * fractSin( 0.1 * lofi( errorTime, 0.2 ) ); 115 | thr = thr * pow( thr, float( i ) ); 116 | d += displace( uv * p + 50.0 * fractSin( 0.1 * time ), thr ) * 0.4 / p; 117 | } 118 | 119 | vec4 col = vec4( 0.0 ); 120 | col += fetch( uv + d * 1.00 ) * vec4( 1.0, 0.0, 0.0, 1.0 ); 121 | col += fetch( uv + d * 1.80 ) * vec4( 0.0, 1.0, 0.0, 1.0 ); 122 | col += fetch( uv + d * 2.60 ) * vec4( 0.0, 0.0, 1.0, 1.0 ); 123 | 124 | if ( col.w == 0.0 ) { discard; } 125 | 126 | gl_FragData[ 0 ] = vPosition; 127 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 128 | gl_FragData[ 2 ] = vec4( col.xyz, 1.0 ); 129 | gl_FragData[ 3 ] = vec4( vec3( 0.8, 0.2, 0.0 ), MTL_PBR ); 130 | } 131 | -------------------------------------------------------------------------------- /src/shaders/fuck.frag: -------------------------------------------------------------------------------- 1 | #define saturate(x) clamp(x,0.,1.) 2 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 3 | 4 | #define MTL_UNLIT 1 5 | #define MTL_PBR 2 6 | 7 | #extension GL_EXT_draw_buffers : enable 8 | 9 | precision highp float; 10 | 11 | varying vec4 vPosition; 12 | varying vec3 vNormal; 13 | varying vec2 vUv; 14 | 15 | uniform float time; 16 | uniform vec2 resolution; 17 | 18 | uniform sampler2D samplerRandom; 19 | uniform sampler2D samplerRandomStatic; 20 | uniform sampler2D samplerCapture; 21 | 22 | mat2 rot2d( float t ) { 23 | float c = cos( t ); 24 | float s = sin( t ); 25 | return mat2( c, -s, s, c ); 26 | } 27 | 28 | vec3 ifs( vec3 p, vec3 r, vec3 t ) { 29 | vec3 s = t; 30 | 31 | for ( int i = 0; i < 5; i ++ ) { 32 | p = abs( p ) - abs( s ) * pow( 0.5, float( i ) ); 33 | 34 | s.yz = rot2d( r.x ) * s.yz; 35 | s.zx = rot2d( r.y ) * s.zx; 36 | s.xy = rot2d( r.z ) * s.xy; 37 | 38 | p.xy = p.x < p.y ? p.yx : p.xy; 39 | p.yz = p.y < p.z ? p.zy : p.yz; 40 | p.xz = p.x < p.z ? p.zx : p.xz; 41 | } 42 | 43 | return p; 44 | } 45 | 46 | float box( vec3 p, vec3 d ) { 47 | vec3 absp = abs( p ); 48 | return max( ( absp.x - d.x ), max( ( absp.y - d.y ), ( absp.z - d.z ) ) ); 49 | } 50 | 51 | float distFunc( vec3 p ) { 52 | float phase = time + 0.1 * p.z; 53 | phase = floor( phase ) - exp( -7.0 * fract( phase ) ); 54 | vec4 dice1 = texture2D( samplerRandomStatic, phase * vec2( 0.0001, 0.0004 ) ); 55 | vec4 dice2 = texture2D( samplerRandomStatic, phase * vec2( 0.0003, 0.0002 ) ); 56 | p.xy = rot2d( 0.2 * ( dice1.w - 0.5 ) * p.z ) * p.xy; 57 | p.z = mod( p.z - 5.0 * time, 5.0 ) - 2.5; 58 | p.x = mod( p.x, 12.0 ) - 6.0; 59 | p.x = abs( p.x ); 60 | p.zx = rot2d( time ) * p.zx; 61 | // p.zx = rot2d( time ) * p.zx; 62 | p.y = mod( p.y, 3.0 ) - 1.5; 63 | p = ifs( p, dice1.xyz, 1.0 + 1.0 * dice2.xyz ); 64 | return box( p, vec3( 0.05 + 0.15 * dice2.w ) ); 65 | } 66 | 67 | vec3 normalFunc( vec3 p, float dd ) { 68 | vec2 d = vec2( 0.0, dd ); 69 | return normalize( vec3( 70 | distFunc( p + d.yxx ) - distFunc( p - d.yxx ), 71 | distFunc( p + d.xyx ) - distFunc( p - d.xyx ), 72 | distFunc( p + d.xxy ) - distFunc( p - d.xxy ) 73 | ) ); 74 | } 75 | 76 | void main() { 77 | vec2 p = vUv * 2.0 - 1.0; 78 | vec3 rayOri = vec3( 0.0, 0.0, 8.0 ); 79 | vec3 rayDir = normalize( vec3( p, -1.0 ) ); 80 | rayDir.xy = rot2d( 0.2 * sin( time ) ) * rayDir.xy; 81 | rayDir.yz = rot2d( 0.1 * sin( 0.7 * time ) ) * rayDir.yz; 82 | rayDir.zx = rot2d( 0.1 * sin( 0.8 * time ) ) * rayDir.zx; 83 | float rayLen = 0.01; 84 | vec3 rayPos = rayOri + rayDir * rayLen; 85 | float dist; 86 | 87 | for ( int i = 0; i < 50; i ++ ) { 88 | dist = distFunc( rayPos ); 89 | rayLen += 0.7 * dist; 90 | rayPos = rayOri + rayDir * rayLen; 91 | 92 | if ( abs( dist ) < 1E-3 ) { break; } 93 | } 94 | 95 | vec3 color = vec3( 0.0 ); 96 | if ( dist < 0.01 ) { 97 | vec3 nor = normalFunc( rayPos, 1E-4 ); 98 | vec3 ligPos = vec3( 3.0, 4.0, 5.0 ); 99 | vec3 ligDir = normalize( rayPos - ligPos ); 100 | float dotVL = dot( -nor, ligDir ); 101 | float dotVN = dot( -nor, rayDir ); 102 | vec3 dif = vec3( 0.1 ) * dotVL; 103 | color = dif + vec3( 2.1, 0.2, 0.4 ) * pow( 1.0 - abs( dotVN ), 3.0 ); 104 | } 105 | 106 | color *= 1.0 - 0.2 * length( p ); 107 | color.x = -0.1 + 1.2 * color.x; 108 | color.y = 0.0 + 0.1 * vUv.y + 0.9 * color.y; 109 | color.z = 0.1 + 0.8 * color.z; 110 | 111 | 112 | // gl_FragData[ 2 ] = vec4( color, 1.0 ); 113 | gl_FragData[ 0 ] = vPosition; 114 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 115 | gl_FragData[ 2 ] = vec4( vUv, 0.5, 1.0 ); 116 | // gl_FragData[ 2 ] = vec4( 0.5, 0.5, 0.5, 1.0 ); 117 | gl_FragData[ 3 ] = vec4( vec3( 1.0, 0.0, 0.0 ), MTL_PBR ); 118 | } 119 | -------------------------------------------------------------------------------- /src/shaders/fxaa.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.14159265 2 | #define V vec3(0.,1.,-1.) 3 | 4 | #define FXAA_REDUCE_MIN (1.0 / 128.0) 5 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 6 | #define FXAA_SPAN_MAX 16.0 7 | 8 | // ------ 9 | 10 | precision highp float; 11 | 12 | uniform vec2 resolution; 13 | 14 | uniform sampler2D sampler0; 15 | 16 | // ------ 17 | 18 | void main() { 19 | vec2 uv = gl_FragCoord.xy / resolution; 20 | 21 | #define T(v) texture2D( sampler0, (v) / resolution ).xyz 22 | vec3 rgb11 = T( gl_FragCoord.xy ); 23 | vec3 rgb00 = T( gl_FragCoord.xy + V.zz ); 24 | vec3 rgb02 = T( gl_FragCoord.xy + V.zy ); 25 | vec3 rgb20 = T( gl_FragCoord.xy + V.yz ); 26 | vec3 rgb22 = T( gl_FragCoord.xy + V.yy ); 27 | #undef T 28 | 29 | vec3 luma = vec3( 0.299, 0.587, 0.114 ); 30 | #define L(c) dot( c, luma ) 31 | float luma11 = L( rgb11 ); 32 | float luma00 = L( rgb00 ); 33 | float luma02 = L( rgb02 ); 34 | float luma20 = L( rgb20 ); 35 | float luma22 = L( rgb22 ); 36 | #undef L 37 | 38 | float lumaMin = min( luma00, min( min( luma00, luma02 ), min( luma20, luma22 ) ) ); 39 | float lumaMax = max( luma00, max( max( luma00, luma02 ), max( luma20, luma22 ) ) ); 40 | 41 | vec2 dir = vec2( 42 | -( ( luma00 + luma20 ) - ( luma02 + luma22 ) ), 43 | ( ( luma00 + luma02 ) - ( luma20 + luma22 ) ) 44 | ); 45 | 46 | float dirReduce = max( 47 | ( luma00 + luma02 + luma20 + luma22 ) * 0.25 * FXAA_REDUCE_MUL, 48 | FXAA_REDUCE_MIN 49 | ); 50 | float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce ); 51 | dir = min( 52 | vec2( FXAA_SPAN_MAX ), 53 | max( 54 | vec2( -FXAA_SPAN_MAX ), 55 | dir * rcpDirMin 56 | ) 57 | ) / resolution; 58 | 59 | vec3 rgbA = 0.5 * ( 60 | texture2D( sampler0, uv + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz + 61 | texture2D( sampler0, uv + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz 62 | ); 63 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 64 | texture2D( sampler0, uv - dir * 0.5 ).xyz + 65 | texture2D( sampler0, uv + dir * 0.5 ).xyz 66 | ); 67 | 68 | float lumaB = dot( rgbB, luma ); 69 | gl_FragColor = ( 70 | ( ( lumaB < lumaMin ) || ( lumaMax < lumaB ) ) ? 71 | vec4( rgbA, 1.0 ) : 72 | vec4( rgbB, 1.0 ) 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /src/shaders/glitch.frag: -------------------------------------------------------------------------------- 1 | #define BARREL_ITER 10 2 | 3 | #define HUGE 9E16 4 | #define PI 3.14159265 5 | #define V vec3(0.,1.,-1.) 6 | #define saturate(i) clamp(i,0.,1.) 7 | #define lofi(i,m) (floor((i)/(m))*(m)) 8 | 9 | // ------ 10 | 11 | precision highp float; 12 | 13 | varying vec2 vUv; 14 | 15 | uniform float time; 16 | uniform vec2 resolution; 17 | uniform float sirGlitch; 18 | uniform float midiCC[ 128 ]; 19 | 20 | uniform float barrelAmp; 21 | uniform float barrelOffset; 22 | 23 | uniform sampler2D sampler0; 24 | 25 | // == common ======================================================================================= 26 | float fractSin( float v ) { 27 | return fract( 17.351 * sin( 27.119 * v ) ); 28 | } 29 | 30 | // == glitch ======================================================================================= 31 | vec2 displace( vec2 uv, float threshold ) { 32 | float seed = fractSin( lofi( uv.y, 0.125 ) + fractSin( lofi( uv.x, 0.25 ) ) ); 33 | if ( seed < threshold ) { return vec2( 0.0 ); } 34 | 35 | vec2 d = vec2( 0.0 ); 36 | seed = fractSin( seed ); 37 | d.x = seed - 0.5; 38 | 39 | return d; 40 | } 41 | 42 | // == fetch ======================================================================================== 43 | vec4 fetch( vec2 uv ) { 44 | vec2 uvt = saturate( uv ); 45 | vec4 color = texture2D( sampler0, uvt ); 46 | return color; 47 | } 48 | 49 | // == main procedure =============================================================================== 50 | void main() { 51 | vec2 uv = vUv.xy; 52 | 53 | vec2 d = vec2( 0.0 ); 54 | for ( int i = 0; i < 3; i ++ ) { 55 | float p = pow( 2.4, float( i ) ); 56 | float thr = 1.0 - pow( midiCC[ 84 ], 6.0 ) - sirGlitch; 57 | thr = thr * pow( thr, float( i ) ); 58 | d += displace( uv * p + 50.0 * fractSin( 0.1 * time ), thr ) * 0.4 / p; 59 | } 60 | 61 | vec4 col = vec4( 0.0 ); 62 | col += fetch( uv + d * 1.00 ) * vec4( 1.0, 0.0, 0.0, 1.0 ); 63 | col += fetch( uv + d * 1.80 ) * vec4( 0.0, 1.0, 0.0, 1.0 ); 64 | col += fetch( uv + d * 2.60 ) * vec4( 0.0, 0.0, 1.0, 1.0 ); 65 | 66 | gl_FragColor = col; 67 | } 68 | -------------------------------------------------------------------------------- /src/shaders/hotplane.frag: -------------------------------------------------------------------------------- 1 | #define saturate(x) clamp(x,0.,1.) 2 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 3 | 4 | #define MTL_UNLIT 1 5 | #define MTL_PBR 2 6 | 7 | #extension GL_EXT_draw_buffers : enable 8 | 9 | precision highp float; 10 | 11 | varying vec4 vPosition; 12 | varying vec3 vNormal; 13 | varying vec2 vUv; 14 | 15 | uniform float time; 16 | uniform vec2 resolution; 17 | 18 | uniform sampler2D samplerRandom; 19 | uniform sampler2D samplerRandomStatic; 20 | uniform sampler2D samplerCapture; 21 | 22 | vec4 greenkey( vec3 rgb ) { 23 | float green = rgb.g - rgb.r - rgb.b; 24 | vec4 result = vec4( rgb, saturate( 1.0 - green ) ); 25 | result.y = saturate( result.y - saturate( green ) ); 26 | return result; 27 | } 28 | 29 | void main() { 30 | vec2 screenUv = vUv; 31 | screenUv.y = 1.0 - screenUv.y; 32 | screenUv = mix( vec2( 0.2, 0.1 ), vec2( 0.9, 0.8 ), screenUv ); 33 | 34 | vec4 color = texture2D( samplerCapture, screenUv ); 35 | color = greenkey( color.xyz ); 36 | if ( color.w < 0.8 ) { discard; } 37 | color.xyz = 0.8 * pow( color.xyz, vec3( 2.2 ) ); 38 | 39 | gl_FragData[ 0 ] = vPosition; 40 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 41 | gl_FragData[ 2 ] = vec4( color.xyz, 1.0 ); 42 | gl_FragData[ 3 ] = vec4( vec3( 1.0, 0.0, 0.0 ), MTL_PBR ); 43 | } 44 | -------------------------------------------------------------------------------- /src/shaders/index.ts: -------------------------------------------------------------------------------- 1 | import bloomBlurFrag from './bloom-blur.frag'; 2 | import bloomPostFrag from './bloom-post.frag'; 3 | import bloomPreFrag from './bloom-pre.frag'; 4 | import invertFrag from './invert.frag'; 5 | import normalFrag from './normal.frag'; 6 | import objectVert from './object.vert'; 7 | import posToDepthFrag from './pos-to-depth.frag'; 8 | import quadVert from './quad.vert'; 9 | import returnFrag from './return.frag'; 10 | import shadowBlurFrag from './shadow-blur.frag'; 11 | 12 | export const Shaders = { 13 | bloomBlurFrag, 14 | bloomPostFrag, 15 | bloomPreFrag, 16 | invertFrag, 17 | normalFrag, 18 | objectVert, 19 | posToDepthFrag, 20 | quadVert, 21 | returnFrag, 22 | shadowBlurFrag, 23 | }; 24 | -------------------------------------------------------------------------------- /src/shaders/init.frag: -------------------------------------------------------------------------------- 1 | #define saturate(x) clamp(x,0.,1.) 2 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 3 | 4 | precision highp float; 5 | 6 | uniform float time; 7 | uniform vec2 resolution; 8 | 9 | uniform sampler2D samplerScreen; 10 | uniform sampler2D samplerRandom; 11 | uniform sampler2D samplerRandomStatic; 12 | uniform sampler2D samplerFeedback; 13 | 14 | void main() { 15 | vec2 uv = gl_FragCoord.xy / resolution; 16 | 17 | vec2 uvScreen = uv; 18 | uvScreen.y = 1.0 - uvScreen.y; 19 | uvScreen = ( uvScreen - 0.5 ) * 0.7 + 0.5; 20 | 21 | vec3 texScreen = texture2D( samplerScreen, uvScreen ).xyz; 22 | float screenAlpha = linearstep( -0.1, 0.0, texScreen.x + texScreen.z - texScreen.y ); 23 | 24 | vec3 color = vec3( uv, 0.5 + 0.5 * sin( time ) ); 25 | color = mix( 26 | color, 27 | texScreen.xyz, 28 | screenAlpha 29 | ); 30 | 31 | gl_FragColor = vec4( color, 1.0 ); 32 | } 33 | -------------------------------------------------------------------------------- /src/shaders/invert.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 uv; 4 | 5 | uniform sampler2D sampler0; 6 | 7 | void main() { 8 | vec3 tex = texture2D( sampler0, uv ).xyz; 9 | gl_FragColor = vec4( 1.0 - tex, 1.0 ); 10 | } 11 | -------------------------------------------------------------------------------- /src/shaders/normal.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define MTL_UNLIT 1 4 | 5 | #extension GL_EXT_draw_buffers : enable 6 | 7 | varying vec4 vPosition; 8 | varying vec3 vNormal; 9 | 10 | void main() { 11 | gl_FragData[ 0 ] = vPosition; 12 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 13 | gl_FragData[ 2 ] = vec4( 0.5 + 0.5 * vNormal, 1.0 ); 14 | gl_FragData[ 3 ] = vec4( vec3( 0.0, 0.0, 0.0 ), MTL_UNLIT ); 15 | } 16 | -------------------------------------------------------------------------------- /src/shaders/object.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 position; 2 | attribute vec3 normal; 3 | attribute vec2 uv; 4 | 5 | varying vec4 vPosition; 6 | varying vec3 vNormal; 7 | varying vec2 vUv; 8 | 9 | #ifdef USE_VERTEX_COLOR 10 | attribute vec4 color; 11 | varying vec4 vColor; 12 | #endif 13 | 14 | uniform vec2 resolution; 15 | 16 | uniform mat4 projectionMatrix; 17 | uniform mat4 viewMatrix; 18 | uniform mat4 modelMatrix; 19 | uniform mat4 normalMatrix; 20 | 21 | // ------ 22 | 23 | void main() { 24 | vNormal = normalize( ( normalMatrix * vec4( normal, 1.0 ) ).xyz ); 25 | 26 | vPosition = modelMatrix * vec4( position, 1.0 ); 27 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 28 | outPos.x *= resolution.y / resolution.x; 29 | gl_Position = outPos; 30 | 31 | vPosition.w = outPos.z / outPos.w; 32 | 33 | #ifdef USE_VERTEX_COLOR 34 | vColor = color; 35 | #endif 36 | 37 | vUv = uv; 38 | } 39 | -------------------------------------------------------------------------------- /src/shaders/pos-to-depth.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define saturate(x) clamp(x,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | 6 | // == varings / uniforms =========================================================================== 7 | varying vec2 vUv; 8 | uniform sampler2D sampler0; 9 | uniform vec3 cameraPos; 10 | uniform vec2 cameraNearFar; 11 | 12 | // == main procedure =============================================================================== 13 | void main() { 14 | vec4 tex = texture2D( sampler0, vUv ); 15 | float depth = linearstep( 16 | cameraNearFar.x, 17 | cameraNearFar.y, 18 | length( cameraPos - tex.xyz ) 19 | ); 20 | gl_FragColor = vec4( depth, depth * depth, depth, 1.0 ); 21 | } 22 | -------------------------------------------------------------------------------- /src/shaders/post.frag: -------------------------------------------------------------------------------- 1 | #define BARREL_ITER 10 2 | #define BARREL_OFFSET ( 0.1 * midiCC[ 49 ] ) 3 | #define BARREL_AMP ( 0.2 * midiCC[ 49 ] ) 4 | 5 | #define HUGE 9E16 6 | #define PI 3.14159265 7 | #define V vec3(0.,1.,-1.) 8 | #define saturate(i) clamp(i,0.,1.) 9 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 10 | #define lofi(i,m) (floor((i)/(m))*(m)) 11 | 12 | // ------ 13 | 14 | precision highp float; 15 | 16 | varying vec2 vUv; 17 | 18 | uniform float time; 19 | uniform float errorTime; 20 | uniform vec2 resolution; 21 | 22 | uniform sampler2D sampler0; 23 | 24 | uniform float midiCC[ 128 ]; 25 | 26 | // ------ 27 | 28 | vec3 colorMap( vec3 i ) { 29 | return vec3( 30 | smoothstep( 0.0, 1.0, i.r ), 31 | i.g, 32 | 0.1 + 0.8 * i.b 33 | ); 34 | } 35 | 36 | vec3 barrel( float amp, vec2 uv ) { 37 | float corn = length( vec2( 0.5 ) ); 38 | float a = min( 3.0 * sqrt( amp ), corn * PI ); 39 | float zoom = corn / ( tan( corn * a ) + corn ); 40 | vec2 p = saturate( 41 | ( uv + normalize( uv - 0.5 ) * tan( length( uv - 0.5 ) * a ) ) * zoom + 42 | 0.5 * ( 1.0 - zoom ) 43 | ); 44 | return texture2D( sampler0, vec2( p.x, p.y ) ).xyz; 45 | } 46 | 47 | // ------ 48 | 49 | void main() { 50 | vec2 uv = vUv; 51 | vec2 p = ( uv * resolution * 2.0 - resolution ) / resolution.y; 52 | float vig = 1.0 - length( p ) * 0.2; 53 | 54 | vec3 tex = vec3( 0.0 ); 55 | 56 | for ( int i = 0; i < BARREL_ITER; i ++ ) { 57 | float fi = ( float( i ) + 0.5 ) / float( BARREL_ITER ); 58 | vec3 a = saturate( vec3( 59 | 1.0 - 3.0 * abs( 1.0 / 6.0 - fi ), 60 | 1.0 - 3.0 * abs( 1.0 / 2.0 - fi ), 61 | 1.0 - 3.0 * abs( 5.0 / 6.0 - fi ) 62 | ) ) / float( BARREL_ITER ) * 4.0; 63 | tex += a * barrel( BARREL_OFFSET + BARREL_AMP * fi, uv ); 64 | } 65 | 66 | tex = mix( vec3( 0.0 ), tex, vig ); 67 | 68 | vec3 col = pow( saturate( tex.xyz ), vec3( 0.4545 ) ); 69 | col.x = linearstep( 0.0, 1.2, col.x + 0.2 * uv.y ); 70 | col = colorMap( col ); 71 | col.xyz *= midiCC[ 83 ]; 72 | 73 | gl_FragColor = vec4( col, 1.0 ); 74 | gl_FragColor.yz *= 0.6 + 0.4 * cos( 5.0 * errorTime ); 75 | } 76 | -------------------------------------------------------------------------------- /src/shaders/quad.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 p; 2 | varying vec2 vUv; 3 | uniform vec4 range; 4 | 5 | void main() { 6 | vUv = 0.5 + 0.5 * p; 7 | gl_Position = vec4( mix( range.xy, range.zw, vUv ), 0.0, 1.0 ); 8 | } 9 | -------------------------------------------------------------------------------- /src/shaders/raymarcher.frag: -------------------------------------------------------------------------------- 1 | #define MARCH_ITER 50 2 | 3 | #define saturate(x) clamp(x,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | 6 | #define MTL_UNLIT 1 7 | #define MTL_PBR 2 8 | #define MTL_GRADIENT 3 9 | #define MTL_IRIDESCENT 4 10 | 11 | #extension GL_EXT_frag_depth : enable 12 | #extension GL_EXT_draw_buffers : enable 13 | 14 | precision highp float; 15 | 16 | varying vec2 vUv; 17 | 18 | uniform float time; 19 | uniform vec2 resolution; 20 | 21 | uniform sampler2D samplerRandom; 22 | uniform sampler2D samplerRandomStatic; 23 | uniform sampler2D samplerCapture; 24 | uniform vec2 cameraNearFar; 25 | uniform mat4 viewMatrix; 26 | uniform mat4 projectionMatrix; 27 | uniform mat4 inversePV; 28 | 29 | uniform float midiCC[ 128 ]; 30 | 31 | vec3 divideByW( vec4 v ) { 32 | return v.xyz / v.w; 33 | } 34 | 35 | #pragma glslify: distFunc = require( ./-distFunc ); 36 | 37 | float distFunc( vec3 p ) { 38 | return distFunc( p, time, midiCC ); 39 | } 40 | 41 | vec3 normalFunc( vec3 p, float dd ) { 42 | vec2 d = vec2( 0.0, dd ); 43 | return normalize( vec3( 44 | distFunc( p + d.yxx ) - distFunc( p - d.yxx ), 45 | distFunc( p + d.xyx ) - distFunc( p - d.xyx ), 46 | distFunc( p + d.xxy ) - distFunc( p - d.xxy ) 47 | ) ); 48 | } 49 | 50 | void main() { 51 | vec2 p = vUv * 2.0 - 1.0; 52 | p.x *= resolution.x / resolution.y; 53 | vec3 rayOri = divideByW( inversePV * vec4( p, 0.0, 1.0 ) ); 54 | vec3 farPos = divideByW( inversePV * vec4( p, 1.0, 1.0 ) ); 55 | vec3 rayDir = normalize( farPos - rayOri ); 56 | float rayLen = cameraNearFar.x; 57 | vec3 rayPos = rayOri + rayDir * rayLen; 58 | float dist; 59 | 60 | for ( int i = 0; i < MARCH_ITER; i ++ ) { 61 | dist = distFunc( rayPos, time, midiCC ); 62 | rayLen += 0.7 * dist; 63 | rayPos = rayOri + rayDir * rayLen; 64 | 65 | if ( abs( dist ) < 1E-3 ) { break; } 66 | } 67 | 68 | if ( 0.01 < dist ) { 69 | discard; 70 | } 71 | 72 | vec3 normal = normalFunc( rayPos, 1E-4 ); 73 | vec4 color = vec4( 0.1, 0.2, 0.4, 1.0 ); 74 | 75 | vec4 projPos = projectionMatrix * viewMatrix * vec4( rayPos, 1.0 ); // terrible 76 | float depth = projPos.z / projPos.w; 77 | gl_FragDepthEXT = 0.5 + 0.5 * depth; 78 | 79 | // gl_FragData[ 2 ] = vec4( color, 1.0 ); 80 | gl_FragData[ 0 ] = vec4( rayPos, depth ); 81 | gl_FragData[ 1 ] = vec4( normal, 1.0 ); 82 | gl_FragData[ 2 ] = color; 83 | gl_FragData[ 3 ] = vec4( vec3( 10.0, 0.6, 0.9 ), MTL_IRIDESCENT ); 84 | } 85 | -------------------------------------------------------------------------------- /src/shaders/return.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vUv; 4 | uniform sampler2D sampler0; 5 | 6 | void main() { 7 | gl_FragColor = texture2D( sampler0, vUv ); 8 | } 9 | -------------------------------------------------------------------------------- /src/shaders/shadow-blur.frag: -------------------------------------------------------------------------------- 1 | #define saturate(i) clamp(i,0.,1.) 2 | #define PI 3.14159265 3 | #define SAMPLES 30 4 | #define MUL_THR 1E-4 5 | 6 | // ------ 7 | 8 | precision highp float; 9 | 10 | varying vec2 vUv; 11 | uniform vec2 resolution; 12 | uniform bool isVert; 13 | uniform sampler2D sampler0; 14 | 15 | float gaussian( float _x, float _v ) { 16 | return 1.0 / sqrt( 2.0 * PI * _v ) * exp( - _x * _x / 2.0 / _v ); 17 | } 18 | 19 | void main() { 20 | vec2 texel = 1.0 / resolution; 21 | 22 | vec2 bv = texel * ( isVert ? vec2( 0.0, 1.0 ) : vec2( 1.0, 0.0 ) ); 23 | vec4 sum = vec4( 0.0 ); 24 | 25 | vec4 tex = texture2D( sampler0, vUv ); 26 | 27 | sum += 0.29411764705882354 * tex; 28 | vec2 suv = vUv - bv * 1.3333333333333333; 29 | sum += 0.35294117647058826 * texture2D( sampler0, suv ); 30 | suv = vUv + bv * 1.3333333333333333; 31 | sum += 0.35294117647058826 * texture2D( sampler0, suv ); 32 | 33 | gl_FragColor = vec4( sum.xy, tex.z, 1.0 ); 34 | } 35 | -------------------------------------------------------------------------------- /src/shaders/sphere-particles-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 1.0 2 | 3 | #define HUGE 9E16 4 | #define PI 3.14159265 5 | #define TAU 6.283185307 6 | #define V vec3(0.,1.,-1.) 7 | #define saturate(i) clamp(i,0.,1.) 8 | #define lofi(i,m) (floor((i)/(m))*(m)) 9 | #define lofir(i,m) (floor((i)/(m)+.5)*(m)) 10 | 11 | // ------ 12 | 13 | precision highp float; 14 | 15 | uniform float time; 16 | uniform float beat; 17 | 18 | uniform float particlesSqrt; 19 | uniform float ppp; 20 | 21 | uniform float totalFrame; 22 | uniform bool init; 23 | uniform float deltaTime; 24 | uniform vec2 resolution; 25 | 26 | uniform sampler2D samplerCompute; 27 | uniform sampler2D samplerRandom; 28 | 29 | uniform float noiseScale; 30 | uniform float noisePhase; 31 | // uniform float velScale; 32 | // uniform float genRate; 33 | 34 | uniform float midiCC[ 128 ]; 35 | 36 | // ------ 37 | 38 | vec2 uvInvT( vec2 _uv ) { 39 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 40 | } 41 | 42 | // ------ 43 | 44 | #pragma glslify: distFunc = require( ./-distFunc ); 45 | 46 | float distFunc( vec3 p ) { 47 | return distFunc( p, time, midiCC ); 48 | } 49 | 50 | vec3 normalFunc( vec3 p, float dd ) { 51 | vec2 d = vec2( 0.0, dd ); 52 | return normalize( vec3( 53 | distFunc( p + d.yxx ) - distFunc( p - d.yxx ), 54 | distFunc( p + d.xyx ) - distFunc( p - d.xyx ), 55 | distFunc( p + d.xxy ) - distFunc( p - d.xxy ) 56 | ) ); 57 | } 58 | 59 | mat2 rotate2D( float _t ) { 60 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 61 | } 62 | 63 | float fractSin( float i ) { 64 | return fract( sin( i ) * 1846.42 ); 65 | } 66 | 67 | vec4 sampleRandom( vec2 _uv ) { 68 | return texture2D( samplerRandom, _uv ); 69 | } 70 | 71 | #pragma glslify: prng = require( ./-prng ); 72 | #pragma glslify: noise = require( ./-simplex4d ); 73 | 74 | vec3 randomSphere( inout vec4 seed ) { 75 | vec3 v; 76 | for ( int i = 0; i < 10; i ++ ) { 77 | v = vec3( 78 | prng( seed ), 79 | prng( seed ), 80 | prng( seed ) 81 | ) * 2.0 - 1.0; 82 | if ( length( v ) < 1.0 ) { break; } 83 | } 84 | return v; 85 | } 86 | 87 | vec2 randomCircle( inout vec4 seed ) { 88 | vec2 v; 89 | for ( int i = 0; i < 10; i ++ ) { 90 | v = vec2( 91 | prng( seed ), 92 | prng( seed ) 93 | ) * 2.0 - 1.0; 94 | if ( length( v ) < 1.0 ) { break; } 95 | } 96 | return v; 97 | } 98 | 99 | vec3 randomBox( inout vec4 seed ) { 100 | vec3 v; 101 | v = vec3( 102 | prng( seed ), 103 | prng( seed ), 104 | prng( seed ) 105 | ) * 2.0 - 1.0; 106 | return v; 107 | } 108 | 109 | // ------ 110 | 111 | void main() { 112 | vec2 uv = gl_FragCoord.xy / resolution; 113 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 114 | float pixId = mod( gl_FragCoord.x, ppp ); 115 | vec2 dpix = vec2( 1.0 ) / resolution; 116 | 117 | float dt = deltaTime; 118 | 119 | // == prepare some vars ========================================================================== 120 | vec4 seed = texture2D( samplerRandom, puv ); 121 | prng( seed ); 122 | 123 | vec4 tex0 = texture2D( samplerCompute, puv ); 124 | vec4 tex1 = texture2D( samplerCompute, puv + dpix * vec2( 1.0, 0.0 ) ); 125 | 126 | vec3 pos = tex0.xyz; 127 | float life = tex0.w; 128 | vec3 vel = tex1.xyz; 129 | 130 | float timing = mix( 131 | 0.0, 132 | PARTICLE_LIFE_LENGTH, 133 | ( floor( puv.x * particlesSqrt ) / particlesSqrt + floor( puv.y * particlesSqrt ) ) / particlesSqrt 134 | ); 135 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 136 | 137 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 138 | timing -= PARTICLE_LIFE_LENGTH; 139 | } 140 | 141 | // == initialize particles ======================================================================= 142 | if ( 143 | time - deltaTime < timing && timing <= time 144 | && prng( seed ) < midiCC[ 16 ] 145 | ) { 146 | dt = time - timing; 147 | 148 | pos = 0.5 * randomSphere( seed ); 149 | 150 | vel = 1.0 * randomSphere( seed ); 151 | 152 | life = 1.0; 153 | } else { 154 | // do nothing 155 | // if you want to remove init frag from the particle, do at here 156 | } 157 | 158 | // == update particles =========================================================================== 159 | // distFunc 160 | float dist = distFunc( pos.xyz ) - 0.2; 161 | vec3 nor = normalFunc( pos.xyz, 1E-4 ); 162 | vel -= dt * 100.0 * dist * nor; 163 | 164 | // spin around center 165 | vel.zx += dt * 20.0 * vec2( -1.0, 1.0 ) * normalize( nor.xz ); 166 | 167 | // noise field 168 | vel += midiCC[ 13 ] * 100.0 * vec3( 169 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 1.485 + sin( time * 0.1 ) + noisePhase ) ), 170 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 3.485 + sin( time * 0.1 ) + noisePhase ) ), 171 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 5.485 + sin( time * 0.1 ) + noisePhase ) ) 172 | ) * dt; 173 | 174 | // resistance 175 | vel *= exp( -10.0 * dt ); 176 | 177 | vec3 v = vel; 178 | float vmax = max( abs( v.x ), max( abs( v.y ), abs( v.z ) ) ); 179 | 180 | pos += vel * dt; 181 | life -= dt / PARTICLE_LIFE_LENGTH; 182 | 183 | gl_FragColor = ( 184 | pixId < 1.0 ? vec4( pos, life ) : 185 | vec4( vel, 1.0 ) 186 | ); 187 | } 188 | -------------------------------------------------------------------------------- /src/shaders/sphere-particles-render.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.14159265 2 | #define TAU 6.28318531 3 | #define saturate(i) clamp(i,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | 6 | #define MTL_UNLIT 1 7 | #define MTL_PBR 2 8 | #define MTL_IRIDESCENT 4 9 | 10 | #extension GL_EXT_draw_buffers : enable 11 | 12 | precision highp float; 13 | 14 | // == varings / uniforms =========================================================================== 15 | varying vec4 vPosition; 16 | varying vec3 vNormal; 17 | varying vec4 vColor; 18 | varying float vLife; 19 | varying vec4 vRandom; 20 | 21 | uniform float time; 22 | 23 | // == common ======================================================================================= 24 | mat2 rotate2D( float _t ) { 25 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 26 | } 27 | 28 | // == main procedure =============================================================================== 29 | void main() { 30 | if ( vColor.a < 0.0 ) { discard; } 31 | 32 | gl_FragData[ 0 ] = vPosition; 33 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 34 | gl_FragData[ 2 ] = vec4( vColor.xyz, 1.0 ); 35 | gl_FragData[ 3 ] = vec4( vec3( 10.0, 0.5, 0.8 ), MTL_IRIDESCENT ); 36 | } 37 | -------------------------------------------------------------------------------- /src/shaders/sphere-particles-render.vert: -------------------------------------------------------------------------------- 1 | #define HUGE 9E16 2 | #define PI 3.141592654 3 | #define TAU 6.283185307 4 | #define V vec3(0.,1.,-1.) 5 | #define saturate(i) clamp(i,0.,1.) 6 | #define lofi(i,m) (floor((i)/(m))*(m)) 7 | #define lofir(i,m) (floor((i+0.5)/(m))*(m)) 8 | 9 | #define MODE_RECT 0 10 | #define MODE_GRID 1 11 | #define MODE_CIRCLE 2 12 | #define MODE_CHAR 3 13 | #define MODE_BUTTON 4 14 | #define MODE_ICON 5 15 | #define MODES 6 16 | 17 | // ------------------------------------------------------------------------------------------------- 18 | 19 | attribute vec2 computeUV; 20 | attribute vec3 position; 21 | attribute vec3 normal; 22 | 23 | varying vec4 vPosition; 24 | varying vec3 vNormal; 25 | varying vec4 vColor; 26 | varying vec2 vUv; 27 | varying vec4 vDice; 28 | varying float vLife; 29 | 30 | uniform vec2 resolution; 31 | uniform vec2 resolutionCompute; 32 | uniform float ppp; 33 | 34 | uniform mat4 projectionMatrix; 35 | uniform mat4 viewMatrix; 36 | uniform mat4 modelMatrix; 37 | uniform mat4 normalMatrix; 38 | 39 | uniform bool isShadow; 40 | 41 | uniform float trailShaker; 42 | uniform float colorVar; 43 | uniform float colorOffset; 44 | 45 | uniform sampler2D samplerCompute; 46 | uniform sampler2D samplerRandomStatic; 47 | 48 | // ------------------------------------------------------------------------------------------------- 49 | 50 | vec3 catColor( float _p ) { 51 | return 0.5 + 0.5 * vec3( 52 | cos( _p ), 53 | cos( _p + PI / 3.0 * 4.0 ), 54 | cos( _p + PI / 3.0 * 2.0 ) 55 | ); 56 | } 57 | 58 | vec4 random( vec2 _uv ) { 59 | return texture2D( samplerRandomStatic, _uv ); 60 | } 61 | 62 | mat2 rotate2D( float _t ) { 63 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 64 | } 65 | 66 | // ------------------------------------------------------------------------------------------------- 67 | 68 | void main() { 69 | vec2 puv = vec2( computeUV ); 70 | vec2 dppix = vec2( 1.0 ) / resolutionCompute; 71 | 72 | // == fetch texture ============================================================================== 73 | vec4 tex0 = texture2D( samplerCompute, puv ); 74 | vec4 tex1 = texture2D( samplerCompute, puv + dppix * vec2( 1.0, 0.0 ) ); 75 | 76 | // == assign varying variables =================================================================== 77 | vDice = random( puv.xy * 182.92 ); 78 | 79 | vColor.xyz = 0.8 * mix( catColor( 2.0 + 40.0 * vDice.x ), vec3( 0.9 ), 0.0 ); 80 | vColor.xyz = vec3( 0.2 ); 81 | 82 | vLife = tex0.w; 83 | 84 | // == compute size =============================================================================== 85 | vPosition = vec4( tex0.xyz, 1.0 ); 86 | 87 | float size = vDice.x * 0.08; 88 | size *= sin( PI * saturate( vLife ) ); 89 | 90 | vec3 shape = position * size; 91 | shape.yz = rotate2D( 7.0 * vPosition.x ) * shape.yz; 92 | shape.zx = rotate2D( 7.0 * vPosition.y ) * shape.zx; 93 | 94 | vPosition.xyz += shape; 95 | 96 | // == compute normals ============================================================================ 97 | vNormal = ( normalMatrix * vec4( normal, 1.0 ) ).xyz; 98 | vNormal.yz = rotate2D( 7.0 * vPosition.x ) * vNormal.yz; 99 | vNormal.zx = rotate2D( 7.0 * vPosition.y ) * vNormal.zx; 100 | 101 | // == send the vertex position =================================================================== 102 | vPosition = modelMatrix * vPosition; 103 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 104 | outPos.x *= resolution.y / resolution.x; 105 | gl_Position = outPos; 106 | 107 | vPosition.w = outPos.z / outPos.w; 108 | 109 | // gl_PointSize = resolution.y * size / outPos.z; 110 | } 111 | -------------------------------------------------------------------------------- /src/shaders/texture.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define MTL_UNLIT 1 4 | 5 | #extension GL_EXT_draw_buffers : enable 6 | 7 | varying vec4 vPosition; 8 | varying vec3 vNormal; 9 | varying vec2 vUv; 10 | 11 | uniform sampler2D sampler0; 12 | 13 | void main() { 14 | vec4 color = texture2D( sampler0, vUv ); 15 | if ( color.w < 0.5 ) { discard; } 16 | 17 | gl_FragData[ 0 ] = vPosition; 18 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 19 | gl_FragData[ 2 ] = vec4( color.xyz, 1.0 ); 20 | gl_FragData[ 3 ] = vec4( vec3( 0.0, 0.0, 0.0 ), MTL_UNLIT ); 21 | } 22 | -------------------------------------------------------------------------------- /src/shaders/trails-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 5.0 2 | 3 | #define HUGE 9E16 4 | #define PI 3.14159265 5 | #define TAU 6.283185307 6 | #define V vec3(0.,1.,-1.) 7 | #define saturate(i) clamp(i,0.,1.) 8 | #define lofi(i,m) (floor((i)/(m))*(m)) 9 | #define lofir(i,m) (floor((i)/(m)+.5)*(m)) 10 | 11 | // ------ 12 | 13 | precision highp float; 14 | 15 | uniform float time; 16 | uniform float beat; 17 | 18 | uniform float trails; 19 | uniform float trailLength; 20 | uniform float ppp; 21 | 22 | uniform float totalFrame; 23 | uniform bool init; 24 | uniform float deltaTime; 25 | uniform vec2 resolution; 26 | 27 | uniform sampler2D samplerCompute; 28 | uniform sampler2D samplerRandom; 29 | 30 | uniform float noiseScale; 31 | uniform float noisePhase; 32 | // uniform float velScale; 33 | // uniform float genRate; 34 | 35 | uniform float midiCC[ 128 ]; 36 | 37 | // ------ 38 | 39 | #pragma glslify: distFunc = require( ./-distFunc ); 40 | 41 | float distFunc( vec3 p ) { 42 | return distFunc( p, time, midiCC ); 43 | } 44 | 45 | vec3 normalFunc( vec3 p, float dd ) { 46 | vec2 d = vec2( 0.0, dd ); 47 | return normalize( vec3( 48 | distFunc( p + d.yxx ) - distFunc( p - d.yxx ), 49 | distFunc( p + d.xyx ) - distFunc( p - d.xyx ), 50 | distFunc( p + d.xxy ) - distFunc( p - d.xxy ) 51 | ) ); 52 | } 53 | 54 | vec2 uvInvT( vec2 _uv ) { 55 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 56 | } 57 | 58 | // ------ 59 | 60 | mat2 rotate2D( float _t ) { 61 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 62 | } 63 | 64 | float fractSin( float i ) { 65 | return fract( sin( i ) * 1846.42 ); 66 | } 67 | 68 | vec4 sampleRandom( vec2 _uv ) { 69 | return texture2D( samplerRandom, _uv ); 70 | } 71 | 72 | #pragma glslify: prng = require( ./-prng ); 73 | #pragma glslify: noise = require( ./-simplex4d ); 74 | 75 | vec3 randomSphere( inout vec4 seed ) { 76 | vec3 v; 77 | for ( int i = 0; i < 10; i ++ ) { 78 | v = vec3( 79 | prng( seed ), 80 | prng( seed ), 81 | prng( seed ) 82 | ) * 2.0 - 1.0; 83 | if ( length( v ) < 1.0 ) { break; } 84 | } 85 | return v; 86 | } 87 | 88 | vec2 randomCircle( inout vec4 seed ) { 89 | vec2 v; 90 | for ( int i = 0; i < 10; i ++ ) { 91 | v = vec2( 92 | prng( seed ), 93 | prng( seed ) 94 | ) * 2.0 - 1.0; 95 | if ( length( v ) < 1.0 ) { break; } 96 | } 97 | return v; 98 | } 99 | 100 | vec3 randomBox( inout vec4 seed ) { 101 | vec3 v; 102 | v = vec3( 103 | prng( seed ), 104 | prng( seed ), 105 | prng( seed ) 106 | ) * 2.0 - 1.0; 107 | return v; 108 | } 109 | 110 | float uneune( float i, float p ) { 111 | return sin( TAU * ( 112 | fractSin( i ) + floor( 1.0 + 4.0 * fractSin( i + 54.12 ) ) * p 113 | ) ); 114 | } 115 | 116 | vec3 uneune3( float i, float p ) { 117 | return vec3( uneune( i, p ), uneune( i + 11.87, p ), uneune( i + 21.92, p ) ); 118 | } 119 | 120 | // ------ 121 | 122 | void main() { 123 | vec2 uv = gl_FragCoord.xy / resolution; 124 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 125 | float pixId = mod( gl_FragCoord.x, ppp ); 126 | vec2 dpix = vec2( 1.0 ) / resolution; 127 | 128 | float dt = deltaTime; 129 | 130 | // == if it is not head of particles ============================================================= 131 | if ( ppp < gl_FragCoord.x ) { 132 | puv.x -= ppp / resolution.x; 133 | vec4 tex0 = texture2D( samplerCompute, puv ); 134 | vec4 tex1 = texture2D( samplerCompute, puv + dpix * vec2( 1.0, 0.0 ) ); 135 | 136 | tex0.w = saturate( tex0.w - 1.0 / trailLength ); // decrease the life 137 | 138 | gl_FragColor = ( 139 | pixId < 1.0 ? tex0 : 140 | tex1 141 | ); 142 | return; 143 | } 144 | 145 | // == prepare some vars ========================================================================== 146 | vec4 seed = texture2D( samplerRandom, puv ); 147 | prng( seed ); 148 | 149 | vec4 tex0 = texture2D( samplerCompute, puv ); 150 | vec4 tex1 = texture2D( samplerCompute, puv + dpix * vec2( 1.0, 0.0 ) ); 151 | 152 | vec3 pos = tex0.xyz; 153 | float life = tex0.w; 154 | vec3 vel = tex1.xyz; 155 | float jumpFlag = tex1.w; 156 | 157 | float timing = mix( 0.0, PARTICLE_LIFE_LENGTH, floor( puv.y * trails ) / trails ); 158 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 159 | 160 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 161 | timing -= PARTICLE_LIFE_LENGTH; 162 | } 163 | 164 | // == initialize particles ======================================================================= 165 | if ( 166 | time - deltaTime < timing && timing <= time 167 | && prng( seed ) < midiCC[ 15 ] 168 | ) { 169 | dt = time - timing; 170 | 171 | pos = 1.0 * randomSphere( seed ); 172 | 173 | vel = 1.0 * randomSphere( seed ); 174 | 175 | life = 1.0; 176 | 177 | jumpFlag = 1.0; 178 | } else { 179 | jumpFlag = 0.0; // remove jumping flag 180 | } 181 | 182 | // == update particles =========================================================================== 183 | // distFunc 184 | float dist = distFunc( pos.xyz ) - 0.2; 185 | vec3 nor = normalFunc( pos.xyz, 1E-4 ); 186 | vel -= dt * 100.0 * dist * nor; 187 | 188 | // spin around center 189 | vel.zx += dt * 20.0 * vec2( -1.0, 1.0 ) * normalize( nor.xz ); 190 | 191 | // noise field 192 | vel += midiCC[ 13 ] * 100.0 * vec3( 193 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 1.485 + sin( time * 0.1 ) + noisePhase ) ), 194 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 3.485 + sin( time * 0.1 ) + noisePhase ) ), 195 | noise( vec4( 2.0 * midiCC[ 14 ] * pos.xyz, 5.485 + sin( time * 0.1 ) + noisePhase ) ) 196 | ) * dt; 197 | 198 | // resistance 199 | vel *= exp( -10.0 * dt ); 200 | // vel.z += 10.0 * dt; 201 | 202 | vec3 v = vel; 203 | // float vmax = max( abs( v.x ), max( abs( v.y ), abs( v.z ) ) ); 204 | // v *= ( 205 | // abs( v.x ) == vmax ? vec3( 1.0, 0.0, 0.0 ) : 206 | // abs( v.y ) == vmax ? vec3( 0.0, 1.0, 0.0 ) : 207 | // vec3( 0.0, 0.0, 1.0 ) 208 | // ); 209 | 210 | // pos.xyz += velScale * v * dt; 211 | pos += v * dt; 212 | life -= dt / PARTICLE_LIFE_LENGTH; 213 | 214 | gl_FragColor = ( 215 | pixId < 1.0 ? vec4( pos, life ) : 216 | vec4( vel, jumpFlag ) 217 | ); 218 | } 219 | -------------------------------------------------------------------------------- /src/shaders/trails-render.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.14159265 2 | #define TAU 6.28318531 3 | #define saturate(i) clamp(i,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | 6 | #define MTL_UNLIT 1 7 | #define MTL_PBR 2 8 | 9 | #extension GL_EXT_draw_buffers : enable 10 | 11 | precision highp float; 12 | 13 | // == varings / uniforms =========================================================================== 14 | varying vec4 vPosition; 15 | varying vec3 vNormal; 16 | varying vec4 vColor; 17 | varying float vLife; 18 | varying vec4 vRandom; 19 | 20 | uniform float time; 21 | 22 | // == common ======================================================================================= 23 | mat2 rotate2D( float _t ) { 24 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 25 | } 26 | 27 | // == main procedure =============================================================================== 28 | void main() { 29 | if ( vColor.a < 0.0 ) { discard; } 30 | 31 | float emissive = 40.0; 32 | // emissive *= 0.5 + 0.5 * sin( TAU * vRandom.z + 20.0 * time ); 33 | 34 | gl_FragData[ 0 ] = vPosition; 35 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 36 | gl_FragData[ 2 ] = 0.1 * vColor; 37 | gl_FragData[ 3 ] = vec4( vec3( 0.9, 0.9, emissive ), MTL_PBR ); 38 | } 39 | -------------------------------------------------------------------------------- /src/shaders/trails-render.vert: -------------------------------------------------------------------------------- 1 | #define HUGE 9E16 2 | #define PI 3.141592654 3 | #define TAU 6.283185307 4 | #define V vec3(0.,1.,-1.) 5 | #define saturate(x) clamp(x,0.,1.) 6 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 7 | #define lofi(i,m) (floor((i)/(m))*(m)) 8 | #define lofir(i,m) (floor((i+0.5)/(m))*(m)) 9 | 10 | // ------------------------------------------------------------------------------------------------- 11 | 12 | attribute float computeU; 13 | attribute float computeV; 14 | attribute float triIndex; 15 | 16 | varying vec4 vPosition; 17 | varying vec3 vNormal; 18 | varying vec4 vColor; 19 | varying float vLife; 20 | varying vec4 vRandom; 21 | 22 | uniform vec2 resolution; 23 | uniform vec2 resolutionCompute; 24 | uniform float ppp; 25 | 26 | uniform mat4 projectionMatrix; 27 | uniform mat4 viewMatrix; 28 | uniform mat4 modelMatrix; 29 | uniform mat4 normalMatrix; 30 | 31 | uniform bool isShadow; 32 | 33 | uniform float trailShaker; 34 | uniform float colorVar; 35 | uniform float colorOffset; 36 | 37 | uniform sampler2D samplerCompute; 38 | uniform sampler2D samplerRandomStatic; 39 | 40 | // ------------------------------------------------------------------------------------------------- 41 | vec3 blurpleGradient( float t ) { 42 | vec3 colorA = vec3( 0.01, 0.04, 0.2 ); 43 | vec3 colorB = vec3( 0.02, 0.3, 0.9 ); 44 | vec3 colorC = vec3( 0.9, 0.01, 0.6 ); 45 | vec3 colorD = vec3( 0.5, 0.02, 0.02 ); 46 | 47 | return mix( 48 | colorA, 49 | mix( 50 | colorB, 51 | mix( 52 | colorC, 53 | colorD, 54 | linearstep( 0.67, 1.0, t ) 55 | ), 56 | linearstep( 0.33, 0.67, t ) 57 | ), 58 | linearstep( 0.0, 0.33, t ) 59 | ); 60 | } 61 | 62 | vec3 catColor( float _p ) { 63 | return 0.5 + 0.5 * vec3( 64 | cos( _p ), 65 | cos( _p + PI / 3.0 * 4.0 ), 66 | cos( _p + PI / 3.0 * 2.0 ) 67 | ); 68 | } 69 | 70 | vec4 random( vec2 _uv ) { 71 | return texture2D( samplerRandomStatic, _uv ); 72 | } 73 | 74 | mat2 rotate2D( float _t ) { 75 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 76 | } 77 | 78 | // ------------------------------------------------------------------------------------------------- 79 | 80 | void main() { 81 | vec2 puv = vec2( computeU, computeV ); 82 | vec2 dppix = vec2( 1.0 ) / resolutionCompute; 83 | 84 | // == fetch texture ============================================================================== 85 | vec4 pos = texture2D( samplerCompute, puv ); 86 | vec4 vel = texture2D( samplerCompute, puv + dppix * vec2( 1.0, 0.0 ) ); 87 | vec4 velp = texture2D( samplerCompute, puv + dppix * vec2( -ppp + 1.0, 0.0 ) ); 88 | 89 | // == assign varying variables =================================================================== 90 | vLife = pos.w; 91 | 92 | vRandom = random( puv.yy * 182.92 ); 93 | 94 | vColor.xyz = ( 95 | vRandom.y < 0.8 96 | ? pow( catColor( TAU * ( ( vRandom.x * 2.0 - 1.0 ) * colorVar + 0.6 + colorOffset ) ), vec3( 2.0 ) ) 97 | : vec3( 0.4 ) 98 | ); 99 | vColor.xyz = blurpleGradient( vLife ); 100 | // vColor.xyz = catColor( 3.0 + 4.0 * vLife ); 101 | 102 | vColor.w = ( velp.w < 0.5 && vel.w < 0.5 && 0.0 < vLife ) ? 1.0 : -1.0; 103 | 104 | // == compute size and direction ================================================================= 105 | float size = 0.005; 106 | size *= 1.0 + pow( vRandom.w, 2.0 ); 107 | // size *= max( 0.0, sin( PI * 10.0 * vLife ) ); 108 | 109 | vec3 dir = normalize( vel.xyz ); 110 | vec3 sid = normalize( cross( dir, vec3( 0.0, 1.0, 0.0 ) ) ); 111 | vec3 top = normalize( cross( sid, dir ) ); 112 | 113 | float theta = triIndex / 3.0 * TAU + vLife * 1.0; 114 | vec2 tri = vec2( sin( theta ), cos( theta ) ); 115 | vNormal = ( normalMatrix * vec4( tri.x * sid + tri.y * top, 1.0 ) ).xyz; 116 | pos.xyz += size * vNormal; 117 | 118 | vPosition = modelMatrix * vec4( pos.xyz, 1.0 ); 119 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 120 | outPos.x *= resolution.y / resolution.x; 121 | gl_Position = outPos; 122 | 123 | vPosition.w = outPos.z / outPos.w; 124 | 125 | // gl_PointSize = resolution.y * size / outPos.z; 126 | } 127 | -------------------------------------------------------------------------------- /src/shaders/ui-particles-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 1.0 2 | 3 | #define HUGE 9E16 4 | #define PI 3.14159265 5 | #define TAU 6.283185307 6 | #define V vec3(0.,1.,-1.) 7 | #define saturate(i) clamp(i,0.,1.) 8 | #define lofi(i,m) (floor((i)/(m))*(m)) 9 | #define lofir(i,m) (floor((i)/(m)+.5)*(m)) 10 | 11 | #define MODE_RECT 0 12 | #define MODE_GRID 1 13 | #define MODE_CIRCLE 2 14 | #define MODE_CHAR 3 15 | #define MODES 4 16 | 17 | // ------ 18 | 19 | precision highp float; 20 | 21 | uniform float time; 22 | uniform float beat; 23 | 24 | uniform float particlesSqrt; 25 | uniform float ppp; 26 | 27 | uniform float totalFrame; 28 | uniform bool init; 29 | uniform float deltaTime; 30 | uniform vec2 resolution; 31 | 32 | uniform sampler2D samplerCompute; 33 | uniform sampler2D samplerRandom; 34 | 35 | uniform float noiseScale; 36 | uniform float noisePhase; 37 | // uniform float velScale; 38 | // uniform float genRate; 39 | 40 | uniform float midiCC[ 128 ]; 41 | 42 | // ------ 43 | 44 | vec2 uvInvT( vec2 _uv ) { 45 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 46 | } 47 | 48 | // ------ 49 | 50 | mat2 rotate2D( float _t ) { 51 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 52 | } 53 | 54 | float fractSin( float i ) { 55 | return fract( sin( i ) * 1846.42 ); 56 | } 57 | 58 | vec4 sampleRandom( vec2 _uv ) { 59 | return texture2D( samplerRandom, _uv ); 60 | } 61 | 62 | #pragma glslify: prng = require( ./-prng ); 63 | #pragma glslify: noise = require( ./-simplex4d ); 64 | 65 | vec3 randomSphere( inout vec4 seed ) { 66 | vec3 v; 67 | for ( int i = 0; i < 10; i ++ ) { 68 | v = vec3( 69 | prng( seed ), 70 | prng( seed ), 71 | prng( seed ) 72 | ) * 2.0 - 1.0; 73 | if ( length( v ) < 1.0 ) { break; } 74 | } 75 | return v; 76 | } 77 | 78 | vec2 randomCircle( inout vec4 seed ) { 79 | vec2 v; 80 | for ( int i = 0; i < 10; i ++ ) { 81 | v = vec2( 82 | prng( seed ), 83 | prng( seed ) 84 | ) * 2.0 - 1.0; 85 | if ( length( v ) < 1.0 ) { break; } 86 | } 87 | return v; 88 | } 89 | 90 | vec3 randomBox( inout vec4 seed ) { 91 | vec3 v; 92 | v = vec3( 93 | prng( seed ), 94 | prng( seed ), 95 | prng( seed ) 96 | ) * 2.0 - 1.0; 97 | return v; 98 | } 99 | 100 | // ------ 101 | 102 | void main() { 103 | vec2 uv = gl_FragCoord.xy / resolution; 104 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 105 | float pixId = mod( gl_FragCoord.x, ppp ); 106 | vec2 dpix = vec2( 1.0 ) / resolution; 107 | 108 | float dt = deltaTime; 109 | 110 | // == prepare some vars ========================================================================== 111 | vec4 seed = texture2D( samplerRandom, puv ); 112 | prng( seed ); 113 | 114 | vec4 tex0 = texture2D( samplerCompute, puv ); 115 | vec4 tex1 = texture2D( samplerCompute, puv + dpix * vec2( 1.0, 0.0 ) ); 116 | 117 | vec3 pos = tex0.xyz; 118 | float life = tex0.w; 119 | vec3 vel = tex1.xyz; 120 | int mode = int( tex1.w ); 121 | 122 | float timing = mix( 123 | 0.0, 124 | PARTICLE_LIFE_LENGTH, 125 | ( floor( puv.x * particlesSqrt ) / particlesSqrt + floor( puv.y * particlesSqrt ) ) / particlesSqrt 126 | ); 127 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 128 | 129 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 130 | timing -= PARTICLE_LIFE_LENGTH; 131 | } 132 | 133 | // == initialize particles ======================================================================= 134 | if ( 135 | time - deltaTime < timing && timing <= time 136 | && prng( seed ) < midiCC[ 17 ] 137 | ) { 138 | dt = time - timing; 139 | 140 | pos.xyz = vec3( 5.0, 5.0, 3.0 ) * randomBox( seed ); 141 | 142 | vel.xyz = 1.0 * randomSphere( seed ); 143 | 144 | life = 1.0; 145 | 146 | mode = int( prng( seed ) * midiCC[ 18 ] * float( MODES ) ); 147 | // mode = prng( seed ) < 0.5 ? MODE_ICON : mode; 148 | } else { 149 | // do nothing 150 | // if you want to remove init frag from the particle, do at here 151 | } 152 | 153 | // == update particles =========================================================================== 154 | pos += vec3( 0.5, 0.0, 0.0 ) * dt; 155 | life -= dt / PARTICLE_LIFE_LENGTH; 156 | 157 | gl_FragColor = ( 158 | pixId < 1.0 ? vec4( pos, life ) : 159 | vec4( vel, mode ) 160 | ); 161 | } 162 | -------------------------------------------------------------------------------- /src/shaders/ui-particles-render.frag: -------------------------------------------------------------------------------- 1 | #define PI 3.14159265 2 | #define TAU 6.28318531 3 | #define saturate(i) clamp(i,0.,1.) 4 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 5 | #define lofi(i,m) (floor((i)/(m))*(m)) 6 | #define xor(a,b) (mix((a),1.0-(a),(b))) 7 | 8 | #define PIXIV_BLUE vec3( 0.0, 0.311, 0.957 ) 9 | #define FADE_10 vec3( 0.914 ) 10 | #define FADE_60 vec3( 0.105 ) 11 | 12 | #define MTL_UNLIT 1 13 | #define MTL_PBR 2 14 | 15 | #define MODE_RECT 0 16 | #define MODE_GRID 1 17 | #define MODE_CIRCLE 2 18 | #define MODE_CHAR 3 19 | #define MODES 4 20 | 21 | #extension GL_EXT_draw_buffers : enable 22 | #extension GL_OES_standard_derivatives : enable 23 | 24 | precision highp float; 25 | 26 | // == varings / uniforms =========================================================================== 27 | varying vec4 vPosition; 28 | varying vec3 vNormal; 29 | varying float vLife; 30 | varying vec2 vUv; 31 | varying vec4 vDice; 32 | varying float vMode; 33 | 34 | uniform float time; 35 | uniform vec2 resolution; 36 | uniform sampler2D samplerDoublequoteRandom; 37 | uniform sampler2D samplerChar; 38 | uniform sampler2D samplerWord; 39 | uniform sampler2D samplerIcon; 40 | 41 | // == common ======================================================================================= 42 | mat2 rotate2D( float _t ) { 43 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 44 | } 45 | 46 | vec2 uvInvT( vec2 uv ) { 47 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * uv; 48 | } 49 | 50 | // == main procedure =============================================================================== 51 | void main() { 52 | int mode = int( vMode + 0.5 ); 53 | 54 | vec3 color = vec3( 0.0 ); 55 | 56 | if ( vLife < 0.0 ) { discard; } 57 | if ( vLife < 0.1 && 0.5 < fract( 30.0 * vLife ) ) { discard; } 58 | 59 | if ( mode == MODE_RECT ) { 60 | vec2 size = vec2( 0.5 ); 61 | size.y *= 1.0 - exp( -10.0 * ( 1.0 - vLife ) ); 62 | 63 | vec2 uv = vUv; 64 | vec2 deltaUv = abs( vec2( dFdx( uv.x ), dFdy( uv.y ) ) ); 65 | vec2 folded = ( size - abs( uv - 0.5 ) ) / deltaUv; 66 | bool isVert = false; 67 | 68 | if ( folded.x < folded.y ) { 69 | uv.xy = vec2( uv.y, uv.x ); 70 | folded.xy = folded.yx; 71 | deltaUv.xy = deltaUv.yx; 72 | isVert = true; 73 | } 74 | 75 | float spinUvx = uv.y < 0.5 ? uv.x : ( 1.0 - uv.x ); 76 | spinUvx = isVert ? ( 1.0 - spinUvx ) : spinUvx; 77 | 78 | float border = step( 0.0, folded.y ) * step( folded.y, 2.0 ); 79 | border *= step( 0.0, sin( 12.0 * time + 0.5 * spinUvx / deltaUv.x ) ); // dashed 80 | 81 | if ( border < 0.5 ) { discard; } 82 | 83 | color = vec3( 1.0 ); 84 | 85 | } else if ( mode == MODE_GRID ) { 86 | float size = 0.2; 87 | size *= 1.0 - exp( -10.0 * ( 1.0 - vLife ) ); 88 | 89 | vec2 uv = vUv; 90 | 91 | vec2 folded = mod( 4.0 * uv, 1.0 ); 92 | 93 | float shape = step( length( folded - 0.5 ), size ); 94 | 95 | if ( shape < 0.5 ) { discard; } 96 | 97 | color = vec3( 1.0 ); 98 | 99 | } else if ( mode == MODE_CIRCLE ) { 100 | float size = 0.5; 101 | size *= 1.0 - exp( -10.0 * ( 1.0 - vLife ) ); 102 | 103 | vec2 uv = vUv; 104 | vec2 deltaUv = abs( vec2( dFdx( uv.x ), dFdy( uv.y ) ) ); 105 | 106 | float r = length( uv - 0.5 ); 107 | float shape = step( r, size ); 108 | shape *= step( size, r + length( deltaUv ) ); 109 | 110 | if ( shape < 0.5 ) { discard; } 111 | 112 | color = vec3( 1.0 ); 113 | 114 | } else if ( mode == MODE_CHAR ) { 115 | float anim = exp( -10.0 * ( 1.0 - vLife ) ); 116 | 117 | vec2 uv = vUv; 118 | 119 | vec2 charUv = uvInvT( vUv ); 120 | charUv *= 0.0625; 121 | charUv.xy += lofi( 122 | mix( 123 | vec2( 0.0, 0.125 ), 124 | vec2( 1.0, 0.5 ), 125 | fract( 777.77 * vDice.xy ) 126 | ), 127 | 0.0625 128 | ); 129 | 130 | float tex = texture2D( samplerChar, charUv ).x; 131 | float shape = step( tex, 0.0 ); 132 | 133 | shape = xor( step( uv.y + 0.01, anim ), shape ); 134 | 135 | shape *= step( abs( uv.x - 0.5 ), 0.4 ); 136 | 137 | if ( shape < 0.5 ) { discard; } 138 | 139 | color = vec3( 1.0 ); 140 | 141 | } 142 | 143 | gl_FragData[ 0 ] = vPosition; 144 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 145 | gl_FragData[ 2 ] = vec4( color, 1.0 ); 146 | gl_FragData[ 3 ] = vec4( vec3( 0.0 ), MTL_UNLIT ); 147 | } 148 | -------------------------------------------------------------------------------- /src/shaders/ui-particles-render.vert: -------------------------------------------------------------------------------- 1 | #define HUGE 9E16 2 | #define PI 3.141592654 3 | #define TAU 6.283185307 4 | #define V vec3(0.,1.,-1.) 5 | #define saturate(i) clamp(i,0.,1.) 6 | #define lofi(i,m) (floor((i)/(m))*(m)) 7 | #define lofir(i,m) (floor((i+0.5)/(m))*(m)) 8 | 9 | #define MODE_RECT 0 10 | #define MODE_GRID 1 11 | #define MODE_CIRCLE 2 12 | #define MODE_CHAR 3 13 | #define MODES 4 14 | 15 | // ------------------------------------------------------------------------------------------------- 16 | 17 | attribute vec2 computeUV; 18 | attribute vec2 p; 19 | 20 | varying vec4 vPosition; 21 | varying vec3 vNormal; 22 | varying vec2 vUv; 23 | varying vec4 vDice; 24 | varying float vMode; 25 | varying float vLife; 26 | 27 | uniform vec2 resolution; 28 | uniform vec2 resolutionCompute; 29 | uniform float ppp; 30 | 31 | uniform mat4 projectionMatrix; 32 | uniform mat4 viewMatrix; 33 | uniform mat4 modelMatrix; 34 | uniform mat4 normalMatrix; 35 | 36 | uniform bool isShadow; 37 | 38 | uniform float trailShaker; 39 | uniform float colorVar; 40 | uniform float colorOffset; 41 | 42 | uniform sampler2D samplerCompute; 43 | uniform sampler2D samplerRandomStatic; 44 | 45 | // ------------------------------------------------------------------------------------------------- 46 | 47 | vec3 catColor( float _p ) { 48 | return 0.5 + 0.5 * vec3( 49 | cos( _p ), 50 | cos( _p + PI / 3.0 * 4.0 ), 51 | cos( _p + PI / 3.0 * 2.0 ) 52 | ); 53 | } 54 | 55 | vec4 random( vec2 _uv ) { 56 | return texture2D( samplerRandomStatic, _uv ); 57 | } 58 | 59 | mat2 rotate2D( float _t ) { 60 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 61 | } 62 | 63 | // ------------------------------------------------------------------------------------------------- 64 | 65 | void main() { 66 | vec2 puv = vec2( computeUV ); 67 | vec2 dppix = vec2( 1.0 ) / resolutionCompute; 68 | 69 | // == fetch texture ============================================================================== 70 | vec4 tex0 = texture2D( samplerCompute, puv ); 71 | vec4 tex1 = texture2D( samplerCompute, puv + dppix * vec2( 1.0, 0.0 ) ); 72 | 73 | // == assign varying variables =================================================================== 74 | vDice = random( tex1.xy * 182.92 ); 75 | 76 | vNormal = normalize( ( normalMatrix * vec4( 0.0, 0.0, 1.0, 1.0 ) ).xyz ); 77 | 78 | vUv = 0.5 + 0.5 * p; 79 | 80 | vLife = tex0.w; 81 | 82 | vMode = tex1.w; 83 | int mode = int( vMode ); 84 | 85 | // == compute size and direction ================================================================= 86 | vPosition = vec4( tex0.xyz, 1.0 ); 87 | 88 | vec2 size; 89 | 90 | if ( mode == MODE_RECT ) { 91 | size = 1.0 * vDice.xy; 92 | 93 | } else if ( mode == MODE_GRID ) { 94 | size = vec2( 0.25 + 0.25 * vDice.x ); 95 | 96 | } else if ( mode == MODE_CIRCLE ) { 97 | size = vec2( 1.0 * vDice.x ); 98 | 99 | } else if ( mode == MODE_CHAR ) { 100 | size = vec2( 0.2 + 0.2 * vDice.x ); 101 | 102 | } 103 | 104 | vPosition.xy += p * size; 105 | 106 | // == send the vertex position =================================================================== 107 | vPosition = modelMatrix * vPosition; 108 | vec4 outPos = projectionMatrix * viewMatrix * vPosition; 109 | outPos.x *= resolution.y / resolution.x; 110 | gl_Position = outPos; 111 | 112 | vPosition.w = outPos.z / outPos.w; 113 | 114 | // gl_PointSize = resolution.y * size / outPos.z; 115 | } 116 | -------------------------------------------------------------------------------- /src/shaders/waku.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define MTL_UNLIT 1 4 | 5 | #define saturate(i) clamp(i,0.,1.) 6 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 7 | #define lofi(i,m) (floor((i)/(m))*(m)) 8 | 9 | #extension GL_EXT_draw_buffers : enable 10 | 11 | varying vec4 vPosition; 12 | varying vec3 vNormal; 13 | varying vec2 vUv; 14 | 15 | uniform float time; 16 | uniform vec2 uvScale; 17 | 18 | #pragma glslify: noise = require( ./-simplex4d ); 19 | 20 | void main() { 21 | vec2 uv = uvScale * ( vUv - 0.5 ); 22 | vec2 uvLo = lofi( uv, 0.0125 ); 23 | vec2 uvCell = fract( uv / 0.0125 ); 24 | 25 | float noiseField = 0.5 + 0.5 * sin( 7.0 * noise( vec4( 4.0 * uvLo, 0.48 * time, 0.4 * time ) ) ); 26 | 27 | float len = max( abs( uvCell.x - 0.5 ), abs( uvCell.y - 0.5 ) ); 28 | float shape = step( linearstep( 0.05, 0.5, len ), noiseField ); 29 | 30 | if ( shape < 0.5 ) { discard; } 31 | 32 | gl_FragData[ 0 ] = vPosition; 33 | gl_FragData[ 1 ] = vec4( vNormal, 1.0 ); 34 | gl_FragData[ 2 ] = vec4( 1.0 ); 35 | gl_FragData[ 3 ] = vec4( vec3( 0.0, 0.0, 0.0 ), MTL_UNLIT ); 36 | } 37 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import url( 'https://fonts.googleapis.com/css?family=Exo' ); 2 | @import url( 'https://fonts.googleapis.com/css?family=Roboto' ); 3 | 4 | body { 5 | width: 100%; 6 | height: 100%; 7 | overflow: hidden; 8 | 9 | font: 500 10px Exo, 'Wt-Position', sans-serif; 10 | text-shadow: 0 0 2px #000, 0 0 2px #000, 0 0 2px #000; 11 | 12 | background: #000; 13 | color: #fff; 14 | 15 | #divActive { 16 | position: fixed; 17 | left: 8px; 18 | top: 8px; 19 | } 20 | 21 | #divMidi { 22 | position: fixed; 23 | left: 8px; 24 | bottom: 8px; 25 | } 26 | 27 | #canvas { 28 | position: fixed; 29 | left: 0; 30 | top: 0; 31 | width: 100%; 32 | height: 100%; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/types/FontFace.d.ts: -------------------------------------------------------------------------------- 1 | declare class FontFace { 2 | public style: string; 3 | public weight: string; 4 | public constructor( name: string, path: string ); 5 | public load(): Promise; 6 | } 7 | -------------------------------------------------------------------------------- /src/types/FontFaceSet.d.ts: -------------------------------------------------------------------------------- 1 | declare interface FontFaceSet extends Set { 2 | load( font: string, text?: string ): Promise; 3 | } 4 | 5 | declare global { 6 | interface Document { 7 | fonts: FontFaceSet; 8 | } 9 | } 10 | 11 | export default undefined; 12 | -------------------------------------------------------------------------------- /src/types/frag.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.frag' { 2 | const source: string; 3 | export default source; 4 | export const addHotListener: ( listener: ( content: string ) => void ) => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/vert.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vert' { 2 | const source: string; 3 | export default source; 4 | export const addHotListener: ( listener: ( content: string ) => void ) => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/EventEmittable.ts: -------------------------------------------------------------------------------- 1 | // Ref: https://github.com/andywer/typed-emitter/blob/master/index.d.ts 2 | 3 | export type EventListener = ( event: T ) => void; 4 | 5 | export class EventEmittable { 6 | protected __eventListeners?: Map[]>; 7 | 8 | public on( 9 | type: TType, 10 | listener: EventListener 11 | ): void { 12 | this.__eventListeners = this.__eventListeners || new Map(); 13 | let array = this.__eventListeners.get( type ); 14 | if ( !array ) { 15 | array = []; 16 | this.__eventListeners.set( type, array ); 17 | } 18 | 19 | array.push( listener ); 20 | } 21 | 22 | public off( 23 | type: TType, 24 | listener: EventListener 25 | ): void { 26 | this.__eventListeners = this.__eventListeners || new Map(); 27 | let array = this.__eventListeners.get( type ); 28 | if ( !array ) { 29 | array = []; 30 | this.__eventListeners.set( type, array ); 31 | } 32 | 33 | const index = array.indexOf( listener ); 34 | if ( index !== -1 ) { 35 | array.splice( index, 1 ); 36 | } 37 | } 38 | 39 | protected __emit( 40 | ...[ type, event ]: TEvents[ TType ] extends void ? [ TType ] : [ TType, TEvents[ TType ] ] 41 | ): void { 42 | this.__eventListeners = this.__eventListeners || new Map(); 43 | this.__eventListeners.get( type )?.slice().forEach( ( listener ) => listener( event ) ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/EventManager.ts: -------------------------------------------------------------------------------- 1 | import { EventEmittable } from './EventEmittable'; 2 | 3 | export class EventManager extends EventEmittable<{ 4 | error: any; 5 | info: string; 6 | words: string[]; 7 | panic: void; 8 | regenerate: void; 9 | }> { 10 | public emitError( e: any ): void { 11 | this.__emit( 'error', e ); 12 | } 13 | 14 | public emitInfo( e: string ): void { 15 | this.__emit( 'info', e ); 16 | } 17 | 18 | public emitWords( e: string[] ): void { 19 | this.__emit( 'words', e ); 20 | } 21 | 22 | public emitPanic(): void { 23 | this.__emit( 'panic' ); 24 | } 25 | 26 | public emitRegenerate(): void { 27 | this.__emit( 'regenerate' ); 28 | } 29 | } 30 | 31 | export const EVENTMAN = new EventManager(); 32 | -------------------------------------------------------------------------------- /src/utils/RandomSphereTexture.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCat, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { Vector3, Xorshift } from '@fms-cat/experimental'; 3 | 4 | export class RandomSphereTexture { 5 | private __texture: GLCatTexture; 6 | private __array: Float32Array; 7 | private __rng: Xorshift; 8 | private __width: number; 9 | private __height: number; 10 | 11 | public constructor( glCat: GLCat, width: number, height: number = width ) { 12 | this.__width = width; 13 | this.__height = height; 14 | this.__rng = new Xorshift(); 15 | this.__array = new Float32Array( width * height * 4 ); 16 | this.__texture = glCat.createTexture()!; 17 | this.__texture.textureFilter( GL.NEAREST ); 18 | this.__texture.textureWrap( GL.REPEAT ); 19 | } 20 | 21 | public get texture(): GLCatTexture { 22 | return this.__texture; 23 | } 24 | 25 | public dispose(): void { 26 | this.__texture.dispose(); 27 | } 28 | 29 | public update( seed?: number ): void { 30 | if ( seed ) { this.__rng.seed = seed; } 31 | 32 | const n = this.__array.length / 4; 33 | for ( let i = 0; i < n; i ++ ) { 34 | let vec: Vector3 | undefined = undefined; 35 | while ( !( vec && vec.length < 1.0 ) ) { 36 | vec = new Vector3( [ 37 | this.__rng.gen() * 2.0 - 1.0, 38 | this.__rng.gen() * 2.0 - 1.0, 39 | this.__rng.gen() * 2.0 - 1.0 40 | ] ); 41 | } 42 | 43 | this.__array[ i * 4 + 0 ] = vec.x; 44 | this.__array[ i * 4 + 1 ] = vec.y; 45 | this.__array[ i * 4 + 2 ] = vec.z; 46 | this.__array[ i * 4 + 3 ] = 1.0; 47 | } 48 | 49 | this.__texture.setTextureFromFloatArray( 50 | this.__width, 51 | this.__height, 52 | this.__array 53 | ); 54 | } 55 | } 56 | 57 | export default RandomSphereTexture; 58 | -------------------------------------------------------------------------------- /src/utils/RandomTexture.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCat, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { Xorshift } from '@fms-cat/experimental'; 3 | 4 | export class RandomTexture { 5 | private __texture: GLCatTexture; 6 | private __array: Uint8Array; 7 | private __rng: Xorshift; 8 | private __width: number; 9 | private __height: number; 10 | 11 | public constructor( glCat: GLCat, width: number, height: number = width ) { 12 | this.__width = width; 13 | this.__height = height; 14 | this.__rng = new Xorshift(); 15 | this.__array = new Uint8Array( width * height * 4 ); 16 | this.__texture = glCat.createTexture()!; 17 | this.__texture.textureWrap( GL.REPEAT ); 18 | } 19 | 20 | public get texture(): GLCatTexture { 21 | return this.__texture; 22 | } 23 | 24 | public dispose(): void { 25 | this.__texture.dispose(); 26 | } 27 | 28 | public update( seed?: number ): void { 29 | if ( seed ) { this.__rng.seed = seed; } 30 | 31 | for ( let i = 0; i < this.__array.length; i ++ ) { 32 | this.__array[ i ] = Math.floor( this.__rng.gen() * 256.0 ); 33 | } 34 | 35 | this.__texture.setTextureFromArray( 36 | this.__width, 37 | this.__height, 38 | this.__array 39 | ); 40 | } 41 | } 42 | 43 | export default RandomTexture; 44 | -------------------------------------------------------------------------------- /src/utils/ScreenCaptureTexture.ts: -------------------------------------------------------------------------------- 1 | import { GLCat, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | 3 | export class ScreenCaptureTexture { 4 | private __video: HTMLVideoElement; 5 | private __texture: GLCatTexture; 6 | 7 | public constructor( glCat: GLCat ) { 8 | this.__video = document.createElement( 'video' ); 9 | this.__texture = glCat.createTexture()!; 10 | this.__texture.setTextureFromArray( 1, 1, new Uint8Array( [ 255, 0, 255, 255 ] ) ); // heck 11 | } 12 | 13 | public get video(): HTMLVideoElement { return this.__video; } 14 | public get texture(): GLCatTexture { return this.__texture; } 15 | 16 | public setup( width: number, height: number ): Promise { 17 | return ( navigator.mediaDevices as any ).getDisplayMedia( 18 | { 19 | video: { width, height }, 20 | } 21 | ).then( ( stream: MediaStream ) => { 22 | this.__video.srcObject = stream; 23 | this.__video.play().then( () => { 24 | this.__texture.setTexture( this.__video ); 25 | return this.__texture; 26 | } ); 27 | } ); 28 | } 29 | 30 | public update(): void { 31 | if ( !this.__video.paused ) { 32 | this.__texture.setTexture( this.__video ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/WebCameraTexture.ts: -------------------------------------------------------------------------------- 1 | import { GLCat, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | 3 | export class WebCameraTexture { 4 | private __video: HTMLVideoElement; 5 | private __texture: GLCatTexture; 6 | 7 | public constructor( glCat: GLCat ) { 8 | this.__video = document.createElement( 'video' ); 9 | this.__texture = glCat.createTexture()!; 10 | this.__texture.setTextureFromArray( 1, 1, new Uint8Array( [ 255, 0, 255, 255 ] ) ); // heck 11 | } 12 | 13 | public get video(): HTMLVideoElement { return this.__video; } 14 | public get texture(): GLCatTexture { return this.__texture; } 15 | 16 | public setup( width: number, height: number ): Promise { 17 | return ( navigator.mediaDevices as any ).getUserMedia( 18 | { 19 | video: { width, height }, 20 | } 21 | ).then( ( stream: MediaStream ) => { 22 | this.__video.srcObject = stream; 23 | this.__video.play().then( () => { 24 | this.__texture.setTexture( this.__video ); 25 | return this.__texture; 26 | } ); 27 | } ); 28 | } 29 | 30 | public update(): void { 31 | if ( !this.__video.paused ) { 32 | this.__texture.setTexture( this.__video ); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/createFontSpritesheetSDF.ts: -------------------------------------------------------------------------------- 1 | // yoinked from https://github.com/mapbox/tiny-sdf (BSD 2-Clause) 2 | 3 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 4 | import { createSDF } from './createSDF'; 5 | 6 | const defaultChars: string[] = []; 7 | for ( let i = 0; i < 256; i ++ ) { 8 | defaultChars[ i ] = String.fromCharCode( i ); 9 | } 10 | 11 | export async function createFontSpritesheetSDF( options: { 12 | texture: GLCatTexture; 13 | charSize: [ number, number ]; 14 | matrix?: [ number, number ]; 15 | font: string; 16 | baseline?: number; 17 | chars?: string[]; 18 | } ): Promise { 19 | const { texture, charSize, font } = options; 20 | const matrix = options.matrix || [ 16, 16 ]; 21 | const chars = options.chars || defaultChars; 22 | const baseline = options.baseline || 0.8; 23 | 24 | const canvas = document.createElement( 'canvas' ); 25 | canvas.width = charSize[ 0 ] * matrix[ 0 ]; 26 | canvas.height = charSize[ 1 ] * matrix[ 1 ]; 27 | 28 | const context = canvas.getContext( '2d' )!; 29 | context.textAlign = 'center'; 30 | context.fillStyle = '#fff'; 31 | 32 | await document.fonts.load( font ); 33 | context.font = font; 34 | 35 | for ( let i = 0; i < 256; i ++ ) { 36 | const char = chars[ i ]; 37 | const x = ( ( i % matrix[ 0 ] ) + 0.5 ) * charSize[ 0 ]; 38 | const y = ( Math.floor( i / matrix[ 0 ] ) + baseline ) * charSize[ 1 ]; 39 | context.fillText( char, x, y ); 40 | } 41 | 42 | createSDF( { 43 | texture, 44 | canvas, 45 | context 46 | } ); 47 | } 48 | -------------------------------------------------------------------------------- /src/utils/createImageSDF.ts: -------------------------------------------------------------------------------- 1 | // yoinked from https://github.com/mapbox/tiny-sdf (BSD 2-Clause) 2 | 3 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 4 | import { createSDF } from './createSDF'; 5 | import { loadImage } from './loadImage'; 6 | 7 | const defaultChars: string[] = []; 8 | for ( let i = 0; i < 256; i ++ ) { 9 | defaultChars[ i ] = String.fromCharCode( i ); 10 | } 11 | 12 | export async function createImageSDF( options: { 13 | texture: GLCatTexture; 14 | url: string; 15 | } ): Promise { 16 | const { texture, url } = options; 17 | 18 | const image = await loadImage( url ); 19 | 20 | const canvas = document.createElement( 'canvas' ); 21 | canvas.width = image.width; 22 | canvas.height = image.height; 23 | 24 | const context = canvas.getContext( '2d' )!; 25 | 26 | context.drawImage( image, 0, 0 ); 27 | 28 | createSDF( { 29 | texture, 30 | canvas, 31 | context 32 | } ); 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/createSDF.ts: -------------------------------------------------------------------------------- 1 | // yoinked from https://github.com/mapbox/tiny-sdf (BSD 2-Clause) 2 | 3 | import { GL, GLCatTexture } from '@fms-cat/glcat-ts'; 4 | import { edt2d } from './edt'; 5 | 6 | const HUGE = 1E8; 7 | 8 | export function createSDF( options: { 9 | texture: GLCatTexture; 10 | canvas: HTMLCanvasElement; 11 | context: CanvasRenderingContext2D; 12 | } ): GLCatTexture { 13 | const { texture, canvas, context } = options; 14 | const { width, height } = canvas; 15 | 16 | const imageData = context.getImageData( 0, 0, canvas.width, canvas.height ); 17 | const inside = new Float32Array( width * height ); 18 | const outside = new Float32Array( width * height ); 19 | 20 | for ( let i = 0; i < width * height; i ++ ) { 21 | const v = imageData.data[ i * 4 + 3 ] / 255.0; 22 | inside[ i ] = ( 23 | v === 1.0 ? HUGE : 24 | v === 0.0 ? 0.0 : 25 | Math.max( 0.0, v - 0.5 ) 26 | ); 27 | 28 | outside[ i ] = ( 29 | v === 1.0 ? 0.0 : 30 | v === 0.0 ? HUGE : 31 | Math.max( 0.0, 0.5 - v ) 32 | ); 33 | } 34 | 35 | edt2d( inside, width, height ); 36 | edt2d( outside, width, height ); 37 | 38 | for ( let i = 0; i < width * height; i ++ ) { 39 | outside[ i ] = ( outside[ i ] - inside[ i ] ); 40 | } 41 | 42 | texture.setTextureFromFloatArray( width, height, outside, GL.LUMINANCE ); 43 | 44 | return texture; 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/doQuadRender.ts: -------------------------------------------------------------------------------- 1 | import { GL, GLCatFramebuffer, GLCatProgramUniformType, GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { DISPLAY } from '../heck/DISPLAY'; 3 | import { Shaders } from '../shaders'; 4 | import { TRIANGLE_STRIP_QUAD } from '@fms-cat/experimental'; 5 | 6 | const vboQuad = DISPLAY.glCat.createBuffer(); 7 | vboQuad.setVertexbuffer( new Float32Array( TRIANGLE_STRIP_QUAD ) ); 8 | 9 | export async function doQuadRender( options: { 10 | width: number; 11 | height: number; 12 | frag: string; 13 | target: GLCatFramebuffer; 14 | uniforms?: Array<{ name: string; type: GLCatProgramUniformType; value: number[] }>; 15 | uniformTextures?: Array<{ name: string; texture: GLCatTexture }>; 16 | range?: [ number, number, number, number ]; 17 | } ): Promise { 18 | const { gl, glCat } = DISPLAY; 19 | 20 | const program = await glCat.lazyProgramAsync( 21 | Shaders.quadVert, 22 | options.frag 23 | ); 24 | 25 | const range = options.range || [ -1.0, -1.0, 1.0, 1.0 ]; 26 | 27 | glCat.useProgram( program ); 28 | 29 | gl.bindFramebuffer( gl.FRAMEBUFFER, options.target.raw ); 30 | glCat.drawBuffers( [ gl.COLOR_ATTACHMENT0 ] ); 31 | gl.viewport( 0, 0, options.width, options.height ); 32 | 33 | program.attribute( 'p', vboQuad, 2 ); 34 | program.uniform2f( 'resolution', options.width, options.height ); 35 | program.uniform4f( 'range', ...range ); 36 | 37 | options.uniforms?.forEach( ( uniform ) => { 38 | program.uniform( uniform.name, uniform.type, ...uniform.value ); 39 | } ); 40 | 41 | options.uniformTextures?.forEach( ( uniform ) => { 42 | program.uniformTexture( uniform.name, uniform.texture ); 43 | } ); 44 | 45 | gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 ); 46 | 47 | glCat.useProgram( null ); 48 | program.dispose(); 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/edt.ts: -------------------------------------------------------------------------------- 1 | // yoinked from https://github.com/mapbox/tiny-sdf (BSD 2-Clause) 2 | // implements http://people.cs.uchicago.edu/~pff/papers/dt.pdf 3 | 4 | const HUGE = 1E20; 5 | 6 | export function edt1d( 7 | data: Float32Array, 8 | offset: number, 9 | stride: number, 10 | length: number 11 | ): void { 12 | // index of rightmost parabola in lower envelope 13 | let k = 0; 14 | 15 | // locations of parabolas in lower envelope 16 | const v = new Float32Array( length ); 17 | v[ 0 ] = 0.0; 18 | 19 | // locations of boundaries between parabolas 20 | const z = new Float32Array( length + 1 ); 21 | z[ 0 ] = -HUGE; 22 | z[ 1 ] = HUGE; 23 | 24 | // create a straight array of input data 25 | const f = new Float32Array( length ); 26 | for ( let q = 0; q < length; q ++ ) { 27 | f[ q ] = data[ offset + q * stride ]; 28 | } 29 | 30 | // compute lower envelope 31 | for ( let q = 1; q < length; q ++ ) { 32 | let s = 0.0; 33 | 34 | while ( 0 <= k ) { 35 | s = ( f[ q ] + q * q - f[ v[ k ] ] - v[ k ] * v[ k ] ) / ( 2.0 * q - 2.0 * v[ k ] ); 36 | if ( s <= z[ k ] ) { 37 | k --; 38 | } else { 39 | break; 40 | } 41 | } 42 | 43 | k ++; 44 | v[ k ] = q; 45 | z[ k ] = s; 46 | z[ k + 1 ] = HUGE; 47 | } 48 | 49 | k = 0; 50 | 51 | // fill in values of distance transform 52 | for ( let q = 0; q < length; q ++ ) { 53 | while ( z[ k + 1 ] < q ) { k ++; } 54 | const qSubVK = q - v[ k ]; 55 | data[ offset + q * stride ] = f[ v[ k ] ] + qSubVK * qSubVK; 56 | } 57 | } 58 | 59 | export function edt2d( 60 | data: Float32Array, 61 | width: number, 62 | height: number 63 | ): void { 64 | for ( let x = 0; x < width; x ++ ) { 65 | edt1d( data, x, width, height ); 66 | } 67 | 68 | for ( let y = 0; y < height; y ++ ) { 69 | edt1d( data, y * width, 1, width ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/utils/loadImage.ts: -------------------------------------------------------------------------------- 1 | export function loadImage( url: string ): Promise { 2 | return new Promise( ( resolve, reject ) => { 3 | const image = new Image(); 4 | image.onload = () => { 5 | resolve( image ); 6 | }; 7 | image.onerror = ( event ) => { 8 | reject( event ); 9 | }; 10 | image.src = url; 11 | } ); 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/loadImageTexture.ts: -------------------------------------------------------------------------------- 1 | import { GLCatTexture } from '@fms-cat/glcat-ts'; 2 | import { loadImage } from './loadImage'; 3 | 4 | export async function loadImageTexture( options: { 5 | url: string; 6 | texture: GLCatTexture; 7 | } ): Promise { 8 | const { url, texture } = options; 9 | const image = await loadImage( url ); 10 | texture.setTexture( image ); 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/matchAll.ts: -------------------------------------------------------------------------------- 1 | export function matchAll( str: string, regex: RegExp ): RegExpExecArray[] { 2 | const matches: RegExpExecArray[] = []; 3 | let match = regex.exec( str ); 4 | while ( match ) { 5 | matches.push( match ); 6 | match = regex.exec( str ); 7 | } 8 | return matches; 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/stringHash.ts: -------------------------------------------------------------------------------- 1 | // https://developer.mozilla.org/ja/docs/Web/API/SubtleCrypto/digest 2 | 3 | export async function stringHash( value: string ): Promise { 4 | const encoded = new TextEncoder().encode( value ); 5 | const hashBuffer = await crypto.subtle.digest( 'SHA-256', encoded ); 6 | const hashArray = Array.from( new Uint8Array( hashBuffer ) ); 7 | const hashHex = hashArray.map( ( b ) => b.toString( 16 ).padStart( 2, '0' ) ).join( '' ); 8 | return hashHex; 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "moduleResolution": "node", 7 | "sourceMap": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "resolveJsonModule": true, 11 | "removeComments": false, 12 | "noUnusedLocals": true, 13 | "lib": [ "dom", "es2017" ], 14 | "rootDir": "./src", 15 | "outDir": "./dist", 16 | "declaration": true, 17 | "declarationDir": "./types" 18 | }, 19 | "include": [ "src" ], 20 | "exclude": [ "node_modules" ] 21 | } 22 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const path = require( 'path' ); 4 | 5 | const ForkTsCheckerWebpackPlugin = require( 'fork-ts-checker-webpack-plugin' ); 6 | const HtmlWebpackPlugin = require( 'html-webpack-plugin' ); 7 | const webpack = require( 'webpack' ); 8 | 9 | module.exports = ( env, argv ) => { 10 | const DEBUG = argv.mode === 'development'; 11 | 12 | return { 13 | entry: path.resolve( __dirname, 'src/main.ts' ), 14 | output: { 15 | path: path.resolve( __dirname, 'dist' ), 16 | filename: 'bundle.js' 17 | }, 18 | module: { 19 | rules: [ 20 | { test: /\.(png|jpg|gif|ttf|otf)$/, use: 'url-loader' }, 21 | { test: /\.(sass|scss|css)$/, use: [ 'style-loader', 'css-loader' ] }, 22 | { 23 | test: /\.(sass|scss)$/, 24 | use: { 25 | loader: 'sass-loader', 26 | options: { 27 | implementation: require( 'sass' ) 28 | } 29 | }, 30 | }, 31 | { test: /\.(glsl|frag|vert)$/, use: [ 'raw-loader', 'glslify-loader' ] }, 32 | { 33 | test: /\.ts$/, 34 | exclude: /node_modules/, 35 | use: [ 36 | { 37 | loader: 'ts-loader', 38 | options: { happyPackMode: true, transpileOnly: true } 39 | } 40 | ] 41 | }, 42 | ] 43 | }, 44 | resolve: { 45 | modules: [ 'node_modules' ], 46 | extensions: [ '.ts', '.js' ], 47 | }, 48 | optimization: { 49 | minimize: !DEBUG 50 | }, 51 | devServer: { 52 | inline: true, 53 | hot: true 54 | }, 55 | devtool: DEBUG ? 'inline-source-map' : false, 56 | plugins: [ 57 | new webpack.DefinePlugin( { 58 | 'process.env': { DEBUG: DEBUG }, 59 | } ), 60 | new HtmlWebpackPlugin( { 61 | template: './src/html/index.html' 62 | } ), 63 | ...( DEBUG ? [ 64 | new webpack.NamedModulesPlugin(), 65 | new ForkTsCheckerWebpackPlugin( { checkSyntacticErrors: true } ), 66 | ] : [] ), 67 | ], 68 | devtool: DEBUG ? 'inline-source-map' : false 69 | }; 70 | }; 71 | --------------------------------------------------------------------------------