├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── browserslist ├── dist ├── bundle.js ├── bundle.prod.js ├── index.html ├── index.prod.html └── out.png.html ├── package.json ├── pnginator.rb ├── src ├── audio.js ├── automaton.fuckyou.js ├── automaton.json ├── config.json ├── geoms │ ├── circle.js │ ├── cube.js │ └── octahedron.js ├── html │ └── index.html ├── images │ └── char5x5.png ├── libs │ ├── glcat-path-gui.js │ ├── glcat-path.js │ ├── glcat.js │ ├── mathcat.js │ ├── ultracat.js │ └── xorshift.js ├── main.js ├── paths │ ├── bloom.js │ ├── box.js │ ├── circle.js │ ├── distance.js │ ├── dof.js │ ├── log.js │ ├── particles.js │ ├── patterns.js │ ├── postfx.js │ ├── racer.js │ ├── raymarch.js │ ├── render.js │ ├── trails.js │ ├── ui.js │ └── very-plane.js ├── shaders │ ├── -common.glsl │ ├── audio.frag │ ├── bg.frag │ ├── bloom-post.frag │ ├── bloom-pre.frag │ ├── box.frag │ ├── box.vert │ ├── circle.frag │ ├── circle.vert │ ├── distance.frag │ ├── dof.frag │ ├── fxaa.frag │ ├── g-buffer.frag │ ├── gauss.frag │ ├── glitch.frag │ ├── inspector.frag │ ├── log-compute.frag │ ├── log-render.frag │ ├── log-render.vert │ ├── object-with-uv.vert │ ├── object.vert │ ├── particles-compute.frag │ ├── particles-render.frag │ ├── particles-render.vert │ ├── patterns-compute.frag │ ├── patterns-render.frag │ ├── patterns-render.vert │ ├── post.frag │ ├── quad.vert │ ├── racer-compute.frag │ ├── racer-render.frag │ ├── racer-render.vert │ ├── raymarch.frag │ ├── render.frag │ ├── return.frag │ ├── shift.glsl │ ├── tone.frag │ ├── trails-compute.frag │ ├── trails-render.frag │ ├── trails-render.vert │ ├── ui.frag │ ├── ui.vert │ ├── uv.frag │ ├── very-plane.frag │ └── very-plane.vert └── styles │ └── main.scss ├── webpack.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "browser": true, 5 | "commonjs": true 6 | }, 7 | 8 | "parserOptions": { 9 | "sourceType": "module", 10 | "ecmaVersion": 2017 11 | }, 12 | 13 | "extends": [ 14 | "eslint:recommended" 15 | ], 16 | 17 | "rules": { 18 | // basics 19 | "indent": [ "error", 2, { // indentation should be 2 spaces 20 | "flatTernaryExpressions": true, // ternary should be performed in flat 21 | "MemberExpression": 0 // member chain should be performed in flat 22 | } ], // it forces 2 spaces indentation 23 | "linebreak-style": [ "error", "unix" ], // fuck you, CRLF 24 | "quotes": [ "error", "single" ], // quotes must be single 25 | "eqeqeq": [ "error", "always" ], // fuck you, `==` 26 | 27 | // variables 28 | "no-unused-vars": [ "off" ], // unused vars are okay 29 | "no-undef": [ "warn" ], // draws yellow line below undefined vars 30 | 31 | // omittables 32 | "semi": [ "error", "always" ], // semicolon is required 33 | "curly": [ "error" ], // it kills `if (foo) bar++;` 34 | "arrow-parens": [ "error", "always" ], // it kills `arg => { func(); }` 35 | 36 | // force spacing (I prefer super sparse code!) 37 | "array-bracket-spacing": [ "error", "always" ], // it kills `[val1, val2]` 38 | "arrow-spacing": [ "error", { "before": true, "after": true } ], // it kills `( arg )=>{ func(); }` 39 | "block-spacing": [ "error", "always" ], // it kills `if ( cond ) {func();}` 40 | "comma-spacing": [ "error", { "before": false, "after": true } ], // it kills `func( arg1,arg2 )` 41 | "computed-property-spacing": [ "error", "always" ], // it kills `arr[i]` 42 | "key-spacing": [ "error", { "beforeColon": false, "afterColon": true } ], // it kills `{ key:val }` 43 | "keyword-spacing": [ "error", { "before": true, "after": true } ], // it kills `}else{` 44 | "object-curly-spacing": [ "error", "always" ], // it kills `{key: val}` 45 | "semi-spacing": [ "error", { "before": false, "after": true } ], // it kills `func1();func2();` 46 | "space-before-blocks": [ "error", "always" ], // it kills `if (cond){` 47 | "space-in-parens": [ "error", "always" ], // it kills `func (arg)` 48 | "space-infix-ops": [ "error" ], // it kills val1+val2 49 | "space-unary-ops": [ "error", { "words": true, "nonwords": false, "overrides": { "++": true, "--": true } } ], // it kills `val++` 50 | "spaced-comment": [ "error", "always" ], // it kills `//this is comment` 51 | 52 | // ban spacing 53 | "func-call-spacing": [ "error", "never" ], // no-trailing-spaces. yea. 54 | "no-trailing-spaces": [ "error", { "ignoreComments": true } ], // no-trailing-spaces. yea. 55 | "no-whitespace-before-property": [ "error" ], // it kills `obj .key` 56 | "space-before-function-paren": [ "error", { "anonymous": "never", "named": "never", "asyncArrow": "always" } ], // it kills `func()` 57 | 58 | // others 59 | "no-eval": [ "warn" ], // wow, are you really going to use `eval()`? are you mad lol 60 | "no-implied-eval": [ "warn" ], // ok don't 61 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', // here is nodejs, console.log is innocent 62 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' // debuggerとか使う? 63 | } 64 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "go", 9 | "problemMatcher": [] 10 | }, 11 | { 12 | "type": "npm", 13 | "script": "build", 14 | "problemMatcher": [] 15 | }, 16 | { 17 | "type": "npm", 18 | "script": "h", 19 | "problemMatcher": [] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 FMS_Cat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Until 2 | 3 | "Until" by FMS_Cat 4 | 64KB WebGL Intro 5 | 2nd Place @ [Tokyo Demo Fest 2018](http://tokyodemofest.jp/2018/), Combined Demo Compo 6 | Heavily relied on [Automaton](https://github.com/FMS-Cat/automaton), my own animation engine 7 | 8 | ![](https://i.imgur.com/rvegbWA.jpg) 9 | 10 | ## Run 11 | 12 | - Production build (It's 64K HTML!) 13 | - [1920x1080](https://fms-cat.github.io/until/dist/out.png.html#1920x1080) 14 | - [1280x720](https://fms-cat.github.io/until/dist/out.png.html#1280x720) 15 | - [640x360](https://fms-cat.github.io/until/dist/out.png.html#640x360) 16 | 17 | - Development build (feat. Automaton GUI and Pass inspector!!) 18 | - [1920x1080](https://fms-cat.github.io/until/dist/index.html#1920x1080) 19 | - [1280x720](https://fms-cat.github.io/until/dist/index.html#1280x720) 20 | - [640x360](https://fms-cat.github.io/until/dist/index.html#640x360) 21 | 22 | ## Links 23 | 24 | - [🔦 Pouet, Prod page](https://www.pouet.net/prod.php?which=79365) 25 | - [🎥 YouTube, Captured stream](https://www.youtube.com/watch?v=XYFGjFPrLIk) 26 | - [💻 Source code (GitHub)](https://github.com/FMS-Cat/until) 27 | - [🐔 Twitter (@FMS_Cat), follow me!](https://twitter.com/FMS_Cat) 28 | 29 | ## License 30 | 31 | MIT -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | > 0.25% 2 | not dead -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webpack App 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dist/index.prod.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Webpack App 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dist/out.png.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/until/769ef1b6a1e952800c9a3ad041c8704419337dab/dist/out.png.html -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wenis", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "go": "webpack-dev-server --hot --mode development", 8 | "fuck": "webpack --mode development && webpack --mode production && ruby pnginator.rb ./dist/bundle.prod.js ./dist/out.png.html", 9 | "h": "http-server dist" 10 | }, 11 | "devDependencies": { 12 | "@fms-cat/automaton": "^2.0.4", 13 | "eslint": "^5.3.0", 14 | "html-webpack-plugin": "^3.2.0", 15 | "jszip": "^3.1.5", 16 | "raw-loader": "^0.5.1", 17 | "url-loader": "^1.0.1", 18 | "webpack": "^4.16.5", 19 | "webpack-cli": "^3.1.0", 20 | "webpack-dev-server": "^3.1.9" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pnginator.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby -w 2 | 3 | # pnginator.rb: pack a .js file into a PNG image with an HTML payload; 4 | # when saved with an .html extension and opened in a browser, the HTML extracts and executes 5 | # the javascript. 6 | 7 | # Usage: ruby pnginator.rb input.js output.png.html 8 | 9 | # By Gasman 10 | # from an original idea by Daeken: http://daeken.com/superpacking-js-demos 11 | 12 | 13 | MAX_WIDTH = 4096 14 | USE_PNGOUT = true 15 | 16 | require 'zlib' 17 | require 'tempfile' 18 | 19 | input_filename, output_filename = ARGV 20 | 21 | f = File.open(input_filename, 'rb') 22 | js = f.read 23 | f.close 24 | 25 | if js.length < MAX_WIDTH 26 | # js fits onto one pixel line 27 | js += "\x00" 28 | scanlines = [js] 29 | width = js.length 30 | height = 1 31 | 32 | # Daeken's single-pixel-row bootstrap (requires js string to be reversed) 33 | # (edit by Gasman: change eval to (1,eval) to force global evaluation and avoid massive slowdown) 34 | # html = "" 35 | 36 | # p01's single-pixel-row bootstrap (requires an 0x00 end marker on the js string) 37 | # (edit by Gasman: move drawImage out of getImageData params (it returns undef, which is invalid) and change eval to (1,eval) to force global evaluation) 38 | html = "" 39 | else 40 | js = "\x00" + js 41 | width = MAX_WIDTH 42 | # split js into scanlines of 'width' pixels; pad the last one with whitespace 43 | scanlines = js.scan(/.{1,#{width}}/m).collect{|line| line.ljust(width, "\x00")} 44 | height = scanlines.length 45 | 46 | # p01's multiple-pixel-row bootstrap (requires a dummy first byte on the js string) 47 | # (edit by Gasman: set explicit canvas width to support widths above 300; move drawImage out of getImageData params; change eval to (1,eval) to force global evaluation) 48 | html = "" 49 | end 50 | 51 | # prepend each scanline with 0x00 to indicate 'no filtering', then concat into one string 52 | image_data = scanlines.collect{|line| "\x00" + line}.join 53 | idat_chunk = Zlib::Deflate.deflate(image_data, 9) # 9 = maximum compression 54 | 55 | def png_chunk(signature, data) 56 | [data.length, signature, data, Zlib::crc32(signature + data)].pack("NA4A*N") 57 | end 58 | 59 | if USE_PNGOUT 60 | # Create a valid (no format hacks) .png file to pass to pngout 61 | f = Tempfile.open(['pnginator', '.png']) 62 | 63 | begin 64 | f.write("\x89PNG\x0d\x0a\x1a\x0a") # PNG file header 65 | f.write(png_chunk("IHDR", [width, height, 8, 0, 0, 0, 0].pack("NNccccc"))) 66 | f.write(png_chunk("IDAT", idat_chunk)) 67 | f.write(png_chunk("IEND", '')) 68 | f.close 69 | 70 | system("pngout", f.path, "-c0", "-y") 71 | 72 | # read file back and extract the IDAT chunk 73 | f.open 74 | f.read(8) 75 | while !f.eof? 76 | length, signature = f.read(8).unpack("NA4") 77 | data = f.read(length) 78 | crc = f.read(4) 79 | 80 | if signature == "IDAT" 81 | idat_chunk = data 82 | break 83 | end 84 | end 85 | ensure 86 | f.close 87 | f.unlink 88 | end 89 | end 90 | 91 | File.open(output_filename, 'wb') do |f| 92 | f.write("\x89PNG\x0d\x0a\x1a\x0a") # PNG file header 93 | 94 | f.write(png_chunk("IHDR", [width, height, 8, 0, 0, 0, 0].pack("NNccccc"))) 95 | 96 | # a custom chunk containing the HTML payload; stated chunk length is 4 less than the actual length, 97 | # leaving the final 4 bytes to take the place of the checksum 98 | f.write([html.length - 4, "jawh", html].pack("NA4A*")) 99 | 100 | # can safely omit the checksum of the IDAT chunk 101 | # f.write([idat_chunk.length, "IDAT", idat_chunk, Zlib::crc32("IDAT" + idat_chunk)].pack("NA4A*N")) 102 | f.write([idat_chunk.length, "IDAT", idat_chunk].pack("NA4A*")) 103 | 104 | # can safely omit the IEND chunk 105 | # f.write([0, "IEND", "", Zlib::crc32("IEND")].pack("NA4A*N")) 106 | end 107 | -------------------------------------------------------------------------------- /src/audio.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from './libs/ultracat'; 2 | 3 | export const Audio = class { 4 | constructor( context ) { 5 | // == certain variables ==================================================== 6 | this.audio = new AudioContext(); 7 | this.sampleRate = this.audio.sampleRate; 8 | 9 | this.bufferSize = 2048; 10 | this.processor = this.audio.createScriptProcessor( this.bufferSize, 2, 2 ); 11 | this.processor.onaudioprocess = ( event ) => this.onprocess( event ); 12 | this.bufferPoolSize = 2048; 13 | this.bufferPoolIndex = 0; 14 | this.bufferPool = new Float32Array( 4 * this.bufferSize * this.bufferPoolSize ); 15 | this.analyserData = new Float32Array( 2048 ); 16 | this.analyser = this.audio.createAnalyser(); 17 | this.analyser.fftSize = 2048; 18 | this.processor.connect( this.audio.destination ); 19 | this.processor.connect( this.analyser ); 20 | 21 | this.isPlaying = false; 22 | this.currentTime = 0; 23 | this.currentTimeDate = Date.now(); 24 | 25 | // == setup gl stuff ======================================================= 26 | const glCatPath = this.glCatPath = context.glCatPath; 27 | const glCat = this.glCat = glCatPath.glCat; 28 | const gl = this.gl = glCat.gl; 29 | 30 | const vboQuad = glCat.createVertexbuffer( 31 | new Float32Array( UltraCat.triangleStripQuad ) 32 | ); 33 | 34 | const textureChord = glCat.createTexture(); 35 | glCat.setTextureFromFloatArray( 36 | textureChord, 37 | 8, 38 | 4, 39 | [ 40 | 0, 5, 10, 12, 7, 19, 17, 22, 41 | 0, 7, 4, 14, 11, 19, 16, 26, 42 | 0, 2, 5, 12, 7, 14, 10, 22, 43 | 0, 7, 4, 19, 11, 26, 16, 31, 44 | ], 45 | gl.LUMINANCE 46 | ); 47 | glCat.textureWrap( textureChord, gl.REPEAT ); 48 | 49 | // == path definition begin ================================================ 50 | glCatPath.add( { 51 | audio: { 52 | width: this.bufferSize, 53 | height: this.bufferPoolSize, 54 | vert: require( './shaders/quad.vert' ), 55 | frag: require( './shaders/audio.frag' ), 56 | blend: [ gl.ONE, gl.ZERO ], 57 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 58 | framebuffer: true, 59 | float: true, 60 | func: ( path, params ) => { 61 | glCat.attribute( 'p', vboQuad, 2 ); 62 | glCat.uniform1f( 'timeHead', params.time % ( 16.0 / 160.0 * 60.0 ) ); 63 | glCat.uniform1f( 'patternHead', Math.floor( params.time / ( 16.0 / 160.0 * 60.0 ) ) ); 64 | glCat.uniform1f( 'bufferSize', this.bufferSize ); 65 | glCat.uniform1f( 'sampleRate', this.sampleRate ); 66 | glCat.uniformTexture( 'samplerChord', textureChord, 0 ); 67 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 68 | } 69 | } 70 | } ); 71 | 72 | if ( module.hot ) { 73 | module.hot.accept( 74 | [ 75 | './shaders/quad.vert', 76 | './shaders/audio.frag' 77 | ], 78 | () => { 79 | glCatPath.replaceProgram( 80 | 'audio', 81 | require( './shaders/quad.vert' ), 82 | require( './shaders/audio.frag' ) 83 | ); 84 | } 85 | ); 86 | } 87 | } 88 | 89 | play() { 90 | this.audio.resume && this.audio.resume(); // against "no do not generate any sound until your user interacts with gui stuff" policy 91 | this.isPlaying = true; 92 | this.currentTimeDate = Date.now(); 93 | this.processor.connect( this.audio.destination ); 94 | this.processor.connect( this.analyser ); 95 | } 96 | 97 | pause() { 98 | this.isPlaying = false; 99 | this.processor.disconnect( this.audio.destination ); 100 | this.processor.disconnect( this.analyser ); 101 | } 102 | 103 | setTime( time ) { 104 | this.currentTime = time; 105 | this.currentTimeDate = Date.now(); 106 | this.bufferPoolIndex = 0; 107 | } 108 | 109 | getTime() { 110 | let time = this.currentTime; 111 | if ( this.isPlaying ) { 112 | time += ( Date.now() - this.currentTimeDate ) / 1000.0; 113 | } 114 | return time; 115 | } 116 | 117 | getAnalyserData() { 118 | this.analyser.getFloatFrequencyData( this.analyserData ); 119 | return this.analyserData; 120 | } 121 | 122 | onprocess( event ) { 123 | const outL = event.outputBuffer.getChannelData( 0 ); 124 | const outR = event.outputBuffer.getChannelData( 1 ); 125 | 126 | const glCatPath = this.glCatPath; 127 | const gl = this.gl; 128 | 129 | this.currentTime += this.bufferSize / this.sampleRate; 130 | this.currentTimeDate = Date.now(); 131 | 132 | if ( this.bufferPoolIndex === 0 ) { 133 | glCatPath.renderOutsideOfPipeline( 'audio', { 134 | time: this.currentTime, 135 | } ); 136 | 137 | gl.readPixels( 138 | 0, // x 139 | 0, // y 140 | this.bufferSize, // width 141 | this.bufferPoolSize, // height 142 | gl.RGBA, // format 143 | gl.FLOAT, // type 144 | this.bufferPool // dst 145 | ); 146 | } 147 | 148 | for ( let i = 0; i < this.bufferSize; i ++ ) { 149 | outL[ i ] = this.bufferPool[ this.bufferPoolIndex + i * 4 ]; 150 | outR[ i ] = this.bufferPool[ this.bufferPoolIndex + i * 4 + 1 ]; 151 | } 152 | this.bufferPoolIndex = ( this.bufferPoolIndex + 4 * this.bufferSize ) % ( 4 * this.bufferSize * this.bufferPoolSize ); 153 | } 154 | }; -------------------------------------------------------------------------------- /src/automaton.fuckyou.js: -------------------------------------------------------------------------------- 1 | // Automaton v2.0.4 - MIT License 2 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Automaton = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i1?t-1:0),a=1;a { 2 | let props = Object.assign( { 3 | radius: 1.0, 4 | hole: 0.5, 5 | segments: 16 6 | }, _props ); 7 | 8 | let pos = new Float32Array( 6 * props.segments ); 9 | let uv = new Float32Array( 4 * props.segments ); 10 | let nor = new Float32Array( 6 * props.segments ); 11 | let ind = new Int16Array( 6 * props.segments ); 12 | 13 | let R = props.radius; 14 | let r = props.hole; 15 | let rpR = props.hole / props.radius; 16 | 17 | const dt = Math.PI * 2.0 / props.segments; 18 | for ( let i = 0; i < props.segments; i ++ ) { 19 | const t = i * dt; 20 | const cosT = Math.cos( t ); 21 | const sinT = Math.sin( t ); 22 | pos.set( [ 23 | cosT * r, sinT * r, 0.0, 24 | cosT * R, sinT * R, 0.0 25 | ], i * 6 ); 26 | uv.set( [ 27 | 0.5 + cosT * 0.5 * rpR, 0.5 + sinT * 0.5 * rpR, 28 | 0.5 + cosT * 0.5, 0.5 + sinT * 0.5 29 | ], i * 4 ); 30 | 31 | nor.set( [ 32 | 0.0, 0.0, 1.0, 33 | 0.0, 0.0, 1.0 34 | ], i * 6 ); 35 | 36 | const i00 = i * 2; 37 | const i01 = i00 + 1; 38 | const i10 = ( i === props.segments - 1 ) ? 0 : ( i * 2 + 2 ); 39 | const i11 = i10 + 1; 40 | ind.set( [ 41 | i00, i01, i11, 42 | i00, i11, i10 43 | ], i * 6 ); 44 | } 45 | 46 | return { 47 | position: pos, 48 | uv: uv, 49 | normal: nor, 50 | index: ind 51 | }; 52 | }; -------------------------------------------------------------------------------- /src/geoms/cube.js: -------------------------------------------------------------------------------- 1 | let genCube = ( _props ) => { 2 | let props = Object.assign( { 3 | size: 1.0 4 | }, _props ); 5 | 6 | let pos = []; 7 | let nor = []; 8 | let ind = []; 9 | 10 | const S = props.size; 11 | 12 | for ( let i = 0; i < 6; i ++ ) { 13 | let p = [ 14 | [ -S, -S, S ], 15 | [ S, -S, S ], 16 | [ -S, S, S ], 17 | [ S, S, S ] 18 | ]; 19 | let n = [ 20 | [ 0, 0, 1 ], 21 | [ 0, 0, 1 ], 22 | [ 0, 0, 1 ], 23 | [ 0, 0, 1 ] 24 | ]; 25 | let id = [ 26 | 0, 1, 3, 27 | 0, 3, 2 28 | ].map( ( v ) => v + i * 4 ); 29 | 30 | if ( i !== 0 ) { 31 | let func = ( v ) => { 32 | if ( i < 4 ) { 33 | let t = i * Math.PI / 2.0; 34 | let x = v[ 0 ]; 35 | let z = v[ 2 ]; 36 | v[ 0 ] = Math.cos( t ) * x - Math.sin( t ) * z; 37 | v[ 2 ] = Math.sin( t ) * x + Math.cos( t ) * z; 38 | } else { 39 | let t = ( i - 0.5 ) * Math.PI; 40 | let y = v[ 1 ]; 41 | let z = v[ 2 ]; 42 | v[ 1 ] = Math.cos( t ) * y - Math.sin( t ) * z; 43 | v[ 2 ] = Math.sin( t ) * y + Math.cos( t ) * z; 44 | } 45 | }; 46 | 47 | p.map( func ); 48 | n.map( func ); 49 | } 50 | 51 | p.map( ( v ) => pos.push( ...v ) ); 52 | n.map( ( v ) => nor.push( ...v ) ); 53 | ind.push( ...id ); 54 | } 55 | 56 | return { 57 | position: pos, 58 | normal: nor, 59 | index: ind 60 | }; 61 | }; 62 | 63 | module.exports = genCube; -------------------------------------------------------------------------------- /src/geoms/octahedron.js: -------------------------------------------------------------------------------- 1 | let genOctahedron = ( _props ) => { 2 | let props = Object.assign( { 3 | div: 1.0 4 | }, _props ); 5 | 6 | let div = parseInt( Math.max( 0, props.div ) ); 7 | 8 | let pos = []; 9 | let nor = []; 10 | let ind = []; 11 | 12 | for ( let ii = 0; ii < 2; ii ++ ) { 13 | for ( let iq = 0; iq < 4; iq ++ ) { 14 | for ( let iy = 0; iy < div + 1; iy ++ ) { 15 | for ( let ix = 0; ix < iy + 1; ix ++ ) { 16 | let lat0 = ( ii * 2.0 + iy / ( div + 1 ) ) * Math.PI / 2.0; 17 | let lat1 = ( ii * 2.0 + ( iy + 1 ) / ( div + 1 ) ) * Math.PI / 2.0; 18 | 19 | let lon0 = ( ii * 2.0 - 1.0 ) * ( ( ix - 1 ) / Math.max( 1, iy ) + iq ) * Math.PI / 2.0; 20 | let lon1 = ( ii * 2.0 - 1.0 ) * ( ix / ( iy + 1 ) + iq ) * Math.PI / 2.0; 21 | let lon2 = ( ii * 2.0 - 1.0 ) * ( ix / Math.max( 1, iy ) + iq ) * Math.PI / 2.0; 22 | let lon3 = ( ii * 2.0 - 1.0 ) * ( ( ix + 1 ) / ( iy + 1 ) + iq ) * Math.PI / 2.0; 23 | 24 | if ( ix !== 0 ) { 25 | ind.push( 26 | pos.length / 3, 27 | pos.length / 3 + 1, 28 | pos.length / 3 + 2 29 | ); 30 | 31 | let x1 = Math.sin( lat0 ) * Math.cos( lon0 ); 32 | let y1 = Math.cos( lat0 ); 33 | let z1 = Math.sin( lat0 ) * Math.sin( lon0 ); 34 | 35 | let x2 = Math.sin( lat1 ) * Math.cos( lon1 ); 36 | let y2 = Math.cos( lat1 ); 37 | let z2 = Math.sin( lat1 ) * Math.sin( lon1 ); 38 | 39 | let x3 = Math.sin( lat0 ) * Math.cos( lon2 ); 40 | let y3 = Math.cos( lat0 ); 41 | let z3 = Math.sin( lat0 ) * Math.sin( lon2 ); 42 | 43 | pos.push( 44 | x1, y1, z1, 45 | x2, y2, z2, 46 | x3, y3, z3 47 | ); 48 | 49 | { 50 | let x = x1 + x2 + x3; 51 | let y = y1 + y2 + y3; 52 | let z = z1 + z2 + z3; 53 | let l = Math.sqrt( x * x + y * y + z * z ); 54 | 55 | for ( let i = 0; i < 3; i ++ ) { 56 | nor.push( 57 | x / l, 58 | y / l, 59 | z / l 60 | ); 61 | } 62 | } 63 | } 64 | 65 | { 66 | ind.push( 67 | pos.length / 3, 68 | pos.length / 3 + 1, 69 | pos.length / 3 + 2 70 | ); 71 | 72 | let x1 = Math.sin( lat0 ) * Math.cos( lon2 ); 73 | let y1 = Math.cos( lat0 ); 74 | let z1 = Math.sin( lat0 ) * Math.sin( lon2 ); 75 | 76 | let x2 = Math.sin( lat1 ) * Math.cos( lon1 ); 77 | let y2 = Math.cos( lat1 ); 78 | let z2 = Math.sin( lat1 ) * Math.sin( lon1 ); 79 | 80 | let x3 = Math.sin( lat1 ) * Math.cos( lon3 ); 81 | let y3 = Math.cos( lat1 ); 82 | let z3 = Math.sin( lat1 ) * Math.sin( lon3 ); 83 | 84 | pos.push( 85 | x1, y1, z1, 86 | x2, y2, z2, 87 | x3, y3, z3 88 | ); 89 | 90 | { 91 | let x = x1 + x2 + x3; 92 | let y = y1 + y2 + y3; 93 | let z = z1 + z2 + z3; 94 | let l = Math.sqrt( x * x + y * y + z * z ); 95 | 96 | for ( let i = 0; i < 3; i ++ ) { 97 | nor.push( 98 | x / l, 99 | y / l, 100 | z / l 101 | ); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | return { 111 | position: pos, 112 | normal: nor, 113 | index: ind 114 | }; 115 | }; 116 | 117 | module.exports = genOctahedron; -------------------------------------------------------------------------------- /src/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 | Active
11 | Save 12 |
13 |
14 |
15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /src/images/char5x5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fms-cat/until/769ef1b6a1e952800c9a3ad041c8704419337dab/src/images/char5x5.png -------------------------------------------------------------------------------- /src/libs/glcat-path-gui.js: -------------------------------------------------------------------------------- 1 | import Path from './glcat-path'; 2 | 3 | let requiredFields = ( object, nanithefuck, fields ) => { 4 | fields.map( ( field ) => { 5 | if ( typeof object[ field ] === 'undefined' ) { 6 | throw 'GLCat-Path: ' + field + ' is required for ' + nanithefuck; 7 | } 8 | } ); 9 | }; 10 | 11 | let PathGUI = class extends Path { 12 | constructor( glCat, params ) { 13 | super( glCat, params ); 14 | let it = this; 15 | 16 | requiredFields( params, 'params', [ 17 | 'canvas', 18 | 'el' 19 | ] ); 20 | 21 | it.gui = { parent: it.params.el }; 22 | 23 | it.gui.info = document.createElement( 'span' ); 24 | it.gui.parent.appendChild( it.gui.info ); 25 | 26 | it.gui.range = document.createElement( 'input' ); 27 | it.gui.range.type = 'range'; 28 | it.gui.range.min = 0; 29 | it.gui.range.max = 0; 30 | it.gui.range.step = 1; 31 | it.gui.parent.appendChild( it.gui.range ); 32 | 33 | it.dateList = new Array( 30 ).fill( 0 ); 34 | it.dateListIndex = 0; 35 | it.totalFrames = 0; 36 | it.fps = 0; 37 | it.currentIndex = 0; 38 | it.viewName = ''; 39 | it.viewIndex = 0; 40 | 41 | let gl = glCat.gl; 42 | let vboQuad = glCat.createVertexbuffer( new Float32Array( [ -1, -1, 1, -1, -1, 1, 1, 1 ] ) ); 43 | it.add( { 44 | __PathGuiReturn: { 45 | width: it.params.canvas.width, 46 | height: it.params.canvas.height, 47 | vert: 'attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}', 48 | frag: 'precision highp float;uniform vec2 r;uniform sampler2D s;void main(){gl_FragColor=texture2D(s,gl_FragCoord.xy/r);}', 49 | blend: [ gl.ONE, gl.ONE ], 50 | clear: [ 0.0, 0.0, 0.0, 1.0 ], 51 | func: ( _p, params ) => { 52 | gl.viewport( 0, 0, it.params.canvas.width, it.params.canvas.height ); 53 | glCat.uniform2fv( 'r', [ it.params.canvas.width, it.params.canvas.height ] ); 54 | 55 | glCat.attribute( 'p', vboQuad, 2 ); 56 | glCat.uniformTexture( 's', params.input, 0 ); 57 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 58 | } 59 | }, 60 | } ); 61 | } 62 | 63 | begin() { 64 | let it = this; 65 | 66 | it.currentIndex = 0; 67 | } 68 | 69 | end() { 70 | let it = this; 71 | 72 | it.gui.range.max = Math.max( it.gui.range.max, it.currentIndex ); 73 | it.currentIndex = 0; 74 | 75 | let now = +new Date() * 1E-3; 76 | it.dateList[ it.dateListIndex ] = now; 77 | it.dateListIndex = ( it.dateListIndex + 1 ) % it.dateList.length; 78 | it.fps = ( 79 | ( it.dateList.length - 1 ) 80 | / ( now - it.dateList[ it.dateListIndex ] ) 81 | ).toFixed( 1 ); 82 | 83 | it.totalFrames ++; 84 | 85 | it.gui.info.innerText = ( 86 | 'Path: ' + it.viewName + ' (' + it.viewIndex + ')\n' 87 | + it.fps + ' FPS\n' 88 | + it.totalFrames + ' frames\n' 89 | ); 90 | } 91 | 92 | render( name, params ) { 93 | let it = this; 94 | 95 | it.currentIndex ++; 96 | let view = parseInt( it.gui.range.value ); 97 | 98 | if ( it.currentIndex <= view || view === 0 ) { 99 | it.viewName = view === 0 ? '*Full*' : name; 100 | it.viewIndex = it.currentIndex; 101 | 102 | super.render( name, params ); 103 | 104 | if ( it.currentIndex === view ) { 105 | let t = ( 106 | ( params && params.target ) 107 | ? params.target 108 | : it.paths[ name ].framebuffer 109 | ); 110 | 111 | if ( t && t.framebuffer ) { 112 | let i = t.textures ? t.textures[ 0 ] : t.texture; 113 | if ( it.params.stretch ) { 114 | super.render( '__PathGuiReturn', { 115 | target: PathGUI.nullFb, 116 | input: i, 117 | width: it.params.canvas.width, 118 | height: it.params.canvas.height 119 | } ); 120 | } else { 121 | it.params.canvas.width = ( params ? params.width : 0 ) || it.paths[ name ].width || it.params.width; 122 | it.params.canvas.height = ( params ? params.height : 0 ) || it.paths[ name ].height || it.params.height; 123 | super.render( '__PathGuiReturn', { 124 | target: PathGUI.nullFb, 125 | input: i 126 | } ); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | renderOutsideOfPipeline( name, params ) { 134 | super.render( name, params ); 135 | } 136 | }; 137 | 138 | export default PathGUI; -------------------------------------------------------------------------------- /src/libs/glcat-path.js: -------------------------------------------------------------------------------- 1 | let requiredFields = ( object, nanithefuck, fields ) => { 2 | fields.map( ( field ) => { 3 | if ( typeof object[ field ] === 'undefined' ) { 4 | throw 'GLCat-Path: ' + field + ' is required for ' + nanithefuck; 5 | } 6 | } ); 7 | }; 8 | 9 | let Path = class { 10 | constructor( glCat, params ) { 11 | let it = this; 12 | 13 | it.glCat = glCat; 14 | it.gl = glCat.gl; 15 | 16 | it.paths = {}; 17 | it.commonShader = ''; 18 | it.globalFunc = () => {}; 19 | it.params = params || {}; 20 | } 21 | 22 | add( paths ) { 23 | let it = this; 24 | 25 | for ( let name in paths ) { 26 | let path = paths[ name ]; 27 | requiredFields( path, 'path object', [ 28 | 'vert', 29 | 'frag' 30 | ] ); 31 | it.paths[ name ] = path; 32 | 33 | if ( typeof path.depthTest === 'undefined' ) { path.depthTest = true; } 34 | if ( typeof path.depthWrite === 'undefined' ) { path.depthWrite = true; } 35 | if ( typeof path.blend === 'undefined' ) { path.blend = [ it.gl.SRC_ALPHA, it.gl.ONE_MINUS_SRC_ALPHA ]; } 36 | if ( typeof path.cull === 'undefined' ) { path.cull = true; } 37 | 38 | if ( path.framebuffer ) { 39 | if ( path.drawbuffers ) { 40 | path.framebuffer = it.glCat.createDrawBuffers( path.width, path.height, path.drawbuffers ); 41 | } else if ( path.float ) { 42 | path.framebuffer = it.glCat.createFloatFramebuffer( path.width, path.height ); 43 | } else { 44 | path.framebuffer = it.glCat.createFramebuffer( path.width, path.height ); 45 | } 46 | 47 | if ( path.swapbuffer ) { 48 | if ( path.drawbuffers ) { 49 | path.swapbuffer = it.glCat.createDrawBuffers( path.width, path.height, path.drawbuffers ); 50 | } else if ( path.float ) { 51 | path.swapbuffer = it.glCat.createFloatFramebuffer( path.width, path.height ); 52 | } else { 53 | path.swapbuffer = it.glCat.createFramebuffer( path.width, path.height ); 54 | } 55 | } 56 | 57 | if ( path.filter ) { 58 | it.glCat.textureFilter( path.framebuffer.texture, path.filter ); 59 | } 60 | if ( path.wrap ) { 61 | it.glCat.textureWrap( path.framebuffer.texture, path.wrap ); 62 | } 63 | } 64 | 65 | path.program = it.glCat.createProgram( 66 | it.commonShader + path.vert, 67 | it.commonShader + path.frag 68 | ); 69 | } 70 | } 71 | 72 | render( name, params ) { 73 | let it = this; 74 | 75 | let path = it.paths[ name ]; 76 | if ( !path ) { throw 'GLCat-Path: The path called ' + name + ' is not defined!'; } 77 | 78 | if ( !params ) { params = {}; } 79 | 80 | if ( params.enable !== undefined && !params.enable && path.isInitialized ) { 81 | return; 82 | } 83 | path.isInitialized = true; 84 | 85 | params.framebuffer = typeof params.target !== 'undefined' ? params.target.framebuffer : path.framebuffer ? path.framebuffer.framebuffer : null; 86 | 87 | let width = params.width || path.width; 88 | let height = params.height || path.height; 89 | 90 | if ( !width || !height ) { 91 | throw 'GLCat-Path: width or height is invalid'; 92 | } 93 | 94 | it.gl.viewport( 0, 0, width, height ); 95 | it.glCat.useProgram( path.program ); 96 | path.cull ? it.gl.enable( it.gl.CULL_FACE ) : it.gl.disable( it.gl.CULL_FACE ); 97 | it.gl.bindFramebuffer( it.gl.FRAMEBUFFER, params.framebuffer ); 98 | if ( it.params.drawbuffers ) { 99 | it.glCat.drawBuffers( path.drawbuffers ? path.drawbuffers : params.framebuffer === null ? [ it.gl.BACK ] : [ it.gl.COLOR_ATTACHMENT0 ] ); 100 | } 101 | it.gl.blendFunc( ...path.blend ); 102 | if ( path.clear ) { it.glCat.clear( ...path.clear ); } 103 | path.depthTest ? it.gl.enable( it.gl.DEPTH_TEST ) : it.gl.disable( it.gl.DEPTH_TEST ); 104 | path.depthWrite ? it.gl.depthMask( true ) : it.gl.depthMask( false ); 105 | 106 | it.glCat.uniform2fv( 'resolution', [ width, height ] ); 107 | it.globalFunc( path, params ); 108 | 109 | if ( path.func ) { path.func( path, params ); } 110 | 111 | if ( path.swapbuffer ) { 112 | const temp = path.framebuffer; 113 | path.framebuffer = path.swapbuffer; 114 | path.swapbuffer = temp; 115 | } 116 | } 117 | 118 | renderOutsideOfPipeline( name, params ) { 119 | this.render( name, params ); 120 | } 121 | 122 | begin() {} 123 | end() {} 124 | 125 | replaceProgram( name, vert, frag ) { 126 | const path = this.paths[ name ]; 127 | if ( !path ) { throw 'GLCat-Path: The path called ' + name + ' is not defined!'; } 128 | 129 | try { 130 | const prevProgram = path.program; 131 | const newProgram = this.glCat.createProgram( 132 | this.commonShader + vert, 133 | this.commonShader + frag 134 | ); 135 | if ( newProgram ) { 136 | path.program = newProgram; 137 | this.gl.deleteProgram( prevProgram.program ); 138 | this.gl.deleteShader( prevProgram.vert ); 139 | this.gl.deleteShader( prevProgram.frag ); 140 | } 141 | } catch ( e ) { 142 | console.error( e ); 143 | } 144 | } 145 | 146 | resize( name, width, height ) { 147 | let it = this; 148 | 149 | let path = it.paths[ name ]; 150 | 151 | path.width = width; 152 | path.height = height; 153 | 154 | if ( path.framebuffer ) { 155 | if ( it.params.drawbuffers && path.drawbuffers ) { 156 | path.framebuffer = it.glCat.createDrawBuffers( path.width, path.height, path.drawbuffers ); 157 | } else if ( path.float ) { 158 | it.glCat.resizeFloatFramebuffer( path.framebuffer, path.width, path.height ); 159 | } else { 160 | it.glCat.resizeFramebuffer( path.framebuffer, path.width, path.height ); 161 | } 162 | } 163 | 164 | if ( typeof path.onresize === 'function' ) { 165 | path.onresize( path, width, height ); 166 | } 167 | } 168 | 169 | setGlobalFunc( func ) { this.globalFunc = func; } 170 | 171 | fb( name ) { 172 | if ( !this.paths[ name ] ) { throw 'glcat-path.fb: path called ' + name + ' is not defined'; } 173 | if ( !this.paths[ name ].framebuffer ) { throw 'glcat-path.fb: there is no framebuffer for the path ' + name; } 174 | 175 | return this.paths[ name ].framebuffer; 176 | } 177 | }; 178 | 179 | Path.nullFb = { framebuffer: null }; 180 | 181 | export default Path; -------------------------------------------------------------------------------- /src/libs/mathcat.js: -------------------------------------------------------------------------------- 1 | // にゃーん 2 | 3 | const MathCat = {}; 4 | 5 | /** 6 | * Add two vecs. 7 | * @param {number[]} a vecN 8 | * @param {number[]} b vecN 9 | * @returns {number[]} vecN, `a + b` 10 | * @static 11 | */ 12 | MathCat.vecAdd = ( a, b ) => a.map( ( e, i ) => e + b[ i ] ); 13 | 14 | /** 15 | * Substract a vec from an another vec. 16 | * @param {number[]} a vecN 17 | * @param {number[]} b vecN 18 | * @returns {number[]} vecN, `a - b` 19 | * @static 20 | */ 21 | MathCat.vecSub = ( a, b ) => a.map( ( e, i ) => e - b[ i ] ); 22 | 23 | 24 | /** 25 | * Multiply two vecs. 26 | * @param {number[]} a vecN 27 | * @param {number[]} b vecN 28 | * @returns {number[]} vecN, `a * b` 29 | * @static 30 | */ 31 | MathCat.vecMul = ( a, b ) => a.map( ( e, i ) => e - b[ i ] ); 32 | 33 | /** 34 | * Return a cross of two vec3s. 35 | * @param {number[]} a vec3 36 | * @param {number[]} b vec3 37 | * @returns {number[]} vec3, cross product of `a` and `b` 38 | * @static 39 | */ 40 | MathCat.vec3Cross = ( a, b ) => [ 41 | a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], 42 | a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], 43 | a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] 44 | ]; 45 | 46 | /** 47 | * Scale a vec by scalar. 48 | * @param {number} s scalar 49 | * @param {number[]} v vecN 50 | * @returns {number[]} vecN, `s * v` 51 | * @static 52 | */ 53 | MathCat.vecScale = ( s, v ) => v.map( ( e ) => e * s ); 54 | 55 | /** 56 | * Dot two vectors. 57 | * @param {number[]} a vecN 58 | * @param {number[]} b vecN 59 | * @returns {number[]} vecN, Dot of `a` and `b` 60 | * @static 61 | */ 62 | MathCat.vecDot = ( a, b ) => a.reduce( ( p, e, i ) => p + e * b[ i ], 0.0 ); 63 | 64 | /** 65 | * Return length of a vec. 66 | * @param {number[]} v vecN 67 | * @returns {number} scalar, length of `v` 68 | * @static 69 | */ 70 | MathCat.vecLength = ( v ) => Math.sqrt( v.reduce( ( p, c ) => p + c * c, 0.0 ) ); 71 | 72 | /** 73 | * Normalize a vec. 74 | * @param {number[]} v vecN 75 | * @returns {number[]} vec, normalized `v` 76 | * @static 77 | */ 78 | MathCat.vecNormalize = ( v ) => MathCat.vecScale( 1.0 / MathCat.vecLength( v ), v ); 79 | 80 | /** 81 | * Multiply two quats. 82 | * @param {number[]} q quat 83 | * @param {number[]} r quat 84 | * @returns {number[]} quat, product of `a` and `b` 85 | * @static 86 | */ 87 | MathCat.quatMul = ( q, r ) => [ 88 | q[ 3 ] * r[ 0 ] + q[ 0 ] * r[ 3 ] + q[ 1 ] * r[ 2 ] - q[ 2 ] * r[ 1 ], 89 | q[ 3 ] * r[ 1 ] - q[ 0 ] * r[ 2 ] + q[ 1 ] * r[ 3 ] + q[ 2 ] * r[ 0 ], 90 | q[ 3 ] * r[ 2 ] + q[ 0 ] * r[ 1 ] - q[ 1 ] * r[ 0 ] + q[ 2 ] * r[ 3 ], 91 | q[ 3 ] * r[ 3 ] - q[ 0 ] * r[ 0 ] - q[ 1 ] * r[ 1 ] - q[ 2 ] * r[ 2 ] 92 | ]; 93 | 94 | /** 95 | * Inverse a quat. 96 | * @param {number[]} q quat 97 | * @returns {number[]} quat, `-q` 98 | * @static 99 | */ 100 | MathCat.quatInv = ( q ) => [ -q[ 0 ], -q[ 1 ], -q[ 2 ], q[ 3 ] ]; 101 | 102 | /** 103 | * Rotate a vec3 using one quat. 104 | * @param {number[]} v vec3 105 | * @param {number[]} q quat 106 | * @returns {number[]} vec3, rotated vector 107 | * @static 108 | */ 109 | MathCat.rotateVecByQuat = ( v, q ) => { 110 | const p = [ v[ 0 ], v[ 1 ], v[ 2 ], 0.0 ]; 111 | const r = MathCat.quatInv( q ); 112 | const res = MathCat.quatMul( MathCat.quatMul( q, p ), r ); 113 | return [ res[ 0 ], res[ 1 ], res[ 2 ] ]; 114 | }; 115 | 116 | /** 117 | * Convert quat into mat4. 118 | * @param {number[]} q quat 119 | * @returns {number[]} mat4, rotation matrix made from quat 120 | * @static 121 | */ 122 | MathCat.quatToMat4 = ( q ) => { 123 | const x = MathCat.rotateVecByQuat( [ 1.0, 0.0, 0.0 ], q ); 124 | const y = MathCat.rotateVecByQuat( [ 0.0, 1.0, 0.0 ], q ); 125 | const z = MathCat.rotateVecByQuat( [ 0.0, 0.0, 1.0 ], q ); 126 | 127 | return [ 128 | x[ 0 ], y[ 0 ], z[ 0 ], 0.0, 129 | x[ 1 ], y[ 1 ], z[ 1 ], 0.0, 130 | x[ 2 ], y[ 2 ], z[ 2 ], 0.0, 131 | 0.0, 0.0, 0.0, 1.0 132 | ]; 133 | }; 134 | 135 | /** 136 | * Generate quat from angle and axis. 137 | * @param {number} angle scalar, Rotation angle in radian 138 | * @param {number[]} axis vec3, Rotation axis 139 | * @returns {number[]} quat, Generated quaternion 140 | * @static 141 | */ 142 | MathCat.quatAngleAxis = ( angle, axis ) => { 143 | const ha = angle / 2.0; 144 | const sha = Math.sin( ha ); 145 | return [ 146 | axis[ 0 ] * sha, 147 | axis[ 1 ] * sha, 148 | axis[ 2 ] * sha, 149 | Math.cos( ha ) 150 | ]; 151 | }; 152 | 153 | /** 154 | * Apply two mat4s. 155 | * @param {number[]} a mat4 156 | * @param {number[]} b mat4 157 | * @returns {number[]} mat4, Applied matrix 158 | * @static 159 | */ 160 | MathCat.mat4Apply = ( ...mat ) => { 161 | const a = mat[ 0 ]; 162 | const b = mat[ 1 ]; 163 | 164 | if ( 3 < mat.length ) { 165 | const m = mat.slice( 2 ); 166 | return MathCat.mat4Apply( MathCat.mat4Apply( a, b ), ...m ); 167 | } 168 | 169 | return [ 170 | a[ 0 ] * b[ 0 ] + a[ 4 ] * b[ 1 ] + a[ 8 ] * b[ 2 ] + a[ 12 ] * b[ 3 ], 171 | a[ 1 ] * b[ 0 ] + a[ 5 ] * b[ 1 ] + a[ 9 ] * b[ 2 ] + a[ 13 ] * b[ 3 ], 172 | a[ 2 ] * b[ 0 ] + a[ 6 ] * b[ 1 ] + a[ 10 ] * b[ 2 ] + a[ 14 ] * b[ 3 ], 173 | a[ 3 ] * b[ 0 ] + a[ 7 ] * b[ 1 ] + a[ 11 ] * b[ 2 ] + a[ 15 ] * b[ 3 ], 174 | 175 | a[ 0 ] * b[ 4 ] + a[ 4 ] * b[ 5 ] + a[ 8 ] * b[ 6 ] + a[ 12 ] * b[ 7 ], 176 | a[ 1 ] * b[ 4 ] + a[ 5 ] * b[ 5 ] + a[ 9 ] * b[ 6 ] + a[ 13 ] * b[ 7 ], 177 | a[ 2 ] * b[ 4 ] + a[ 6 ] * b[ 5 ] + a[ 10 ] * b[ 6 ] + a[ 14 ] * b[ 7 ], 178 | a[ 3 ] * b[ 4 ] + a[ 7 ] * b[ 5 ] + a[ 11 ] * b[ 6 ] + a[ 15 ] * b[ 7 ], 179 | 180 | a[ 0 ] * b[ 8 ] + a[ 4 ] * b[ 9 ] + a[ 8 ] * b[ 10 ] + a[ 12 ] * b[ 11 ], 181 | a[ 1 ] * b[ 8 ] + a[ 5 ] * b[ 9 ] + a[ 9 ] * b[ 10 ] + a[ 13 ] * b[ 11 ], 182 | a[ 2 ] * b[ 8 ] + a[ 6 ] * b[ 9 ] + a[ 10 ] * b[ 10 ] + a[ 14 ] * b[ 11 ], 183 | a[ 3 ] * b[ 8 ] + a[ 7 ] * b[ 9 ] + a[ 11 ] * b[ 10 ] + a[ 15 ] * b[ 11 ], 184 | 185 | a[ 0 ] * b[ 12 ] + a[ 4 ] * b[ 13 ] + a[ 8 ] * b[ 14 ] + a[ 12 ] * b[ 15 ], 186 | a[ 1 ] * b[ 12 ] + a[ 5 ] * b[ 13 ] + a[ 9 ] * b[ 14 ] + a[ 13 ] * b[ 15 ], 187 | a[ 2 ] * b[ 12 ] + a[ 6 ] * b[ 13 ] + a[ 10 ] * b[ 14 ] + a[ 14 ] * b[ 15 ], 188 | a[ 3 ] * b[ 12 ] + a[ 7 ] * b[ 13 ] + a[ 11 ] * b[ 14 ] + a[ 15 ] * b[ 15 ] 189 | ]; 190 | }; 191 | 192 | /** 193 | * Invert a mat4. 194 | * @param {number[]} m mat4 195 | * @returns {number[]} mat4, Inverted matrix 196 | * @static 197 | */ 198 | MathCat.mat4Inverse = ( m ) => { 199 | const 200 | a00 = m[ 0 ], a01 = m[ 1 ], a02 = m[ 2 ], a03 = m[ 3 ], 201 | a10 = m[ 4 ], a11 = m[ 5 ], a12 = m[ 6 ], a13 = m[ 7 ], 202 | a20 = m[ 8 ], a21 = m[ 9 ], a22 = m[ 10 ], a23 = m[ 11 ], 203 | a30 = m[ 12 ], a31 = m[ 13 ], a32 = m[ 14 ], a33 = m[ 15 ], 204 | b00 = a00 * a11 - a01 * a10, b01 = a00 * a12 - a02 * a10, 205 | b02 = a00 * a13 - a03 * a10, b03 = a01 * a12 - a02 * a11, 206 | b04 = a01 * a13 - a03 * a11, b05 = a02 * a13 - a03 * a12, 207 | b06 = a20 * a31 - a21 * a30, b07 = a20 * a32 - a22 * a30, 208 | b08 = a20 * a33 - a23 * a30, b09 = a21 * a32 - a22 * a31, 209 | b10 = a21 * a33 - a23 * a31, b11 = a22 * a33 - a23 * a32; 210 | 211 | return MathCat.vecScale( 1.0 / b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06, [ 212 | a11 * b11 - a12 * b10 + a13 * b09, 213 | a02 * b10 - a01 * b11 - a03 * b09, 214 | a31 * b05 - a32 * b04 + a33 * b03, 215 | a22 * b04 - a21 * b05 - a23 * b03, 216 | a12 * b08 - a10 * b11 - a13 * b07, 217 | a00 * b11 - a02 * b08 + a03 * b07, 218 | a32 * b02 - a30 * b05 - a33 * b01, 219 | a20 * b05 - a22 * b02 + a23 * b01, 220 | a10 * b10 - a11 * b08 + a13 * b06, 221 | a01 * b08 - a00 * b10 - a03 * b06, 222 | a30 * b04 - a31 * b02 + a33 * b00, 223 | a21 * b02 - a20 * b04 - a23 * b00, 224 | a11 * b07 - a10 * b09 - a12 * b06, 225 | a00 * b09 - a01 * b07 + a02 * b06, 226 | a31 * b01 - a30 * b03 - a32 * b00, 227 | a20 * b03 - a21 * b01 + a22 * b00 228 | ] ); 229 | }; 230 | 231 | /** 232 | * Apply a mat4 to a vec4. 233 | * @param {number[]} m mat4 234 | * @param {number[]} v vec4 235 | * @returns {number[]} vec4, Applied vector 236 | * @static 237 | */ 238 | MathCat.mat4ApplyToVec4 = ( m, v ) => { 239 | return [ 240 | m[ 0 ] * v[ 0 ] + m[ 4 ] * v[ 1 ] + m[ 8 ] * v[ 2 ] + m[ 12 ] * v[ 3 ], 241 | m[ 1 ] * v[ 0 ] + m[ 5 ] * v[ 1 ] + m[ 9 ] * v[ 2 ] + m[ 13 ] * v[ 3 ], 242 | m[ 2 ] * v[ 0 ] + m[ 6 ] * v[ 1 ] + m[ 10 ] * v[ 2 ] + m[ 14 ] * v[ 3 ], 243 | m[ 3 ] * v[ 0 ] + m[ 7 ] * v[ 1 ] + m[ 11 ] * v[ 2 ] + m[ 15 ] * v[ 3 ] 244 | ]; 245 | }; 246 | 247 | /** 248 | * Transpose a mat4. 249 | * @param {number[]} m mat4 250 | * @returns {number[]} mat4, Transposed matrix 251 | * @static 252 | */ 253 | MathCat.mat4Transpose = ( m ) => [ 254 | m[ 0 ], m[ 4 ], m[ 8 ], m[ 12 ], 255 | m[ 1 ], m[ 5 ], m[ 9 ], m[ 13 ], 256 | m[ 2 ], m[ 6 ], m[ 10 ], m[ 14 ], 257 | m[ 3 ], m[ 7 ], m[ 11 ], m[ 15 ] 258 | ]; 259 | 260 | /** 261 | * Generate an indentity mat4. 262 | * @returns {number[]} mat4, Identity matrix 263 | * @static 264 | */ 265 | MathCat.mat4Identity = () => [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; 266 | 267 | /** 268 | * Generate a 3d translate matrix. 269 | * @param {number[]} v vec3, Translation 270 | * @returns {number[]} mat4, Generated matrix 271 | * @static 272 | */ 273 | MathCat.mat4Translate = ( v ) => [ 274 | 1, 0, 0, 0, 275 | 0, 1, 0, 0, 276 | 0, 0, 1, 0, 277 | v[ 0 ], v[ 1 ], v[ 2 ], 1 278 | ]; 279 | 280 | /** 281 | * Generate a 3d scale matrix. 282 | * See also: {@link MathCat#mat4ScaleXYZ} 283 | * @param {number[]} v vec3, Scaling 284 | * @returns {number[]} mat4, Generated matrix 285 | * @static 286 | */ 287 | MathCat.mat4Scale = ( v ) => [ 288 | v[ 0 ], 0, 0, 0, 289 | 0, v[ 1 ], 0, 0, 290 | 0, 0, v[ 2 ], 0, 291 | 0, 0, 0, 1 292 | ]; 293 | 294 | /** 295 | * Generate a 3d scale matrix. 296 | * See also: {@link MathCat#mat4Scale} 297 | * @param {number} s scalar, Scaling 298 | * @returns {number[]} mat4, Generated matrix 299 | * @static 300 | */ 301 | MathCat.mat4ScaleXYZ = ( s ) => [ 302 | s, 0, 0, 0, 303 | 0, s, 0, 0, 304 | 0, 0, s, 0, 305 | 0, 0, 0, 1 306 | ]; 307 | 308 | /** 309 | * Generate a 3d rotation matrix. 310 | * 2d rotation around x axis. 311 | * @param {number} t scalar, Rotation angle in radians 312 | * @returns {number[]} mat4, Generated matrix 313 | * @static 314 | */ 315 | MathCat.mat4RotateX = ( t ) => [ 316 | 1, 0, 0, 0, 317 | 0, Math.cos( t ), -Math.sin( t ), 0, 318 | 0, Math.sin( t ), Math.cos( t ), 0, 319 | 0, 0, 0, 1 320 | ]; 321 | 322 | /** 323 | * Generate a 3d rotation matrix. 324 | * 2d rotation around y axis. 325 | * @param {number} t scalar, Rotation angle in radians 326 | * @returns {number[]} mat4, Generated matrix 327 | * @static 328 | */ 329 | MathCat.mat4RotateY = ( t ) => [ 330 | Math.cos( t ), 0, Math.sin( t ), 0, 331 | 0, 1, 0, 0, 332 | -Math.sin( t ), 0, Math.cos( t ), 0, 333 | 0, 0, 0, 1 334 | ]; 335 | 336 | /** 337 | * Generate a 3d rotation matrix. 338 | * 2d rotation around z axis. 339 | * @param {number} t scalar, Rotation angle in radians 340 | * @returns {number[]} mat4, Generated matrix 341 | * @static 342 | */ 343 | MathCat.mat4RotateZ = ( t ) => [ 344 | Math.cos( t ), -Math.sin( t ), 0, 0, 345 | Math.sin( t ), Math.cos( t ), 0, 0, 346 | 0, 0, 1, 0, 347 | 0, 0, 0, 1 348 | ]; 349 | 350 | /** 351 | * Generate a "LookAt" view matrix. 352 | * @param {number[]} pos vec3, Position 353 | * @param {number[]} tar vec3, Target 354 | * @param {number[]} [air=[ 0.0, 1.0, 0.0 ]] vec3, Up vector 355 | * @param {number} [rot=0.0] scalar, Roll. yeahhhh I think such lookAt generator should have roll parameter 356 | * @returns {number[]} mat4, Generated matrix 357 | * @static 358 | */ 359 | MathCat.mat4LookAt = ( pos, tar, air, rot ) => { 360 | const dir = MathCat.vecNormalize( MathCat.vecSub( tar, pos ) ); 361 | let sid = MathCat.vecNormalize( MathCat.vec3Cross( dir, air || [ 0.0, 1.0, 0.0 ] ) ); 362 | let top = MathCat.vec3Cross( sid, dir ); 363 | sid = MathCat.vecAdd( 364 | MathCat.vecScale( Math.cos( rot || 0.0 ), sid ), 365 | MathCat.vecScale( Math.sin( rot || 0.0 ), top ) 366 | ); 367 | top = MathCat.vec3Cross( sid, dir ); 368 | 369 | return [ 370 | sid[ 0 ], top[ 0 ], dir[ 0 ], 0.0, 371 | sid[ 1 ], top[ 1 ], dir[ 1 ], 0.0, 372 | sid[ 2 ], top[ 2 ], dir[ 2 ], 0.0, 373 | -sid[ 0 ] * pos[ 0 ] - sid[ 1 ] * pos[ 1 ] - sid[ 2 ] * pos[ 2 ], 374 | -top[ 0 ] * pos[ 0 ] - top[ 1 ] * pos[ 1 ] - top[ 2 ] * pos[ 2 ], 375 | -dir[ 0 ] * pos[ 0 ] - dir[ 1 ] * pos[ 1 ] - dir[ 2 ] * pos[ 2 ], 376 | 1.0 377 | ]; 378 | }; 379 | 380 | /** 381 | * Generate a "Perspective" projection matrix. 382 | * It won't include aspect! 383 | * @param {number} fov scalar 384 | * @param {number} near scalar 385 | * @param {number} far scalar 386 | * @returns {number[]} mat4, Generated matrix 387 | * @static 388 | */ 389 | MathCat.mat4Perspective = ( fov, near, far ) => { 390 | const p = 1.0 / Math.tan( fov * Math.PI / 360.0 ); 391 | const d = ( far - near ); 392 | return [ 393 | p, 0.0, 0.0, 0.0, 394 | 0.0, p, 0.0, 0.0, 395 | 0.0, 0.0, ( far + near ) / d, 1.0, 396 | 0.0, 0.0, -2 * far * near / d, 0.0 397 | ]; 398 | }; 399 | 400 | export default MathCat; -------------------------------------------------------------------------------- /src/libs/ultracat.js: -------------------------------------------------------------------------------- 1 | // お前、ナンデモアリかよ! 2 | 3 | export const triangleStripQuad = [ -1, -1, 1, -1, -1, 1, 1, 1 ]; 4 | export const triangleStripQuad3 = [ -1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0 ]; 5 | export const triangleStripQuadNor = [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 ]; 6 | export const triangleStripQuadUV = [ 0, 0, 1, 0, 0, 1, 1, 1 ]; 7 | 8 | // destructive 9 | export const shuffleArrayD = ( array, dice ) => { 10 | const f = dice ? dice : () => Math.random(); 11 | for ( let i = 0; i < array.length - 1; i ++ ) { 12 | const ir = i + Math.floor( f() * ( array.length - i ) ); 13 | const temp = array[ ir ]; 14 | array[ ir ] = array[ i ]; 15 | array[ i ] = temp; 16 | } 17 | return array; 18 | }; 19 | 20 | export const triIndexToLineIndex = ( array ) => { 21 | let ret = []; 22 | for ( let i = 0; i < array.length / 3; i ++ ) { 23 | const head = i * 3; 24 | ret.push( 25 | array[ head ], array[ head + 1 ], 26 | array[ head + 1 ], array[ head + 2 ], 27 | array[ head + 2 ], array[ head ] 28 | ); 29 | } 30 | return ret; 31 | }; 32 | 33 | export const matrix1d = ( w ) => { 34 | let arr = []; 35 | for ( let ix = 0; ix < w; ix ++ ) { 36 | arr.push( ix ); 37 | } 38 | return arr; 39 | }; 40 | 41 | export const matrix2d = ( w, h ) => { 42 | let arr = []; 43 | for ( let iy = 0; iy < h; iy ++ ) { 44 | for ( let ix = 0; ix < w; ix ++ ) { 45 | arr.push( ix, iy ); 46 | } 47 | } 48 | return arr; 49 | }; 50 | 51 | export const matrix3d = ( w, h, d ) => { 52 | let arr = []; 53 | for ( let iz = 0; iz < d; iz ++ ) { 54 | for ( let iy = 0; iy < h; iy ++ ) { 55 | for ( let ix = 0; ix < w; ix ++ ) { 56 | arr.push( ix, iy, iz ); 57 | } 58 | } 59 | } 60 | return arr; 61 | }; 62 | 63 | export const lerp = ( a, b, x ) => a + ( b - a ) * x; 64 | export const clamp = ( x, l, h ) => Math.min( Math.max( x, l ), h ); 65 | export const saturate = ( x ) => Math.min( Math.max( x, 0.0 ), 1.0 ); 66 | export const linearstep = ( a, b, x ) => saturate( ( x - a ) / ( b - a ) ); 67 | export const smoothstep = ( a, b, x ) => { 68 | const t = linearstep( a, b, x ); 69 | return t * t * ( 3.0 - 2.0 * t ); 70 | }; 71 | 72 | export const ExpSmooth = class { 73 | constructor( factor ) { 74 | this.factor = factor; 75 | this.value = 0.0; 76 | } 77 | 78 | update( value, dt ) { 79 | this.value = lerp( value, this.value, Math.exp( -this.factor * dt ) ); 80 | return this.value; 81 | } 82 | }; -------------------------------------------------------------------------------- /src/libs/xorshift.js: -------------------------------------------------------------------------------- 1 | export const Xorshift = class { 2 | constructor( _seed ) { 3 | this.set( _seed ); 4 | } 5 | 6 | gen( _seed ) { 7 | if ( _seed ) { this.set( _seed ); } 8 | this.seed = this.seed ^ ( this.seed << 13 ); 9 | this.seed = this.seed ^ ( this.seed >>> 17 ); 10 | this.seed = this.seed ^ ( this.seed << 5 ); 11 | return this.seed / Math.pow( 2, 32 ) + 0.5; 12 | } 13 | 14 | genFloat32Array( _count ) { 15 | const arr = new Float32Array( _count ); 16 | for ( let i = 0; i < _count; i ++ ) { 17 | arr[ i ] = this.gen(); 18 | } 19 | return arr; 20 | } 21 | 22 | set( _seed ) { 23 | this.seed = _seed || this.seed || 1; 24 | } 25 | }; -------------------------------------------------------------------------------- /src/paths/bloom.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat'; 2 | 3 | // ------ 4 | 5 | export default ( context ) => { 6 | const glCatPath = context.glCatPath; 7 | const glCat = glCatPath.glCat; 8 | const gl = glCat.gl; 9 | 10 | const width = context.width; 11 | const height = context.height; 12 | 13 | // ------ 14 | 15 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 16 | 17 | // ------ 18 | 19 | glCatPath.add( { 20 | preBloom: { 21 | width: width / 4, 22 | height: height / 4, 23 | vert: require( '../shaders/quad.vert' ), 24 | frag: require( '../shaders/bloom-pre.frag' ), 25 | blend: [ gl.ONE, gl.ONE ], 26 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 27 | framebuffer: true, 28 | float: true, 29 | func: ( path, params ) => { 30 | glCat.attribute( 'p', vboQuad, 2 ); 31 | glCat.uniform3fv( 'bias', params.bias ); 32 | glCat.uniform3fv( 'factor', params.factor ); 33 | glCat.uniformTexture( 'sampler0', params.input, 0 ); 34 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 35 | } 36 | }, 37 | 38 | bloom: { 39 | width: width / 4, 40 | height: height / 4, 41 | vert: require( '../shaders/quad.vert' ), 42 | frag: require( '../shaders/gauss.frag' ), 43 | blend: [ gl.ONE, gl.ONE ], 44 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 45 | framebuffer: true, 46 | float: true, 47 | tempFb: glCat.createFloatFramebuffer( width / 4, height / 4 ), 48 | func: ( path, params ) => { 49 | glCat.attribute( 'p', vboQuad, 2 ); 50 | 51 | for ( let i = 0; i < 3; i ++ ) { 52 | let gaussVar = [ 0.003, 0.01, 0.05 ][ i ] * height; 53 | glCat.uniform1f( 'var', gaussVar ); 54 | 55 | gl.bindFramebuffer( gl.FRAMEBUFFER, path.tempFb.framebuffer ); 56 | glCat.clear( ...path.clear ); 57 | glCat.uniform1i( 'isVert', false ); 58 | glCat.uniformTexture( 59 | 'sampler0', 60 | i === 0 ? glCatPath.fb( 'preBloom' ).texture : path.framebuffer.texture, 61 | 0 62 | ); 63 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 64 | 65 | gl.bindFramebuffer( gl.FRAMEBUFFER, params.framebuffer ); 66 | glCat.uniform1i( 'isVert', true ); 67 | glCat.uniformTexture( 'sampler0', path.tempFb.texture, 0 ); 68 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 69 | } 70 | } 71 | }, 72 | 73 | postBloom: { 74 | width: width, 75 | height: height, 76 | vert: require( '../shaders/quad.vert' ), 77 | frag: require( '../shaders/bloom-post.frag' ), 78 | blend: [ gl.ONE, gl.ZERO ], 79 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 80 | framebuffer: true, 81 | float: true, 82 | func: ( path, params ) => { 83 | glCat.attribute( 'p', vboQuad, 2 ); 84 | glCat.uniformTexture( 'samplerDry', params.dry, 0 ); 85 | glCat.uniformTexture( 'samplerWet', glCatPath.fb( 'bloom' ).texture, 1 ); 86 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 87 | } 88 | }, 89 | } ); 90 | }; -------------------------------------------------------------------------------- /src/paths/box.js: -------------------------------------------------------------------------------- 1 | import MathCat from '../libs/mathcat'; 2 | import * as UltraCat from '../libs/ultracat'; 3 | import genCube from '../geoms/cube'; 4 | 5 | export default ( context ) => { 6 | // == hi context ============================================================= 7 | const glCatPath = context.glCatPath; 8 | const glCat = glCatPath.glCat; 9 | const gl = glCat.gl; 10 | 11 | const auto = context.automaton.auto; 12 | 13 | // == hi vbo ================================================================= 14 | const box = genCube(); 15 | const vboBoxPos = glCat.createVertexbuffer( new Float32Array( box.position ) ); 16 | const vboBoxNor = glCat.createVertexbuffer( new Float32Array( box.normal ) ); 17 | const iboBox = glCat.createIndexbuffer( new Int16Array( box.index ) ); 18 | 19 | const matrix = UltraCat.matrix2d( 11, 11 ).map( ( v, i ) => ( 20 | v / 5.0 - 1.0 21 | ) ); 22 | const vboMatrix = glCat.createVertexbuffer( new Float32Array( matrix ) ); 23 | 24 | // == path definition begin ================================================== 25 | glCatPath.add( { 26 | box: { 27 | vert: require( '../shaders/box.vert' ), 28 | frag: require( '../shaders/box.frag' ), 29 | blend: [ gl.ONE, gl.ZERO ], 30 | drawbuffers: 3, 31 | func: ( path, params ) => { 32 | glCat.attribute( 'aPos', vboBoxPos, 3 ); 33 | glCat.attribute( 'aNor', vboBoxNor, 3 ); 34 | glCat.attributeDivisor( 'aMatrix', vboMatrix, 2, 1 ); 35 | 36 | glCat.uniform1i( 'isShadow', params.isShadow ); 37 | 38 | glCat.uniform1f( 'size', auto( 'box-size' ) ); 39 | glCat.uniform1f( 'posOffset', auto( 'box-posOffset' ) ); 40 | glCat.uniform1f( 'spinOffset', auto( 'box-spinOffset' ) ); 41 | 42 | let matM = MathCat.mat4Identity(); 43 | glCat.uniformMatrix4fv( 'matM', matM ); 44 | 45 | let ext = glCat.getExtension( 'ANGLE_instanced_arrays' ); 46 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, iboBox ); 47 | ext.drawElementsInstancedANGLE( gl.TRIANGLES, box.index.length, gl.UNSIGNED_SHORT, 0, matrix.length / 2 ); 48 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); 49 | } 50 | } 51 | } ); 52 | 53 | // == hot reload stuff ======================================================= 54 | if ( module.hot ) { 55 | module.hot.accept( 56 | [ 57 | '../shaders/box.vert', 58 | '../shaders/box.frag' 59 | ], 60 | () => { 61 | glCatPath.replaceProgram( 62 | 'box', 63 | require( '../shaders/box.vert' ), 64 | require( '../shaders/box.frag' ) 65 | ); 66 | } 67 | ); 68 | } 69 | }; -------------------------------------------------------------------------------- /src/paths/circle.js: -------------------------------------------------------------------------------- 1 | import { Xorshift } from '../libs/xorshift.js'; 2 | import * as UltraCat from '../libs/ultracat'; 3 | import MathCat from '../libs/mathcat.js'; 4 | 5 | export default ( context ) => { 6 | // == hi context ============================================================= 7 | const glCatPath = context.glCatPath; 8 | const glCat = glCatPath.glCat; 9 | const gl = glCat.gl; 10 | 11 | const auto = context.automaton.auto; 12 | 13 | // == hi vbo ================================================================= 14 | const circle = require( '../geoms/circle.js' )( { 15 | radius: 1.0, 16 | hole: 0.8 17 | } ); 18 | const vboPos = glCat.createVertexbuffer( circle.position ); 19 | const vboUv = glCat.createVertexbuffer( circle.uv ); 20 | const vboNor = glCat.createVertexbuffer( circle.normal ); 21 | const vboMatrix = glCat.createVertexbuffer( 22 | new Float32Array( UltraCat.matrix1d( 30 ) ) 23 | ); 24 | const ibo = glCat.createIndexbuffer( circle.index ); 25 | 26 | // == path definition begin ================================================== 27 | glCatPath.add( { 28 | circle: { 29 | vert: require( '../shaders/circle.vert' ), 30 | frag: require( '../shaders/circle.frag' ), 31 | blend: [ gl.ONE, gl.ZERO ], 32 | cull: false, 33 | drawbuffers: 3, 34 | func: ( path, params ) => { 35 | glCat.attribute( 'aPos', vboPos, 3 ); 36 | glCat.attribute( 'aUv', vboUv, 2 ); 37 | glCat.attribute( 'aNor', vboNor, 3 ); 38 | glCat.attributeDivisor( 'aMatrix', vboMatrix, 1, 1 ); 39 | 40 | glCat.uniform1i( 'isShadow', params.isShadow ); 41 | glCat.uniform1f( 'availZ', auto( 'circle-availZ' ) ); 42 | glCat.uniform3fv( 'color', [ 1.0, 0.1, 0.3 ] ); 43 | 44 | let ext = glCat.getExtension( 'ANGLE_instanced_arrays' ); 45 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, ibo ); 46 | ext.drawElementsInstancedANGLE( gl.TRIANGLES, circle.index.length, gl.UNSIGNED_SHORT, 0, 30 ); 47 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); 48 | } 49 | } 50 | } ); 51 | 52 | // == hot reload stuff ======================================================= 53 | if ( module.hot ) { 54 | module.hot.accept( 55 | [ 56 | '../shaders/circle.vert', 57 | '../shaders/circle.frag' 58 | ], 59 | () => { 60 | glCatPath.replaceProgram( 61 | 'circle', 62 | require( '../shaders/circle.vert' ), 63 | require( '../shaders/circle.frag' ) 64 | ); 65 | } 66 | ); 67 | } 68 | }; -------------------------------------------------------------------------------- /src/paths/distance.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat'; 2 | 3 | export default ( context ) => { 4 | // == hi context ============================================================= 5 | const glCatPath = context.glCatPath; 6 | const glCat = glCatPath.glCat; 7 | const gl = glCat.gl; 8 | 9 | const width = context.width; 10 | const height = context.height; 11 | 12 | // == hi vbo ================================================================= 13 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 14 | 15 | // == path definition begin ================================================== 16 | glCatPath.add( { 17 | distance: { 18 | width: width, 19 | height: height, 20 | vert: require( '../shaders/quad.vert' ), 21 | frag: require( '../shaders/distance.frag' ), 22 | blend: [ gl.ONE, gl.ZERO ], 23 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 24 | framebuffer: true, 25 | float: true, 26 | func: ( path, params ) => { 27 | glCat.attribute( 'p', vboQuad, 2 ); 28 | glCat.uniformTexture( 'sampler0', params.input, 0 ); 29 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 30 | } 31 | } 32 | } ); 33 | }; -------------------------------------------------------------------------------- /src/paths/dof.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat'; 2 | 3 | export default ( context ) => { 4 | // == hi context ============================================================= 5 | const glCatPath = context.glCatPath; 6 | const glCat = glCatPath.glCat; 7 | const gl = glCat.gl; 8 | 9 | const width = context.width; 10 | const height = context.height; 11 | 12 | const auto = context.automaton.auto; 13 | 14 | // == hi vbo ================================================================= 15 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 16 | 17 | // == path definition begin ================================================== 18 | glCatPath.add( { 19 | dof: { 20 | width: width, 21 | height: height, 22 | vert: require( '../shaders/quad.vert' ), 23 | frag: require( '../shaders/dof.frag' ), 24 | blend: [ gl.ONE, gl.ZERO ], 25 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 26 | framebuffer: true, 27 | float: true, 28 | func: ( path, params ) => { 29 | glCat.attribute( 'p', vboQuad, 2 ); 30 | glCat.uniform1f( 'bokehAmp', auto( 'dof-amp' ) ); 31 | glCat.uniform1f( 'bokehFocus', auto( 'dof-focus' ) ); 32 | glCat.uniformTexture( 'samplerDry', params.dry, 0 ); 33 | glCat.uniformTexture( 'samplerDepth', params.depth, 1 ); 34 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 35 | } 36 | } 37 | } ); 38 | 39 | // == hot reload stuff ======================================================= 40 | if ( module.hot ) { 41 | module.hot.accept( 42 | [ 43 | '../shaders/quad.vert', 44 | '../shaders/dof.frag' 45 | ], 46 | () => { 47 | glCatPath.replaceProgram( 48 | 'dof', 49 | require( '../shaders/quad.vert' ), 50 | require( '../shaders/dof.frag' ) 51 | ); 52 | } 53 | ); 54 | } 55 | }; -------------------------------------------------------------------------------- /src/paths/log.js: -------------------------------------------------------------------------------- 1 | // == load some modules ======================================================== 2 | import * as UltraCat from '../libs/ultracat'; 3 | 4 | // == very basic constants ===================================================== 5 | const ppp = 2; 6 | const nParticleSqrt = 32; 7 | const nParticle = nParticleSqrt * nParticleSqrt; 8 | 9 | // == log ====================================================================== 10 | let logIndex = 0; 11 | let logBuffer = []; 12 | let LOG_RETURN = 114514; 13 | 14 | const log = {}; 15 | let logDrop = false; 16 | let logPosition = [ 0.0, 0.0, 0.0 ]; 17 | let logColor = [ 0.7, 0.8, 1.1 ]; 18 | 19 | log.line = () => { 20 | logBuffer.push( LOG_RETURN ); 21 | logPosition[ 0 ] = 0.0; 22 | }; 23 | 24 | log.print = ( mes ) => { 25 | for ( let i = 0; i < mes.length; i ++ ) { 26 | logBuffer.push( [ 27 | logPosition.concat(), 28 | logColor.concat( [ mes.codePointAt( i ) ] ) 29 | ] ); 30 | logPosition[ 0 ] += 0.1; 31 | } 32 | }; 33 | 34 | log.code = ( code ) => { 35 | logBuffer.push( [ 36 | logPosition.concat(), 37 | logColor.concat( [ code ] ) 38 | ] ); 39 | logPosition[ 0 ] += 0.1; 40 | }; 41 | 42 | log.pos = ( pos ) => { 43 | logPosition = pos || [ 0.0, 0.0, 0.0 ]; 44 | }; 45 | 46 | log.color = ( col ) => { 47 | logColor = col || [ 1.0, 1.2, 2.0 ]; 48 | }; 49 | 50 | log.err = ( mes ) => { 51 | log.line(); 52 | log.color( [ 1.7, 0.2, 0.5 ] ); 53 | log.print( '[ERR!] ' ); 54 | log.color(); 55 | log.print( mes ); 56 | }; 57 | 58 | log.warn = ( mes ) => { 59 | log.line(); 60 | log.color( [ 1.3, 0.8, 0.2 ] ); 61 | log.print( '[WARN] ' ); 62 | log.color(); 63 | log.print( mes ); 64 | }; 65 | 66 | log.info = ( mes ) => { 67 | log.line(); 68 | log.color( [ 0.2, 1.5, 0.9 ] ); 69 | log.print( '[INFO] ' ); 70 | log.color(); 71 | log.print( mes ); 72 | }; 73 | 74 | log.verb = ( mes ) => { 75 | log.line(); 76 | log.color( [ 0.5, 0.1, 1.0 ] ); 77 | log.print( '[VERB] ' ); 78 | log.color( [ 0.6, 0.6, 0.6 ] ); 79 | log.print( mes ); 80 | }; 81 | 82 | log.drop = () => { 83 | logBuffer.splice( 0 ); 84 | logDrop = true; 85 | }; 86 | 87 | export default ( context ) => { 88 | // == prepare context ======================================================== 89 | const glCatPath = context.glCatPath; 90 | const glCat = glCatPath.glCat; 91 | const gl = glCat.gl; 92 | 93 | const auto = context.automaton.auto; 94 | 95 | // == prepare vbos =========================================================== 96 | const vboQuad = glCat.createVertexbuffer( 97 | new Float32Array( UltraCat.triangleStripQuad ) 98 | ); 99 | 100 | const vboComputeUV = glCat.createVertexbuffer( new Float32Array( 101 | UltraCat.matrix2d( nParticleSqrt, nParticleSqrt ).map( ( v, i ) => ( 102 | i % 2 === 0 103 | ? ( v * ppp + 0.5 ) / nParticleSqrt / ppp 104 | : ( v + 0.5 ) / nParticleSqrt 105 | ) ) 106 | ) ); 107 | 108 | // == prepare sprite sheet =================================================== 109 | const textureSprite = glCat.createTexture(); 110 | { 111 | const imageSprite = new Image(); 112 | imageSprite.onload = () => { 113 | glCat.setTexture( textureSprite, imageSprite ); 114 | glCat.textureFilter( textureSprite, gl.NEAREST ); 115 | }; 116 | imageSprite.src = require( '../images/char5x5.png' ); 117 | } 118 | 119 | // == Toby Fox - Dummy! ====================================================== 120 | const textureDummy = glCat.createTexture(); 121 | glCat.setTextureFromArray( textureDummy, 1, 1, new Uint8Array( [ 0, 0, 0, 0 ] ) ); 122 | 123 | // == let's create paths ===================================================== 124 | glCatPath.add( { 125 | // == compute particles ===================================================== 126 | logCompute: { 127 | width: nParticleSqrt * ppp, 128 | height: nParticleSqrt, 129 | vert: require( '../shaders/quad.vert' ), 130 | frag: require( '../shaders/log-compute.frag' ), 131 | blend: [ gl.ONE, gl.ZERO ], 132 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 133 | framebuffer: true, 134 | swapbuffer: true, 135 | float: true, 136 | func: ( path, params ) => { 137 | // == a ================================================================ 138 | glCat.uniform1i( 'logReturn', false ); 139 | if ( 0 < logBuffer.length ) { 140 | const buf = logBuffer.shift(); 141 | if ( buf === LOG_RETURN ) { 142 | glCat.uniform1i( 'logReturn', true ); 143 | } else { 144 | buf[ 0 ][ 3 ] = logIndex; 145 | glCat.uniform4fv( 'logPos', buf[ 0 ] ); 146 | glCat.uniform4fv( 'logCol', buf[ 1 ] ); 147 | logIndex = ( logIndex + 1 ) % nParticle; 148 | } 149 | } else { 150 | glCat.uniform4fv( 'logPos', [ 0.0, 0.0, 0.0, -1.0 ] ); 151 | } 152 | glCat.uniform1i( 'logDrop', logDrop ); 153 | logDrop = false; 154 | 155 | // == render =========================================================== 156 | glCat.attribute( 'p', vboQuad, 2 ); 157 | 158 | glCat.uniform1f( 'nParticle', nParticle ); 159 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 160 | glCat.uniform1f( 'ppp', ppp ); 161 | 162 | glCat.uniform3fv( 'logOffset', [ 163 | auto( 'log-offsetX' ), 164 | auto( 'log-offsetY' ), 165 | auto( 'log-offsetZ' ) 166 | ] ); 167 | glCat.uniformTexture( 'samplerPcompute', path.swapbuffer.texture, 0 ); 168 | 169 | glCat.uniform1f( 'noisePhase', auto( 'particles-noisePhase' ) ); 170 | glCat.uniform1f( 'genRate', auto( 'particles-genRate' ) ); 171 | 172 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 173 | } 174 | }, 175 | 176 | // == render logs ========================================================== 177 | logRender: { 178 | vert: require( '../shaders/log-render.vert' ), 179 | frag: require( '../shaders/log-render.frag' ), 180 | blend: [ gl.ONE, gl.ZERO ], 181 | drawbuffers: 3, 182 | depthTest: false, 183 | depthWrite: false, 184 | func: ( path, params ) => { 185 | glCat.attribute( 'computeUV', vboComputeUV, 2 ); 186 | 187 | glCat.uniform1f( 'nParticle', nParticle ); 188 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 189 | glCat.uniform1f( 'ppp', ppp ); 190 | 191 | glCat.uniform2fv( 'resolutionPcompute', [ nParticleSqrt * ppp, nParticleSqrt ] ); 192 | 193 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 194 | 195 | glCat.uniformTexture( 'samplerPcompute', glCatPath.fb( 'logCompute' ).texture, 0 ); 196 | glCat.uniformTexture( 'samplerSprite', textureSprite, 1 ); 197 | glCat.uniformTexture( 'samplerShadow', params.textureShadow || textureDummy, 2 ); 198 | 199 | gl.drawArrays( gl.POINTS, 0, nParticle ); 200 | } 201 | }, 202 | } ); 203 | 204 | if ( module.hot ) { 205 | module.hot.accept( 206 | [ 207 | '../shaders/quad.vert', 208 | '../shaders/log-compute.frag' 209 | ], 210 | () => { 211 | glCatPath.replaceProgram( 212 | 'logCompute', 213 | require( '../shaders/quad.vert' ), 214 | require( '../shaders/log-compute.frag' ) 215 | ); 216 | } 217 | ); 218 | 219 | module.hot.accept( 220 | [ 221 | '../shaders/log-render.vert', 222 | '../shaders/log-render.frag' 223 | ], 224 | () => { 225 | glCatPath.replaceProgram( 226 | 'logRender', 227 | require( '../shaders/log-render.vert' ), 228 | require( '../shaders/log-render.frag' ) 229 | ); 230 | } 231 | ); 232 | } 233 | 234 | return log; 235 | }; -------------------------------------------------------------------------------- /src/paths/particles.js: -------------------------------------------------------------------------------- 1 | // == load some modules ======================================================== 2 | import { Xorshift } from '../libs/xorshift'; 3 | import * as UltraCat from '../libs/ultracat'; 4 | 5 | // == roll the dice ============================================================ 6 | const seed = 15881342356; 7 | let xorshift = new Xorshift( seed ); 8 | 9 | // == very basic constants ===================================================== 10 | const ppp = 2; 11 | const nParticleSqrt = 512; 12 | const nParticle = nParticleSqrt * nParticleSqrt; 13 | 14 | export default ( context ) => { 15 | // == prepare context ======================================================== 16 | const glCatPath = context.glCatPath; 17 | const glCat = glCatPath.glCat; 18 | const gl = glCat.gl; 19 | 20 | const auto = context.automaton.auto; 21 | 22 | // == prepare vbos =========================================================== 23 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 24 | 25 | const vboComputeUV = glCat.createVertexbuffer( new Float32Array( 26 | UltraCat.matrix2d( nParticleSqrt, nParticleSqrt ).map( ( v, i ) => ( 27 | i % 2 === 0 28 | ? ( v * ppp + 0.5 ) / nParticleSqrt / ppp 29 | : ( v + 0.5 ) / nParticleSqrt 30 | ) ) 31 | ) ); 32 | 33 | // == Toby Fox - Dummy! ====================================================== 34 | const textureDummy = glCat.createTexture(); 35 | glCat.setTextureFromArray( textureDummy, 1, 1, new Uint8Array( [ 0, 0, 0, 0 ] ) ); 36 | 37 | // == let's create paths ===================================================== 38 | glCatPath.add( { 39 | // == compute particles ======================================================= 40 | particlesCompute: { 41 | width: nParticleSqrt * ppp, 42 | height: nParticleSqrt, 43 | vert: require( '../shaders/quad.vert' ), 44 | frag: require( '../shaders/particles-compute.frag' ), 45 | blend: [ gl.ONE, gl.ZERO ], 46 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 47 | framebuffer: true, 48 | swapbuffer: true, 49 | float: true, 50 | func: ( path, params ) => { 51 | glCat.attribute( 'p', vboQuad, 2 ); 52 | 53 | glCat.uniform1f( 'nParticle', nParticle ); 54 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 55 | glCat.uniform1f( 'ppp', ppp ); 56 | 57 | glCat.uniformTexture( 'samplerPcompute', path.swapbuffer.texture, 0 ); 58 | 59 | glCat.uniform1f( 'noisePhase', auto( 'particles-noisePhase' ) ); 60 | glCat.uniform1f( 'genRate', auto( 'particles-genRate' ) ); 61 | 62 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 63 | } 64 | }, 65 | 66 | // == render particles ===================================================== 67 | particlesRender: { 68 | vert: require( '../shaders/particles-render.vert' ), 69 | frag: require( '../shaders/particles-render.frag' ), 70 | blend: [ gl.ONE, gl.ZERO ], 71 | drawbuffers: 3, 72 | func: ( path, params ) => { 73 | glCat.attribute( 'computeUV', vboComputeUV, 2 ); 74 | 75 | glCat.uniform1f( 'nParticle', nParticle ); 76 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 77 | glCat.uniform1f( 'ppp', ppp ); 78 | 79 | glCat.uniform2fv( 'resolutionPcompute', [ nParticleSqrt * ppp, nParticleSqrt ] ); 80 | 81 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 82 | 83 | glCat.uniform1f( 'colorVar', auto( 'particles-colorVar' ) ); 84 | glCat.uniform1f( 'colorOffset', auto( 'particles-colorOffset' ) ); 85 | 86 | glCat.uniformTexture( 'samplerPcompute', glCatPath.fb( 'particlesCompute' ).texture, 0 ); 87 | glCat.uniformTexture( 'samplerShadow', params.textureShadow || textureDummy, 1 ); 88 | 89 | gl.drawArrays( gl.POINTS, 0, nParticle ); 90 | } 91 | }, 92 | } ); 93 | 94 | if ( module.hot ) { 95 | module.hot.accept( 96 | [ 97 | '../shaders/quad.vert', 98 | '../shaders/particles-compute.frag' 99 | ], 100 | () => { 101 | glCatPath.replaceProgram( 102 | 'particlesCompute', 103 | require( '../shaders/quad.vert' ), 104 | require( '../shaders/particles-compute.frag' ) 105 | ); 106 | } 107 | ); 108 | 109 | module.hot.accept( 110 | [ 111 | '../shaders/particles-render.vert', 112 | '../shaders/particles-render.frag' 113 | ], 114 | () => { 115 | glCatPath.replaceProgram( 116 | 'particlesRender', 117 | require( '../shaders/particles-render.vert' ), 118 | require( '../shaders/particles-render.frag' ) 119 | ); 120 | } 121 | ); 122 | } 123 | }; -------------------------------------------------------------------------------- /src/paths/patterns.js: -------------------------------------------------------------------------------- 1 | // == load some modules ======================================================== 2 | import { Xorshift } from '../libs/xorshift'; 3 | import * as UltraCat from '../libs/ultracat'; 4 | 5 | // == roll the dice ============================================================ 6 | const seed = 15881342356; 7 | let xorshift = new Xorshift( seed ); 8 | 9 | // == very basic constants ===================================================== 10 | const ppp = 1; 11 | const nParticleSqrt = 8; 12 | const nParticle = nParticleSqrt * nParticleSqrt; 13 | 14 | export default ( context ) => { 15 | // == prepare context ======================================================== 16 | const glCatPath = context.glCatPath; 17 | const glCat = glCatPath.glCat; 18 | const gl = glCat.gl; 19 | 20 | const auto = context.automaton.auto; 21 | 22 | // == prepare vbos =========================================================== 23 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 24 | 25 | const vboPos = glCat.createVertexbuffer( new Float32Array( 26 | UltraCat.triangleStripQuad3 27 | ) ); 28 | 29 | const vboComputeUV = glCat.createVertexbuffer( new Float32Array( 30 | UltraCat.matrix2d( nParticleSqrt, nParticleSqrt ).map( ( v, i ) => ( 31 | i % 2 === 0 32 | ? ( v * ppp + 0.5 ) / nParticleSqrt / ppp 33 | : ( v + 0.5 ) / nParticleSqrt 34 | ) ) 35 | ) ); 36 | 37 | // == Toby Fox - Dummy! ====================================================== 38 | const textureDummy = glCat.createTexture(); 39 | glCat.setTextureFromArray( textureDummy, 1, 1, new Uint8Array( [ 0, 0, 0, 0 ] ) ); 40 | 41 | // == let's create paths ===================================================== 42 | glCatPath.add( { 43 | // == compute particles ======================================================= 44 | patternsCompute: { 45 | width: nParticleSqrt * ppp, 46 | height: nParticleSqrt, 47 | vert: require( '../shaders/quad.vert' ), 48 | frag: require( '../shaders/patterns-compute.frag' ), 49 | blend: [ gl.ONE, gl.ZERO ], 50 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 51 | framebuffer: true, 52 | swapbuffer: true, 53 | float: true, 54 | func: ( path, params ) => { 55 | glCat.attribute( 'p', vboQuad, 2 ); 56 | 57 | glCat.uniform1f( 'nParticle', nParticle ); 58 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 59 | glCat.uniform1f( 'ppp', ppp ); 60 | 61 | glCat.uniformTexture( 'samplerPcompute', path.swapbuffer.texture, 0 ); 62 | 63 | glCat.uniform1f( 'noisePhase', auto( 'patterns-noisePhase' ) ); 64 | glCat.uniform1f( 'genRate', auto( 'patterns-genRate' ) ); 65 | 66 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 67 | } 68 | }, 69 | 70 | // == render particles ===================================================== 71 | patternsRender: { 72 | vert: require( '../shaders/patterns-render.vert' ), 73 | frag: require( '../shaders/patterns-render.frag' ), 74 | blend: [ gl.ONE, gl.ZERO ], 75 | drawbuffers: 3, 76 | func: ( path, params ) => { 77 | glCat.attribute( 'aPos', vboPos, 3 ); 78 | glCat.attributeDivisor( 'aComputeUV', vboComputeUV, 2, 1 ); 79 | 80 | glCat.uniform1f( 'nParticle', nParticle ); 81 | glCat.uniform1f( 'nParticleSqrt', nParticleSqrt ); 82 | glCat.uniform1f( 'ppp', ppp ); 83 | 84 | glCat.uniform2fv( 'resolutionPcompute', [ nParticleSqrt * ppp, nParticleSqrt ] ); 85 | 86 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 87 | 88 | glCat.uniformTexture( 'samplerPcompute', glCatPath.fb( 'patternsCompute' ).texture, 0 ); 89 | glCat.uniformTexture( 'samplerShadow', params.textureShadow || textureDummy, 1 ); 90 | 91 | const ext = glCat.getExtension( 'ANGLE_instanced_arrays' ); 92 | ext.drawArraysInstancedANGLE( gl.TRIANGLE_STRIP, 0, 4, nParticle ); 93 | } 94 | }, 95 | } ); 96 | 97 | if ( module.hot ) { 98 | module.hot.accept( 99 | [ 100 | '../shaders/quad.vert', 101 | '../shaders/patterns-compute.frag' 102 | ], 103 | () => { 104 | glCatPath.replaceProgram( 105 | 'patternsCompute', 106 | require( '../shaders/quad.vert' ), 107 | require( '../shaders/patterns-compute.frag' ) 108 | ); 109 | } 110 | ); 111 | 112 | module.hot.accept( 113 | [ 114 | '../shaders/patterns-render.vert', 115 | '../shaders/patterns-render.frag' 116 | ], 117 | () => { 118 | glCatPath.replaceProgram( 119 | 'patternsRender', 120 | require( '../shaders/patterns-render.vert' ), 121 | require( '../shaders/patterns-render.frag' ) 122 | ); 123 | } 124 | ); 125 | } 126 | }; -------------------------------------------------------------------------------- /src/paths/postfx.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat'; 2 | 3 | // ------ 4 | 5 | export default ( context ) => { 6 | const glCatPath = context.glCatPath; 7 | const glCat = glCatPath.glCat; 8 | const gl = glCat.gl; 9 | 10 | const width = context.width; 11 | const height = context.height; 12 | 13 | const auto = context.automaton.auto; 14 | 15 | // ------ 16 | 17 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 18 | 19 | // ------ 20 | 21 | glCatPath.add( { 22 | post: { 23 | width: width, 24 | height: height, 25 | vert: require( '../shaders/quad.vert' ), 26 | frag: require( '../shaders/post.frag' ), 27 | blend: [ gl.ONE, gl.ZERO ], 28 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 29 | framebuffer: true, 30 | float: true, 31 | func: ( path, params ) => { 32 | glCat.attribute( 'p', vboQuad, 2 ); 33 | glCat.uniform1f( 'barrelAmp', auto( 'post-barrelAmp' ) ); 34 | glCat.uniform1f( 'barrelOffset', auto( 'post-barrelOffset' ) ); 35 | glCat.uniformTexture( 'sampler0', params.input, 0 ); 36 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 37 | } 38 | }, 39 | 40 | glitch: { 41 | width: width, 42 | height: height, 43 | vert: require( '../shaders/quad.vert' ), 44 | frag: require( '../shaders/glitch.frag' ), 45 | blend: [ gl.ONE, gl.ZERO ], 46 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 47 | framebuffer: true, 48 | float: true, 49 | func: ( path, params ) => { 50 | glCat.attribute( 'p', vboQuad, 2 ); 51 | glCat.uniform1f( 'amp', auto( 'glitch-amp' ) ); 52 | glCat.uniform1f( 'fadeout', auto( 'glitch-fadeout' ) ); 53 | glCat.uniformTexture( 'sampler0', params.input, 0 ); 54 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 55 | } 56 | }, 57 | 58 | fxaa: { 59 | width: width, 60 | height: height, 61 | vert: require( '../shaders/quad.vert' ), 62 | frag: require( '../shaders/fxaa.frag' ), 63 | blend: [ gl.ONE, gl.ZERO ], 64 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 65 | framebuffer: true, 66 | float: true, 67 | func: ( path, params ) => { 68 | glCat.attribute( 'p', vboQuad, 2 ); 69 | glCat.uniformTexture( 'sampler0', params.input, 0 ); 70 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 71 | } 72 | }, 73 | } ); 74 | }; -------------------------------------------------------------------------------- /src/paths/racer.js: -------------------------------------------------------------------------------- 1 | // == load some modules ======================================================== 2 | import { Xorshift } from '../libs/xorshift'; 3 | import * as UltraCat from '../libs/ultracat'; 4 | 5 | // == roll the dice ============================================================ 6 | const seed = 15881342356; 7 | let xorshift = new Xorshift( seed ); 8 | 9 | // == very basic constants ===================================================== 10 | const ppp = 2; 11 | const trailLength = 64; 12 | const trails = 256; 13 | 14 | export default ( context ) => { 15 | // == prepare context ======================================================== 16 | const glCatPath = context.glCatPath; 17 | const glCat = glCatPath.glCat; 18 | const gl = glCat.gl; 19 | 20 | const auto = context.automaton.auto; 21 | 22 | // == prepare vbos =========================================================== 23 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 24 | 25 | const vboComputeUV = glCat.createVertexbuffer( new Float32Array( 26 | UltraCat.matrix2d( trailLength, trails ).map( ( v, i ) => ( 27 | i % 2 === 0 28 | ? ( v * ppp + 0.5 ) / trailLength / ppp 29 | : ( v + 0.5 ) / trails 30 | ) ) 31 | ) ); 32 | 33 | // == Toby Fox - Dummy! ====================================================== 34 | const textureDummy = glCat.createTexture(); 35 | glCat.setTextureFromArray( textureDummy, 1, 1, new Uint8Array( [ 0, 0, 0, 0 ] ) ); 36 | 37 | // == let's create paths ===================================================== 38 | glCatPath.add( { 39 | // == compute trails ======================================================= 40 | racerCompute: { 41 | width: trailLength * ppp, 42 | height: trails, 43 | vert: require( '../shaders/quad.vert' ), 44 | frag: require( '../shaders/racer-compute.frag' ), 45 | blend: [ gl.ONE, gl.ZERO ], 46 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 47 | framebuffer: true, 48 | swapbuffer: true, 49 | float: true, 50 | func: ( path, params ) => { 51 | glCat.attribute( 'p', vboQuad, 2 ); 52 | 53 | glCat.uniform1f( 'trails', trails ); 54 | glCat.uniform1f( 'trailLength', trailLength ); 55 | glCat.uniform1f( 'ppp', ppp ); 56 | 57 | glCat.uniformTexture( 'samplerPcompute', path.swapbuffer.texture, 0 ); 58 | 59 | glCat.uniform1f( 'genRate', auto( 'racer-genRate' ) ); 60 | 61 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 62 | } 63 | }, 64 | 65 | // render trails =========================================================== 66 | racerRender: { 67 | vert: require( '../shaders/racer-render.vert' ), 68 | frag: require( '../shaders/racer-render.frag' ), 69 | blend: [ gl.ONE, gl.ZERO ], 70 | drawbuffers: 3, 71 | func: ( path, params ) => { 72 | glCat.attribute( 'aComputeUV', vboComputeUV, 2 ); 73 | 74 | glCat.uniform1f( 'trails', trails ); 75 | glCat.uniform1f( 'trailLength', trailLength ); 76 | glCat.uniform1f( 'ppp', ppp ); 77 | 78 | glCat.uniform2fv( 'resolutionPcompute', [ trailLength * ppp, trails ] ); 79 | 80 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 81 | 82 | glCat.uniformTexture( 'samplerPcompute', glCatPath.fb( 'racerCompute' ).texture, 0 ); 83 | glCat.uniformTexture( 'samplerShadow', params.textureShadow || textureDummy, 1 ); 84 | 85 | gl.drawArrays( gl.POINTS, 0, trailLength * trails ); 86 | } 87 | }, 88 | } ); 89 | 90 | if ( module.hot ) { 91 | module.hot.accept( 92 | [ 93 | '../shaders/quad.vert', 94 | '../shaders/racer-compute.frag' 95 | ], 96 | () => { 97 | glCatPath.replaceProgram( 98 | 'racerCompute', 99 | require( '../shaders/quad.vert' ), 100 | require( '../shaders/racer-compute.frag' ) 101 | ); 102 | } 103 | ); 104 | 105 | module.hot.accept( 106 | [ 107 | '../shaders/racer-render.vert', 108 | '../shaders/racer-render.frag' 109 | ], 110 | () => { 111 | glCatPath.replaceProgram( 112 | 'racerRender', 113 | require( '../shaders/racer-render.vert' ), 114 | require( '../shaders/racer-render.frag' ) 115 | ); 116 | } 117 | ); 118 | } 119 | }; -------------------------------------------------------------------------------- /src/paths/raymarch.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat'; 2 | 3 | export default ( context ) => { 4 | // == hi context ============================================================= 5 | const glCatPath = context.glCatPath; 6 | const glCat = glCatPath.glCat; 7 | const gl = glCat.gl; 8 | 9 | const width = context.width; 10 | const height = context.height; 11 | 12 | const auto = context.automaton.auto; 13 | 14 | // == hi vbo ================================================================= 15 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 16 | 17 | // == path definition begin ================================================== 18 | glCatPath.add( { 19 | raymarch: { 20 | vert: require( '../shaders/quad.vert' ), 21 | frag: require( '../shaders/raymarch.frag' ), 22 | blend: [ gl.ONE, gl.ZERO ], 23 | drawbuffers: 3, 24 | func: ( path, params ) => { 25 | glCat.attribute( 'p', vboQuad, 2 ); 26 | 27 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 28 | glCat.uniform4fv( 'ifsParams', [ 29 | auto( 'raymarch-ifsParamsX' ), 30 | auto( 'raymarch-ifsParamsY' ), 31 | auto( 'raymarch-ifsParamsZ' ), 32 | auto( 'raymarch-ifsParamsW' ) 33 | ] ); 34 | 35 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 36 | } 37 | } 38 | } ); 39 | 40 | // == hot reload stuff ======================================================= 41 | if ( module.hot ) { 42 | module.hot.accept( 43 | [ 44 | '../shaders/quad.vert', 45 | '../shaders/raymarch.frag' 46 | ], 47 | () => { 48 | glCatPath.replaceProgram( 49 | 'raymarch', 50 | require( '../shaders/quad.vert' ), 51 | require( '../shaders/raymarch.frag' ) 52 | ); 53 | } 54 | ); 55 | } 56 | }; -------------------------------------------------------------------------------- /src/paths/render.js: -------------------------------------------------------------------------------- 1 | import * as UltraCat from '../libs/ultracat.js'; 2 | 3 | export default ( context ) => { 4 | // == hi context ============================================================= 5 | const glCatPath = context.glCatPath; 6 | const glCat = glCatPath.glCat; 7 | const gl = glCat.gl; 8 | 9 | const width = context.width; 10 | const height = context.height; 11 | 12 | // == hi vbo ================================================================= 13 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 14 | 15 | // == path definition begin ================================================== 16 | glCatPath.add( { 17 | render: { 18 | width: width, 19 | height: height, 20 | vert: require( '../shaders/quad.vert' ), 21 | frag: require( '../shaders/render.frag' ), 22 | blend: [ gl.ONE, gl.ZERO ], 23 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 24 | framebuffer: true, 25 | float: true, 26 | func: ( path, params ) => { 27 | glCat.attribute( 'p', vboQuad, 2 ); 28 | glCat.uniformTexture( 'sampler0', params.inputs[ 0 ], 0 ); 29 | glCat.uniformTexture( 'sampler1', params.inputs[ 1 ], 1 ); 30 | glCat.uniformTexture( 'sampler2', params.inputs[ 2 ], 2 ); 31 | glCat.uniformTexture( 'samplerShadow', params.shadow, 3 ); 32 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 33 | } 34 | } 35 | } ); 36 | 37 | // == hot reload stuff ======================================================= 38 | if ( module.hot ) { 39 | module.hot.accept( 40 | [ 41 | '../shaders/quad.vert', 42 | '../shaders/render.frag' 43 | ], 44 | () => { 45 | glCatPath.replaceProgram( 46 | 'render', 47 | require( '../shaders/quad.vert' ), 48 | require( '../shaders/render.frag' ) 49 | ); 50 | } 51 | ); 52 | } 53 | }; -------------------------------------------------------------------------------- /src/paths/trails.js: -------------------------------------------------------------------------------- 1 | // == load some modules ======================================================== 2 | import { Xorshift } from '../libs/xorshift'; 3 | import * as UltraCat from '../libs/ultracat'; 4 | 5 | // == roll the dice ============================================================ 6 | const seed = 15881342356; 7 | let xorshift = new Xorshift( seed ); 8 | 9 | // == very basic constants ===================================================== 10 | const ppp = 2; 11 | const trailLength = 8; 12 | const trails = 4096; 13 | 14 | export default ( context ) => { 15 | // == prepare context ======================================================== 16 | const glCatPath = context.glCatPath; 17 | const glCat = glCatPath.glCat; 18 | const gl = glCat.gl; 19 | 20 | const auto = context.automaton.auto; 21 | 22 | // == prepare vbos =========================================================== 23 | const vboQuad = glCat.createVertexbuffer( new Float32Array( UltraCat.triangleStripQuad ) ); 24 | 25 | const vboComputeU = glCat.createVertexbuffer( ( () => { 26 | let ret = new Float32Array( 3 * trailLength ); 27 | for ( let i = 0; i < trailLength; i ++ ) { 28 | const u = ( i * ppp + 0.5 ) / ( trailLength * ppp ); 29 | ret[ i * 3 + 0 ] = u; 30 | ret[ i * 3 + 1 ] = u; 31 | ret[ i * 3 + 2 ] = u; 32 | } 33 | return ret; 34 | } )() ); 35 | 36 | const vboTriIndex = glCat.createVertexbuffer( ( () => { 37 | let ret = new Float32Array( 3 * trailLength ); 38 | for ( let i = 0; i < trailLength; i ++ ) { 39 | ret[ i * 3 + 0 ] = 0; 40 | ret[ i * 3 + 1 ] = 1; 41 | ret[ i * 3 + 2 ] = 2; 42 | } 43 | return ret; 44 | } )() ); 45 | 46 | const ibo = glCat.createIndexbuffer( ( () => { 47 | let ret = new Uint16Array( 18 * ( trailLength - 1 ) ); 48 | for ( let i = 0; i < trailLength - 1; i ++ ) { 49 | for ( let j = 0; j < 3; j ++ ) { 50 | const jn = ( j + 1 ) % 3; 51 | ret[ 18 * i + 6 * j + 0 ] = i * 3 + j; 52 | ret[ 18 * i + 6 * j + 1 ] = i * 3 + 3 + j; 53 | ret[ 18 * i + 6 * j + 2 ] = i * 3 + 3 + jn; 54 | ret[ 18 * i + 6 * j + 3 ] = i * 3 + j; 55 | ret[ 18 * i + 6 * j + 4 ] = i * 3 + 3 + jn; 56 | ret[ 18 * i + 6 * j + 5 ] = i * 3 + jn; 57 | } 58 | } 59 | return ret; 60 | } )() ); 61 | 62 | const vboComputeV = glCat.createVertexbuffer( ( () => { 63 | let ret = new Float32Array( trails ); 64 | for ( let i = 0; i < trails; i ++ ) { 65 | ret[ i ] = ( i + 0.5 ) / trails; 66 | } 67 | return ret; 68 | } )() ); 69 | 70 | // == Toby Fox - Dummy! ====================================================== 71 | const textureDummy = glCat.createTexture(); 72 | glCat.setTextureFromArray( textureDummy, 1, 1, new Uint8Array( [ 0, 0, 0, 0 ] ) ); 73 | 74 | // == let's create paths ===================================================== 75 | glCatPath.add( { 76 | // == compute trails ======================================================= 77 | trailsCompute: { 78 | width: trailLength * ppp, 79 | height: trails, 80 | vert: require( '../shaders/quad.vert' ), 81 | frag: require( '../shaders/trails-compute.frag' ), 82 | blend: [ gl.ONE, gl.ZERO ], 83 | clear: [ 0.0, 0.0, 0.0, 0.0 ], 84 | framebuffer: true, 85 | swapbuffer: true, 86 | float: true, 87 | func: ( path, params ) => { 88 | glCat.attribute( 'p', vboQuad, 2 ); 89 | 90 | glCat.uniform1f( 'trails', trails ); 91 | glCat.uniform1f( 'trailLength', trailLength ); 92 | glCat.uniform1f( 'ppp', ppp ); 93 | 94 | glCat.uniformTexture( 'samplerPcompute', path.swapbuffer.texture, 0 ); 95 | 96 | glCat.uniform1f( 'genRate', auto( 'trails-genRate' ) ); 97 | glCat.uniform1f( 'noiseScale', auto( 'trails-noiseScale' ) ); 98 | 99 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 4 ); 100 | } 101 | }, 102 | 103 | // render trails =========================================================== 104 | trailsRender: { 105 | vert: require( '../shaders/trails-render.vert' ), 106 | frag: require( '../shaders/trails-render.frag' ), 107 | blend: [ gl.ONE, gl.ZERO ], 108 | drawbuffers: 3, 109 | func: ( path, params ) => { 110 | glCat.attribute( 'computeU', vboComputeU, 1 ); 111 | glCat.attribute( 'triIndex', vboTriIndex, 1 ); 112 | glCat.attributeDivisor( 'computeV', vboComputeV, 1, 1 ); 113 | 114 | glCat.uniform1f( 'trails', trails ); 115 | glCat.uniform1f( 'trailLength', trailLength ); 116 | glCat.uniform1f( 'ppp', ppp ); 117 | 118 | glCat.uniform2fv( 'resolutionPcompute', [ trailLength * ppp, trails ] ); 119 | 120 | glCat.uniform1i( 'isShadow', params.isShadow ? 1 : 0 ); 121 | 122 | glCat.uniformTexture( 'samplerPcompute', glCatPath.fb( 'trailsCompute' ).texture, 0 ); 123 | glCat.uniformTexture( 'samplerShadow', params.textureShadow || textureDummy, 1 ); 124 | 125 | let ext = glCat.getExtension( 'ANGLE_instanced_arrays' ); 126 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, ibo ); 127 | ext.drawElementsInstancedANGLE( gl.TRIANGLES, 18 * ( trailLength - 1 ), gl.UNSIGNED_SHORT, 0, trails ); 128 | gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); 129 | } 130 | }, 131 | } ); 132 | 133 | if ( module.hot ) { 134 | module.hot.accept( 135 | [ 136 | '../shaders/quad.vert', 137 | '../shaders/trails-compute.frag' 138 | ], 139 | () => { 140 | glCatPath.replaceProgram( 141 | 'trailsCompute', 142 | require( '../shaders/quad.vert' ), 143 | require( '../shaders/trails-compute.frag' ) 144 | ); 145 | } 146 | ); 147 | 148 | module.hot.accept( 149 | [ 150 | '../shaders/trails-render.vert', 151 | '../shaders/trails-render.frag' 152 | ], 153 | () => { 154 | glCatPath.replaceProgram( 155 | 'trailsRender', 156 | require( '../shaders/trails-render.vert' ), 157 | require( '../shaders/trails-render.frag' ) 158 | ); 159 | } 160 | ); 161 | } 162 | }; -------------------------------------------------------------------------------- /src/paths/ui.js: -------------------------------------------------------------------------------- 1 | import MathCat from '../libs/mathcat'; 2 | import * as UltraCat from '../libs/ultracat'; 3 | 4 | export default ( context ) => { 5 | // == hi context ============================================================= 6 | const glCatPath = context.glCatPath; 7 | const glCat = glCatPath.glCat; 8 | const gl = glCat.gl; 9 | 10 | const auto = context.automaton.auto; 11 | 12 | // == hi vbo ================================================================= 13 | const vboPos = glCat.createVertexbuffer( new Float32Array( [ 14 | -4.5, 2.5, 0.0, -3.5, 1.5, 0.0, 15 | +4.5, 2.5, 0.0, 3.5, 1.5, 0.0, 16 | +4.5, -2.5, 0.0, 3.5, -1.5, 0.0, 17 | -4.5, -2.5, 0.0, -3.5, -1.5, 0.0, 18 | -4.5, 2.5, 0.0, -3.5, 1.5, 0.0 19 | ] ) ); 20 | const vboNor = glCat.createVertexbuffer( new Float32Array( [ 21 | 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 22 | 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 23 | 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 24 | 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 25 | 0.0, 0.0, 1.0, 0.0, 0.0, 1.0 26 | ] ) ); 27 | 28 | // == hi texture ============================================================= 29 | const textureLv = glCat.createTexture(); 30 | 31 | // == path definition begin ================================================== 32 | glCatPath.add( { 33 | ui: { 34 | vert: require( '../shaders/ui.vert' ), 35 | frag: require( '../shaders/ui.frag' ), 36 | blend: [ gl.ONE, gl.ZERO ], 37 | drawbuffers: 3, 38 | depthTest: false, 39 | depthWrite: false, 40 | func: ( path, params ) => { 41 | glCat.attribute( 'aPos', vboPos, 3 ); 42 | glCat.attribute( 'aNor', vboNor, 3 ); 43 | 44 | glCat.uniform1i( 'isShadow', params.isShadow ); 45 | glCat.uniform4fv( 'color', [ 0.2, 0.3, 0.4, 3.0 ] ); 46 | 47 | glCat.setTextureFromFloatArray( textureLv, 2048, 1, params.analyserData, gl.LUMINANCE ); 48 | glCat.uniformTexture( 'samplerLv', textureLv, 0 ); 49 | 50 | let matM = MathCat.mat4Identity(); 51 | glCat.uniformMatrix4fv( 'matM', matM ); 52 | 53 | gl.drawArrays( gl.TRIANGLE_STRIP, 0, 10 ); 54 | } 55 | } 56 | } ); 57 | 58 | // == hot reload stuff ======================================================= 59 | if ( module.hot ) { 60 | module.hot.accept( 61 | [ 62 | '../shaders/ui.vert', 63 | '../shaders/ui.frag' 64 | ], 65 | () => { 66 | glCatPath.replaceProgram( 67 | 'ui', 68 | require( '../shaders/ui.vert' ), 69 | require( '../shaders/ui.frag' ) 70 | ); 71 | } 72 | ); 73 | } 74 | }; -------------------------------------------------------------------------------- /src/paths/very-plane.js: -------------------------------------------------------------------------------- 1 | import MathCat from '../libs/mathcat'; 2 | import * as UltraCat from '../libs/ultracat'; 3 | 4 | export default ( context ) => { 5 | // == hi context ============================================================= 6 | const glCatPath = context.glCatPath; 7 | const glCat = glCatPath.glCat; 8 | const gl = glCat.gl; 9 | 10 | const auto = context.automaton.auto; 11 | 12 | // == hi vbo ================================================================= 13 | const vboPos = glCat.createVertexbuffer( new Float32Array( 14 | UltraCat.triangleStripQuad3 15 | ) ); 16 | const vboNor = glCat.createVertexbuffer( new Float32Array( 17 | UltraCat.triangleStripQuadNor 18 | ) ); 19 | const vboUv = glCat.createVertexbuffer( new Float32Array( 20 | UltraCat.triangleStripQuadUV 21 | ) ); 22 | 23 | const matrix = UltraCat.matrix2d( 75, 75 ).map( ( v, i ) => ( 24 | v * 0.4 - 14.8 25 | ) ); 26 | const vboMatrix = glCat.createVertexbuffer( new Float32Array( matrix ) ); 27 | 28 | // == path definition begin ================================================== 29 | glCatPath.add( { 30 | veryPlane: { 31 | vert: require( '../shaders/very-plane.vert' ), 32 | frag: require( '../shaders/very-plane.frag' ), 33 | blend: [ gl.ONE, gl.ZERO ], 34 | cull: false, 35 | drawbuffers: 3, 36 | func: ( path, params ) => { 37 | glCat.attribute( 'aPos', vboPos, 3 ); 38 | glCat.attribute( 'aNor', vboNor, 3 ); 39 | glCat.attributeDivisor( 'aMatrix', vboMatrix, 2, 1 ); 40 | 41 | glCat.uniform1i( 'isShadow', params.isShadow ); 42 | glCat.uniform1f( 'flipThreshold', auto( 'veryPlane-flipThreshold' ) ); 43 | glCat.uniform4fv( 'color', [ 0.2, 0.3, 0.4, 3.0 ] ); 44 | 45 | let matM = MathCat.mat4Apply( 46 | MathCat.mat4Translate( [ 0.0, -5.0, 0.0 ] ), 47 | MathCat.mat4RotateX( Math.PI / 2.0 ), 48 | MathCat.mat4Identity() 49 | ); 50 | glCat.uniformMatrix4fv( 'matM', matM ); 51 | 52 | let ext = glCat.getExtension( 'ANGLE_instanced_arrays' ); 53 | ext.drawArraysInstancedANGLE( gl.TRIANGLE_STRIP, 0, 4, matrix.length / 2 ); 54 | } 55 | } 56 | } ); 57 | 58 | // == hot reload stuff ======================================================= 59 | if ( module.hot ) { 60 | module.hot.accept( 61 | [ 62 | '../shaders/very-plane.vert', 63 | '../shaders/very-plane.frag' 64 | ], 65 | () => { 66 | glCatPath.replaceProgram( 67 | 'veryPlane', 68 | require( '../shaders/very-plane.vert' ), 69 | require( '../shaders/very-plane.frag' ) 70 | ); 71 | } 72 | ); 73 | } 74 | }; -------------------------------------------------------------------------------- /src/shaders/-common.glsl: -------------------------------------------------------------------------------- 1 | // == defines ================================================================== 2 | #define PI 3.141592654 3 | #define TAU 6.283185307 4 | #define HUGE 9E16 5 | #define BPM 160.0 6 | #define saturate(i) clamp(i, 0.,1.) 7 | #define aSaturate(i) clamp(i, -1.,1.) 8 | #define linearstep(a,b,x) saturate(((x)-(a))/((b)-(a))) 9 | #define lofi(i,m) (floor((i)/(m))*(m)) 10 | #define lofir(i,m) (floor((i)/(m)+0.5)*(m)) 11 | #define pwm(x,d) (step(fract(x),(d))*2.0-1.0) 12 | #define tri(p) (1.-4.*abs(fract(p)-0.5)) 13 | #define n2f(n) (440.0*pow(2.0,(n)/12.0)) 14 | #define inRange(a,b,x) ((a)<=(x)&&(x)<(b)) 15 | #define geta(i,m) (m) 16 | #define sinexp(i) (sin(PI*exp(i))) 17 | #define mp2zp(i) (.5+.5*(i)) 18 | #define zp2mp(i) ((i)*2.-1.) 19 | #define beat *60.0/BPM 20 | 21 | // == sections ================================================================= 22 | #define SECTION_HI_RIM (48.0 beat) 23 | #define SECTION_HI_ACID (80.0 beat) 24 | #define SECTION_HI_ARP (112.0 beat) 25 | #define SECTION_ITS_BEGINNING (144.0 beat) 26 | #define SECTION_AAAAA (240.0 beat) 27 | #define SECTION_WHAT_THE (304.0 beat) 28 | #define SECTION_FINISH (368.0 beat) 29 | 30 | // == h ======================================================================== 31 | precision highp float; 32 | 33 | // == globals ================================================================== 34 | float kickTime; 35 | float sidechain; 36 | vec2 rimshotTime; 37 | float clavTime; 38 | float hihatTime; 39 | float hihatOpen; 40 | 41 | // == uniforms ================================================================= 42 | uniform bool isInitialFrame; 43 | uniform float time; 44 | uniform vec2 resolution; 45 | uniform float progress; 46 | uniform float deltaTime; 47 | uniform float totalFrame; 48 | uniform vec3 cameraPos; 49 | uniform vec3 cameraTar; 50 | uniform float cameraRoll; 51 | uniform float perspFov; 52 | uniform float perspNear; 53 | uniform float perspFar; 54 | uniform vec3 lightPos; 55 | uniform vec3 lightCol; 56 | uniform mat4 matP; 57 | uniform mat4 matV; 58 | uniform mat4 matPL; 59 | uniform mat4 matVL; 60 | uniform sampler2D samplerRandomStatic; 61 | uniform sampler2D samplerRandomDynamic; 62 | uniform vec2 zOffset; 63 | uniform vec2 mouse; 64 | uniform vec3 bgColor; 65 | 66 | // == prng ===================================================================== 67 | float prng(inout vec4 n) 68 | { 69 | // Based on the post http://gpgpu.org/forums/viewtopic.php?t=2591&sid=17051481b9f78fb49fba5b98a5e0f1f3 70 | // (The page no longer exists as of March 17th, 2015. Please let me know if you see why this code works.) 71 | const vec4 q = vec4( 1225.0, 1585.0, 2457.0, 2098.0); 72 | const vec4 r = vec4( 1112.0, 367.0, 92.0, 265.0); 73 | const vec4 a = vec4( 3423.0, 2646.0, 1707.0, 1999.0); 74 | const vec4 m = vec4(4194287.0, 4194277.0, 4194191.0, 4194167.0); 75 | 76 | vec4 beta = floor(n / q); 77 | vec4 p = a * (n - beta * q) - beta * r; 78 | beta = (sign(-p) + vec4(1.0)) * vec4(0.5) * m; 79 | n = (p + beta); 80 | 81 | return fract(dot(n / m, vec4(1.0, -1.0, 1.0, -1.0))); 82 | } 83 | 84 | vec3 randomSphere( inout vec4 seed ) { 85 | vec3 v; 86 | for ( int i = 0; i < 10; i ++ ) { 87 | v = vec3( 88 | prng( seed ), 89 | prng( seed ), 90 | prng( seed ) 91 | ) * 2.0 - 1.0; 92 | if ( length( v ) < 1.0 ) { break; } 93 | } 94 | return v; 95 | } 96 | 97 | vec2 randomCircle( inout vec4 seed ) { 98 | vec2 v; 99 | for ( int i = 0; i < 10; i ++ ) { 100 | v = vec2( 101 | prng( seed ), 102 | prng( seed ) 103 | ) * 2.0 - 1.0; 104 | if ( length( v ) < 1.0 ) { break; } 105 | } 106 | return v; 107 | } 108 | 109 | vec3 randomBox( inout vec4 seed ) { 110 | vec3 v; 111 | v = vec3( 112 | prng( seed ), 113 | prng( seed ), 114 | prng( seed ) 115 | ) * 2.0 - 1.0; 116 | return v; 117 | } 118 | 119 | // == snoise =================================================================== 120 | // 121 | // Description : Array and textureless GLSL 2D/3D/4D simplex 122 | // noise functions. 123 | // Author : Ian McEwan, Ashima Arts. 124 | // Maintainer : ijm 125 | // Lastmod : 20110822 (ijm) 126 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 127 | // Distributed under the MIT License. See LICENSE file. 128 | // https://github.com/ashima/webgl-noise 129 | // 130 | 131 | vec4 mod289(vec4 x) { 132 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 133 | 134 | float mod289(float x) { 135 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 136 | 137 | vec4 permute(vec4 x) { 138 | return mod289(((x*34.0)+1.0)*x); 139 | } 140 | 141 | float permute(float x) { 142 | return mod289(((x*34.0)+1.0)*x); 143 | } 144 | 145 | vec4 taylorInvSqrt(vec4 r) 146 | { 147 | return 1.79284291400159 - 0.85373472095314 * r; 148 | } 149 | 150 | float taylorInvSqrt(float r) 151 | { 152 | return 1.79284291400159 - 0.85373472095314 * r; 153 | } 154 | 155 | vec4 grad4(float j, vec4 ip) 156 | { 157 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 158 | vec4 p,s; 159 | 160 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 161 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 162 | s = vec4(lessThan(p, vec4(0.0))); 163 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 164 | 165 | return p; 166 | } 167 | 168 | // (sqrt(5) - 1)/4 = F4, used once below 169 | #define F4 0.309016994374947451 170 | 171 | float noise4d(vec4 v) 172 | { 173 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 174 | 0.276393202250021, // 2 * G4 175 | 0.414589803375032, // 3 * G4 176 | -0.447213595499958); // -1 + 4 * G4 177 | 178 | // First corner 179 | vec4 i = floor(v + dot(v, vec4(F4)) ); 180 | vec4 x0 = v - i + dot(i, C.xxxx); 181 | 182 | // Other corners 183 | 184 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 185 | vec4 i0; 186 | vec3 isX = step( x0.yzw, x0.xxx ); 187 | vec3 isYZ = step( x0.zww, x0.yyz ); 188 | // i0.x = dot( isX, vec3( 1.0 ) ); 189 | i0.x = isX.x + isX.y + isX.z; 190 | i0.yzw = 1.0 - isX; 191 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 192 | i0.y += isYZ.x + isYZ.y; 193 | i0.zw += 1.0 - isYZ.xy; 194 | i0.z += isYZ.z; 195 | i0.w += 1.0 - isYZ.z; 196 | 197 | // i0 now contains the unique values 0,1,2,3 in each channel 198 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 199 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 200 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 201 | 202 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 203 | // x1 = x0 - i1 + 1.0 * C.xxxx 204 | // x2 = x0 - i2 + 2.0 * C.xxxx 205 | // x3 = x0 - i3 + 3.0 * C.xxxx 206 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 207 | vec4 x1 = x0 - i1 + C.xxxx; 208 | vec4 x2 = x0 - i2 + C.yyyy; 209 | vec4 x3 = x0 - i3 + C.zzzz; 210 | vec4 x4 = x0 + C.wwww; 211 | 212 | // Permutations 213 | i = mod289(i); 214 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 215 | vec4 j1 = permute( permute( permute( permute ( 216 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 217 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 218 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 219 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 220 | 221 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 222 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 223 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 224 | 225 | vec4 p0 = grad4(j0, ip); 226 | vec4 p1 = grad4(j1.x, ip); 227 | vec4 p2 = grad4(j1.y, ip); 228 | vec4 p3 = grad4(j1.z, ip); 229 | vec4 p4 = grad4(j1.w, ip); 230 | 231 | // Normalise gradients 232 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 233 | p0 *= norm.x; 234 | p1 *= norm.y; 235 | p2 *= norm.z; 236 | p3 *= norm.w; 237 | p4 *= taylorInvSqrt(dot(p4,p4)); 238 | 239 | // Mix contributions from the five corners 240 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 241 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 242 | m0 = m0 * m0; 243 | m1 = m1 * m1; 244 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 245 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 246 | 247 | } 248 | 249 | // == misc. ==================================================================== 250 | float calcDepth( float z ) { 251 | return linearstep( perspNear, perspFar, z ); 252 | } 253 | 254 | float calcDepth( vec3 v ) { 255 | return calcDepth( dot( normalize( cameraTar - cameraPos ), v ) ); 256 | } 257 | 258 | float calcDepthL( vec3 v ) { 259 | return calcDepth( dot( normalize( cameraTar - lightPos ), v ) ); 260 | } 261 | 262 | vec4 random4( float t ) { 263 | return texture2D( samplerRandomStatic, lofi( t * vec2( 0.741, 0.891 ), 1.0 / 2048.0 ) + 0.5 / 2048.0 ); 264 | } 265 | 266 | vec4 random4( vec2 v ) { 267 | return texture2D( samplerRandomStatic, lofi( v, 1.0 / 2048.0 ) + 0.5 / 2048.0 ); 268 | } 269 | 270 | mat2 rotate2D( float _t ) { 271 | return mat2( cos( _t ), sin( _t ), -sin( _t ), cos( _t ) ); 272 | } 273 | 274 | float smin( float a, float b, float k ) { 275 | float res = exp( -k * a ) + exp( -k * b ); 276 | return -log( res ) / k; 277 | } 278 | 279 | vec3 catColor( float _p ) { 280 | return 0.5 + 0.5 * vec3( 281 | cos( _p ), 282 | cos( _p + PI / 3.0 * 4.0 ), 283 | cos( _p + PI / 3.0 * 2.0 ) 284 | ); 285 | } 286 | 287 | // == rhythms ================================================================== 288 | float calcRhythms( float aTime, float rawTime ) { 289 | // -- glitch ----------------------------------------------------------------- 290 | { 291 | vec4 dice = random4( lofi( aTime, 0.5 beat ) - 4.78 ); 292 | float thr = ( 293 | rawTime < SECTION_AAAAA ? 294 | 0.2 * linearstep( SECTION_AAAAA - 10.0 beat, SECTION_AAAAA, rawTime ) : 295 | 0.0 296 | ); 297 | 298 | if ( dice.x < thr ) { 299 | aTime -= 0.82 * lofi( mod( aTime, 0.5 beat ), 0.03 ); 300 | } else if ( dice.x < 2.0 * thr ) { 301 | aTime -= 0.95 * lofi( mod( aTime, 0.5 beat ), 0.017 ); 302 | } else if ( dice.x < 3.0 * thr ) { 303 | aTime -= lofi( mod( aTime, 0.5 beat ), 0.125 beat ); 304 | } else if ( dice.x < 4.0 * thr ) { 305 | aTime -= pow( 2.0 * mod( aTime, 0.5 beat ), 2.0 ); 306 | } 307 | } 308 | 309 | // -- kick ------------------------------------------------------------------- 310 | kickTime = mod( mod( aTime, 2.0 beat ), 0.75 beat ); 311 | sidechain = smoothstep( 0.0, 0.3, kickTime ); 312 | kickTime = ( 313 | rawTime < ( SECTION_ITS_BEGINNING - 8.0 beat ) ? kickTime : 314 | rawTime < SECTION_ITS_BEGINNING ? aTime - 8.0 beat : 315 | rawTime < ( SECTION_AAAAA - 4.0 beat ) ? mod( aTime, 2.0 beat ) : 316 | rawTime < SECTION_AAAAA ? aTime - 12.0 beat : 317 | inRange( SECTION_WHAT_THE - 4.0 beat, SECTION_WHAT_THE, rawTime ) ? aTime - 12.0 beat : 318 | SECTION_FINISH + 0.75 beat < rawTime ? rawTime - SECTION_FINISH : 319 | kickTime 320 | ); 321 | kickTime += 15.75 beat < aTime ? ( aTime - 15.75 beat ) : 0.0; 322 | 323 | // -- rimshot ---------------------------------------------------------------- 324 | rimshotTime = ( 325 | inRange( SECTION_HI_RIM, SECTION_ITS_BEGINNING - 4.0 beat, rawTime ) 326 | ? vec2( 327 | mod( mod( mod( aTime, 2.25 beat ), 1.25 beat ), 0.5 beat ), 328 | mod( mod( mod( aTime - 0.25 beat, 2.75 beat ), 0.75 beat ), 0.5 beat ) 329 | ) 330 | : vec2( 1E9 ) 331 | ); 332 | 333 | // -- clav ------------------------------------------------------------------- 334 | clavTime = ( 335 | inRange( SECTION_HI_RIM, SECTION_ITS_BEGINNING, rawTime ) 336 | ? mod( mod( mod( aTime - 0.75, 3.75 beat ), 2.75 beat ), 0.75 beat ) 337 | : 1E9 338 | ); 339 | 340 | // -- hihat ------------------------------------------------------------------ 341 | hihatTime = mod( aTime, 0.5 beat ); 342 | vec4 dice = random4( aTime - hihatTime ); 343 | float trrr = rawTime < SECTION_ITS_BEGINNING ? 1.9 : 2.2; 344 | hihatOpen = rawTime < SECTION_ITS_BEGINNING ? 200.0 : mix( 20.0, 200.0, dice.x ); 345 | hihatTime = mod( hihatTime, 0.5 / pow( 2.0, floor( trrr * dice.z ) ) beat ); 346 | hihatTime = ( 347 | inRange( SECTION_ITS_BEGINNING - 4.0 beat, SECTION_AAAAA - 32.0 beat, rawTime ) ? 348 | 1E9 : 349 | hihatTime 350 | ); 351 | 352 | // -- end -------------------------------------------------------------------- 353 | return aTime; 354 | } 355 | 356 | void calcRhythms() { 357 | calcRhythms( mod( time, 16.0 beat ), time ); 358 | } 359 | -------------------------------------------------------------------------------- /src/shaders/audio.frag: -------------------------------------------------------------------------------- 1 | float aTime; 2 | float rawTime; 3 | float pattern; 4 | 5 | uniform float timeHead; 6 | uniform float patternHead; 7 | uniform float sampleRate; 8 | uniform float bufferSize; 9 | uniform sampler2D samplerChord; 10 | 11 | vec2 aNoise2( float t ) { 12 | return 1.0 - 2.0 * texture2D( samplerRandomStatic, t * vec2( 0.741, 0.891 ) ).xy; 13 | } 14 | 15 | float i2f( float i ) { 16 | float chord = ( 17 | rawTime < SECTION_WHAT_THE ? 18 | 0.0 : 19 | mod( floor( rawTime / ( 8.0 beat ) ), 4.0 ) 20 | ); 21 | 22 | vec2 noteUv = ( vec2( mod( floor( i ), 8.0 ), chord ) + 0.5 ) / vec2( 8.0, 4.0 ); 23 | float note = texture2D( samplerChord, noteUv ).x; 24 | 25 | float trans = -16.8 + ( 26 | rawTime < SECTION_WHAT_THE ? 27 | 0.0 : 28 | mod( 5.0 - floor( rawTime / ( 8.0 beat ) ), 12.0 ) - 5.0 29 | ); 30 | 31 | return n2f( 32 | note + trans + 33 | floor( i / 8.0 ) * 24.0 + 34 | 12.0 * linearstep( SECTION_AAAAA - 16.0 beat, SECTION_AAAAA, rawTime ) + 35 | ( SECTION_AAAAA < rawTime ? -12.0 : 0.0 ) 36 | ); 37 | } 38 | 39 | vec2 kick( float t ) { 40 | float attack = rawTime < SECTION_AAAAA ? 3.0 : 20.0; 41 | float lorate = rawTime < SECTION_AAAAA ? 1E-9 : 0.04; 42 | 43 | return vec2( exp( -10.0 * t ) * sin( TAU * lofi( 44 | i2f( -8.0 ) * t - attack * ( exp( -40.0 * t ) + exp( -200.0 * t ) ), 45 | lorate ) ) ); 46 | } 47 | 48 | vec2 snare( float _phase ) { 49 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 50 | return aSaturate( ( 51 | aNoise2( _phase / 0.01 ).xy + 52 | sin( _phase * 3600.0 * vec2( 1.005, 0.995 ) - exp( -_phase * 800.0 ) * 30.0 ) 53 | ) * 4.0 * exp( -_phase * 23.0 ) ); 54 | } 55 | 56 | vec2 snare909( float _phase ) { 57 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 58 | return aSaturate( ( 59 | aNoise2( _phase / 0.01 ).xy * 2.0 + 60 | sin( _phase * 1400.0 * vec2( 1.005, 0.995 ) - exp( -_phase * 80.0 ) * 30.0 ) 61 | ) * 1.0 * exp( -_phase * 10.0 ) ); 62 | } 63 | 64 | vec2 tableTalking( float t, float offset ) { 65 | vec4 dice = random4( offset ); 66 | return exp( -mix( 10.0, 30.0, dice.w ) * t ) * aSaturate( 5.0 * aNoise2( 67 | 100.0 * mod( t, mix( 0.008, 0.08, dice.x ) ) * mix( 0.01, 0.03, dice.y ) + 100.0 * dice.z 68 | ) ); 69 | } 70 | 71 | vec2 bass( float freq, float t ) { 72 | vec2 hi = 16.0 + 0.1 * smoothstep( 0.0, 0.4, t ) * vec2( -1.0, 1.0 ); 73 | vec2 fm = 0.1 * smoothstep( 0.0, 0.4, t ) * sin( 74 | TAU * freq * t + sin( TAU * hi * freq * t ) 75 | ); 76 | return vec2( tri( lofi( freq * t + fm, 0.0625 ) ) ); 77 | } 78 | 79 | vec2 filterSaw( float freq, float time, float cutoff, float resonance ) { 80 | if ( time < 0.0 ) { return vec2( 0.0 ); } 81 | vec2 sum = vec2( 0.0 ); 82 | for ( int i = 1; i <= 32; i ++ ) { 83 | float fi = float( i ); 84 | float cut = smoothstep( cutoff * 1.2, cutoff * 0.8, fi * freq ); 85 | cut += smoothstep( cutoff * 0.3, 0.0, abs( cutoff - fi * freq ) ) * resonance; 86 | vec2 offset = vec2( -1.0, 1.0 ) * ( 0.1 * ( fi - 1.0 ) ); 87 | sum += sin( fi * freq * time * TAU + offset ) / fi * cut; 88 | } 89 | return sum; 90 | } 91 | 92 | vec2 rimshot( float _phase ) { 93 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 94 | float attack = exp( -_phase * 400.0 ) * 0.6; 95 | vec2 wave = ( 96 | tri( _phase * 450.0 * vec2( 1.005, 0.995 ) - attack ) + 97 | tri( _phase * 1800.0 * vec2( 0.995, 1.005 ) - attack ) 98 | ); 99 | return aSaturate( 4.0 * wave * exp( -_phase * 400.0 ) ); 100 | } 101 | 102 | vec2 cowbell( float _phase ) { 103 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 104 | float attack = exp( -_phase * 800.0 ) * 20.0; 105 | vec2 wave = ( 106 | pwm( _phase * 1600.0 * vec2( 1.005, 0.995 ) - attack, vec2( 0.5 ) ) + 107 | pwm( _phase * 1080.0 * vec2( 0.995, 1.005 ) - attack, vec2( 0.5 ) ) 108 | ) * sin( _phase * 40.0 * TAU ); 109 | return wave * exp( -_phase * 20.0 ); 110 | } 111 | 112 | vec2 chordSaw( float freq, float time ) { 113 | vec2 p = fract( time * vec2( 0.99, 1.01 ) * freq ); 114 | return ( 115 | fract( 2.0 * p + 1.6 * sin( 2.0 * TAU * p ) ) - 0.5 + 116 | fract( 1.0 * p ) - 0.5 117 | ); 118 | } 119 | 120 | vec2 wavetable( float freq, float time, float speed, float offset ) { 121 | if ( time < 0.0 ) { return vec2( 0.0 ); } 122 | float p = tri( freq * time ); 123 | return aNoise2( p * speed + offset ); 124 | } 125 | 126 | vec2 clap( float _phase ) { 127 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 128 | float amp = exp( -14.0 * _phase ); 129 | amp *= mix( 130 | smoothstep( 0.5, 0.4, sin( 500.0 * _phase ) ), 131 | 1.0, 132 | smoothstep( 0.04, 0.05, _phase ) 133 | ); 134 | vec2 wave = wavetable( 1.2, _phase, 1.0, 0.0 ); 135 | return lofir( amp * wave, 0.25 ); 136 | } 137 | 138 | vec2 crash( float _phase, float decay ) { 139 | if ( _phase < 0.0 ) { return vec2( 0.0 ); } 140 | float amp = exp( -decay * _phase ); 141 | vec2 wave = wavetable( 96.1, _phase, 0.5, 0.0 ); 142 | return amp * wave; 143 | } 144 | 145 | vec2 fmArp( float freq, float time ) { 146 | if ( time < 0.0 ) { return vec2( 0.0 ); } 147 | vec2 p = freq * vec2( 0.999, 1.001 ) * time * TAU; 148 | float dl = exp( -time * 1.0 ); 149 | float ds = exp( -time * 10.0 ); 150 | return ( 151 | sin( p * 1.0003 + sin( p * 11.0035 ) * 1.5 * ds + sin( p * 1.0003 ) * 1.0 * dl ) + 152 | sin( p * 0.9997 + sin( p * 0.9997 + sin( p * 4.9984 ) * 2.0 * dl ) * 0.5 * ds ) 153 | ) * 0.5 * ds; 154 | } 155 | 156 | void main() { 157 | aTime = timeHead + ( floor( gl_FragCoord.x ) + bufferSize * floor( gl_FragCoord.y ) ) / sampleRate; 158 | rawTime = aTime + 16.0 beat * patternHead; 159 | pattern = patternHead + ( 16.0 beat < aTime ? 1.0 : 0.0 ); 160 | aTime = mod( aTime, 16.0 beat ); 161 | 162 | vec2 dest = vec2( 0.0 ); 163 | 164 | // -- tone ------------------------------------------------------------------- 165 | if ( rawTime < 16.0 beat ) { 166 | if ( 8.0 beat < aTime ) { 167 | float freq = mod( aTime, 4.0 beat ) < 1.0 beat ? 2000.0 : 1000.0; 168 | float amp = smoothstep( 0.05, 0.04, mod( aTime, 1.0 beat ) ); 169 | dest.xy += amp * sin( freq * TAU * aTime ); 170 | } 171 | 172 | gl_FragColor = vec4( dest, 0.0, 0.0 ); 173 | return; 174 | } 175 | 176 | // -- boom ------------------------------------------------------------------- 177 | if ( inRange( SECTION_AAAAA - 0.6 beat, SECTION_AAAAA, rawTime ) ) { 178 | float t = ( aTime - 15.5 beat ); 179 | gl_FragColor = vec4( 0.4 * wavetable( 180 | 70.0, t, 0.2 * exp( -12.0 * t ), 0.0 181 | ), 0.0, 0.0 ); 182 | return; 183 | } 184 | 185 | aTime = calcRhythms( aTime, rawTime ); 186 | 187 | // -- kick ------------------------------------------------------------------- 188 | dest += 0.5 * kick( kickTime ); 189 | 190 | // -- sweep ------------------------------------------------------------------ 191 | if ( inRange( SECTION_AAAAA - 16.0 beat, SECTION_AAAAA, rawTime ) ) { 192 | float buildup = linearstep( SECTION_AAAAA - 16.0 beat, SECTION_AAAAA, rawTime ); 193 | dest += 0.1 * sqrt( buildup ) * wavetable( 40.0, aTime, 0.1 * buildup, 0.0 ); 194 | } 195 | 196 | // -- crash ------------------------------------------------------------------ 197 | { 198 | float amp = 0.2 * mix( 0.4, 1.0, sidechain ); 199 | dest += ( 200 | inRange( SECTION_HI_ACID, SECTION_HI_ACID + 16.0 beat, rawTime ) || 201 | inRange( SECTION_ITS_BEGINNING, SECTION_ITS_BEGINNING + 16.0 beat, rawTime ) || 202 | inRange( SECTION_AAAAA, SECTION_AAAAA + 16.0 beat, rawTime ) || 203 | inRange( SECTION_WHAT_THE, SECTION_WHAT_THE + 16.0 beat, rawTime ) || 204 | inRange( SECTION_FINISH, SECTION_FINISH + 16.0 beat, rawTime ) 205 | ) ? amp * crash( aTime, rawTime < SECTION_AAAAA ? 4.0 : 1.0 ) : vec2( 0.0 ); 206 | } 207 | 208 | // -- clap ------------------------------------------------------------------- 209 | if ( inRange( SECTION_HI_ACID - 1.0 beat, SECTION_ITS_BEGINNING, rawTime ) ) { 210 | dest += ( 211 | rawTime < SECTION_ITS_BEGINNING - 8.0 beat ? 212 | 0.2 * clap( mod( aTime - 1.0 beat, 2.0 beat ) ) : 213 | vec2( 0.0 ) 214 | ); 215 | } 216 | 217 | // -- acid ------------------------------------------------------------------- 218 | if ( inRange( SECTION_HI_ACID, SECTION_ITS_BEGINNING, rawTime ) ) { 219 | if ( mod( aTime, 0.25 beat ) < 0.2 beat ) { 220 | float t = mod( aTime, 0.25 beat ); 221 | vec4 dice = random4( lofi( rawTime + 4.12, 0.25 beat ) ); 222 | float filt = ( 223 | 100.0 + 224 | mix( 1000.0, 4000.0, dice.z ) * exp( -mix( 20.0, 40.0, dice.w ) * t ) 225 | ); 226 | float freq = ( 227 | i2f( -8.0 + 8.0 * pow( dice.y, 5.0 ) ) * 228 | ( dice.x < 0.2 ? 1.335 : 1.0 ) * 229 | ( dice.w < 0.5 ? 2.0 : 1.0 ) 230 | ); 231 | float amp = 0.16 * smoothstep( 0.25 beat, 0.2 beat, t ); 232 | dest += amp * aSaturate( 0.7 * filterSaw( freq, t, filt, filt / 500.0 ) ); 233 | } 234 | } 235 | 236 | // -- bass ------------------------------------------------------------------- 237 | if ( SECTION_AAAAA < rawTime ) { 238 | dest += 0.4 * sidechain * bass( i2f( -8.0 ), kickTime ); 239 | } 240 | 241 | // -- hihat ------------------------------------------------------------------ 242 | { 243 | float amp = 0.3 * exp( -hihatOpen * hihatTime ); 244 | vec2 wave = wavetable( 80.0, hihatTime, 0.4, 0.0 ); 245 | dest += amp * wave; 246 | } 247 | 248 | // -- click ------------------------------------------------------------------ 249 | if ( rawTime < SECTION_ITS_BEGINNING - 1.0 beat ) { 250 | float t = mod( aTime, 0.25 beat ); 251 | vec4 dice = random4( lofi( aTime, 0.25 beat ) ); 252 | t = mod( t, 0.25 / pow( 2.0, floor( 5.0 * pow( dice.z, 20.0 ) ) ) beat ); 253 | float amp = 0.3 * exp( -100.0 * t ); 254 | dest += amp * dice.xy; 255 | } 256 | 257 | // -- clav ------------------------------------------------------------------- 258 | if ( inRange( SECTION_HI_RIM, SECTION_ITS_BEGINNING, rawTime ) ) { 259 | float amp = exp( -100.0 * clavTime ); 260 | float phase = clavTime + tri( 700.0 * clavTime ); 261 | dest += 0.2 * amp * tri( phase * vec2( 0.9, 1.1 ) ); 262 | } 263 | 264 | // -- rimshot ---------------------------------------------------------------- 265 | if ( inRange( SECTION_HI_RIM, SECTION_ITS_BEGINNING - 4.0 beat, rawTime ) ) { 266 | dest.x += 0.3 * rimshot( rimshotTime.x ).x; 267 | dest.y += 0.3 * rimshot( rimshotTime.y ).y; 268 | } 269 | 270 | // -- snare ------------------------------------------------------------------ 271 | if ( 272 | inRange( SECTION_AAAAA, SECTION_FINISH, rawTime ) && 273 | !inRange( SECTION_WHAT_THE - 3.0 beat, SECTION_WHAT_THE, rawTime ) 274 | ) { 275 | dest += 0.2 * snare( mod( aTime - 1.0 beat, 2.0 beat ) ); 276 | } 277 | 278 | // -- snare909 --------------------------------------------------------------- 279 | if ( inRange( SECTION_WHAT_THE - 8.0 beat, SECTION_WHAT_THE, rawTime ) ) { 280 | float t = rawTime < SECTION_WHAT_THE - 2.0 beat ? mod( aTime, 0.25 beat ) : mod( aTime, 0.125 beat ); 281 | float amp = 0.14 * smoothstep( SECTION_WHAT_THE - 8.0 beat, SECTION_WHAT_THE, rawTime ); 282 | dest += amp * snare909( t ); 283 | } 284 | 285 | // -- cowbell ---------------------------------------------------------------- 286 | if ( SECTION_AAAAA < rawTime ) { 287 | dest += 0.1 * cowbell( mod( aTime - 0.25 beat, 2.0 beat ) ); 288 | } 289 | 290 | // -- ???? ------------------------------------------------------------------- 291 | if ( SECTION_AAAAA < rawTime ) { 292 | float t = mod( aTime, 32.0 beat ); 293 | float amp = 0.06 * sidechain; 294 | vec2 wave = wavetable( 170.0, t, sidechain * 0.05, 0.0 ); 295 | dest += amp * wave; 296 | 297 | dest += 0.08 * tableTalking( mod( aTime, 0.25 beat ), 4.8 * lofi( rawTime, 0.25 beat ) ); 298 | } 299 | 300 | // -- pads ------------------------------------------------------------------- 301 | { 302 | vec2 wave = vec2( 0.0 ); 303 | for ( int i = 0; i < 8; i ++ ) { 304 | wave += ( 305 | rawTime < SECTION_ITS_BEGINNING + 16.0 beat ? 306 | vec2( sin( TAU * lofi( 307 | i2f( float( i ) ) * aTime + sin( float( i ) + 2.0 * aTime ), 308 | 0.0625 309 | ) ) ) : 310 | chordSaw( i2f( 2.3 * float( i - 2 ) ), aTime ) 311 | ); 312 | } 313 | float env = 0.3 * linearstep( SECTION_ITS_BEGINNING + 16.0 beat, SECTION_AAAAA, rawTime ); 314 | float amp = 0.07 * sidechain * ( 315 | rawTime < SECTION_ITS_BEGINNING + 16.0 beat ? 316 | 0.8 * smoothstep( SECTION_ITS_BEGINNING + 16.0 beat, SECTION_ITS_BEGINNING, rawTime ) : 317 | smoothstep( ( 0.1 + env ) beat, ( -0.2 + env ) beat, mod( aTime, 0.25 beat ) ) 318 | ); 319 | dest += amp * wave; 320 | } 321 | 322 | // -- arps ------------------------------------------------------------------- 323 | if ( SECTION_HI_ARP < rawTime ) { 324 | for ( int i = 0; i < 3; i ++ ) { 325 | float timeD = aTime - float( i ) * 0.75 beat; 326 | float t = mod( timeD, 0.25 beat ); 327 | vec4 dice = random4( timeD - t + 2.59 ); 328 | 329 | float buildup = smoothstep( SECTION_ITS_BEGINNING, SECTION_AAAAA, rawTime ); 330 | 331 | float amp = ( 332 | rawTime < SECTION_WHAT_THE ? 333 | 0.12 * exp( -mix( 40.0, 10.0, buildup ) * t ) : 334 | 0.1 335 | ) * exp( -mix( 4.0, 1.0, buildup ) * float( i ) ); 336 | float freq = ( 337 | rawTime < SECTION_WHAT_THE ? 338 | i2f( 1.3 * floor( mod( timeD / ( 0.25 beat ), 8.0 ) ) ) : 339 | i2f( 340 | floor( 3.0 + 3.0 * tri( ( timeD - t ) / ( 1.0 beat ) ) + dice.x * 10.0 ) + 341 | ( 0.8 < dice.y && 0.125 beat < t ? 2.0 : 0.0 ) 342 | ) 343 | ); 344 | float form = ( 345 | rawTime < SECTION_WHAT_THE ? 346 | mix( 0.001, exp( -8.0 * t ) * 0.004, buildup ) : 347 | -0.0015 * smoothstep( 0.4 beat, 0.0, t ) 348 | ); 349 | 350 | dest += amp * wavetable( freq, t, form, dice.w ); 351 | } 352 | } 353 | 354 | // -- chip ------------------------------------------------------------------- 355 | if ( SECTION_AAAAA < rawTime ) { 356 | float freq = 2.0 * i2f( floor( mod( aTime * BPM / 3.75, 8.0 ) ) ); 357 | dest += vec2( 0.07 ) * sidechain * pwm( freq * aTime, 0.25 ); 358 | } 359 | 360 | // fade out 361 | dest *= smoothstep( 150.0, 142.0, rawTime ); 362 | 363 | gl_FragColor = vec4( dest, 0.0, 0.0 ); 364 | } 365 | -------------------------------------------------------------------------------- /src/shaders/bg.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | precision highp float; 3 | 4 | // ------ 5 | 6 | void main() { 7 | gl_FragData[ 0 ] = vec4( bgColor, 1.0 ); 8 | gl_FragData[ 1 ] = vec4( perspFar, 0.0, 0.0, 1.0 ); 9 | } -------------------------------------------------------------------------------- /src/shaders/bloom-post.frag: -------------------------------------------------------------------------------- 1 | uniform sampler2D samplerDry; 2 | uniform sampler2D samplerWet; 3 | 4 | void main() { 5 | vec2 uv = gl_FragCoord.xy / resolution; 6 | vec3 dry = texture2D( samplerDry, uv ).xyz; 7 | vec3 wet = texture2D( samplerWet, uv ).xyz; 8 | gl_FragColor = vec4( saturate( dry + wet ), 1.0 ); 9 | } 10 | -------------------------------------------------------------------------------- /src/shaders/bloom-pre.frag: -------------------------------------------------------------------------------- 1 | uniform vec3 bias; 2 | uniform vec3 factor; 3 | uniform sampler2D sampler0; 4 | 5 | void main() { 6 | vec2 uv = gl_FragCoord.xy / resolution; 7 | vec2 deltaTexel = 0.25 / resolution; 8 | vec2 uvOrigin = ( floor( gl_FragCoord.xy ) + deltaTexel * 0.5 ) / resolution; 9 | 10 | vec3 sum = vec3( 0.0 ); 11 | for ( int iy = 0; iy < 4; iy ++ ) { 12 | for ( int ix = 0; ix < 4; ix ++ ) { 13 | vec2 uv = uvOrigin + vec2( ix, iy ) * deltaTexel; 14 | sum += texture2D( sampler0, uv ).xyz / 16.0; 15 | } 16 | } 17 | 18 | gl_FragColor = vec4( 19 | max( vec3( 0.0 ), ( sum + bias ) * factor ), 20 | 1.0 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/shaders/box.frag: -------------------------------------------------------------------------------- 1 | // == shit ===================================================================== 2 | #extension GL_EXT_draw_buffers : require 3 | 4 | // == varyings ================================================================= 5 | varying vec3 vPos; 6 | varying vec3 vNor; 7 | 8 | // == uniforms ================================================================= 9 | uniform bool isShadow; 10 | 11 | // == main ===================================================================== 12 | void main() { 13 | if ( isShadow ) { 14 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 15 | return; 16 | } 17 | 18 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 19 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 20 | gl_FragData[ 2 ] = vec4( 0.01, 0.03, 0.1, 3.0 ); 21 | } -------------------------------------------------------------------------------- /src/shaders/box.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec3 aNor; 4 | attribute vec2 aMatrix; 5 | 6 | // == varyings ================================================================= 7 | varying vec3 vPos; 8 | varying vec3 vNor; 9 | 10 | uniform bool isShadow; 11 | 12 | uniform float posOffset; 13 | uniform float spinOffset; 14 | uniform float size; 15 | 16 | uniform mat4 matM; 17 | 18 | // == main ===================================================================== 19 | void main() { 20 | calcRhythms(); 21 | 22 | vec4 pos = vec4( ( 1.0 + 0.2 * sin( PI * exp( -10.0 * kickTime ) ) ) * size * aPos, 1.0 ); 23 | pos.yz = rotate2D( spinOffset * aMatrix.x + 1.3 * time ) * pos.yz; 24 | pos.zx = rotate2D( spinOffset * aMatrix.y + 0.7 * time ) * pos.zx; 25 | pos.xy += posOffset * aMatrix; 26 | pos = matM * pos; 27 | vPos = pos.xyz; 28 | 29 | vec4 nor = vec4( aNor, 0.0 ); 30 | nor.yz = rotate2D( spinOffset * aMatrix.x + 1.3 * time ) * nor.yz; 31 | nor.zx = rotate2D( spinOffset * aMatrix.y + 0.7 * time ) * nor.zx; 32 | nor = normalize( matM * nor ); 33 | vNor = nor.xyz; 34 | 35 | vec4 outPos; 36 | if ( isShadow ) { 37 | outPos = matPL * matVL * pos; 38 | } else { 39 | outPos = matP * matV * pos; 40 | outPos.x /= resolution.x / resolution.y; 41 | } 42 | gl_Position = outPos; 43 | } -------------------------------------------------------------------------------- /src/shaders/circle.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | // == varyings ================================================================= 4 | varying vec3 vPos; 5 | varying vec2 vUv; 6 | varying vec3 vNor; 7 | varying float vScale; 8 | varying float vPattern; 9 | 10 | // == uniforms ================================================================= 11 | uniform bool isShadow; 12 | uniform float availZ; 13 | 14 | // == main ===================================================================== 15 | void main() { 16 | if ( isShadow ) { 17 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 18 | return; 19 | } 20 | 21 | if ( 22 | vPos.z < availZ || 23 | inRange( availZ + 0.5, availZ + 1.0, vPos.z ) 24 | ) { discard; } 25 | 26 | // this bool means visibility 27 | bool b = false; 28 | 29 | vec2 uv = vUv; 30 | 31 | if ( vPattern < 0.5 ) { 32 | vec2 uv = abs( uv - 0.5 ); 33 | b = ( 34 | ( uv.x < 0.15 / vScale ) ? ( 35 | ( uv.y - 0.45 < 0.03 / vScale ) && 36 | !( ( uv.y - 0.45 ) - ( uv.x - 0.025 / vScale ) * sqrt( 3.0 ) < 0.0 ) 37 | ) : abs( length( uv ) - 0.45 ) < 0.01 / vScale 38 | ); 39 | } else if ( vPattern < 1.5 ) { 40 | vec2 uv = uv - 0.5; 41 | b = ( 42 | abs( fract( atan( uv.y, uv.x ) * 40.0 / PI ) - 0.5 ) < 0.2 / vScale / length( uv ) && 43 | abs( length( uv ) - 0.45 ) < 0.05 / vScale 44 | ); 45 | } else if ( vPattern < 2.5 ) { 46 | vec2 uv = abs( uv - 0.5 ); 47 | b = ( 48 | abs( length( uv ) - 0.45 ) < 0.04 / vScale && 49 | 0.1 / vScale < min( uv.x, uv.y ) || 50 | abs( length( uv ) - 0.47 ) < 0.01 / vScale 51 | ); 52 | } 53 | 54 | if ( !b ) { discard; } 55 | 56 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 57 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 58 | gl_FragData[ 2 ] = vec4( 2.0, 0.0, 0.0, 4.0 ); 59 | } -------------------------------------------------------------------------------- /src/shaders/circle.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec2 aUv; 4 | attribute vec3 aNor; 5 | attribute float aMatrix; 6 | 7 | // == varyings ================================================================= 8 | varying vec3 vPos; 9 | varying vec2 vUv; 10 | varying vec3 vNor; 11 | varying float vScale; 12 | varying float vPattern; 13 | 14 | // == uniforms ================================================================= 15 | uniform bool isShadow; 16 | 17 | // == main ===================================================================== 18 | void main() { 19 | vec4 pos = vec4( aPos, 1.0 ); 20 | pos.z += mod( aMatrix + zOffset.x, 30.0 ) - 20.0; 21 | vPos = pos.xyz; 22 | 23 | float rot = time * zp2mp( random4( aMatrix * 0.4 + 0.1 ).x ); 24 | pos.xy = rotate2D( rot ) * pos.xy; 25 | 26 | vUv = aUv; 27 | vScale = 1.5; 28 | vPattern = mod( aMatrix, 3.0 ); 29 | 30 | vec4 nor = normalize( vec4( aNor, 0.0 ) ); 31 | vNor = nor.xyz; 32 | 33 | vec4 outPos; 34 | if ( isShadow ) { 35 | outPos = matPL * matVL * pos; 36 | } else { 37 | outPos = matP * matV * pos; 38 | outPos.x /= resolution.x / resolution.y; 39 | } 40 | gl_Position = outPos; 41 | } -------------------------------------------------------------------------------- /src/shaders/distance.frag: -------------------------------------------------------------------------------- 1 | uniform sampler2D sampler0; 2 | 3 | void main() { 4 | vec2 uv = gl_FragCoord.xy / resolution; 5 | vec4 pos = texture2D( sampler0, uv ); 6 | gl_FragColor = vec4( calcDepth( pos.xyz - cameraPos ), 0.0, 0.0, 1.0 ); 7 | } -------------------------------------------------------------------------------- /src/shaders/dof.frag: -------------------------------------------------------------------------------- 1 | // Ref: https://www.shadertoy.com/view/4d2Xzw 2 | 3 | #define FOG_ONE calcDepth(5.0) 4 | #define FOG_ZERO calcDepth(20.0) 5 | #define BOKEH_ITER 10 6 | #define BOKEH_ANGLE 2.39996 7 | #define BOKEH_RADIUS_MAX (resolution.x / 64.0) 8 | 9 | uniform sampler2D samplerDry; 10 | uniform sampler2D samplerDepth; 11 | 12 | uniform float bokehAmp; 13 | uniform float bokehFocus; 14 | 15 | void main() { 16 | vec2 uv = gl_FragCoord.xy / resolution; 17 | 18 | float radiusPerStep = BOKEH_RADIUS_MAX / float( BOKEH_ITER ); 19 | float radius = radiusPerStep * 1E-5; // EPSILON 20 | vec4 seed = texture2D( samplerRandomDynamic, uv ); 21 | prng( seed ); // mix well 22 | prng( seed ); // mix well 23 | vec2 nOffset = vec2( 0.0, 1.0 ) * rotate2D( TAU * prng( seed ) ); 24 | mat2 rotator = rotate2D( BOKEH_ANGLE ); 25 | 26 | vec4 sum = vec4( 0.0 ); 27 | 28 | for ( int i = 0; i < BOKEH_ITER; i ++ ) { 29 | vec2 uv = ( gl_FragCoord.xy + nOffset * radius ) / resolution; 30 | vec3 col = texture2D( samplerDry, uv ).xyz; 31 | float len = texture2D( samplerDepth, uv ).x; 32 | 33 | col = mix( bgColor.xyz, col, linearstep( FOG_ZERO, FOG_ONE, len ) ); 34 | 35 | float r = min( max( abs( 1.0 / len - 1.0 / calcDepth( bokehFocus ) ) * bokehAmp, 1E-2 ), BOKEH_RADIUS_MAX ); 36 | float amp = 1.0 / ( r / BOKEH_RADIUS_MAX * float( BOKEH_ITER ) ); 37 | amp *= linearstep( radius, radius + radiusPerStep, r ); 38 | sum += vec4( col, 1.0 ) * amp; 39 | 40 | nOffset = rotator * nOffset; 41 | radius += radiusPerStep; 42 | } 43 | 44 | gl_FragColor = vec4( sum.xyz / sum.w, 1.0 ); 45 | } 46 | -------------------------------------------------------------------------------- /src/shaders/fxaa.frag: -------------------------------------------------------------------------------- 1 | #define FXAA_REDUCE_MIN (1.0 / 128.0) 2 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 3 | #define FXAA_SPAN_MAX 16.0 4 | 5 | // ------ 6 | 7 | precision highp float; 8 | 9 | uniform sampler2D sampler0; 10 | 11 | // ------ 12 | 13 | void main() { 14 | vec2 uv = gl_FragCoord.xy / resolution; 15 | 16 | #define T(v) texture2D( sampler0, (v) / resolution ).xyz 17 | vec3 rgb11 = T( gl_FragCoord.xy ); 18 | vec3 rgb00 = T( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ); 19 | vec3 rgb02 = T( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ); 20 | vec3 rgb20 = T( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ); 21 | vec3 rgb22 = T( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ); 22 | #undef T 23 | 24 | vec3 luma = vec3( 0.299, 0.587, 0.114 ); 25 | #define L(c) dot( c, luma ) 26 | float luma11 = L( rgb11 ); 27 | float luma00 = L( rgb00 ); 28 | float luma02 = L( rgb02 ); 29 | float luma20 = L( rgb20 ); 30 | float luma22 = L( rgb22 ); 31 | #undef L 32 | 33 | float lumaMin = min( luma00, min( min( luma00, luma02 ), min( luma20, luma22 ) ) ); 34 | float lumaMax = max( luma00, max( max( luma00, luma02 ), max( luma20, luma22 ) ) ); 35 | 36 | vec2 dir = vec2( 37 | -( ( luma00 + luma20 ) - ( luma02 + luma22 ) ), 38 | ( ( luma00 + luma02 ) - ( luma20 + luma22 ) ) 39 | ); 40 | 41 | float dirReduce = max( 42 | ( luma00 + luma02 + luma20 + luma22 ) * 0.25 * FXAA_REDUCE_MUL, 43 | FXAA_REDUCE_MIN 44 | ); 45 | float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce ); 46 | dir = min( 47 | vec2( FXAA_SPAN_MAX ), 48 | max( 49 | vec2( -FXAA_SPAN_MAX ), 50 | dir * rcpDirMin 51 | ) 52 | ) / resolution; 53 | 54 | vec3 rgbA = 0.5 * ( 55 | texture2D( sampler0, uv + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz + 56 | texture2D( sampler0, uv + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz 57 | ); 58 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 59 | texture2D( sampler0, uv - dir * 0.5 ).xyz + 60 | texture2D( sampler0, uv + dir * 0.5 ).xyz 61 | ); 62 | 63 | float lumaB = dot( rgbB, luma ); 64 | gl_FragColor = ( 65 | ( ( lumaB < lumaMin ) || ( lumaMax < lumaB ) ) ? 66 | vec4( rgbA, 1.0 ) : 67 | vec4( rgbB, 1.0 ) 68 | ); 69 | } -------------------------------------------------------------------------------- /src/shaders/g-buffer.frag: -------------------------------------------------------------------------------- 1 | // == shit ===================================================================== 2 | #extension GL_EXT_draw_buffers : require 3 | 4 | // == varyings ================================================================= 5 | varying vec3 vPos; 6 | varying vec3 vNor; 7 | varying vec4 vCol; 8 | 9 | // == uniforms ================================================================= 10 | uniform bool isShadow; 11 | 12 | // == main ===================================================================== 13 | void main() { 14 | if ( isShadow ) { 15 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 16 | return; 17 | } 18 | 19 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 20 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 21 | gl_FragData[ 2 ] = vCol; 22 | } -------------------------------------------------------------------------------- /src/shaders/gauss.frag: -------------------------------------------------------------------------------- 1 | #define SAMPLES 20 2 | #define MUL_THR 0.01 3 | 4 | // ------ 5 | 6 | uniform bool isVert; 7 | uniform sampler2D sampler0; 8 | 9 | uniform float var; 10 | 11 | float gaussian( float _x, float _v ) { 12 | return 1.0 / sqrt( 2.0 * PI * _v ) * exp( - _x * _x / 2.0 / _v ); 13 | } 14 | 15 | void main() { 16 | vec2 uv = gl_FragCoord.xy / resolution; 17 | 18 | if ( var <= 0.0 ) { 19 | gl_FragColor = texture2D( sampler0, uv ); 20 | return; 21 | } 22 | 23 | vec2 bv = ( isVert ? vec2( 0.0, 1.0 ) : vec2( 1.0, 0.0 ) ) / resolution; 24 | 25 | vec3 sum = vec3( 0.0 ); 26 | for ( int i = 0; i <= SAMPLES; i ++ ) { 27 | float mul = gaussian( abs( float( i ) ), var ); 28 | if ( mul < MUL_THR ) { break; } 29 | for ( int j = -1; j < 2; j += 2 ) { 30 | vec2 v = saturate( uv + bv * float( i * j ) ); 31 | vec3 tex = texture2D( sampler0, v ).xyz; 32 | sum += tex * mul; 33 | if ( i == 0 ) { break; } 34 | } 35 | } 36 | 37 | gl_FragColor = vec4( sum, 1.0 ); 38 | } 39 | -------------------------------------------------------------------------------- /src/shaders/glitch.frag: -------------------------------------------------------------------------------- 1 | uniform float amp; 2 | uniform float fadeout; 3 | uniform sampler2D sampler0; 4 | 5 | void main() { 6 | vec2 uv = gl_FragCoord.xy / resolution; 7 | vec2 uv2 = uv; 8 | for ( int i = 0; i < 5; i ++ ) { 9 | float p = pow( 2.0, float( i ) ); 10 | uv2 += amp * vec2( 1.0, 0.0 ) * zp2mp( 11 | random4( uv2 / vec2( 256.0, 128.0 ) * p + 0.5 * mod( totalFrame, 2.0 ) ).xy 12 | ) / p; 13 | } 14 | vec2 displace = uv2 - uv; 15 | displace += 0.2 * fadeout * vec2( zp2mp( random4( lofi( uv.y, 1.0 / 48.0 ) ).x ), 0.0 ); 16 | gl_FragColor = vec4( ( 1.0 - fadeout ) * vec3( 17 | texture2D( sampler0, fract( uv + displace ) ).x, 18 | texture2D( sampler0, fract( uv + 1.2 * displace ) ).y, 19 | texture2D( sampler0, fract( uv + 1.4 * displace ) ).z 20 | ), 1.0 ); 21 | } -------------------------------------------------------------------------------- /src/shaders/inspector.frag: -------------------------------------------------------------------------------- 1 | #define RADIUS 40.0 2 | 3 | uniform vec3 circleColor; 4 | uniform sampler2D sampler0; 5 | 6 | bool print( in vec2 _coord, float _in ) { 7 | vec2 coord = _coord; 8 | 9 | // vertical restriction 10 | if ( coord.y <= 0.0 || 5.0 <= coord.y ) { return false; } 11 | 12 | // dot 13 | if ( 0.0 < coord.x && coord.x < 2.0 ) { 14 | return coord.x < 1.0 && coord.y < 1.0; 15 | } 16 | 17 | // padded by dot 18 | if ( 2.0 < coord.x ) { coord.x -= 2.0; } 19 | 20 | // determine digit 21 | float ci = floor( coord.x / 5.0 ) + 1.0; 22 | 23 | // too low / too high 24 | if ( 4.0 < ci ) { return false; } 25 | if ( ci < -4.0 ) { return false; } 26 | 27 | // x of char 28 | float cfx = floor( mod( coord.x, 5.0 ) ); 29 | 30 | // width is 4 31 | if ( 4.0 == cfx ) { return false; } 32 | 33 | // y of char 34 | float cfy = floor( coord.y ); 35 | 36 | // bit of char 37 | float cf = cfx + 4.0 * cfy; 38 | 39 | // determine char 40 | float num = 0.0; 41 | if ( 0.0 < ci ) { 42 | float n = abs( _in ); 43 | for ( int i = 0; i < 6; i ++ ) { 44 | if ( ci < float( i ) ) { break; } 45 | 46 | num = mod( floor( n ), 10.0 ); 47 | n -= num; 48 | n *= 10.0; 49 | } 50 | } else { 51 | float n = abs( _in ); 52 | for ( int i = 0; i < 6; i ++ ) { 53 | if ( -ci < float( i ) ) { break; } 54 | 55 | if ( ci != 0.0 && n < 1.0 ) { 56 | // minus 57 | return float( i ) == -ci && _in < 0.0 && cfy == 2.0 && 0.0 < cfx; 58 | } 59 | num = mod( floor( n ), 10.0 ); 60 | n -= num; 61 | n /= 10.0; 62 | } 63 | } 64 | 65 | bool a; 66 | a = 1.0 == mod( floor( ( 67 | num == 0.0 ? 432534.0 : 68 | num == 1.0 ? 410692.0 : 69 | num == 2.0 ? 493087.0 : 70 | num == 3.0 ? 493191.0 : 71 | num == 4.0 ? 630408.0 : 72 | num == 5.0 ? 989063.0 : 73 | num == 6.0 ? 399254.0 : 74 | num == 7.0 ? 1016898.0 : 75 | num == 8.0 ? 431766.0 : 76 | 433798.0 77 | ) / pow( 2.0, cf ) ), 2.0 ); 78 | 79 | return a ? true : false; 80 | } 81 | 82 | void main() { 83 | vec2 uv = gl_FragCoord.xy / resolution; 84 | 85 | vec2 m = floor( vec2( 0.0, resolution.y ) + vec2( 1.0, -1.0 ) * mouse ); 86 | vec2 center = floor( m + vec2( 1.0, 0.7 ) * RADIUS ); 87 | float circle = length( gl_FragCoord.xy - center ) - RADIUS; 88 | 89 | vec4 col = texture2D( sampler0, uv ); 90 | vec4 mcol = texture2D( sampler0, ( m + 0.5 ) / resolution ); 91 | vec4 bcol = vec4( circleColor, 1.0 ); 92 | 93 | col = mix( 94 | col, 95 | mix( 96 | bcol, 97 | mcol, 98 | smoothstep( 1.0, 0.0, circle + 5.0 ) 99 | ), 100 | smoothstep( 1.0, 0.0, circle ) 101 | ); 102 | 103 | if ( circle < 0.0 ) { 104 | col = print( gl_FragCoord.xy - center - vec2( 0.0, 8.0 ), mcol.x ) ? bcol : col; 105 | col = print( gl_FragCoord.xy - center - vec2( 0.0, 0.0 ), mcol.y ) ? bcol : col; 106 | col = print( gl_FragCoord.xy - center - vec2( 0.0, -8.0 ), mcol.z ) ? bcol : col; 107 | col = print( gl_FragCoord.xy - center - vec2( 0.0, -16.0 ), mcol.w ) ? bcol : col; 108 | } 109 | 110 | gl_FragColor = col; 111 | } -------------------------------------------------------------------------------- /src/shaders/log-compute.frag: -------------------------------------------------------------------------------- 1 | uniform float nParticleSqrt; 2 | uniform float nParticle; 3 | uniform float ppp; 4 | 5 | uniform sampler2D samplerPcompute; 6 | 7 | uniform vec4 logPos; 8 | uniform vec3 logOffset; 9 | uniform vec4 logCol; 10 | uniform bool logReturn; 11 | uniform bool logDrop; 12 | 13 | // ------ 14 | 15 | vec4 sampleRandom( vec2 _uv ) { 16 | return texture2D( samplerRandomDynamic, _uv ); 17 | } 18 | 19 | // ------ 20 | 21 | void main() { 22 | vec2 uv = gl_FragCoord.xy / resolution; 23 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 24 | float mode = mod( gl_FragCoord.x, ppp ); 25 | vec2 dpix = vec2( 1.0 ) / resolution; 26 | 27 | // == prepare some vars ====================================================== 28 | vec4 seed = texture2D( samplerRandomStatic, puv ); 29 | prng( seed ); 30 | 31 | vec4 pos = texture2D( samplerPcompute, puv ); 32 | vec4 col = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 33 | 34 | float index = floor( uv.x * nParticleSqrt ) + floor( uv.y * nParticleSqrt ) * nParticleSqrt; 35 | 36 | if ( logPos.w == index ) { 37 | // == generate particles =================================================== 38 | pos = logPos; 39 | pos.xyz += logOffset; 40 | pos.w = 0.0; // lifetime 41 | 42 | col = logCol; 43 | } 44 | 45 | // == update particles ======================================================= 46 | pos.y += logReturn ? 0.12 : 0.0; 47 | pos.w += deltaTime + ( logDrop ? 1E9 : 0.0 ); 48 | 49 | gl_FragColor = ( 50 | mode < 1.0 ? pos : 51 | col 52 | ); 53 | } -------------------------------------------------------------------------------- /src/shaders/log-render.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | varying vec4 vPos; 4 | varying vec3 vCol; 5 | varying vec2 vUv; 6 | varying float vSize; 7 | 8 | uniform bool isShadow; 9 | 10 | uniform sampler2D samplerSprite; 11 | 12 | // == main procedure =========================================================== 13 | void main() { 14 | if ( vSize == 0.0 ) { discard; } 15 | 16 | vec2 uv = ( gl_PointCoord + vUv ) / 16.0; 17 | float tex = texture2D( samplerSprite, uv ).x; 18 | if ( tex < 0.5 ) { discard; } 19 | 20 | if ( isShadow ) { 21 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos.xyz - lightPos ), 0.0, 0.0, 1.0 ); 22 | return; 23 | } 24 | 25 | gl_FragData[ 0 ] = vec4( vPos.xyz, 1.0 ); 26 | gl_FragData[ 1 ] = vec4( 0.0, 0.0, 1.0, 1.0 ); 27 | gl_FragData[ 2 ] = vec4( vCol, 2.0 ); 28 | } -------------------------------------------------------------------------------- /src/shaders/log-render.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 computeUV; 2 | 3 | varying vec4 vPos; 4 | varying vec3 vCol; 5 | varying vec2 vUv; 6 | varying float vSize; 7 | 8 | uniform vec2 resolutionPcompute; 9 | 10 | uniform bool isShadow; 11 | 12 | uniform float colorVar; 13 | uniform float colorOffset; 14 | 15 | uniform sampler2D samplerPcompute; 16 | 17 | void main() { 18 | // == fetch compute texture ================================================== 19 | vec2 puv = computeUV; 20 | vec2 dppix = vec2( 1.0 ) / resolutionPcompute; 21 | 22 | vec4 pos = texture2D( samplerPcompute, puv ); 23 | vec4 col = texture2D( samplerPcompute, puv + dppix * vec2( 1.0, 0.0 ) ); 24 | 25 | // == geometry =============================================================== 26 | float kickBeat = exp( -10.0 * mod( mod( time, 2.0 beat ), 0.75 beat ) ); 27 | 28 | vSize = 0.042 * ( 29 | ( 1.0 - exp( -10.0 * pos.w ) ) * 30 | smoothstep( 10.0, 9.0, pos.w ) 31 | ); 32 | 33 | // == special chars ========================================================== 34 | if ( col.w == 3.0 ) { // ♥ 35 | float wave = 0.5 + 0.5 * sin( 10.0 * pos.x + 10.0 * time ); 36 | col = vec4( mix( 37 | col.xyz, 38 | vec3( 1.8, 0.1, 0.5 ), 39 | wave 40 | ), 3.0 ); 41 | vSize *= 1.0 + 0.5 * wave; 42 | } else if ( col.w == 14.0 ) { // 音 43 | float b = exp( -5.0 * mod( mod( time, 2.0 beat ), 0.75 beat ) ); 44 | col = vec4( mix( 45 | col.xyz, 46 | vec3( 1.8, 0.8, 3.8 ), 47 | kickBeat 48 | ), 14.0 ); 49 | vSize *= 1.0 + kickBeat; 50 | } else if ( col.w == 2.0 ) { // 顔 51 | col = vec4( mix( 52 | vec3( 1.1, 0.5, 0.1 ), 53 | vec3( 0.7, 1.4, 0.3 ), 54 | floor( mod( totalFrame, 4.0 ) / 2.0 ) 55 | ), 2.0 ); 56 | } 57 | 58 | // == assign varying variables =============================================== 59 | vCol = col.xyz; 60 | float ch = col.w; 61 | vUv = floor( vec2( mod( ch, 16.0 ), col.w / 16.0 ) ); 62 | 63 | // == finalize =============================================================== 64 | vPos = pos; 65 | 66 | vec4 outPos; 67 | if ( isShadow ) { 68 | outPos = matPL * matVL * vec4( pos.xyz, 1.0 ); 69 | } else { 70 | outPos = matP * matV * vec4( pos.xyz, 1.0 ); 71 | outPos.x /= resolution.x / resolution.y; 72 | } 73 | gl_Position = outPos; 74 | gl_PointSize = vSize * resolution.y / outPos.z / tan( perspFov * PI / 360.0 ); 75 | } -------------------------------------------------------------------------------- /src/shaders/object-with-uv.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec2 aUv; 4 | attribute vec3 aNor; 5 | 6 | // == varyings ================================================================= 7 | varying vec3 vPos; 8 | varying vec2 vUv; 9 | varying vec3 vNor; 10 | 11 | // == uniforms ================================================================= 12 | uniform bool isShadow; 13 | uniform mat4 matM; 14 | 15 | // == main ===================================================================== 16 | void main() { 17 | vec4 pos = matM * vec4( aPos, 1.0 ); 18 | vPos = pos.xyz; 19 | 20 | vUv = aUv; 21 | 22 | vec4 nor = normalize( matM * vec4( aNor, 0.0 ) ); 23 | vNor = nor.xyz; 24 | 25 | vec4 outPos; 26 | if ( isShadow ) { 27 | outPos = matPL * matVL * pos; 28 | } else { 29 | outPos = matP * matV * pos; 30 | outPos.x /= resolution.x / resolution.y; 31 | } 32 | gl_Position = outPos; 33 | } -------------------------------------------------------------------------------- /src/shaders/object.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec3 aNor; 4 | 5 | // == varyings ================================================================= 6 | varying vec3 vPos; 7 | varying vec3 vNor; 8 | 9 | // == uniforms ================================================================= 10 | uniform bool isShadow; 11 | uniform mat4 matM; 12 | 13 | // == main ===================================================================== 14 | void main() { 15 | vec4 pos = matM * vec4( aPos, 1.0 ); 16 | vPos = pos.xyz; 17 | 18 | vec4 nor = normalize( matM * vec4( aNor, 0.0 ) ); 19 | vNor = nor.xyz; 20 | 21 | vec4 outPos; 22 | if ( isShadow ) { 23 | outPos = matPL * matVL * pos; 24 | } else { 25 | outPos = matP * matV * pos; 26 | outPos.x /= resolution.x / resolution.y; 27 | } 28 | gl_Position = outPos; 29 | } -------------------------------------------------------------------------------- /src/shaders/particles-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 3.0 2 | 3 | uniform float nParticleSqrt; 4 | uniform float nParticle; 5 | uniform float ppp; 6 | 7 | uniform sampler2D samplerPcompute; 8 | 9 | uniform float noisePhase; 10 | uniform float velScale; 11 | uniform float genRate; 12 | 13 | // ------ 14 | 15 | vec2 vInvert( vec2 _uv ) { 16 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 17 | } 18 | 19 | // ------ 20 | 21 | vec4 sampleRandom( vec2 _uv ) { 22 | return texture2D( samplerRandomDynamic, _uv ); 23 | } 24 | 25 | // ------ 26 | 27 | void main() { 28 | calcRhythms(); 29 | 30 | vec2 uv = gl_FragCoord.xy / resolution; 31 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 32 | float mode = mod( gl_FragCoord.x, ppp ); 33 | vec2 dpix = vec2( 1.0 ) / resolution; 34 | 35 | float dt = deltaTime; 36 | 37 | // == prepare some vars ====================================================== 38 | vec4 seed = texture2D( samplerRandomDynamic, puv ); 39 | prng( seed ); 40 | 41 | vec4 pos = texture2D( samplerPcompute, puv ); 42 | vec4 vel = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 43 | 44 | float timing = mix( 0.0, PARTICLE_LIFE_LENGTH, floor( puv.y * nParticleSqrt ) / nParticleSqrt ); 45 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 46 | 47 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 48 | timing -= PARTICLE_LIFE_LENGTH; 49 | } 50 | 51 | // == generate particles ===================================================== 52 | if ( 53 | time - deltaTime < timing && timing <= time && 54 | prng( seed ) < genRate 55 | ) { 56 | dt = time - timing; 57 | 58 | pos.xyz = 4.0 * randomSphere( seed ) - vec3( 0.0, 0.0, 5.0 ); 59 | 60 | vel.xyz = 0.0 * randomSphere( seed ); 61 | vel.w = 0.0; 62 | 63 | pos.w = 1.0; // life 64 | } else { 65 | // == update particles ======================================================= 66 | vel.xyz += dt * ( 10.0 + 20.0 * exp( -5.0 * kickTime ) ) * vec3( 67 | noise4d( vec4( 0.8 * pos.xyz, 1.485 + 0.1 * time ) ), 68 | noise4d( vec4( 0.8 * pos.xyz, 3.485 + 0.1 * time ) ), 69 | noise4d( vec4( 0.8 * pos.xyz, 5.485 + 0.1 * time ) ) 70 | ); 71 | vel.xyz += 4.0 * dt * smoothstep( 3.0, 1.5, length( pos.xyz ) ) * normalize( pos.xyz ); 72 | vel.xyz *= exp( -5.0 * dt ); 73 | 74 | pos.xyz += vel.xyz * dt; 75 | pos.z += zOffset.y * dt; 76 | pos.w -= dt / PARTICLE_LIFE_LENGTH; 77 | } 78 | 79 | gl_FragColor = ( 80 | mode < 1.0 ? pos : 81 | vel 82 | ); 83 | } -------------------------------------------------------------------------------- /src/shaders/particles-render.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | varying vec3 vPos; 4 | varying vec3 vCol; 5 | varying float vLife; 6 | 7 | uniform bool isShadow; 8 | 9 | uniform sampler2D samplerShadow; 10 | 11 | // == main procedure =========================================================== 12 | void main() { 13 | if ( vLife <= 0.0 ) { discard; } 14 | 15 | if ( 0.5 < length( gl_PointCoord - 0.5 ) ) { discard; } 16 | 17 | if ( isShadow ) { 18 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 19 | return; 20 | } 21 | 22 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 23 | gl_FragData[ 1 ] = vec4( 0.0, 0.0, 1.0, 1.0 ); 24 | gl_FragData[ 2 ] = vec4( 0.8, 0.9, 2.0, 4.0 ); 25 | } -------------------------------------------------------------------------------- /src/shaders/particles-render.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 computeUV; 2 | 3 | varying vec3 vPos; 4 | varying vec3 vCol; 5 | varying float vLife; 6 | 7 | uniform vec2 resolutionPcompute; 8 | 9 | uniform bool isShadow; 10 | 11 | uniform float colorVar; 12 | uniform float colorOffset; 13 | 14 | uniform sampler2D samplerPcompute; 15 | 16 | void main() { 17 | // == fetch compute texture ================================================== 18 | vec2 puv = computeUV; 19 | vec2 dppix = vec2( 1.0 ) / resolutionPcompute; 20 | 21 | vec4 pos = texture2D( samplerPcompute, puv ); 22 | vec4 vel = texture2D( samplerPcompute, puv + dppix * vec2( 1.0, 0.0 ) ); 23 | 24 | // == ???????? =============================================================== 25 | vec4 dice = texture2D( samplerRandomStatic, puv.xy * 182.92 ); 26 | 27 | // == assign varying variables =============================================== 28 | vLife = pos.w; 29 | 30 | // vCol = ( 31 | // dice.y < 0.8 32 | // ? pow( catColor( TAU * ( ( dice.x * 2.0 - 1.0 ) * colorVar + colorOffset ) ), vec3( 2.0 ) ) 33 | // : vec3( 0.4 ) 34 | // ); 35 | // vCol = abs( vel.xyz ); 36 | vCol = 2.0 * exp( 2.3 * ( vLife - 1.0 ) ) * catColor( 6.9 + 2.0 * ( 1.0 - vLife ) ); 37 | 38 | // == geometry =============================================================== 39 | float size = ( 0.001 + 0.01 * pow( dice.w, 6.0 ) ) * pow( sin( PI * vLife ), 0.25 ); 40 | 41 | // == finalize =============================================================== 42 | vPos = pos.xyz; 43 | 44 | vec4 outPos; 45 | if ( isShadow ) { 46 | outPos = matPL * matVL * vec4( pos.xyz, 1.0 ); 47 | } else { 48 | outPos = matP * matV * vec4( pos.xyz, 1.0 ); 49 | outPos.x /= resolution.x / resolution.y; 50 | } 51 | gl_Position = outPos; 52 | gl_PointSize = resolution.y * size / outPos.z / tan( perspFov * PI / 360.0 ); 53 | } -------------------------------------------------------------------------------- /src/shaders/patterns-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 0.5 2 | 3 | uniform float nParticleSqrt; 4 | uniform float nParticle; 5 | uniform float ppp; 6 | 7 | uniform sampler2D samplerPcompute; 8 | 9 | uniform float genRate; 10 | 11 | // ------ 12 | 13 | vec2 vInvert( vec2 _uv ) { 14 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 15 | } 16 | 17 | // ------ 18 | 19 | vec4 sampleRandom( vec2 _uv ) { 20 | return texture2D( samplerRandomDynamic, _uv ); 21 | } 22 | 23 | // ------ 24 | 25 | void main() { 26 | vec2 uv = gl_FragCoord.xy / resolution; 27 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 28 | float mode = mod( gl_FragCoord.x, ppp ); 29 | vec2 dpix = vec2( 1.0 ) / resolution; 30 | 31 | float dt = deltaTime; 32 | 33 | // == prepare some vars ====================================================== 34 | vec4 seed = texture2D( samplerRandomDynamic, puv ); 35 | prng( seed ); 36 | 37 | vec4 pos = texture2D( samplerPcompute, puv ); 38 | 39 | float timing = mix( 0.0, PARTICLE_LIFE_LENGTH, floor( puv.y * nParticleSqrt ) / nParticleSqrt ); 40 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 41 | 42 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 43 | timing -= PARTICLE_LIFE_LENGTH; 44 | } 45 | 46 | // == generate particles ===================================================== 47 | if ( 48 | time - deltaTime < timing && timing <= time && 49 | prng( seed ) < genRate 50 | ) { 51 | dt = time - timing; 52 | 53 | pos.xyz = 3.0 * randomBox( seed ) - vec3( 0.0, 0.0, 2.0 ); 54 | pos.w = 1.0; // life 55 | } else { 56 | // == update particles ===================================================== 57 | pos.z += zOffset.y * dt; 58 | pos.w -= dt / PARTICLE_LIFE_LENGTH; 59 | } 60 | 61 | gl_FragColor = pos; 62 | } -------------------------------------------------------------------------------- /src/shaders/patterns-render.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | varying vec3 vPos; 4 | varying vec3 vCol; 5 | varying vec3 vRawPos; 6 | varying float vLife; 7 | varying float vMode; 8 | 9 | uniform bool isShadow; 10 | 11 | uniform sampler2D samplerShadow; 12 | 13 | // == patterns ================================================================= 14 | bool ptn0( vec2 _p ) { 15 | float inner = pow( 1.0 - vLife, 2.0 ); 16 | float outer = 1.0 - exp( -5.0 * ( 1.0 - vLife ) ); 17 | vec2 p = _p; 18 | return inner < length( p ) && length( p ) < outer; 19 | } 20 | 21 | bool ptn1( vec2 _p ) { 22 | float inner = 0.7 * pow( 1.0 - vLife, 2.0 ); 23 | float outer = 0.7 * ( 1.0 - exp( -5.0 * ( 1.0 - vLife ) ) ); 24 | vec2 p = _p * rotate2D( PI * ( 1.0 - exp( -2.0 * ( 1.0 - vLife ) ) ) ); 25 | return ( 26 | max( abs( p.x ), abs( p.y ) ) < outer && 27 | inner < max( abs( p.x ), abs( p.y ) ) 28 | ); 29 | } 30 | 31 | bool ptn2( vec2 _p ) { 32 | float inner = 0.7 * pow( 1.0 - vLife, 2.0 ); 33 | float outer = 0.7 * ( 1.0 - exp( -5.0 * ( 1.0 - vLife ) ) ); 34 | vec2 p = _p * rotate2D( PI / 4.0 ); 35 | return ( 36 | max( abs( p.x ), abs( p.y ) ) < outer && 37 | inner < min( abs( p.x ), abs( p.y ) ) 38 | ); 39 | } 40 | 41 | bool ptn3( vec2 _p ) { 42 | float inner = 0.3 * ( 1.0 - pow( 1.0 - vLife, 2.0 ) ); 43 | float outer = 0.3 * ( ( 1.0 - exp( -5.0 * ( 1.0 - vLife ) ) ) ); 44 | float radius = 0.7 * ( ( 1.0 - exp( -5.0 * ( 1.0 - vLife ) ) ) ); 45 | 46 | vec2 p = _p; 47 | p = rotate2D( -PI / 2.0 ) * p; 48 | p = rotate2D( -lofir( atan( p.y, p.x ), TAU / 6.0 ) ) * p; 49 | p.x -= radius * ( ( 1.0 - exp( -5.0 * ( 1.0 - vLife ) ) ) ); 50 | return ( 51 | length( p ) < inner && 52 | length( p ) < outer 53 | ); 54 | } 55 | 56 | // == main procedure =========================================================== 57 | void main() { 58 | if ( vLife <= 0.0 ) { discard; } 59 | 60 | // if ( 0.5 < length( gl_PointCoord - 0.5 ) ) { discard; } 61 | 62 | if ( isShadow ) { 63 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 64 | return; 65 | } 66 | 67 | vec2 p = vRawPos.xy; 68 | bool b = false; 69 | 70 | if ( vMode < 0.5 ) { 71 | b = b || ptn0( p ); 72 | 73 | } else if ( vMode < 1.5 ) { 74 | b = b || ptn1( p ); 75 | 76 | } else if ( vMode < 2.5 ) { 77 | b = b || ptn2( p ); 78 | 79 | } else { 80 | b = b || ptn3( p ); 81 | 82 | } 83 | 84 | if ( !b ) { discard; } 85 | 86 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 87 | gl_FragData[ 1 ] = vec4( 0.0, 0.0, 1.0, 1.0 ); 88 | gl_FragData[ 2 ] = vec4( 1.7, 0.8, 1.1, 2.0 ); 89 | } -------------------------------------------------------------------------------- /src/shaders/patterns-render.vert: -------------------------------------------------------------------------------- 1 | attribute vec3 aPos; 2 | attribute vec2 aComputeUV; 3 | 4 | varying vec3 vPos; 5 | varying vec3 vRawPos; 6 | varying vec3 vCol; 7 | varying float vLife; 8 | varying float vMode; 9 | 10 | uniform vec2 resolutionPcompute; 11 | 12 | uniform bool isShadow; 13 | 14 | uniform float colorVar; 15 | uniform float colorOffset; 16 | 17 | uniform sampler2D samplerPcompute; 18 | 19 | void main() { 20 | // == fetch compute texture ================================================== 21 | vec2 puv = aComputeUV; 22 | vec2 dppix = vec2( 1.0 ) / resolutionPcompute; 23 | 24 | vec4 pos = texture2D( samplerPcompute, puv ); 25 | 26 | // == ???????? =============================================================== 27 | vec4 dice = texture2D( samplerRandomStatic, puv.xy * 182.92 ); 28 | 29 | // == assign varying variables =============================================== 30 | vLife = pos.w; 31 | 32 | // vCol = ( 33 | // dice.y < 0.8 34 | // ? pow( catColor( TAU * ( ( dice.x * 2.0 - 1.0 ) * colorVar + colorOffset ) ), vec3( 2.0 ) ) 35 | // : vec3( 0.4 ) 36 | // ); 37 | // vCol = abs( vel.xyz ); 38 | vCol = 2.0 * exp( 2.3 * ( vLife - 1.0 ) ) * catColor( 6.9 + 2.0 * ( 1.0 - vLife ) ); 39 | 40 | // == geometry =============================================================== 41 | float size = 0.2; 42 | pos.xyz += aPos * size; 43 | vRawPos = aPos; 44 | vMode = floor( 4.0 * dice.y ); 45 | 46 | // == finalize =============================================================== 47 | vPos = pos.xyz; 48 | 49 | vec4 outPos; 50 | if ( isShadow ) { 51 | outPos = matPL * matVL * vec4( pos.xyz, 1.0 ); 52 | } else { 53 | outPos = matP * matV * vec4( pos.xyz, 1.0 ); 54 | outPos.x /= resolution.x / resolution.y; 55 | } 56 | gl_Position = outPos; 57 | } -------------------------------------------------------------------------------- /src/shaders/post.frag: -------------------------------------------------------------------------------- 1 | #define BARREL_ITER 10 2 | 3 | // == uniforms ================================================================= 4 | uniform float barrelAmp; 5 | uniform float barrelOffset; 6 | uniform sampler2D sampler0; 7 | 8 | // == distort a coordination and sample a texture ============================== 9 | vec3 barrel( float amp, vec2 uv ) { 10 | float corn = length( vec2( 0.5 ) ); 11 | float a = min( 3.0 * sqrt( amp ), corn * PI ); 12 | float zoom = corn / ( tan( corn * a ) + corn ); 13 | vec2 p = saturate( 14 | ( uv + normalize( uv - 0.5 ) * tan( length( uv - 0.5 ) * a ) ) * zoom + 15 | 0.5 * ( 1.0 - zoom ) 16 | ); 17 | return texture2D( sampler0, vec2( p.x, p.y ) ).xyz; 18 | } 19 | 20 | // == main ===================================================================== 21 | void main() { 22 | vec2 uv = gl_FragCoord.xy / resolution; 23 | vec2 p = ( gl_FragCoord.xy * 2.0 - resolution ) / resolution.y; 24 | 25 | // == glitch ================================================================= 26 | vec3 tex = vec3( 0.0 ); 27 | 28 | // == do barrel distortion =================================================== 29 | for ( int i = 0; i < BARREL_ITER; i ++ ) { 30 | float fi = ( float( i ) + 0.5 ) / float( BARREL_ITER ); 31 | vec3 a = saturate( vec3( 32 | 1.0 - 3.0 * abs( 1.0 / 6.0 - fi ), 33 | 1.0 - 3.0 * abs( 1.0 / 2.0 - fi ), 34 | 1.0 - 3.0 * abs( 5.0 / 6.0 - fi ) 35 | ) ) / float( BARREL_ITER ) * 4.0; 36 | tex += a * barrel( barrelOffset + barrelAmp * fi, uv ); 37 | } 38 | 39 | // == do vignette ============================================================ 40 | float vig = 1.0 - length( p ) * 0.4; 41 | tex = mix( vec3( 0.0 ), tex, vig ); 42 | 43 | // == do color correction ==================================================== 44 | vec3 col = pow( saturate( tex.xyz ), vec3( 1.0 / 1.6 ) ); 45 | col = vec3( 46 | smoothstep( 0.00, 1.00, col.x ), 47 | col.y, 48 | 0.1 + 0.8 * col.z 49 | ); 50 | 51 | // == done =================================================================== 52 | gl_FragColor = vec4( col, 1.0 ); 53 | } -------------------------------------------------------------------------------- /src/shaders/quad.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 p; 2 | 3 | void main() { 4 | gl_Position = vec4( p, 0.0, 1.0 ); 5 | } -------------------------------------------------------------------------------- /src/shaders/racer-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 3.0 2 | 3 | uniform float trails; 4 | uniform float trailLength; 5 | uniform float ppp; 6 | 7 | uniform sampler2D samplerPcompute; 8 | uniform float genRate; 9 | 10 | // ------ 11 | 12 | vec2 vInvert( vec2 _uv ) { 13 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 14 | } 15 | 16 | // ------ 17 | 18 | vec4 random( vec2 _uv ) { 19 | return texture2D( samplerRandomDynamic, _uv ); 20 | } 21 | 22 | // ------ 23 | 24 | void main() { 25 | vec2 uv = gl_FragCoord.xy / resolution; 26 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 27 | float mode = mod( gl_FragCoord.x, ppp ); 28 | vec2 dpix = vec2( 1.0 ) / resolution; 29 | 30 | float dt = deltaTime; 31 | 32 | // == if it is not head of particles ========================================= 33 | if ( ppp < gl_FragCoord.x ) { 34 | puv.x -= ppp / resolution.x; 35 | vec4 pos = texture2D( samplerPcompute, puv ); 36 | vec4 vel = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 37 | 38 | pos.z += zOffset.y * dt; 39 | pos.w = saturate( pos.w - 1.0 / trailLength ); 40 | 41 | gl_FragColor = ( 42 | mode < 1.0 ? pos : 43 | vel 44 | ); 45 | return; 46 | } 47 | 48 | // == prepare some vars for fuck around head particle ======================== 49 | vec4 seed = texture2D( samplerRandomDynamic, puv ); 50 | prng( seed ); 51 | 52 | vec4 pos = texture2D( samplerPcompute, puv ); 53 | vec4 vel = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 54 | 55 | float timing = mix( 0.0, PARTICLE_LIFE_LENGTH, floor( puv.y * trails ) / trails ); 56 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 57 | 58 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 59 | timing -= PARTICLE_LIFE_LENGTH; 60 | } 61 | 62 | // == initialize particles =================================================== 63 | if ( 64 | time - deltaTime < timing && timing <= time 65 | ) { 66 | dt = time - timing; 67 | 68 | pos.xyz = 2.0 * randomSphere( seed ) - vec3( 0.0, 0.0, zOffset.y ); 69 | 70 | vel.xyz = 1.0 * randomSphere( seed ); 71 | vel.w = 1.0; // jumping flag 72 | 73 | pos.w = prng( seed ) < genRate ? 1.0 : 0.0; // life 74 | } else { 75 | vel.w = 0.0; // remove jumping flag 76 | } 77 | 78 | // == update particles ======================================================= 79 | vel.xyz += 10.0 * vec3( 80 | noise4d( vec4( 0.4 * pos.xyz, 1.845 + 0.1 * time ) ), 81 | noise4d( vec4( 0.4 * pos.xyz, 2.853 + 0.1 * time ) ), 82 | noise4d( vec4( 0.4 * pos.xyz, 4.129 + 0.1 * time ) ) 83 | ) * dt; 84 | vel.xy += 4.0 * dt * smoothstep( 2.0, 1.0, length( pos.xy ) ) * normalize( pos.xy ); 85 | vel.xyz *= exp( -1.0 * dt ); 86 | 87 | vec3 velt = vel.xyz; 88 | velt.x *= abs( velt.x ) < abs( velt.y ) ? 0.0 : abs( velt.x ) < abs( velt.z ) ? 0.0 : 1.0; 89 | velt.y *= abs( velt.y ) < abs( velt.z ) ? 0.0 : abs( velt.y ) < abs( velt.x ) ? 0.0 : 1.0; 90 | velt.z *= abs( velt.z ) < abs( velt.x ) ? 0.0 : abs( velt.z ) < abs( velt.y ) ? 0.0 : 1.0; 91 | 92 | pos.xyz += normalize( velt ) * 0.04; 93 | pos.z += zOffset.y * dt; 94 | 95 | gl_FragColor = ( 96 | mode < 1.0 ? pos : 97 | vel 98 | ); 99 | } -------------------------------------------------------------------------------- /src/shaders/racer-render.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | varying vec3 vPos; 4 | varying vec3 vCol; 5 | varying float vLife; 6 | 7 | uniform bool isShadow; 8 | 9 | uniform sampler2D samplerShadow; 10 | 11 | // == main procedure =========================================================== 12 | void main() { 13 | if ( vLife <= 0.0 ) { discard; } 14 | 15 | if ( 0.5 < length( gl_PointCoord - 0.5 ) ) { discard; } 16 | 17 | if ( isShadow ) { 18 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 19 | return; 20 | } 21 | 22 | float lumi = ( 23 | 1.3 + 24 | 4.0 * exp( -3.0 * ( 1.0 - vLife ) ) + 25 | 9.0 * exp( -20.0 * ( 1.0 - vLife ) ) 26 | ); 27 | 28 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 29 | gl_FragData[ 1 ] = vec4( 0.0, 0.0, 1.0, 1.0 ); 30 | gl_FragData[ 2 ] = vec4( lumi, 0.0, 0.0, 4.0 ); 31 | } -------------------------------------------------------------------------------- /src/shaders/racer-render.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 aComputeUV; 2 | 3 | varying vec3 vPos; 4 | varying float vLife; 5 | 6 | uniform float trails; 7 | uniform float trailLength; 8 | uniform float ppp; 9 | 10 | uniform bool isShadow; 11 | 12 | uniform float colorVar; 13 | uniform float colorOffset; 14 | 15 | uniform sampler2D samplerPcompute; 16 | 17 | void main() { 18 | // == fetch compute texture ================================================== 19 | vec2 puv = aComputeUV; 20 | vec2 dppix = vec2( 1.0 ) / vec2( trailLength, trails ); 21 | 22 | vec4 pos = texture2D( samplerPcompute, puv ); 23 | vec4 vel = texture2D( samplerPcompute, puv + dppix * vec2( 1.0, 0.0 ) ); 24 | 25 | // == ???????? =============================================================== 26 | vec4 dice = texture2D( samplerRandomStatic, puv.xy * 182.92 ); 27 | 28 | // == assign varying variables =============================================== 29 | vLife = pos.w; 30 | 31 | // == finalize =============================================================== 32 | vPos = pos.xyz; 33 | 34 | vec4 outPos; 35 | if ( isShadow ) { 36 | outPos = matPL * matVL * vec4( pos.xyz, 1.0 ); 37 | } else { 38 | outPos = matP * matV * vec4( pos.xyz, 1.0 ); 39 | outPos.x /= resolution.x / resolution.y; 40 | } 41 | gl_Position = outPos; 42 | gl_PointSize = resolution.y * 0.01 / outPos.z / tan( perspFov * PI / 360.0 ); 43 | } -------------------------------------------------------------------------------- /src/shaders/raymarch.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_frag_depth : require 2 | #extension GL_EXT_draw_buffers : require 3 | 4 | #define TRACE_ITER 1 5 | #define MARCH_MUL 0.8 6 | #define MARCH_ITER 60 7 | #define RAYLEN_INIT 0.01 8 | #define INTERSECT_MIN 0.01 9 | #define MARCH_FAR 20.0 10 | #define FOV 90.0 11 | 12 | uniform bool isShadow; 13 | uniform vec4 ifsParams; 14 | 15 | // ------ 16 | 17 | struct Camera { 18 | vec3 pos; 19 | vec3 dir; 20 | vec3 sid; 21 | vec3 top; 22 | float fov; 23 | }; 24 | 25 | struct Ray { 26 | vec3 dir; 27 | vec3 ori; 28 | }; 29 | 30 | // ------ 31 | 32 | Camera camInit( in vec3 _pos, in vec3 _tar, in float _rot, in float _fov ) { 33 | Camera cam; 34 | cam.pos = _pos; 35 | cam.dir = normalize( _tar - _pos ); 36 | cam.sid = normalize( cross( cam.dir, vec3( 0.0, 1.0, 0.0 ) ) ); 37 | cam.top = normalize( cross( cam.sid, cam.dir ) ); 38 | cam.sid = cos( _rot ) * cam.sid + sin( _rot ) * cam.top; 39 | cam.top = normalize( cross( cam.sid, cam.dir ) ); 40 | cam.fov = _fov; 41 | 42 | return cam; 43 | } 44 | 45 | Ray rayInit( in vec3 _ori, in vec3 _dir ) { 46 | Ray ray; 47 | ray.dir = _dir; 48 | ray.ori = _ori; 49 | return ray; 50 | } 51 | 52 | Ray rayFromCam( in vec2 _p, in Camera _cam ) { 53 | vec3 dir = normalize( 54 | _p.x * _cam.sid 55 | + _p.y * _cam.top 56 | + _cam.dir / tan( _cam.fov * PI / 360.0 ) // Is this correct? 57 | ); 58 | return rayInit( _cam.pos, dir ); 59 | } 60 | 61 | // ------ 62 | 63 | float distBox( vec3 _p, vec3 _s ) { 64 | vec3 d = abs( _p ) - _s; 65 | return min( max( d.x, max( d.y, d.z ) ), 0.0 ) + length( max( d, 0.0 ) ); 66 | } 67 | 68 | vec3 typeIfs( vec3 _p, vec3 _rot, vec3 _shift ) { 69 | vec3 pos = _p; 70 | 71 | vec3 shift = _shift; 72 | 73 | for ( int i = 0; i < 5; i ++ ) { 74 | float intensity = pow( 2.0, -float( i ) ); 75 | 76 | pos = abs( pos ) - shift * intensity; 77 | 78 | shift.yz = rotate2D( _rot.x ) * shift.yz; 79 | shift.zx = rotate2D( _rot.y ) * shift.zx; 80 | shift.xy = rotate2D( _rot.z ) * shift.xy; 81 | 82 | if ( pos.x < pos.y ) { pos.xy = pos.yx; } 83 | if ( pos.x < pos.z ) { pos.xz = pos.zx; } 84 | if ( pos.y < pos.z ) { pos.yz = pos.zy; } 85 | } 86 | 87 | return pos; 88 | } 89 | 90 | float distFunc( vec3 _p, out vec4 mtl ) { 91 | float dist = 1E9; 92 | 93 | vec3 p = _p - vec3( 0.0, 0.0, zOffset.x ); 94 | 95 | { 96 | vec3 p = mod( p, 2.0 ) - 1.0; 97 | float ch = min( distBox( p, vec3( 0.1, 0.01, 0.01 ) ), distBox( p, vec3( 0.01, 0.1, 0.01 ) ) ); 98 | 99 | mtl = ch < dist ? vec4( 0.8, 0.8, 0.8, 1.0 ) : mtl; 100 | dist = ch < dist ? ch : dist; 101 | } 102 | 103 | 104 | if ( 224.0 beat < time ) { 105 | vec3 p = p; 106 | p.z = mod( p.z - 2.5, 5.0 ) - 2.5; 107 | p = typeIfs( p, vec3( -0.01, 0.06, -0.01 ), vec3( ifsParams.x * 4.8, ifsParams.x * 6.8, 4.8 ) ); 108 | float ch = 1.8 * distBox( p / 1.8, vec3( ifsParams.y * 0.2 ) ); 109 | 110 | mtl = ch < dist ? vec4( 0.02, 0.05, 0.08, 3.0 ) : mtl; 111 | dist = ch < dist ? ch : dist; 112 | } 113 | 114 | if ( 224.0 beat < time ) { 115 | vec3 p = p; 116 | p.z = mod( p.z - 1.0, 2.0 ) - 1.0; 117 | p = typeIfs( p, vec3( 0.02, -0.13, 0.01 ), vec3( ifsParams.x * 11.4, ifsParams.x * 4.8, 5.1 ) ); 118 | float ch = distBox( p, vec3( ifsParams.y * 0.4 ) ); 119 | 120 | mtl = ch < dist ? vec4( 0.02, 0.05, 0.08, 3.0 ) : mtl; 121 | dist = ch < dist ? ch : dist; 122 | } 123 | 124 | return dist; 125 | } 126 | 127 | float distFunc( vec3 _p ) { 128 | vec4 dummy; 129 | return distFunc( _p, dummy ); 130 | } 131 | 132 | vec3 normalFunc( in vec3 _p, in float _d ) { 133 | vec2 d = vec2( 0.0, _d ); 134 | return normalize( vec3( 135 | distFunc( _p + d.yxx ) - distFunc( _p - d.yxx ), 136 | distFunc( _p + d.xyx ) - distFunc( _p - d.xyx ), 137 | distFunc( _p + d.xxy ) - distFunc( _p - d.xxy ) 138 | ) ); 139 | } 140 | 141 | // ------ 142 | 143 | void main() { 144 | vec2 uv = gl_FragCoord.xy / resolution; 145 | vec2 p = ( gl_FragCoord.xy * 2.0 - resolution ) / resolution.y; 146 | 147 | Camera cam = camInit( 148 | isShadow ? lightPos : cameraPos, 149 | cameraTar, 150 | isShadow ? 0.0 : cameraRoll, 151 | perspFov 152 | ); 153 | Ray ray = rayFromCam( p, cam ); 154 | 155 | float rayLen = RAYLEN_INIT; 156 | vec3 rayPos = ray.ori + ray.dir * rayLen; 157 | vec4 mtl; 158 | 159 | float dist; 160 | for ( int iMarch = 0; iMarch < MARCH_ITER; iMarch ++ ) { 161 | dist = distFunc( rayPos, mtl ); 162 | rayLen += dist * MARCH_MUL; 163 | rayPos = ray.ori + ray.dir * rayLen; 164 | 165 | if ( abs( dist ) < INTERSECT_MIN ) { break; } 166 | if ( MARCH_FAR < dist ) { break; } 167 | } 168 | 169 | vec3 normal = vec3( 0.0 ); 170 | if ( abs( dist ) < INTERSECT_MIN ) { 171 | normal = normalFunc( rayPos, 1E-4 ); 172 | 173 | float z = dot( normalize( cameraTar - cam.pos ), rayPos - cam.pos ); 174 | float a = ( perspFar + perspNear ) / ( perspFar - perspNear ); 175 | float b = 2.0 * perspFar * perspNear / ( perspFar - perspNear ); 176 | gl_FragDepthEXT = 0.5 + 0.5 * ( a - b / z ); 177 | } else { 178 | gl_FragDepthEXT = 1.0; 179 | } 180 | 181 | if ( isShadow ) { 182 | gl_FragData[ 0 ] = vec4( calcDepthL( rayPos - lightPos ), 0.0, 0.0, 1.0 ); 183 | return; 184 | } 185 | 186 | gl_FragData[ 0 ] = vec4( rayPos, 1.0 ); 187 | gl_FragData[ 1 ] = vec4( normal, 1.0 ); 188 | gl_FragData[ 2 ] = mtl; 189 | } -------------------------------------------------------------------------------- /src/shaders/render.frag: -------------------------------------------------------------------------------- 1 | // == uniforms ================================================================= 2 | uniform sampler2D sampler0; 3 | uniform sampler2D sampler1; 4 | uniform sampler2D sampler2; 5 | uniform sampler2D samplerShadow; 6 | 7 | // == struct: isect ============================================================ 8 | struct Isect { 9 | vec3 pos; 10 | vec3 nor; 11 | int mtl; 12 | vec4 props; 13 | }; 14 | 15 | Isect getIsect( vec2 _uv ) { 16 | vec4 tex0 = texture2D( sampler0, _uv ); 17 | vec4 tex1 = texture2D( sampler1, _uv ); 18 | vec4 tex2 = texture2D( sampler2, _uv ); 19 | 20 | Isect isect; 21 | isect.pos = tex0.xyz; 22 | isect.nor = tex1.xyz; 23 | isect.mtl = int( tex2.w ); 24 | isect.props = vec4( tex2.xyz, fract( floor( tex2.w ) ) ); 25 | 26 | return isect; 27 | } 28 | 29 | float getEdge( vec2 _uv ) { 30 | vec4 tex0 = texture2D( sampler0, _uv - vec2( 0.0, 0.0 ) / resolution ); 31 | vec4 tex1 = texture2D( sampler1, _uv - vec2( 0.0, 0.0 ) / resolution ); 32 | vec4 tex2 = texture2D( sampler2, _uv - vec2( 0.0, 0.0 ) / resolution ); 33 | vec3 ray = tex0.xyz - cameraPos; 34 | vec3 rayDir = normalize( ray ); 35 | float rayLen = length( ray ); 36 | 37 | float f = mod( gl_FragCoord.x + gl_FragCoord.y, 2.0 ) < 1.0 ? 1.0 : -1.0; 38 | vec4 tex0x = texture2D( sampler0, _uv + vec2( f, 0.0 ) / resolution ); 39 | vec4 tex0y = texture2D( sampler0, _uv + vec2( 0.0, f ) / resolution ); 40 | vec4 tex1x = texture2D( sampler1, _uv + vec2( f, 0.0 ) / resolution ); 41 | vec4 tex1y = texture2D( sampler1, _uv + vec2( 0.0, f ) / resolution ); 42 | vec4 tex2x = texture2D( sampler2, _uv + vec2( f, 0.0 ) / resolution ); 43 | vec4 tex2y = texture2D( sampler2, _uv + vec2( 0.0, f ) / resolution ); 44 | 45 | float validx = tex2.w == tex2x.w ? 1.0 : 0.0; 46 | float validy = tex2.w == tex2y.w ? 1.0 : 0.0; 47 | 48 | return ( 49 | abs( dot( rayDir, tex0x.xyz ) - dot( rayDir, tex0.xyz ) ) / rayLen * validx + 50 | abs( dot( rayDir, tex0y.xyz ) - dot( rayDir, tex0.xyz ) ) / rayLen * validy + 51 | length( tex1x.xyz - tex1.xyz ) * validx + 52 | length( tex1y.xyz - tex1.xyz ) * validy 53 | ); 54 | } 55 | 56 | // == shadow =================================================================== 57 | float shadow( Isect _isect ) { 58 | vec3 lig = _isect.pos - lightPos; 59 | float d = max( 0.001, dot( -_isect.nor, normalize( lig ) ) ); 60 | 61 | vec4 pl = matPL * matVL * vec4( _isect.pos, 1.0 ); 62 | vec2 uv = pl.xy / pl.w * 0.5 + 0.5; 63 | 64 | float dc = calcDepthL( lig ); 65 | float ret = 0.0; 66 | for ( int iy = -1; iy <= 1; iy ++ ) { 67 | for ( int ix = -1; ix <= 1; ix ++ ) { 68 | vec2 uv = uv + vec2( float( ix ), float ( iy ) ) * 1E-3; 69 | float proj = texture2D( samplerShadow, uv ).x; 70 | float bias = 0.001 + ( 1.0 - d ) * 0.003; 71 | 72 | float dif = mix( 73 | smoothstep( bias * 2.0, bias, abs( dc - proj ) ), 74 | 1.0, 75 | smoothstep( 0.4, 0.5, max( abs( uv.x - 0.5 ), abs( uv.y - 0.5 ) ) ) 76 | ); 77 | ret += dif / 9.0; 78 | } 79 | } 80 | return ret; 81 | } 82 | 83 | // == do shading =============================================================== 84 | vec3 radiance( Isect _isect, vec3 dif, vec3 spe, float rough ) { 85 | // Ref: https://www.shadertoy.com/view/lsXSz7 86 | 87 | // calc a bunch of vectors 88 | vec3 ligDir = normalize( _isect.pos - lightPos ); 89 | vec3 viewDir = normalize( _isect.pos - cameraPos ); 90 | vec3 halfDir = normalize( ligDir + viewDir ); 91 | 92 | float dotLig = max( 0.001, dot( -_isect.nor, ligDir ) ); 93 | float dotView = max( 0.001, dot( -_isect.nor, viewDir ) ); 94 | float dotHalf = max( 0.001, dot( -_isect.nor, halfDir ) ); 95 | float dotHalfView = max( 0.001, dot( halfDir, viewDir ) ); 96 | 97 | // Cook-Torrance 98 | float G = min( 1.0, 2.0 * dotHalf * min( dotView, dotLig ) / dotHalfView ); 99 | 100 | // Beckmann 101 | float sqDotHalf = dotHalf * dotHalf; 102 | float sqDotHalfRough = sqDotHalf * rough * rough; 103 | float D = exp( ( sqDotHalf - 1.0 ) / sqDotHalfRough ) / ( sqDotHalf * sqDotHalfRough ); 104 | 105 | // Fresnel 106 | vec3 Fspe = spe + ( 1.0 - spe ) * pow( 1.0 - dotHalfView, 5.0 ); 107 | vec3 Fdif = spe + ( 1.0 - spe ) * pow( 1.0 - dotLig, 5.0 ); 108 | 109 | // BRDF 110 | vec3 brdfSpe = Fspe * D * G / ( dotView * dotLig * 4.0 ); 111 | vec3 brdfDif = dif * ( 1.0 - Fdif ); 112 | 113 | // shadow 114 | float sh = mix( 0.6, 1.0, shadow( _isect ) ); 115 | 116 | return ( brdfSpe + brdfDif ) * lightCol * dotLig * sh; 117 | } 118 | 119 | // == main ===================================================================== 120 | void main() { 121 | vec2 uv = gl_FragCoord.xy / resolution; 122 | Isect isect = getIsect( uv ); 123 | 124 | // if there are no normal, it's an air 125 | if ( length( isect.nor ) < 0.5 ) { 126 | gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); 127 | return; 128 | } 129 | 130 | vec3 thisColorIsCool = 0.2 + 0.8 * catColor( 131 | 3.0 + smoothstep( -5.0, 2.0, isect.pos.z ) 132 | ); 133 | 134 | // material 135 | vec3 col; 136 | if ( isect.mtl == 1 ) { 137 | col = radiance( isect, isect.props.xyz, vec3( 0.1 ), 0.2 ); 138 | } else if ( isect.mtl == 2 ) { 139 | col = isect.props.xyz; 140 | } else if ( isect.mtl == 3 ) { 141 | col = radiance( isect, isect.props.xyz, vec3( 0.2 ), 0.2 ); 142 | col += 2.4 * thisColorIsCool * smoothstep( 0.03, 0.2, getEdge( uv ) ); 143 | } else if ( isect.mtl == 4 ) { 144 | col = isect.props.x * thisColorIsCool; 145 | } 146 | 147 | gl_FragColor = vec4( col, 1.0 ); 148 | } -------------------------------------------------------------------------------- /src/shaders/return.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform sampler2D sampler0; 4 | 5 | void main() { 6 | vec2 uv = gl_FragCoord.xy / resolution; 7 | gl_FragColor = texture2D( sampler0, uv ); 8 | } -------------------------------------------------------------------------------- /src/shaders/tone.frag: -------------------------------------------------------------------------------- 1 | void main() { 2 | vec2 uv = gl_FragCoord.xy / resolution; 3 | if ( time < 8.0 beat ) { 4 | gl_FragColor = vec4( uv, time / ( 8.0 beat ), 1.0 ); 5 | } else { 6 | vec2 p = ( gl_FragCoord.xy * 2.0 - resolution ) / resolution.y; 7 | float radius = 0.3 * exp( -fract( time / ( 1.0 beat ) ) ); 8 | float shape = linearstep( 2.0 / resolution.y, 0.0, length( p ) - radius ); 9 | gl_FragColor = vec4( vec3( shape ), 1.0 ); 10 | } 11 | } -------------------------------------------------------------------------------- /src/shaders/trails-compute.frag: -------------------------------------------------------------------------------- 1 | #define PARTICLE_LIFE_LENGTH 3.0 2 | 3 | uniform float trails; 4 | uniform float trailLength; 5 | uniform float ppp; 6 | 7 | uniform sampler2D samplerPcompute; 8 | uniform float genRate; 9 | 10 | // ------ 11 | 12 | vec2 vInvert( vec2 _uv ) { 13 | return vec2( 0.0, 1.0 ) + vec2( 1.0, -1.0 ) * _uv; 14 | } 15 | 16 | // ------ 17 | 18 | vec4 random( vec2 _uv ) { 19 | return texture2D( samplerRandomDynamic, _uv ); 20 | } 21 | 22 | // ------ 23 | 24 | void main() { 25 | vec2 uv = gl_FragCoord.xy / resolution; 26 | vec2 puv = vec2( ( floor( gl_FragCoord.x / ppp ) * ppp + 0.5 ) / resolution.x, uv.y ); 27 | float mode = mod( gl_FragCoord.x, ppp ); 28 | vec2 dpix = vec2( 1.0 ) / resolution; 29 | 30 | float dt = deltaTime; 31 | 32 | // == if it is not head of particles ========================================= 33 | if ( ppp < gl_FragCoord.x ) { 34 | puv.x -= ppp / resolution.x; 35 | vec4 pos = texture2D( samplerPcompute, puv ); 36 | vec4 vel = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 37 | 38 | pos.z += zOffset.y * dt; 39 | pos.w = saturate( pos.w - 1.0 / trailLength ); 40 | 41 | gl_FragColor = ( 42 | mode < 1.0 ? pos : 43 | vel 44 | ); 45 | return; 46 | } 47 | 48 | // == prepare some vars for fuck around head particle ======================== 49 | vec4 seed = texture2D( samplerRandomDynamic, puv ); 50 | prng( seed ); 51 | 52 | vec4 pos = texture2D( samplerPcompute, puv ); 53 | vec4 vel = texture2D( samplerPcompute, puv + dpix * vec2( 1.0, 0.0 ) ); 54 | 55 | float timing = mix( 0.0, PARTICLE_LIFE_LENGTH, floor( puv.y * trails ) / trails ); 56 | timing += lofi( time, PARTICLE_LIFE_LENGTH ); 57 | 58 | if ( time - deltaTime + PARTICLE_LIFE_LENGTH < timing ) { 59 | timing -= PARTICLE_LIFE_LENGTH; 60 | } 61 | 62 | // == initialize particles =================================================== 63 | if ( 64 | time - deltaTime < timing && timing <= time 65 | ) { 66 | dt = time - timing; 67 | 68 | pos.xyz = 2.0 * vec3( sin( time ), cos( time ), 0.0 ); 69 | pos.yz = rotate2D( -1.0 ) * pos.yz; 70 | pos.xyz *= prng( seed ) < 0.5 ? 1.0 : -1.0; 71 | pos.xyz += 0.5 * randomSphere( seed ); 72 | 73 | vel.xyz = 1.0 * randomSphere( seed ); 74 | vel.w = 1.0; // jumping flag 75 | 76 | pos.w = prng( seed ) < genRate ? 1.0 : 0.0; // life 77 | } else { 78 | vel.w = 0.0; // remove jumping flag 79 | } 80 | 81 | // == update particles ======================================================= 82 | vel.xyz += 40.0 * vec3( 83 | noise4d( vec4( 0.4 * pos.xyz, 1.845 + 0.1 * time ) ), 84 | noise4d( vec4( 0.4 * pos.xyz, 2.853 + 0.1 * time ) ), 85 | noise4d( vec4( 0.4 * pos.xyz, 4.129 + 0.1 * time ) ) 86 | ) * dt; 87 | vel.xyz += 4.0 * dt * smoothstep( 3.0, 1.5, length( pos.xyz ) ) * normalize( pos.xyz ); 88 | vel.xyz *= exp( -5.0 * dt ); 89 | 90 | pos.xyz += vel.xyz * dt; 91 | pos.w -= dt / PARTICLE_LIFE_LENGTH; 92 | 93 | gl_FragColor = ( 94 | mode < 1.0 ? pos : 95 | vel 96 | ); 97 | } -------------------------------------------------------------------------------- /src/shaders/trails-render.frag: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_draw_buffers : require 2 | 3 | varying vec3 vPos; 4 | varying vec3 vNor; 5 | varying vec3 vCol; 6 | varying float vLife; 7 | varying float vIsOkayToDraw; 8 | 9 | uniform bool isShadow; 10 | 11 | void main() { 12 | if ( vIsOkayToDraw < 0.5 ) { discard; } 13 | if ( vLife <= 0.0 ) { discard; } 14 | 15 | if ( isShadow ) { 16 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 17 | return; 18 | } 19 | 20 | vec3 col = 4.0 * vCol; 21 | 22 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 23 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 24 | gl_FragData[ 2 ] = vec4( col, 1.0 ); 25 | } -------------------------------------------------------------------------------- /src/shaders/trails-render.vert: -------------------------------------------------------------------------------- 1 | attribute float computeU; 2 | attribute float computeV; 3 | attribute float triIndex; 4 | 5 | varying vec3 vPos; 6 | varying vec3 vNor; 7 | varying vec3 vCol; 8 | varying float vLife; 9 | varying float vIsOkayToDraw; 10 | 11 | uniform vec2 resolutionPcompute; 12 | uniform float ppp; 13 | 14 | uniform sampler2D samplerPcompute; 15 | 16 | void main() { 17 | vec2 puv = vec2( computeU, computeV ); 18 | vec2 dppix = vec2( 1.0 ) / resolutionPcompute; 19 | 20 | // == fetch texture ========================================================== 21 | vec4 pos = texture2D( samplerPcompute, puv ); 22 | vec4 vel = texture2D( samplerPcompute, puv + dppix * vec2( 1.0, 0.0 ) ); 23 | vec4 velp = texture2D( samplerPcompute, puv + dppix * vec2( -ppp + 1.0, 0.0 ) ); 24 | 25 | // == assign varying variables =============================================== 26 | vLife = pos.w; 27 | vPos = pos.xyz; 28 | 29 | vec4 dice = texture2D( samplerRandomStatic, puv.yy * 182.92 ); 30 | vCol = dice.z < 0.7 ? vec3( 0.8, 0.2 * dice.xy ) : vec3( 0.8 ); 31 | 32 | vIsOkayToDraw = ( velp.w < 0.5 && vel.w < 0.5 ) ? 1.0 : 0.0; 33 | 34 | // == compute size and direction ============================================= 35 | float size = 0.003 + 0.01 * pow( dice.w, 2.0 ); 36 | vec3 dir = normalize( vel.xyz ); 37 | vec3 sid = normalize( cross( dir, vec3( 0.0, 1.0, 0.0 ) ) ); 38 | vec3 top = normalize( cross( sid, dir ) ); 39 | 40 | float theta = triIndex / 3.0 * TAU + vLife * 1.0; 41 | vec2 tri = vec2( sin( theta ), cos( theta ) ); 42 | vNor = ( tri.x * sid + tri.y * top ); 43 | pos.xyz += size * vNor; 44 | 45 | vec4 outPos = matP * matV * vec4( pos.xyz, 1.0 ); 46 | outPos.x /= resolution.x / resolution.y; 47 | gl_Position = outPos; 48 | } -------------------------------------------------------------------------------- /src/shaders/ui.frag: -------------------------------------------------------------------------------- 1 | // == shit ===================================================================== 2 | #extension GL_EXT_draw_buffers : require 3 | 4 | // == varyings ================================================================= 5 | varying vec3 vPos; 6 | varying vec3 vRawPos; 7 | varying vec3 vNor; 8 | 9 | // == uniforms ================================================================= 10 | uniform bool isShadow; 11 | 12 | uniform vec4 color; 13 | 14 | uniform sampler2D samplerLv; 15 | 16 | // == main ===================================================================== 17 | void main() { 18 | if ( isShadow ) { 19 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 20 | return; 21 | } 22 | 23 | calcRhythms(); 24 | 25 | bool b = 4.47 < abs( vRawPos.x ) || 2.47 < abs( vRawPos.y ); 26 | 27 | vec2 circ = vRawPos.xy - vec2( 3.8, -2.1 ); 28 | circ = rotate2D( zOffset.x ) * circ; 29 | b = b || ( 30 | length( circ ) < 0.25 && 31 | 0.2 < length( circ ) && 32 | 0.03 < abs( circ.y ) 33 | ) || length( circ ) < 0.15; 34 | 35 | vec4 rectLv = vec4( 3.5, -1.7, 4.1, 2.4 ); 36 | 37 | float lvuv = 0.01 + 0.4 * linearstep( rectLv.y, rectLv.w, lofi( vRawPos.y, 0.1 ) ); 38 | float lv = 0.1 + 0.9 * linearstep( -90.0, -30.0, texture2D( samplerLv, vec2( lvuv, 0.5 ) ).x ); 39 | b = b || ( 40 | inRange( rectLv.y, rectLv.w, vRawPos.y ) && 41 | inRange( mix( rectLv.z, rectLv.x, lv ), rectLv.z, vRawPos.x ) && 42 | abs( mod( vRawPos.y, 0.1 ) - 0.05 ) < 0.03 43 | ); 44 | 45 | b = b || ( 46 | abs( abs( vRawPos.x ) - 4.3 ) < 0.1 && 47 | abs( vRawPos.y ) < 2.4 && 48 | mod( vRawPos.x + vRawPos.y + 0.4 * sign( vRawPos.x ) * time, 0.2 ) < 0.15 49 | ); 50 | 51 | vec2 vRawPosYAbs = vec2( vRawPos.x, abs( vRawPos.y ) ); 52 | 53 | b = b || ( 54 | length( vRawPosYAbs.xy - vec2( 0.0, 2.3 ) ) < 0.05 * smoothstep( 0.2, 0.0, clavTime ) 55 | ); 56 | 57 | b = b || ( 58 | length( vRawPosYAbs.xy - vec2( -0.2, 2.3 ) ) < 0.05 * smoothstep( 0.2, 0.0, rimshotTime.x ) 59 | ); 60 | 61 | b = b || ( 62 | length( vRawPosYAbs.xy - vec2( 0.2, 2.3 ) ) < 0.05 * smoothstep( 0.2, 0.0, rimshotTime.y ) 63 | ); 64 | 65 | b = b || ( 66 | inRange( 2.40, 2.41, vRawPosYAbs.y ) && 67 | abs( vRawPosYAbs.x ) < 0.3 + 0.3 * exp( -0.1 * hihatOpen * hihatTime ) 68 | ); 69 | 70 | if ( !b ) { discard; } 71 | 72 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 73 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 74 | gl_FragData[ 2 ] = vec4( 0.7, 0.9, 1.1, 2.0 ); 75 | } -------------------------------------------------------------------------------- /src/shaders/ui.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec3 aNor; 4 | 5 | // == varyings ================================================================= 6 | varying vec3 vPos; 7 | varying vec3 vRawPos; 8 | varying vec3 vNor; 9 | 10 | // == uniforms ================================================================= 11 | uniform bool isShadow; 12 | uniform mat4 matM; 13 | 14 | // == main ===================================================================== 15 | void main() { 16 | vRawPos = aPos; 17 | vec4 pos = matM * vec4( aPos, 1.0 ); 18 | vPos = pos.xyz; 19 | 20 | vec4 nor = normalize( matM * vec4( aNor, 0.0 ) ); 21 | vNor = nor.xyz; 22 | 23 | vec4 outPos; 24 | if ( isShadow ) { 25 | outPos = matPL * matVL * pos; 26 | } else { 27 | outPos = matP * matV * pos; 28 | outPos.x /= resolution.x / resolution.y; 29 | } 30 | gl_Position = outPos; 31 | } -------------------------------------------------------------------------------- /src/shaders/uv.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | varying vec2 vUv; 4 | 5 | void main() { 6 | gl_FragColor = vec4( vUv, 0.0, 1.0 ); 7 | } -------------------------------------------------------------------------------- /src/shaders/very-plane.frag: -------------------------------------------------------------------------------- 1 | // == shit ===================================================================== 2 | #extension GL_EXT_draw_buffers : require 3 | 4 | // == varyings ================================================================= 5 | varying vec3 vPos; 6 | varying vec3 vNor; 7 | varying float vFlip; 8 | 9 | // == uniforms ================================================================= 10 | uniform bool isShadow; 11 | 12 | // == main ===================================================================== 13 | void main() { 14 | if ( isShadow ) { 15 | gl_FragData[ 0 ] = vec4( calcDepthL( vPos - lightPos ), 0.0, 0.0, 1.0 ); 16 | return; 17 | } 18 | 19 | vec4 col = vec4( mix( 20 | vec3( 0.2, 0.3, 0.4 ), 21 | vec3( 0.7, 1.8, 1.2 ), 22 | sin( vFlip ) 23 | ), 1.0 ); 24 | 25 | gl_FragData[ 0 ] = vec4( vPos, 1.0 ); 26 | gl_FragData[ 1 ] = vec4( vNor, 1.0 ); 27 | gl_FragData[ 2 ] = col; 28 | } -------------------------------------------------------------------------------- /src/shaders/very-plane.vert: -------------------------------------------------------------------------------- 1 | // == attributes =============================================================== 2 | attribute vec3 aPos; 3 | attribute vec3 aNor; 4 | attribute vec2 aMatrix; 5 | 6 | // == varyings ================================================================= 7 | varying vec3 vPos; 8 | varying vec3 vNor; 9 | varying float vFlip; 10 | 11 | // == nani ===================================================================== 12 | uniform bool isShadow; 13 | uniform float flipThreshold; 14 | 15 | uniform mat4 matM; 16 | 17 | // == main ===================================================================== 18 | void main() { 19 | vec4 pos = vec4( 0.4 * aPos, 1.0 ); 20 | float t = mod( time - 1.0 beat, 2.0 beat ); 21 | float aaaa = random4( 0.179 * aMatrix + 0.188 * ( time - t ) ).x; 22 | 23 | float flip = PI * smoothstep( 1.0, 0.1, exp( -5.0 * t ) ); 24 | pos.zx = rotate2D( 25 | aaaa < flipThreshold * 0.25 ? flip : 26 | aaaa < flipThreshold * 0.5 ? -flip : 27 | 0.0 28 | ) * pos.zx; 29 | pos.yz = rotate2D( 30 | aaaa < flipThreshold * 0.5 ? 0.0 : 31 | aaaa < flipThreshold * 0.75 ? flip : 32 | aaaa < flipThreshold ? -flip : 33 | 0.0 34 | ) * pos.yz; 35 | vFlip = sin( aaaa < flipThreshold ? sin( flip ) : 0.0 ); 36 | 37 | pos.x += 2.0 * aMatrix.x; 38 | pos.y += mod( 2.0 * aMatrix.y - zOffset.x, 60.0 ) - 20.0; 39 | pos = matM * pos; 40 | 41 | float bbbb = random4( 0.179 * aMatrix ).x; 42 | pos.y += 1.0 * noise4d( vec4( 0.1 * pos.zx, 0.1 * time, 6.724 ) ); 43 | vPos = pos.xyz; 44 | 45 | vec4 nor = vec4( aNor, 0.0 ); 46 | nor = normalize( matM * nor ); 47 | vNor = nor.xyz; 48 | 49 | vec4 outPos; 50 | if ( isShadow ) { 51 | outPos = matPL * matVL * pos; 52 | } else { 53 | outPos = matP * matV * pos; 54 | outPos.x /= resolution.x / resolution.y; 55 | } 56 | gl_Position = outPos; 57 | } -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | width: 100%; 3 | height: 100%; 4 | overflow: hidden; 5 | 6 | font: 500 10px 'Wt-Position', sans-serif; 7 | 8 | background: #000; 9 | color: #fff; 10 | 11 | #divActive { 12 | position: fixed; 13 | left: 8px; 14 | bottom: 248px; 15 | } 16 | 17 | #divMidi { 18 | position: fixed; 19 | left: 8px; 20 | bottom: 248px; 21 | } 22 | 23 | #divPath { 24 | position: fixed; 25 | right: 8px; 26 | bottom: 248px; 27 | 28 | text-align: right; 29 | } 30 | 31 | #divAutomaton { 32 | position: fixed; 33 | left: 0; 34 | bottom: 0; 35 | width: 100%; 36 | height: 240px; 37 | } 38 | 39 | #canvas { 40 | position: fixed; 41 | left: 0; 42 | top: 0; 43 | width: 1280px; 44 | height: 720px; 45 | } 46 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const path = require( 'path' ); 4 | 5 | const webpack = require( 'webpack' ); 6 | const HtmlWebpackPlugin = require( 'html-webpack-plugin' ); 7 | 8 | module.exports = ( env, argv ) => { 9 | return { 10 | devtool: argv.mode === 'production' ? false : 'inline-source-map', 11 | entry: path.resolve( __dirname, 'src/main.js' ), 12 | output: { 13 | path: path.resolve( __dirname, 'dist' ), 14 | filename: ( 15 | argv.mode === 'production' 16 | ? 'bundle.prod.js' 17 | : 'bundle.js' 18 | ) 19 | }, 20 | devServer: { 21 | inline: true, 22 | hot: true 23 | }, 24 | resolve: { 25 | alias: { 26 | '@fms-cat/automaton': ( 27 | argv.mode === 'production' 28 | ? path.resolve( __dirname, 'src/automaton.fuckyou.js' ) 29 | : '@fms-cat/automaton' 30 | ), 31 | 'glcat-path': ( 32 | argv.mode === 'production' 33 | ? path.resolve( __dirname, 'src/libs/glcat-path.js' ) 34 | : path.resolve( __dirname, 'src/libs/glcat-path-gui.js' ) 35 | ) 36 | } 37 | }, 38 | module: { 39 | rules: [ 40 | { test: /\.(png|jpg|gif|ttf|otf)$/, use: 'url-loader' }, 41 | { test: /\.(glsl|frag|vert)$/, use: [ 'raw-loader' ] } 42 | ] 43 | }, 44 | optimization: { 45 | minimize: argv.mode === 'production' 46 | }, 47 | plugins: [ 48 | new HtmlWebpackPlugin( { 49 | filename: ( 50 | argv.mode === 'production' 51 | ? path.resolve( __dirname, 'dist/index.prod.html' ) 52 | : path.resolve( __dirname, 'dist/index.html' ) 53 | ) 54 | } ), 55 | new webpack.DefinePlugin( { 56 | PRODUCTION: JSON.stringify( argv.mode === 'production' ) 57 | } ) 58 | ] 59 | }; 60 | }; --------------------------------------------------------------------------------