├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── assets
├── sat-32x32.png
├── sat-8x8-upscaled-to-256x256.png
└── sat-8x8.png
├── glsl-sat-demo.js
├── glsl-sat.js
├── package.json
└── scripts
└── publish-static-demos.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | node_modules/*
3 | *.DS_Store
4 | coverage/*
5 | bench/*.html
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bench/*
2 | example/*
3 | test/*
4 | www/*
5 | bin/*
6 | dist/*
7 | coverage/*
8 | images/*
9 | compare/*
10 | CNAME
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | language: node_js
3 | node_js:
4 | - '6.3'
5 | env:
6 | global:
7 | - CXX=g++-4.8
8 | #GH_TOKEN
9 | - secure: >-
10 | Sq3mj3BFGc090A+I1nBpvMHC4up6jXQF1Tz22IBeTAPdygVrOWl9YDOjG0+jEloRrX/GYvXhluAgb8b4RHmvs+ZlYRykn6Y4HFBcnT88T04tzdBj+huPjOYo3gvw7u8o9Wh/4LfIgOOk9js3TKtqxh7h8GAqsU++5UIP54GPlrLYvC+wJ5FslYElWmIDhCrXBZERGjEGNRweaVeVorE1vNiyqvB5NYYF728fQuiwQKyOGQYQAlyYan80lPiQEzWVQWPblUocZqgawYC/MgQSU0xs5R+p+PcQyV/Px60A/jZjgNFhFwdJea/GzrVD9xPbMU+YNOM5NDKSLDvSUMABhodbDdc+rlOgC5Qj6H2FdZM+I7CP8a1w+fPNxJ9q3FCV4wp7j5RimbA9U0JckPcv2HOx1UV2Yx0/pTfRJiHNDgqUDft69ZjcaghEvmTuC+uC99N/YudUWDJad+KPf3ETuUHubqrqOzJl0tTkNYnyJ5b/kVVBFdlAp+AH1uZCEP6BRfAZX1z1E7wMXNS4pQJFgPb8EXJtg5CFf5UTeLZuWuEEmRgw0YhH7DQTg0YP51xYh6AAIHneqbvxaTnUPVztIH1YDWgUKc4q46AshRy5SD5r/OUZw0G6ms5qOfsQ4CB5T36qgljLcKUvKVf4oxzX1ulyqZ40c6CnHfsg3X1uMZY=
11 | addons:
12 | apt:
13 | sources:
14 | - ubuntu-toolchain-r-test
15 | packages:
16 | - g++-4.8
17 | install:
18 | - npm install --global browserify
19 | - npm install
20 | script:
21 | - npm run mytest
22 | - npm run build
23 | - bash scripts/publish-static-demos.sh
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Azriel Fasten
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 |
2 | glsl-sat
3 | ---
4 |
5 |
6 | ####Description
7 |
8 | glsl-sat is a shader generator for WebGL, to generate a summed-area-table texture of an input texture.
9 |
10 | Based on [**Summed-Area Tables Area Tables** And Their Application to Dynamic And Their Application to
11 | Dynamic Glossy Environment Reflections](http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/GDC2005_SATEnvironmentReflections.pdf)
12 |
13 |
14 | See `glsl-sat-demo.js` for usage.
15 |
16 | ####Dependencies
17 |
18 | * nodejs
19 | * browserify
20 | * [glsl-quad](https://github.com/realazthat/glsl-quad)
21 | * [regl](https://github.com/mikolalysenko/regl)
22 | * [glsl-numerify](https://github.com/realazthat/glsl-numerify) (for demo)
23 | * [resl](https://github.com/mikolalysenko/resl) (for demo)
24 | * budo (for quick demo as an alternative to running browserify)
25 |
26 |
27 | ####Demo
28 |
29 | To run the demo, run:
30 |
31 | ```
32 | cd ./glsl-sat
33 |
34 | #install npm dependencies
35 | npm install
36 |
37 | #browser should open with the demo
38 | budo glsl-sat-demo.js --open
39 |
40 |
41 | ```
42 |
43 | **Live:**
44 |
45 | branch | demo
46 | -------|-------
47 | master | [glsl-sat-demo](https://realazthat.github.io/glsl-sat/master/www/glsl-sat-demo/index.html)
48 | develop | [glsl-sat-demo](https://realazthat.github.io/glsl-sat/develop/www/glsl-sat-demo/index.html)
49 |
50 |
51 | Source Upscaled | Source Red Numerified | SAT Result Upscaled | SAT Result Red Numerified
52 | -----------------|-----------------------|---------------------|----------------------------
53 | |
|
|
54 |
55 |
56 |
57 | ####Docs
58 |
59 | ```
60 | const sat = require('./glsl-sat.js');
61 | ```
62 |
63 | ##### `sat.computeNumPasses ({textureSize, sampleSize})`
64 |
65 | * Computes the number of passes that will be required for a texture of this size, for a single direction.
66 | Actual number of passes will be double what this returns.
67 | * `textureSize` - the size of the texture in pixels. This should be the largest side.
68 | * `sampleSize` - Sample size is stuck at 16 right now, so use 16.
69 |
70 | ##### `sat.computeNumBitsRequired ({width, height, channelBitDepth})`
71 |
72 | * Computes the number of bits of precision required to process and hold the resulting SAT texture, in the
73 | intermediary and result FBOs. Note that this is theoretical; a few bits might be lost with 32 bit floats,
74 | experimentation required.
75 | * `width` the input texture width.
76 | * `height` the input texture height.
77 | * `channelBitDepth` the input texture bits per channel.
78 |
79 | ##### `sat.computeSat ({regl, texture, fbos, currentFboIndex = 0, outFbo = null, components = 'rgba', type = 'vec4', clipY = 1})
80 |
81 | * Does all the heavy lifting and computes the summed area table.
82 | * `regl` - a regl context.
83 | * `texture` - the regl input texture. should prolly be in opengl form; where the origin uv is the lower left of the texture.
84 | * `fbos` - an array with at least 2 regl FBOs, used for ping-ponging during processing; should prolly have
85 | a type of float (32-bit) for each channel.
86 | * `currentFboIndex` the regl FBO index in `fbos` array to begin at for ping-ponging. The function will begin by incrementing this
87 | value and using the next FBO in the array. The function will return a value in the form of
88 | `{currentFboIndex}` with the position of the last-used FBO. Defaults to `0`.
89 | * `outFbo` - destination regl FBO. Can be null, in which case the SAT will be left inside the `fbos` array
90 | on the last ping-pong; the return value with be of the form `{currentFboIndex}` so that you
91 | can retrieve it.
92 | * `components` - a string indicating which components need to be processed and summed; defaults to `'rgba'`.
93 | * `type` - a glsl type in string format indicating the type that can hold the compnents that need to be processed; defaults to `'vec4'`.
94 | * `clipY` - a value that represents the clipspace y multiple; a default value of `1` indicates opengl-style lower-left-corner-as-origin;
95 | a value of `-1` would mean a upper-left-corner-as-origin.
96 | * returns a value in the form of `{currentFboIndex}` with the position of the last-used FBO.
97 |
98 | ####Usage
99 |
100 | See `glsl-sat-demo.js` for a full demo using [regl](https://github.com/mikolalysenko/regl)
101 | and [resl](https://github.com/mikolalysenko/resl).
102 |
103 | An excerpt:
104 |
105 | ```
106 |
107 | computeSat({texture: texture, fbos: fbos, outFbo: outFbo, regl});
108 |
109 |
110 |
111 | ```
112 |
113 |
114 |
--------------------------------------------------------------------------------
/assets/sat-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realazthat/glsl-sat/6383851d201e4a3548d62d3c8853a862fce3efd2/assets/sat-32x32.png
--------------------------------------------------------------------------------
/assets/sat-8x8-upscaled-to-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realazthat/glsl-sat/6383851d201e4a3548d62d3c8853a862fce3efd2/assets/sat-8x8-upscaled-to-256x256.png
--------------------------------------------------------------------------------
/assets/sat-8x8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realazthat/glsl-sat/6383851d201e4a3548d62d3c8853a862fce3efd2/assets/sat-8x8.png
--------------------------------------------------------------------------------
/glsl-sat-demo.js:
--------------------------------------------------------------------------------
1 |
2 | const $ = require('jquery-browserify');
3 | const resl = require('resl');
4 | const regl = require('regl')({
5 | extensions: ['OES_texture_float'],
6 | // TODO: FIXME: dunno why we need this here, we do not read non-uint8 data from screen,
7 | // but it fails without this on gh-pages for some reason.
8 | attributes: {preserveDrawingBuffer: true}
9 | });
10 |
11 | const computeSat = require('./glsl-sat.js').computeSat;
12 |
13 | const numerify = require('glsl-numerify');
14 | const quad = require('glsl-quad');
15 |
16 | // command to copy a texture to an FBO, assumes the texture is in opengl-order
17 | // where the origin is the lower left of the texture.
18 | const drawTextureToFbo = regl({
19 | frag: quad.shader.frag,
20 | vert: quad.shader.vert,
21 | attributes: {
22 | a_position: quad.verts,
23 | a_uv: quad.uvs
24 | },
25 | elements: quad.indices,
26 | uniforms: {
27 | u_tex: regl.prop('texture'),
28 | u_clip_y: 1
29 | },
30 | framebuffer: regl.prop('fbo')
31 | });
32 |
33 | // command to copy a texture to an FBO, but flipping the Y axis so that the uvs begin
34 | // at the upper right corner, so that it can be drawn to canvas etc.
35 | const drawToCanvasFBO = regl({
36 | frag: quad.shader.frag,
37 | vert: quad.shader.vert,
38 | attributes: {
39 | a_position: quad.verts,
40 | a_uv: quad.uvs
41 | },
42 | elements: quad.indices,
43 | uniforms: {
44 | u_tex: regl.prop('texture'),
45 | u_clip_y: -1
46 | },
47 | framebuffer: regl.prop('fbo')
48 | });
49 |
50 | function dataURIFromFBO ({fbo, width, height, regl}) {
51 | let canvasFBO = regl.framebuffer({
52 | color: regl.texture({
53 | width: width,
54 | height: height,
55 | stencil: false,
56 | format: 'rgba',
57 | type: 'uint8',
58 | depth: false,
59 | wrap: 'clamp',
60 | mag: 'nearest',
61 | min: 'nearest'
62 | })
63 | });
64 |
65 | let data = [];
66 | try {
67 | drawToCanvasFBO({texture: fbo.color[0], fbo: canvasFBO});
68 |
69 | let bindFbo = regl({framebuffer: canvasFBO});
70 | bindFbo(function () {
71 | data = regl.read();
72 | });
73 | } finally {
74 | canvasFBO.destroy();
75 | }
76 |
77 | var canvas = document.createElement('canvas');
78 | canvas.width = width;
79 | canvas.height = height;
80 | var context = canvas.getContext('2d');
81 |
82 | // Copy the pixels to a 2D canvas
83 | var imageData = context.createImageData(width, height);
84 | imageData.data.set(data);
85 | context.putImageData(imageData, 0, 0);
86 |
87 | return canvas.toDataURL();
88 | }
89 |
90 | const sat8x8pnguri = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYA
91 | 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`.replace(/\s*/g, '');
141 |
142 | resl({
143 | manifest: {
144 | texture: {
145 | type: 'image',
146 | src: sat8x8pnguri,
147 | parser: (data) => regl.texture({
148 | data: data,
149 | mag: 'nearest',
150 | min: 'nearest',
151 | flipY: true
152 | })
153 | }, digitsTexture: {
154 | type: 'image',
155 | src: numerify.digits.uri,
156 | parser: (data) => regl.texture({
157 | data: data,
158 | mag: 'nearest',
159 | min: 'nearest',
160 | flipY: true
161 | })
162 | }
163 | },
164 | onDone: ({texture, digitsTexture}) => {
165 | // make a bunch of fbos for ping-ponging intermediate computations, and the output buffer etc.
166 | let fbos = [null, null, null, null].map(function () {
167 | return regl.framebuffer({
168 | color: regl.texture({
169 | width: texture.width,
170 | height: texture.height,
171 | stencil: false,
172 | format: 'rgba',
173 | type: 'float',
174 | depth: false,
175 | wrap: 'clamp',
176 | mag: 'nearest',
177 | min: 'nearest'
178 | }),
179 | stencil: false,
180 | depth: false,
181 | depthStencil: false,
182 | wrap: 'clamp',
183 | mag: 'nearest',
184 | min: 'nearest'
185 | });
186 | });
187 |
188 | // use one FBO for the output.
189 | let outFbo = fbos.pop();
190 |
191 | // and another for the input, for later use.
192 | let inFbo = fbos.pop();
193 |
194 | computeSat({texture: texture, fbos: fbos, outFbo: outFbo, regl, components: 'rgb', type: 'vec3'});
195 |
196 | let upscaledCellWidth = 32;
197 | let upscaledCellHeight = 32;
198 | let upscaledWidth = texture.width * Math.max(upscaledCellWidth, upscaledCellHeight);
199 | let upscaledHeight = texture.height * Math.max(upscaledCellWidth, upscaledCellHeight);
200 |
201 | // a command to take an input texture and "numerify" it and place it in a destination FBO.
202 | const drawNumbersToFbo = regl({
203 | frag: numerify.makeFrag({ multiplier: 256.0,
204 | sourceSize: `vec2(${texture.width}, ${texture.height})`,
205 | destinationCellSize: `vec2(${upscaledCellWidth - 1}, ${upscaledCellHeight - 1})`,
206 | destinationSize: `vec2(${upscaledWidth}, ${upscaledHeight})`,
207 | component: 'r'}),
208 | vert: numerify.makeVert(),
209 | attributes: {
210 | a_position: quad.verts,
211 | a_uv: quad.uvs
212 | },
213 | elements: quad.indices,
214 | uniforms: {
215 | digits_texture: digitsTexture,
216 | source_texture: regl.prop('texture'),
217 | u_clip_y: 1
218 | },
219 | framebuffer: regl.prop('fbo')
220 | });
221 |
222 | // allocate two FBOS to store the numerified textures.
223 | let numbersFBOs = [null, null].map(function () {
224 | return regl.framebuffer({
225 | color: regl.texture({
226 | width: upscaledWidth,
227 | height: upscaledHeight,
228 | stencil: false,
229 | format: 'rgba',
230 | type: 'uint8',
231 | depth: false,
232 | wrap: 'clamp',
233 | mag: 'nearest',
234 | min: 'nearest'
235 | })
236 | });
237 | });
238 |
239 | // one FBO to store the input texture as a numerified texture.
240 | let inNumbersFBO = numbersFBOs[0];
241 | // and a second FBO to store the SAT result texture as a numerified texture.
242 | let outNumbersFBO = numbersFBOs[1];
243 |
244 | // copy the input texture to the `inFbo`.
245 | drawTextureToFbo({texture, fbo: inFbo});
246 | // "numerify" the input texture.
247 | drawNumbersToFbo({texture: inFbo.color[0], fbo: inNumbersFBO});
248 | // "numerify" the SAT result texture.
249 | drawNumbersToFbo({texture: outFbo.color[0], fbo: outNumbersFBO});
250 |
251 | // draw the stuff to img tags, and put everything into the DOM for display.
252 |
253 | let $srcDiv = $('