Skip to content

Commit 1a37081

Browse files
authored
Merge pull request #13 from davepagurek/feat/float-textures
Add support for float textures + an example
2 parents 347c052 + a430daf commit 1a37081

File tree

8 files changed

+253
-18
lines changed

8 files changed

+253
-18
lines changed

.prettierrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"useTabs": false,
3+
"tabWidth": 2,
4+
"semi": false,
5+
"singleQuote": true,
6+
"trailingComma": "all",
7+
"printWidth": 80,
8+
"arrowParens": "always"
9+
}

README.md

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Add the library to your source code, *after* loading p5 but *before* loading you
1818

1919
### Via CDN
2020
```html
21-
<script src="https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].2/p5.Framebuffer.min.js"></script>
21+
<script src="https://cdn.jsdelivr.net/npm/@davepagurek/[email protected].3/p5.Framebuffer.min.js"></script>
2222
```
2323

2424
### Self-hosted
@@ -73,12 +73,124 @@ function draw() {
7373
</tr>
7474
</table>
7575

76+
Methods:
77+
- `p5.prototype.createFramebuffer(options?: Options)`
78+
- `options.colorFormat: 'float' | 'unsigned_byte'`
79+
- Specify whether to use floating point storage for the color texture
80+
- Defaults to `'unsigned_byte'`
81+
- Note: If you use floating point colors, in Firefox you must also call `setAttributes({ alpha: true })`
82+
7683
Notes:
7784
- `draw()` uses the same p5 context as the rest of your sketch! Make sure to wrap your callback code in a `push()` and `pop()` to ensure your settings don't leak out into your non-Framebuffer code.
7885
- When you `resizeCanvas`, the Framebuffer will automatically resize accordingly. You probably will want to clear it and redraw to it if you had a texture cached.
7986

8087
A live example: https://davepagurek.github.io/p5.Framebuffer/examples/simple
8188

89+
### Floating point textures
90+
91+
Sometimes, you want to write code that adds on to or modifies the previous frame. You may notice weird artifacts that show up due to the fact that colors are internally stored as integers: sometimes if you overlay a color with a very small alpha, the change in color is too small to round the resulting color up to the next integer value, so it doesn't change at all.
92+
93+
This can be fixed if you store colors as floating point values! You can specify this in an optional options object when creating a Framebuffer object.
94+
95+
<table>
96+
<tr>
97+
<td rowspan="4">
98+
99+
```js
100+
let fboPrev, fboNext
101+
let canvas
102+
103+
function setup() {
104+
canvas = createCanvas(400, 400, WEBGL)
105+
// There's a bug in Firefox where you can only make floating point textures
106+
// if they're RGBA, and it breaks if it's just RGB
107+
setAttributes({ alpha: true })
108+
109+
// Try changing `float` to `unsigned_byte` to see it leave a trail
110+
options = { colorFormat: 'float' }
111+
fboPrev = createFramebuffer(options)
112+
fboNext = createFramebuffer(options)
113+
imageMode(CENTER)
114+
rectMode(CENTER)
115+
noStroke()
116+
}
117+
118+
function draw() {
119+
// Swap prev and next so that we can use the previous frame as a texture
120+
// when drawing the current frame
121+
[fboPrev, fboNext] = [fboNext, fboPrev]
122+
123+
// Draw to the Framebuffer
124+
fboNext.draw(() => {
125+
clear()
126+
127+
background(255)
128+
129+
// Disable depth testing so that the image of the previous
130+
// frame doesn't cut off the sube
131+
_renderer.GL.disable(_renderer.GL.DEPTH_TEST)
132+
push()
133+
scale(1.003)
134+
texture(fboPrev.color)
135+
plane(width, -height)
136+
pop()
137+
138+
push()
139+
// Fade to white slowly. This will leave a permanent trail if you don't
140+
// use floating point textures.
141+
fill(255, 1)
142+
rect(0, 0, width, height)
143+
pop()
144+
_renderer.GL.enable(_renderer.GL.DEPTH_TEST)
145+
146+
push()
147+
normalMaterial()
148+
translate(100*sin(frameCount * 0.014), 100*sin(frameCount * 0.02), 0)
149+
rotateX(frameCount * 0.01)
150+
rotateY(frameCount * 0.01)
151+
box(50)
152+
pop()
153+
})
154+
155+
clear()
156+
push()
157+
texture(fboNext.color)
158+
plane(width, -height)
159+
pop()
160+
}
161+
```
162+
163+
164+
</td>
165+
<th>
166+
With <code>colorFormat: 'float'</code>
167+
</th>
168+
</tr>
169+
<tr>
170+
<td>
171+
<img src="https://user-images.githubusercontent.com/5315059/178152103-07914de2-d09f-423f-99cc-84f83c422e8b.png">
172+
</td>
173+
</tr>
174+
<tr>
175+
<th>
176+
With <code>colorFormat: 'unsigned_byte'</code> (the default)
177+
</th>
178+
</tr>
179+
<tr>
180+
<td>
181+
<img src="https://user-images.githubusercontent.com/5315059/178152105-756356b0-d741-42b6-a460-9b7b2b571f16.png">
182+
</td>
183+
</tr>
184+
</table>
185+
186+
187+
Methods:
188+
- `p5.prototype.createFramebuffer(options?: Options)`
189+
- `options.colorFormat: 'float' | 'unsigned_byte'`
190+
- Specify whether to use floating point storage for the color texture
191+
- Defaults to `'unsigned_byte'`
192+
- Note: If you use floating point colors, in Firefox you must also call `setAttributes({ alpha: true })`
193+
82194
### Depth of field blur
83195

84196
The library provides a helper that bundles a Framebuffer with a shader that applies focal blur, leaving objects at a provided distance in focus and blurring things more the farther away from that point they are.

examples/feedback/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html><html lang="en"><head>
2+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js"></script>
3+
<script src="../../p5.Framebuffer.js" type="text/javascript"></script>
4+
<link rel="stylesheet" type="text/css" href="style.css">
5+
<meta charset="utf-8">
6+
7+
</head>
8+
<body>
9+
<script src="sketch.js"></script>
10+
11+
12+
</body></html>

examples/feedback/sketch.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
let fboPrev, fboNext
2+
let canvas
3+
4+
function setup() {
5+
canvas = createCanvas(400, 400, WEBGL)
6+
// There's a bug in Firefox where you can only make floating point textures
7+
// if they're RGBA, and it breaks if it's just RGB
8+
setAttributes({ alpha: true })
9+
10+
// Try changing `float` to `unsigned_byte` to see it leave a trail
11+
options = { colorFormat: 'float' }
12+
fboPrev = createFramebuffer(options)
13+
fboNext = createFramebuffer(options)
14+
imageMode(CENTER)
15+
rectMode(CENTER)
16+
noStroke()
17+
}
18+
19+
function draw() {
20+
// Swap prev and next so that we can use the previous frame as a texture
21+
// when drawing the current frame
22+
[fboPrev, fboNext] = [fboNext, fboPrev]
23+
24+
// Draw to the Framebuffer
25+
fboNext.draw(() => {
26+
clear()
27+
28+
background(255)
29+
30+
// Disable depth testing so that the image of the previous
31+
// frame doesn't cut off the sube
32+
_renderer.GL.disable(_renderer.GL.DEPTH_TEST)
33+
push()
34+
scale(1.003)
35+
texture(fboPrev.color)
36+
plane(width, -height)
37+
pop()
38+
39+
push()
40+
// Fade to white slowly. This will leave a permanent trail if you don't
41+
// use floating point textures.
42+
fill(255, 1)
43+
rect(0, 0, width, height)
44+
pop()
45+
_renderer.GL.enable(_renderer.GL.DEPTH_TEST)
46+
47+
push()
48+
normalMaterial()
49+
translate(100*sin(frameCount * 0.014), 100*sin(frameCount * 0.02), 0)
50+
rotateX(frameCount * 0.01)
51+
rotateY(frameCount * 0.01)
52+
box(50)
53+
pop()
54+
})
55+
56+
clear()
57+
push()
58+
texture(fboNext.color)
59+
plane(width, -height)
60+
pop()
61+
}

examples/feedback/style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
html, body {
2+
margin: 0;
3+
padding: 0;
4+
}
5+
canvas {
6+
display: block;
7+
}

p5.Framebuffer.js

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const _createFramebuffer = function() {
2-
const fb = new Framebuffer(this)
1+
const _createFramebuffer = function (options) {
2+
const fb = new Framebuffer(this, options)
33

44
// Extend the old resize handler to also update the size of the framebuffer
55
const oldResize = this._renderer.resize
@@ -14,7 +14,7 @@ p5.prototype.createFramebuffer = _createFramebuffer
1414
p5.Graphics.prototype.createFramebuffer = _createFramebuffer
1515

1616
const parentGetTexture = p5.RendererGL.prototype.getTexture
17-
p5.RendererGL.prototype.getTexture = function(imgOrTexture) {
17+
p5.RendererGL.prototype.getTexture = function (imgOrTexture) {
1818
if (imgOrTexture instanceof p5.Texture) {
1919
return imgOrTexture
2020
} else {
@@ -31,12 +31,7 @@ p5.RendererGL.prototype.getTexture = function(imgOrTexture) {
3131
// that looks like a p5 texture but that never tries to update
3232
// data in order to use framebuffer textures inside p5.
3333
class RawTextureWrapper extends p5.Texture {
34-
constructor(
35-
renderer,
36-
obj,
37-
w,
38-
h,
39-
) {
34+
constructor(renderer, obj, w, h) {
4035
super(renderer, obj)
4136
this.width = w
4237
this.height = h
@@ -64,15 +59,33 @@ class RawTextureWrapper extends p5.Texture {
6459
}
6560

6661
class Framebuffer {
67-
constructor(canvas) {
62+
constructor(canvas, options = {}) {
6863
this._renderer = canvas._renderer
69-
7064
const gl = this._renderer.GL
71-
const ext = gl.getExtension('WEBGL_depth_texture')
72-
if (!ext) {
65+
if (!gl.getExtension('WEBGL_depth_texture')) {
7366
throw new Error('Unable to create depth textures in this environment')
7467
}
7568

69+
this.colorFormat = this.glColorFormat(options.colorFormat)
70+
this.depthFormat = this.glDepthFormat(options.depthFormat)
71+
if (
72+
(options.colorFormat === 'float' || options.depthFormat === 'float') &&
73+
(!gl.getExtension('OES_texture_float') ||
74+
!gl.getExtension('OES_texture_float_linear') ||
75+
!gl.getExtension('WEBGL_color_buffer_float'))
76+
) {
77+
// Reset to default
78+
if (options.colorFormat === 'float') {
79+
this.colorFormat = this.glColorFormat()
80+
}
81+
if (options.depthFormat === 'float') {
82+
this.depthFormat = this.glDepthFormat()
83+
}
84+
console.warn(
85+
'Warning: Unable to create floating point textures in this environment. Falling back to integers',
86+
)
87+
}
88+
7689
const framebuffer = gl.createFramebuffer()
7790
if (!framebuffer) {
7891
throw new Error('Unable to create a framebuffer')
@@ -81,6 +94,21 @@ class Framebuffer {
8194
this.recreateTextures()
8295
}
8396

97+
glColorFormat(format) {
98+
const gl = this._renderer.GL
99+
if (format === 'float') {
100+
return gl.FLOAT
101+
}
102+
return gl.UNSIGNED_BYTE
103+
}
104+
glDepthFormat(format) {
105+
const gl = this._renderer.GL
106+
if (format === 'float') {
107+
return gl.FLOAT
108+
}
109+
return gl.UNSIGNED_SHORT
110+
}
111+
84112
handleResize() {
85113
const oldColor = this.colorTexture
86114
const oldDepth = this.depthTexture
@@ -117,7 +145,7 @@ class Framebuffer {
117145
height * density,
118146
0,
119147
hasAlpha ? gl.RGBA : gl.RGB,
120-
gl.UNSIGNED_BYTE,
148+
this.colorFormat,
121149
null,
122150
)
123151

@@ -140,7 +168,7 @@ class Framebuffer {
140168
height * density,
141169
0,
142170
gl.DEPTH_COMPONENT,
143-
gl.UNSIGNED_SHORT,
171+
this.depthFormat,
144172
null,
145173
)
146174

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@davepagurek/p5.framebuffer",
3-
"version": "0.0.2",
3+
"version": "0.0.3",
44
"main": "p5.Framebuffer.js",
55
"author": "Dave Pagurek <[email protected]>",
66
"license": "MIT",
@@ -14,7 +14,8 @@
1414
"homepage": "https://github.com/davepagurek/p5.Framebuffer",
1515
"dependencies": {},
1616
"devDependencies": {
17-
"minify": "^9.0.0"
17+
"minify": "^9.0.0",
18+
"prettier": "^2.7.1"
1819
},
1920
"scripts": {
2021
"build:core": "minify p5.Framebuffer.js > p5.Framebuffer.core.min.js",

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ path-exists@^5.0.0:
206206
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7"
207207
integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==
208208

209+
prettier@^2.7.1:
210+
version "2.7.1"
211+
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
212+
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
213+
209214
readjson@^2.2.0, readjson@^2.2.2:
210215
version "2.2.2"
211216
resolved "https://registry.yarnpkg.com/readjson/-/readjson-2.2.2.tgz#ed940ebdd72b88b383e02db7117402f980158959"

0 commit comments

Comments
 (0)