p5 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{src → dist}/accessibility/color_namer.js +48 -3
- package/{src → dist}/accessibility/describe.js +2 -2
- package/{src → dist}/accessibility/gridOutput.js +2 -2
- package/dist/accessibility/index.js +60 -0
- package/{src → dist}/accessibility/outputs.js +2 -2
- package/{src → dist}/accessibility/textOutput.js +2 -2
- package/dist/app.js +120 -0
- package/{src → dist}/color/color_conversion.js +48 -10
- package/{src → dist}/color/color_spaces/hsb.js +3 -1
- package/dist/color/creating_reading.js +3 -0
- package/dist/color/index.js +13 -0
- package/dist/color/p5.Color.culori.js +1 -0
- package/dist/color/p5.Color.js +3 -0
- package/{src → dist}/color/setting.js +9 -6
- package/{src/core/constants.js → dist/constants-C-g_eAdC.js} +266 -130
- package/{src → dist}/core/States.js +3 -1
- package/dist/core/constants.js +1 -0
- package/{src → dist}/core/environment.js +7 -6
- package/{src → dist}/core/friendly_errors/browser_errors.js +1 -1
- package/{src → dist}/core/friendly_errors/fes_core.js +14 -44
- package/{src → dist}/core/friendly_errors/file_errors.js +6 -3
- package/dist/core/friendly_errors/index.js +23 -0
- package/dist/core/friendly_errors/param_validator.js +5455 -0
- package/{src → dist}/core/friendly_errors/sketch_reader.js +50 -4
- package/{src → dist}/core/friendly_errors/sketch_verifier.js +6 -6
- package/{src → dist}/core/friendly_errors/stacktrace.js +3 -5
- package/{src → dist}/core/friendly_errors/validate_params.js +50 -41
- package/{src → dist}/core/helpers.js +9 -6
- package/dist/core/init.js +105 -0
- package/dist/core/internationalization.js +302 -0
- package/dist/core/legacy.js +73 -0
- package/dist/core/main.js +44 -0
- package/dist/core/noop.js +3 -0
- package/dist/core/p5.Graphics.js +40 -0
- package/dist/core/p5.Renderer.js +11 -0
- package/dist/core/p5.Renderer2D.js +44 -0
- package/dist/core/reference.js +1 -0
- package/dist/core/rendering.js +40 -0
- package/{src → dist}/core/structure.js +3 -3
- package/{src → dist}/core/transform.js +2 -2
- package/{src/color/creating_reading.js → dist/creating_reading-D4AAKRbx.js} +841 -13
- package/{src → dist}/data/index.js +3 -1
- package/{src → dist}/data/local_storage.js +2 -8
- package/{src → dist}/dom/dom.js +11 -5
- package/dist/dom/index.js +18 -0
- package/{src → dist}/dom/p5.Element.js +14 -12
- package/{src → dist}/dom/p5.File.js +4 -4
- package/{src → dist}/dom/p5.MediaElement.js +10 -4
- package/{src → dist}/events/acceleration.js +2 -2
- package/{src → dist}/events/index.js +3 -1
- package/{src → dist}/events/keyboard.js +14 -11
- package/{src → dist}/events/pointer.js +16 -17
- package/dist/image/const.js +9 -0
- package/{src → dist}/image/filterRenderer2D.js +57 -37
- package/{src → dist}/image/filters.js +1 -3
- package/dist/image/image.js +40 -0
- package/dist/image/index.js +51 -0
- package/dist/image/loading_displaying.js +40 -0
- package/dist/image/p5.Image.js +11 -0
- package/{src → dist}/image/pixels.js +4 -3
- package/{src → dist}/io/csv.js +72 -70
- package/dist/io/files.js +40 -0
- package/dist/io/index.js +51 -0
- package/{src → dist}/io/p5.Table.js +6 -6
- package/{src → dist}/io/p5.TableRow.js +3 -4
- package/{src → dist}/io/p5.XML.js +2 -5
- package/{src → dist}/io/utilities.js +1 -1
- package/{src/core/p5.Renderer2D.js → dist/main-s72KWcUy.js} +735 -57
- package/{src → dist}/math/Matrices/Matrix.js +10 -8
- package/{src → dist}/math/Matrices/MatrixInterface.js +5 -3
- package/{src → dist}/math/Matrices/MatrixNumjs.js +12 -26
- package/{src → dist}/math/calculation.js +2 -2
- package/{src → dist}/math/index.js +6 -3
- package/{src → dist}/math/math.js +2 -2
- package/{src → dist}/math/noise.js +2 -2
- package/{src → dist}/math/p5.Matrix.js +7 -4
- package/{src → dist}/math/p5.Vector.js +6 -6
- package/{src → dist}/math/random.js +2 -2
- package/{src → dist}/math/trigonometry.js +16 -15
- package/{src/image/p5.Image.js → dist/p5.Renderer-CwAYZOC2.js} +390 -19
- package/dist/rendering--aAe5aq3.js +24925 -0
- package/{src → dist}/shape/2d_primitives.js +18 -17
- package/{src → dist}/shape/attributes.js +18 -17
- package/{src → dist}/shape/curves.js +2 -2
- package/{src → dist}/shape/custom_shapes.js +44 -64
- package/{src → dist}/shape/index.js +10 -2
- package/{src → dist}/shape/vertex.js +2 -3
- package/dist/type/index.js +25 -0
- package/{src → dist}/type/lib/Typr.js +76 -94
- package/{src → dist}/type/p5.Font.js +37 -61
- package/{src → dist}/type/textCore.js +34 -57
- package/{src → dist}/type/unicodeRanges.js +3 -1
- package/{src → dist}/utilities/conversion.js +2 -2
- package/{src → dist}/utilities/index.js +3 -1
- package/{src → dist}/utilities/time_date.js +6 -7
- package/{src → dist}/utilities/utility_functions.js +2 -2
- package/dist/webgl/3d_primitives.js +40 -0
- package/{src → dist}/webgl/GeometryBufferCache.js +3 -1
- package/{src → dist}/webgl/GeometryBuilder.js +12 -8
- package/{src → dist}/webgl/ShaderGenerator.js +79 -82
- package/{src → dist}/webgl/ShapeBuilder.js +26 -23
- package/dist/webgl/index.js +76 -0
- package/{src → dist}/webgl/interaction.js +7 -6
- package/dist/webgl/light.js +40 -0
- package/{src → dist}/webgl/loading.js +45 -12
- package/dist/webgl/material.js +40 -0
- package/dist/webgl/p5.Camera.js +40 -0
- package/{src → dist}/webgl/p5.DataArray.js +3 -5
- package/dist/webgl/p5.Framebuffer.js +40 -0
- package/{src → dist}/webgl/p5.Geometry.js +12 -15
- package/{src → dist}/webgl/p5.Quat.js +5 -4
- package/{src → dist}/webgl/p5.RenderBuffer.js +2 -3
- package/dist/webgl/p5.RendererGL.js +40 -0
- package/dist/webgl/p5.Shader.js +40 -0
- package/dist/webgl/p5.Texture.js +40 -0
- package/{src → dist}/webgl/text.js +51 -9
- package/lib/p5.esm.js +102 -48
- package/lib/p5.js +102 -48
- package/lib/p5.min.js +1 -1
- package/package.json +17 -16
- package/translations/dev.js +6 -6
- package/translations/index.js +1 -1
- package/src/README.md +0 -27
- package/src/accessibility/index.js +0 -13
- package/src/app.js +0 -61
- package/src/color/index.js +0 -9
- package/src/color/p5.Color.culori.js +0 -66
- package/src/color/p5.Color.js +0 -851
- package/src/core/README.md +0 -91
- package/src/core/friendly_errors/index.js +0 -13
- package/src/core/friendly_errors/param_validator.js +0 -561
- package/src/core/init.js +0 -58
- package/src/core/internationalization.js +0 -195
- package/src/core/legacy.js +0 -29
- package/src/core/main.js +0 -689
- package/src/core/noop.js +0 -1
- package/src/core/p5.Graphics.js +0 -696
- package/src/core/p5.Renderer.js +0 -408
- package/src/core/reference.js +0 -2060
- package/src/core/rendering.js +0 -697
- package/src/dom/index.js +0 -11
- package/src/image/const.js +0 -6
- package/src/image/image.js +0 -731
- package/src/image/index.js +0 -15
- package/src/image/loading_displaying.js +0 -1431
- package/src/io/files.js +0 -2210
- package/src/io/index.js +0 -11
- package/src/math/README.md +0 -40
- package/src/type/index.js +0 -9
- package/src/webgl/3d_primitives.js +0 -2741
- package/src/webgl/index.js +0 -37
- package/src/webgl/light.js +0 -1851
- package/src/webgl/material.js +0 -3854
- package/src/webgl/p5.Camera.js +0 -4010
- package/src/webgl/p5.Framebuffer.js +0 -1865
- package/src/webgl/p5.RendererGL.js +0 -2867
- package/src/webgl/p5.Shader.js +0 -1505
- package/src/webgl/p5.Texture.js +0 -541
- package/src/webgl/shaders/basic.frag +0 -6
- package/src/webgl/shaders/filters/base.frag +0 -22
- package/src/webgl/shaders/filters/base.vert +0 -19
- package/src/webgl/shaders/filters/blur.frag +0 -60
- package/src/webgl/shaders/filters/default.vert +0 -18
- package/src/webgl/shaders/filters/dilate.frag +0 -39
- package/src/webgl/shaders/filters/erode.frag +0 -39
- package/src/webgl/shaders/filters/gray.frag +0 -16
- package/src/webgl/shaders/filters/invert.frag +0 -15
- package/src/webgl/shaders/filters/opaque.frag +0 -12
- package/src/webgl/shaders/filters/posterize.frag +0 -29
- package/src/webgl/shaders/filters/threshold.frag +0 -23
- package/src/webgl/shaders/font.frag +0 -216
- package/src/webgl/shaders/font.vert +0 -44
- package/src/webgl/shaders/imageLight.vert +0 -33
- package/src/webgl/shaders/imageLightDiffused.frag +0 -82
- package/src/webgl/shaders/imageLightSpecular.frag +0 -134
- package/src/webgl/shaders/light.vert +0 -37
- package/src/webgl/shaders/light_texture.frag +0 -26
- package/src/webgl/shaders/lighting.glsl +0 -227
- package/src/webgl/shaders/line.frag +0 -74
- package/src/webgl/shaders/line.vert +0 -294
- package/src/webgl/shaders/normal.frag +0 -6
- package/src/webgl/shaders/normal.vert +0 -72
- package/src/webgl/shaders/phong.frag +0 -84
- package/src/webgl/shaders/phong.vert +0 -87
- package/src/webgl/shaders/point.frag +0 -29
- package/src/webgl/shaders/point.vert +0 -19
- package/src/webgl/shaders/sphereMapping.frag +0 -26
- package/src/webgl/shaders/webgl2Compatibility.glsl +0 -34
|
@@ -1,1865 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module Rendering
|
|
3
|
-
* @requires constants
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as constants from '../core/constants';
|
|
7
|
-
import { RGB, RGBA } from '../color/creating_reading';
|
|
8
|
-
import { checkWebGLCapabilities } from './p5.Texture';
|
|
9
|
-
import { readPixelsWebGL, readPixelWebGL } from './p5.RendererGL';
|
|
10
|
-
import { Camera } from './p5.Camera';
|
|
11
|
-
import { Texture } from './p5.Texture';
|
|
12
|
-
import { Image } from '../image/p5.Image';
|
|
13
|
-
|
|
14
|
-
const constrain = (n, low, high) => Math.max(Math.min(n, high), low);
|
|
15
|
-
|
|
16
|
-
class FramebufferCamera extends Camera {
|
|
17
|
-
constructor(framebuffer) {
|
|
18
|
-
super(framebuffer.renderer);
|
|
19
|
-
this.fbo = framebuffer;
|
|
20
|
-
|
|
21
|
-
// WebGL textures are upside-down compared to textures that come from
|
|
22
|
-
// images and graphics. Framebuffer cameras need to invert their y
|
|
23
|
-
// axes when being rendered to so that the texture comes out rightway up
|
|
24
|
-
// when read in shaders or image().
|
|
25
|
-
this.yScale = -1;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
_computeCameraDefaultSettings() {
|
|
29
|
-
super._computeCameraDefaultSettings();
|
|
30
|
-
this.defaultAspectRatio = this.fbo.width / this.fbo.height;
|
|
31
|
-
this.defaultCameraFOV =
|
|
32
|
-
2 * Math.atan(this.fbo.height / 2 / this.defaultEyeZ);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
class FramebufferTexture {
|
|
37
|
-
constructor(framebuffer, property) {
|
|
38
|
-
this.framebuffer = framebuffer;
|
|
39
|
-
this.property = property;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
get width() {
|
|
43
|
-
return this.framebuffer.width * this.framebuffer.density;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get height() {
|
|
47
|
-
return this.framebuffer.height * this.framebuffer.density;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
rawTexture() {
|
|
51
|
-
return this.framebuffer[this.property];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
class Framebuffer {
|
|
56
|
-
constructor(renderer, settings = {}) {
|
|
57
|
-
this.renderer = renderer;
|
|
58
|
-
this.renderer.framebuffers.add(this);
|
|
59
|
-
|
|
60
|
-
this._isClipApplied = false;
|
|
61
|
-
|
|
62
|
-
this.pixels = [];
|
|
63
|
-
|
|
64
|
-
this.format = settings.format || constants.UNSIGNED_BYTE;
|
|
65
|
-
this.channels = settings.channels || (
|
|
66
|
-
this.renderer._pInst._glAttributes.alpha
|
|
67
|
-
? RGBA
|
|
68
|
-
: RGB
|
|
69
|
-
);
|
|
70
|
-
this.useDepth = settings.depth === undefined ? true : settings.depth;
|
|
71
|
-
this.depthFormat = settings.depthFormat || constants.FLOAT;
|
|
72
|
-
this.textureFiltering = settings.textureFiltering || constants.LINEAR;
|
|
73
|
-
if (settings.antialias === undefined) {
|
|
74
|
-
this.antialiasSamples = this.renderer._pInst._glAttributes.antialias
|
|
75
|
-
? 2
|
|
76
|
-
: 0;
|
|
77
|
-
} else if (typeof settings.antialias === 'number') {
|
|
78
|
-
this.antialiasSamples = settings.antialias;
|
|
79
|
-
} else {
|
|
80
|
-
this.antialiasSamples = settings.antialias ? 2 : 0;
|
|
81
|
-
}
|
|
82
|
-
this.antialias = this.antialiasSamples > 0;
|
|
83
|
-
if (this.antialias && this.renderer.webglVersion !== constants.WEBGL2) {
|
|
84
|
-
console.warn('Antialiasing is unsupported in a WebGL 1 context');
|
|
85
|
-
this.antialias = false;
|
|
86
|
-
}
|
|
87
|
-
this.density = settings.density || this.renderer._pixelDensity;
|
|
88
|
-
const gl = this.renderer.GL;
|
|
89
|
-
this.gl = gl;
|
|
90
|
-
if (settings.width && settings.height) {
|
|
91
|
-
const dimensions =
|
|
92
|
-
this.renderer._adjustDimensions(settings.width, settings.height);
|
|
93
|
-
this.width = dimensions.adjustedWidth;
|
|
94
|
-
this.height = dimensions.adjustedHeight;
|
|
95
|
-
this._autoSized = false;
|
|
96
|
-
} else {
|
|
97
|
-
if ((settings.width === undefined) !== (settings.height === undefined)) {
|
|
98
|
-
console.warn(
|
|
99
|
-
'Please supply both width and height for a framebuffer to give it a ' +
|
|
100
|
-
'size. Only one was given, so the framebuffer will match the size ' +
|
|
101
|
-
'of its canvas.'
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
this.width = this.renderer.width;
|
|
105
|
-
this.height = this.renderer.height;
|
|
106
|
-
this._autoSized = true;
|
|
107
|
-
}
|
|
108
|
-
this._checkIfFormatsAvailable();
|
|
109
|
-
|
|
110
|
-
if (settings.stencil && !this.useDepth) {
|
|
111
|
-
console.warn('A stencil buffer can only be used if also using depth. Since the framebuffer has no depth buffer, the stencil buffer will be ignored.');
|
|
112
|
-
}
|
|
113
|
-
this.useStencil = this.useDepth &&
|
|
114
|
-
(settings.stencil === undefined ? true : settings.stencil);
|
|
115
|
-
|
|
116
|
-
this.framebuffer = gl.createFramebuffer();
|
|
117
|
-
if (!this.framebuffer) {
|
|
118
|
-
throw new Error('Unable to create a framebuffer');
|
|
119
|
-
}
|
|
120
|
-
if (this.antialias) {
|
|
121
|
-
this.aaFramebuffer = gl.createFramebuffer();
|
|
122
|
-
if (!this.aaFramebuffer) {
|
|
123
|
-
throw new Error('Unable to create a framebuffer for antialiasing');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this._recreateTextures();
|
|
128
|
-
|
|
129
|
-
const prevCam = this.renderer.states.curCamera;
|
|
130
|
-
this.defaultCamera = this.createCamera();
|
|
131
|
-
this.filterCamera = this.createCamera();
|
|
132
|
-
this.renderer.states.setValue('curCamera', prevCam);
|
|
133
|
-
|
|
134
|
-
this.draw(() => this.renderer.clear());
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Resizes the framebuffer to a given width and height.
|
|
139
|
-
*
|
|
140
|
-
* The parameters, `width` and `height`, set the dimensions of the
|
|
141
|
-
* framebuffer. For example, calling `myBuffer.resize(300, 500)` resizes
|
|
142
|
-
* the framebuffer to 300×500 pixels, then sets `myBuffer.width` to 300
|
|
143
|
-
* and `myBuffer.height` 500.
|
|
144
|
-
*
|
|
145
|
-
* @param {Number} width width of the framebuffer.
|
|
146
|
-
* @param {Number} height height of the framebuffer.
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* <div>
|
|
150
|
-
* <code>
|
|
151
|
-
* let myBuffer;
|
|
152
|
-
*
|
|
153
|
-
* function setup() {
|
|
154
|
-
* createCanvas(100, 100, WEBGL);
|
|
155
|
-
*
|
|
156
|
-
* // Create a p5.Framebuffer object.
|
|
157
|
-
* myBuffer = createFramebuffer();
|
|
158
|
-
*
|
|
159
|
-
* describe('A multicolor sphere on a white surface. The image grows larger or smaller when the user moves the mouse, revealing a gray background.');
|
|
160
|
-
* }
|
|
161
|
-
*
|
|
162
|
-
* function draw() {
|
|
163
|
-
* background(200);
|
|
164
|
-
*
|
|
165
|
-
* // Draw to the p5.Framebuffer object.
|
|
166
|
-
* myBuffer.begin();
|
|
167
|
-
* background(255);
|
|
168
|
-
* normalMaterial();
|
|
169
|
-
* sphere(20);
|
|
170
|
-
* myBuffer.end();
|
|
171
|
-
*
|
|
172
|
-
* // Display the p5.Framebuffer object.
|
|
173
|
-
* image(myBuffer, -50, -50);
|
|
174
|
-
* }
|
|
175
|
-
*
|
|
176
|
-
* // Resize the p5.Framebuffer object when the
|
|
177
|
-
* // user moves the mouse.
|
|
178
|
-
* function mouseMoved() {
|
|
179
|
-
* myBuffer.resize(mouseX, mouseY);
|
|
180
|
-
* }
|
|
181
|
-
* </code>
|
|
182
|
-
* </div>
|
|
183
|
-
*/
|
|
184
|
-
resize(width, height) {
|
|
185
|
-
this._autoSized = false;
|
|
186
|
-
const dimensions =
|
|
187
|
-
this.renderer._adjustDimensions(width, height);
|
|
188
|
-
width = dimensions.adjustedWidth;
|
|
189
|
-
height = dimensions.adjustedHeight;
|
|
190
|
-
this.width = width;
|
|
191
|
-
this.height = height;
|
|
192
|
-
this._handleResize();
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Sets the framebuffer's pixel density or returns its current density.
|
|
197
|
-
*
|
|
198
|
-
* Computer displays are grids of little lights called pixels. A display's
|
|
199
|
-
* pixel density describes how many pixels it packs into an area. Displays
|
|
200
|
-
* with smaller pixels have a higher pixel density and create sharper
|
|
201
|
-
* images.
|
|
202
|
-
*
|
|
203
|
-
* The parameter, `density`, is optional. If a number is passed, as in
|
|
204
|
-
* `myBuffer.pixelDensity(1)`, it sets the framebuffer's pixel density. By
|
|
205
|
-
* default, the framebuffer's pixel density will match that of the canvas
|
|
206
|
-
* where it was created. All canvases default to match the display's pixel
|
|
207
|
-
* density.
|
|
208
|
-
*
|
|
209
|
-
* Calling `myBuffer.pixelDensity()` without an argument returns its current
|
|
210
|
-
* pixel density.
|
|
211
|
-
*
|
|
212
|
-
* @param {Number} [density] pixel density to set.
|
|
213
|
-
* @returns {Number} current pixel density.
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* <div>
|
|
217
|
-
* <code>
|
|
218
|
-
* let myBuffer;
|
|
219
|
-
*
|
|
220
|
-
* function setup() {
|
|
221
|
-
* createCanvas(100, 100, WEBGL);
|
|
222
|
-
*
|
|
223
|
-
* // Create a p5.Framebuffer object.
|
|
224
|
-
* myBuffer = createFramebuffer();
|
|
225
|
-
*
|
|
226
|
-
* describe("A white circle on a gray canvas. The circle's edge become fuzzy while the user presses and holds the mouse.");
|
|
227
|
-
* }
|
|
228
|
-
*
|
|
229
|
-
* function draw() {
|
|
230
|
-
* // Draw to the p5.Framebuffer object.
|
|
231
|
-
* myBuffer.begin();
|
|
232
|
-
* background(200);
|
|
233
|
-
* circle(0, 0, 40);
|
|
234
|
-
* myBuffer.end();
|
|
235
|
-
*
|
|
236
|
-
* // Display the p5.Framebuffer object.
|
|
237
|
-
* image(myBuffer, -50, -50);
|
|
238
|
-
* }
|
|
239
|
-
*
|
|
240
|
-
* // Decrease the pixel density when the user
|
|
241
|
-
* // presses the mouse.
|
|
242
|
-
* function mousePressed() {
|
|
243
|
-
* myBuffer.pixelDensity(1);
|
|
244
|
-
* }
|
|
245
|
-
*
|
|
246
|
-
* // Increase the pixel density when the user
|
|
247
|
-
* // releases the mouse.
|
|
248
|
-
* function mouseReleased() {
|
|
249
|
-
* myBuffer.pixelDensity(2);
|
|
250
|
-
* }
|
|
251
|
-
* </code>
|
|
252
|
-
* </div>
|
|
253
|
-
*
|
|
254
|
-
* <div>
|
|
255
|
-
* <code>
|
|
256
|
-
* let myBuffer;
|
|
257
|
-
* let myFont;
|
|
258
|
-
*
|
|
259
|
-
* async function setup() {
|
|
260
|
-
* // Load a font and create a p5.Font object.
|
|
261
|
-
* myFont = await loadFont('assets/inconsolata.otf');
|
|
262
|
-
*
|
|
263
|
-
* createCanvas(100, 100, WEBGL);
|
|
264
|
-
*
|
|
265
|
-
* background(200);
|
|
266
|
-
*
|
|
267
|
-
* // Create a p5.Framebuffer object.
|
|
268
|
-
* myBuffer = createFramebuffer();
|
|
269
|
-
*
|
|
270
|
-
* // Get the p5.Framebuffer object's pixel density.
|
|
271
|
-
* let d = myBuffer.pixelDensity();
|
|
272
|
-
*
|
|
273
|
-
* // Style the text.
|
|
274
|
-
* textAlign(CENTER, CENTER);
|
|
275
|
-
* textFont(myFont);
|
|
276
|
-
* textSize(16);
|
|
277
|
-
* fill(0);
|
|
278
|
-
*
|
|
279
|
-
* // Display the pixel density.
|
|
280
|
-
* text(`Density: ${d}`, 0, 0);
|
|
281
|
-
*
|
|
282
|
-
* describe(`The text "Density: ${d}" written in black on a gray background.`);
|
|
283
|
-
* }
|
|
284
|
-
* </code>
|
|
285
|
-
* </div>
|
|
286
|
-
*/
|
|
287
|
-
pixelDensity(density) {
|
|
288
|
-
if (density) {
|
|
289
|
-
this._autoSized = false;
|
|
290
|
-
this.density = density;
|
|
291
|
-
this._handleResize();
|
|
292
|
-
} else {
|
|
293
|
-
return this.density;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Toggles the framebuffer's autosizing mode or returns the current mode.
|
|
299
|
-
*
|
|
300
|
-
* By default, the framebuffer automatically resizes to match the canvas
|
|
301
|
-
* that created it. Calling `myBuffer.autoSized(false)` disables this
|
|
302
|
-
* behavior and calling `myBuffer.autoSized(true)` re-enables it.
|
|
303
|
-
*
|
|
304
|
-
* Calling `myBuffer.autoSized()` without an argument returns `true` if
|
|
305
|
-
* the framebuffer automatically resizes and `false` if not.
|
|
306
|
-
*
|
|
307
|
-
* @param {Boolean} [autoSized] whether to automatically resize the framebuffer to match the canvas.
|
|
308
|
-
* @returns {Boolean} current autosize setting.
|
|
309
|
-
*
|
|
310
|
-
* @example
|
|
311
|
-
* <div>
|
|
312
|
-
* <code>
|
|
313
|
-
* // Double-click to toggle the autosizing mode.
|
|
314
|
-
*
|
|
315
|
-
* let myBuffer;
|
|
316
|
-
*
|
|
317
|
-
* function setup() {
|
|
318
|
-
* createCanvas(100, 100, WEBGL);
|
|
319
|
-
*
|
|
320
|
-
* // Create a p5.Framebuffer object.
|
|
321
|
-
* myBuffer = createFramebuffer();
|
|
322
|
-
*
|
|
323
|
-
* describe('A multicolor sphere on a gray background. The image resizes when the user moves the mouse.');
|
|
324
|
-
* }
|
|
325
|
-
*
|
|
326
|
-
* function draw() {
|
|
327
|
-
* background(50);
|
|
328
|
-
*
|
|
329
|
-
* // Draw to the p5.Framebuffer object.
|
|
330
|
-
* myBuffer.begin();
|
|
331
|
-
* background(200);
|
|
332
|
-
* normalMaterial();
|
|
333
|
-
* sphere(width / 4);
|
|
334
|
-
* myBuffer.end();
|
|
335
|
-
*
|
|
336
|
-
* // Display the p5.Framebuffer object.
|
|
337
|
-
* image(myBuffer, -width / 2, -height / 2);
|
|
338
|
-
* }
|
|
339
|
-
*
|
|
340
|
-
* // Resize the canvas when the user moves the mouse.
|
|
341
|
-
* function mouseMoved() {
|
|
342
|
-
* let w = constrain(mouseX, 0, 100);
|
|
343
|
-
* let h = constrain(mouseY, 0, 100);
|
|
344
|
-
* resizeCanvas(w, h);
|
|
345
|
-
* }
|
|
346
|
-
*
|
|
347
|
-
* // Toggle autoSizing when the user double-clicks.
|
|
348
|
-
* // Note: opened an issue to fix(?) this.
|
|
349
|
-
* function doubleClicked() {
|
|
350
|
-
* let isAuto = myBuffer.autoSized();
|
|
351
|
-
* myBuffer.autoSized(!isAuto);
|
|
352
|
-
* }
|
|
353
|
-
* </code>
|
|
354
|
-
* </div>
|
|
355
|
-
*/
|
|
356
|
-
autoSized(autoSized) {
|
|
357
|
-
if (autoSized === undefined) {
|
|
358
|
-
return this._autoSized;
|
|
359
|
-
} else {
|
|
360
|
-
this._autoSized = autoSized;
|
|
361
|
-
this._handleResize();
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Checks the capabilities of the current WebGL environment to see if the
|
|
367
|
-
* settings supplied by the user are capable of being fulfilled. If they
|
|
368
|
-
* are not, warnings will be logged and the settings will be changed to
|
|
369
|
-
* something close that can be fulfilled.
|
|
370
|
-
*
|
|
371
|
-
* @private
|
|
372
|
-
*/
|
|
373
|
-
_checkIfFormatsAvailable() {
|
|
374
|
-
const gl = this.gl;
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
this.useDepth &&
|
|
378
|
-
this.renderer.webglVersion === constants.WEBGL &&
|
|
379
|
-
!gl.getExtension('WEBGL_depth_texture')
|
|
380
|
-
) {
|
|
381
|
-
console.warn(
|
|
382
|
-
'Unable to create depth textures in this environment. Falling back ' +
|
|
383
|
-
'to a framebuffer without depth.'
|
|
384
|
-
);
|
|
385
|
-
this.useDepth = false;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (
|
|
389
|
-
this.useDepth &&
|
|
390
|
-
this.renderer.webglVersion === constants.WEBGL &&
|
|
391
|
-
this.depthFormat === constants.FLOAT
|
|
392
|
-
) {
|
|
393
|
-
console.warn(
|
|
394
|
-
'FLOAT depth format is unavailable in WebGL 1. ' +
|
|
395
|
-
'Defaulting to UNSIGNED_INT.'
|
|
396
|
-
);
|
|
397
|
-
this.depthFormat = constants.UNSIGNED_INT;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (![
|
|
401
|
-
constants.UNSIGNED_BYTE,
|
|
402
|
-
constants.FLOAT,
|
|
403
|
-
constants.HALF_FLOAT
|
|
404
|
-
].includes(this.format)) {
|
|
405
|
-
console.warn(
|
|
406
|
-
'Unknown Framebuffer format. ' +
|
|
407
|
-
'Please use UNSIGNED_BYTE, FLOAT, or HALF_FLOAT. ' +
|
|
408
|
-
'Defaulting to UNSIGNED_BYTE.'
|
|
409
|
-
);
|
|
410
|
-
this.format = constants.UNSIGNED_BYTE;
|
|
411
|
-
}
|
|
412
|
-
if (this.useDepth && ![
|
|
413
|
-
constants.UNSIGNED_INT,
|
|
414
|
-
constants.FLOAT
|
|
415
|
-
].includes(this.depthFormat)) {
|
|
416
|
-
console.warn(
|
|
417
|
-
'Unknown Framebuffer depth format. ' +
|
|
418
|
-
'Please use UNSIGNED_INT or FLOAT. Defaulting to FLOAT.'
|
|
419
|
-
);
|
|
420
|
-
this.depthFormat = constants.FLOAT;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const support = checkWebGLCapabilities(this.renderer);
|
|
424
|
-
if (!support.float && this.format === constants.FLOAT) {
|
|
425
|
-
console.warn(
|
|
426
|
-
'This environment does not support FLOAT textures. ' +
|
|
427
|
-
'Falling back to UNSIGNED_BYTE.'
|
|
428
|
-
);
|
|
429
|
-
this.format = constants.UNSIGNED_BYTE;
|
|
430
|
-
}
|
|
431
|
-
if (
|
|
432
|
-
this.useDepth &&
|
|
433
|
-
!support.float &&
|
|
434
|
-
this.depthFormat === constants.FLOAT
|
|
435
|
-
) {
|
|
436
|
-
console.warn(
|
|
437
|
-
'This environment does not support FLOAT depth textures. ' +
|
|
438
|
-
'Falling back to UNSIGNED_INT.'
|
|
439
|
-
);
|
|
440
|
-
this.depthFormat = constants.UNSIGNED_INT;
|
|
441
|
-
}
|
|
442
|
-
if (!support.halfFloat && this.format === constants.HALF_FLOAT) {
|
|
443
|
-
console.warn(
|
|
444
|
-
'This environment does not support HALF_FLOAT textures. ' +
|
|
445
|
-
'Falling back to UNSIGNED_BYTE.'
|
|
446
|
-
);
|
|
447
|
-
this.format = constants.UNSIGNED_BYTE;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (
|
|
451
|
-
this.channels === RGB &&
|
|
452
|
-
[constants.FLOAT, constants.HALF_FLOAT].includes(this.format)
|
|
453
|
-
) {
|
|
454
|
-
console.warn(
|
|
455
|
-
'FLOAT and HALF_FLOAT formats do not work cross-platform with only ' +
|
|
456
|
-
'RGB channels. Falling back to RGBA.'
|
|
457
|
-
);
|
|
458
|
-
this.channels = RGBA;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Creates new textures and renderbuffers given the current size of the
|
|
464
|
-
* framebuffer.
|
|
465
|
-
*
|
|
466
|
-
* @private
|
|
467
|
-
*/
|
|
468
|
-
_recreateTextures() {
|
|
469
|
-
const gl = this.gl;
|
|
470
|
-
|
|
471
|
-
this._updateSize();
|
|
472
|
-
|
|
473
|
-
const prevBoundTexture = gl.getParameter(gl.TEXTURE_BINDING_2D);
|
|
474
|
-
const prevBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
|
|
475
|
-
|
|
476
|
-
const colorTexture = gl.createTexture();
|
|
477
|
-
if (!colorTexture) {
|
|
478
|
-
throw new Error('Unable to create color texture');
|
|
479
|
-
}
|
|
480
|
-
gl.bindTexture(gl.TEXTURE_2D, colorTexture);
|
|
481
|
-
const colorFormat = this._glColorFormat();
|
|
482
|
-
gl.texImage2D(
|
|
483
|
-
gl.TEXTURE_2D,
|
|
484
|
-
0,
|
|
485
|
-
colorFormat.internalFormat,
|
|
486
|
-
this.width * this.density,
|
|
487
|
-
this.height * this.density,
|
|
488
|
-
0,
|
|
489
|
-
colorFormat.format,
|
|
490
|
-
colorFormat.type,
|
|
491
|
-
null
|
|
492
|
-
);
|
|
493
|
-
this.colorTexture = colorTexture;
|
|
494
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
495
|
-
gl.framebufferTexture2D(
|
|
496
|
-
gl.FRAMEBUFFER,
|
|
497
|
-
gl.COLOR_ATTACHMENT0,
|
|
498
|
-
gl.TEXTURE_2D,
|
|
499
|
-
colorTexture,
|
|
500
|
-
0
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
if (this.useDepth) {
|
|
504
|
-
// Create the depth texture
|
|
505
|
-
const depthTexture = gl.createTexture();
|
|
506
|
-
if (!depthTexture) {
|
|
507
|
-
throw new Error('Unable to create depth texture');
|
|
508
|
-
}
|
|
509
|
-
const depthFormat = this._glDepthFormat();
|
|
510
|
-
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
|
|
511
|
-
gl.texImage2D(
|
|
512
|
-
gl.TEXTURE_2D,
|
|
513
|
-
0,
|
|
514
|
-
depthFormat.internalFormat,
|
|
515
|
-
this.width * this.density,
|
|
516
|
-
this.height * this.density,
|
|
517
|
-
0,
|
|
518
|
-
depthFormat.format,
|
|
519
|
-
depthFormat.type,
|
|
520
|
-
null
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
gl.framebufferTexture2D(
|
|
524
|
-
gl.FRAMEBUFFER,
|
|
525
|
-
this.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
|
|
526
|
-
gl.TEXTURE_2D,
|
|
527
|
-
depthTexture,
|
|
528
|
-
0
|
|
529
|
-
);
|
|
530
|
-
this.depthTexture = depthTexture;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Create separate framebuffer for antialiasing
|
|
534
|
-
if (this.antialias) {
|
|
535
|
-
this.colorRenderbuffer = gl.createRenderbuffer();
|
|
536
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, this.colorRenderbuffer);
|
|
537
|
-
gl.renderbufferStorageMultisample(
|
|
538
|
-
gl.RENDERBUFFER,
|
|
539
|
-
Math.max(
|
|
540
|
-
0,
|
|
541
|
-
Math.min(this.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))
|
|
542
|
-
),
|
|
543
|
-
colorFormat.internalFormat,
|
|
544
|
-
this.width * this.density,
|
|
545
|
-
this.height * this.density
|
|
546
|
-
);
|
|
547
|
-
|
|
548
|
-
if (this.useDepth) {
|
|
549
|
-
const depthFormat = this._glDepthFormat();
|
|
550
|
-
this.depthRenderbuffer = gl.createRenderbuffer();
|
|
551
|
-
gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthRenderbuffer);
|
|
552
|
-
gl.renderbufferStorageMultisample(
|
|
553
|
-
gl.RENDERBUFFER,
|
|
554
|
-
Math.max(
|
|
555
|
-
0,
|
|
556
|
-
Math.min(this.antialiasSamples, gl.getParameter(gl.MAX_SAMPLES))
|
|
557
|
-
),
|
|
558
|
-
depthFormat.internalFormat,
|
|
559
|
-
this.width * this.density,
|
|
560
|
-
this.height * this.density
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.aaFramebuffer);
|
|
565
|
-
gl.framebufferRenderbuffer(
|
|
566
|
-
gl.FRAMEBUFFER,
|
|
567
|
-
gl.COLOR_ATTACHMENT0,
|
|
568
|
-
gl.RENDERBUFFER,
|
|
569
|
-
this.colorRenderbuffer
|
|
570
|
-
);
|
|
571
|
-
if (this.useDepth) {
|
|
572
|
-
gl.framebufferRenderbuffer(
|
|
573
|
-
gl.FRAMEBUFFER,
|
|
574
|
-
this.useStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
|
|
575
|
-
gl.RENDERBUFFER,
|
|
576
|
-
this.depthRenderbuffer
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (this.useDepth) {
|
|
582
|
-
this.depth = new FramebufferTexture(this, 'depthTexture');
|
|
583
|
-
const depthFilter = gl.NEAREST;
|
|
584
|
-
this.depthP5Texture = new Texture(
|
|
585
|
-
this.renderer,
|
|
586
|
-
this.depth,
|
|
587
|
-
{
|
|
588
|
-
minFilter: depthFilter,
|
|
589
|
-
magFilter: depthFilter
|
|
590
|
-
}
|
|
591
|
-
);
|
|
592
|
-
this.renderer.textures.set(this.depth, this.depthP5Texture);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
this.color = new FramebufferTexture(this, 'colorTexture');
|
|
596
|
-
const filter = this.textureFiltering === constants.LINEAR
|
|
597
|
-
? gl.LINEAR
|
|
598
|
-
: gl.NEAREST;
|
|
599
|
-
this.colorP5Texture = new Texture(
|
|
600
|
-
this.renderer,
|
|
601
|
-
this.color,
|
|
602
|
-
{
|
|
603
|
-
minFilter: filter,
|
|
604
|
-
magFilter: filter
|
|
605
|
-
}
|
|
606
|
-
);
|
|
607
|
-
this.renderer.textures.set(this.color, this.colorP5Texture);
|
|
608
|
-
|
|
609
|
-
gl.bindTexture(gl.TEXTURE_2D, prevBoundTexture);
|
|
610
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, prevBoundFramebuffer);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* To create a WebGL texture, one needs to supply three pieces of information:
|
|
615
|
-
* the type (the data type each channel will be stored as, e.g. int or float),
|
|
616
|
-
* the format (the color channels that will each be stored in the previously
|
|
617
|
-
* specified type, e.g. rgb or rgba), and the internal format (the specifics
|
|
618
|
-
* of how data for each channel, in the aforementioned type, will be packed
|
|
619
|
-
* together, such as how many bits to use, e.g. RGBA32F or RGB565.)
|
|
620
|
-
*
|
|
621
|
-
* The format and channels asked for by the user hint at what these values
|
|
622
|
-
* need to be, and the WebGL version affects what options are avaiable.
|
|
623
|
-
* This method returns the values for these three properties, given the
|
|
624
|
-
* framebuffer's settings.
|
|
625
|
-
*
|
|
626
|
-
* @private
|
|
627
|
-
*/
|
|
628
|
-
_glColorFormat() {
|
|
629
|
-
let type, format, internalFormat;
|
|
630
|
-
const gl = this.gl;
|
|
631
|
-
|
|
632
|
-
if (this.format === constants.FLOAT) {
|
|
633
|
-
type = gl.FLOAT;
|
|
634
|
-
} else if (this.format === constants.HALF_FLOAT) {
|
|
635
|
-
type = this.renderer.webglVersion === constants.WEBGL2
|
|
636
|
-
? gl.HALF_FLOAT
|
|
637
|
-
: gl.getExtension('OES_texture_half_float').HALF_FLOAT_OES;
|
|
638
|
-
} else {
|
|
639
|
-
type = gl.UNSIGNED_BYTE;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (this.channels === RGBA) {
|
|
643
|
-
format = gl.RGBA;
|
|
644
|
-
} else {
|
|
645
|
-
format = gl.RGB;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (this.renderer.webglVersion === constants.WEBGL2) {
|
|
649
|
-
// https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
|
|
650
|
-
const table = {
|
|
651
|
-
[gl.FLOAT]: {
|
|
652
|
-
[gl.RGBA]: gl.RGBA32F
|
|
653
|
-
// gl.RGB32F is not available in Firefox without an alpha channel
|
|
654
|
-
},
|
|
655
|
-
[gl.HALF_FLOAT]: {
|
|
656
|
-
[gl.RGBA]: gl.RGBA16F
|
|
657
|
-
// gl.RGB16F is not available in Firefox without an alpha channel
|
|
658
|
-
},
|
|
659
|
-
[gl.UNSIGNED_BYTE]: {
|
|
660
|
-
[gl.RGBA]: gl.RGBA8, // gl.RGBA4
|
|
661
|
-
[gl.RGB]: gl.RGB8 // gl.RGB565
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
internalFormat = table[type][format];
|
|
665
|
-
} else if (this.format === constants.HALF_FLOAT) {
|
|
666
|
-
internalFormat = gl.RGBA;
|
|
667
|
-
} else {
|
|
668
|
-
internalFormat = format;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
return { internalFormat, format, type };
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* To create a WebGL texture, one needs to supply three pieces of information:
|
|
676
|
-
* the type (the data type each channel will be stored as, e.g. int or float),
|
|
677
|
-
* the format (the color channels that will each be stored in the previously
|
|
678
|
-
* specified type, e.g. rgb or rgba), and the internal format (the specifics
|
|
679
|
-
* of how data for each channel, in the aforementioned type, will be packed
|
|
680
|
-
* together, such as how many bits to use, e.g. RGBA32F or RGB565.)
|
|
681
|
-
*
|
|
682
|
-
* This method takes into account the settings asked for by the user and
|
|
683
|
-
* returns values for these three properties that can be used for the
|
|
684
|
-
* texture storing depth information.
|
|
685
|
-
*
|
|
686
|
-
* @private
|
|
687
|
-
*/
|
|
688
|
-
_glDepthFormat() {
|
|
689
|
-
let type, format, internalFormat;
|
|
690
|
-
const gl = this.gl;
|
|
691
|
-
|
|
692
|
-
if (this.useStencil) {
|
|
693
|
-
if (this.depthFormat === constants.FLOAT) {
|
|
694
|
-
type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV;
|
|
695
|
-
} else if (this.renderer.webglVersion === constants.WEBGL2) {
|
|
696
|
-
type = gl.UNSIGNED_INT_24_8;
|
|
697
|
-
} else {
|
|
698
|
-
type = gl.getExtension('WEBGL_depth_texture').UNSIGNED_INT_24_8_WEBGL;
|
|
699
|
-
}
|
|
700
|
-
} else {
|
|
701
|
-
if (this.depthFormat === constants.FLOAT) {
|
|
702
|
-
type = gl.FLOAT;
|
|
703
|
-
} else {
|
|
704
|
-
type = gl.UNSIGNED_INT;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (this.useStencil) {
|
|
709
|
-
format = gl.DEPTH_STENCIL;
|
|
710
|
-
} else {
|
|
711
|
-
format = gl.DEPTH_COMPONENT;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
if (this.useStencil) {
|
|
715
|
-
if (this.depthFormat === constants.FLOAT) {
|
|
716
|
-
internalFormat = gl.DEPTH32F_STENCIL8;
|
|
717
|
-
} else if (this.renderer.webglVersion === constants.WEBGL2) {
|
|
718
|
-
internalFormat = gl.DEPTH24_STENCIL8;
|
|
719
|
-
} else {
|
|
720
|
-
internalFormat = gl.DEPTH_STENCIL;
|
|
721
|
-
}
|
|
722
|
-
} else if (this.renderer.webglVersion === constants.WEBGL2) {
|
|
723
|
-
if (this.depthFormat === constants.FLOAT) {
|
|
724
|
-
internalFormat = gl.DEPTH_COMPONENT32F;
|
|
725
|
-
} else {
|
|
726
|
-
internalFormat = gl.DEPTH_COMPONENT24;
|
|
727
|
-
}
|
|
728
|
-
} else {
|
|
729
|
-
internalFormat = gl.DEPTH_COMPONENT;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
return { internalFormat, format, type };
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* A method that will be called when recreating textures. If the framebuffer
|
|
737
|
-
* is auto-sized, it will update its width, height, and density properties.
|
|
738
|
-
*
|
|
739
|
-
* @private
|
|
740
|
-
*/
|
|
741
|
-
_updateSize() {
|
|
742
|
-
if (this._autoSized) {
|
|
743
|
-
this.width = this.renderer.width;
|
|
744
|
-
this.height = this.renderer.height;
|
|
745
|
-
this.density = this.renderer._pixelDensity;
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
/**
|
|
750
|
-
* Called when the canvas that the framebuffer is attached to resizes. If the
|
|
751
|
-
* framebuffer is auto-sized, it will update its textures to match the new
|
|
752
|
-
* size.
|
|
753
|
-
*
|
|
754
|
-
* @private
|
|
755
|
-
*/
|
|
756
|
-
_canvasSizeChanged() {
|
|
757
|
-
if (this._autoSized) {
|
|
758
|
-
this._handleResize();
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Called when the size of the framebuffer has changed (either by being
|
|
764
|
-
* manually updated or from auto-size updates when its canvas changes size.)
|
|
765
|
-
* Old textures and renderbuffers will be deleted, and then recreated with the
|
|
766
|
-
* new size.
|
|
767
|
-
*
|
|
768
|
-
* @private
|
|
769
|
-
*/
|
|
770
|
-
_handleResize() {
|
|
771
|
-
const oldColor = this.color;
|
|
772
|
-
const oldDepth = this.depth;
|
|
773
|
-
const oldColorRenderbuffer = this.colorRenderbuffer;
|
|
774
|
-
const oldDepthRenderbuffer = this.depthRenderbuffer;
|
|
775
|
-
|
|
776
|
-
this._deleteTexture(oldColor);
|
|
777
|
-
if (oldDepth) this._deleteTexture(oldDepth);
|
|
778
|
-
const gl = this.gl;
|
|
779
|
-
if (oldColorRenderbuffer) gl.deleteRenderbuffer(oldColorRenderbuffer);
|
|
780
|
-
if (oldDepthRenderbuffer) gl.deleteRenderbuffer(oldDepthRenderbuffer);
|
|
781
|
-
|
|
782
|
-
this._recreateTextures();
|
|
783
|
-
this.defaultCamera._resize();
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
* Creates a new
|
|
788
|
-
* <a href="#/p5.Camera">p5.Camera</a> object to use with the framebuffer.
|
|
789
|
-
*
|
|
790
|
-
* The new camera is initialized with a default position `(0, 0, 800)` and a
|
|
791
|
-
* default perspective projection. Its properties can be controlled with
|
|
792
|
-
* <a href="#/p5.Camera">p5.Camera</a> methods such as `myCamera.lookAt(0, 0, 0)`.
|
|
793
|
-
*
|
|
794
|
-
* Framebuffer cameras should be created between calls to
|
|
795
|
-
* <a href="#/p5.Framebuffer/begin">myBuffer.begin()</a> and
|
|
796
|
-
* <a href="#/p5.Framebuffer/end">myBuffer.end()</a> like so:
|
|
797
|
-
*
|
|
798
|
-
* ```js
|
|
799
|
-
* let myCamera;
|
|
800
|
-
*
|
|
801
|
-
* myBuffer.begin();
|
|
802
|
-
*
|
|
803
|
-
* // Create the camera for the framebuffer.
|
|
804
|
-
* myCamera = myBuffer.createCamera();
|
|
805
|
-
*
|
|
806
|
-
* myBuffer.end();
|
|
807
|
-
* ```
|
|
808
|
-
*
|
|
809
|
-
* Calling <a href="#/p5/setCamera">setCamera()</a> updates the
|
|
810
|
-
* framebuffer's projection using the camera.
|
|
811
|
-
* <a href="#/p5/resetMatrix">resetMatrix()</a> must also be called for the
|
|
812
|
-
* view to change properly:
|
|
813
|
-
*
|
|
814
|
-
* ```js
|
|
815
|
-
* myBuffer.begin();
|
|
816
|
-
*
|
|
817
|
-
* // Set the camera for the framebuffer.
|
|
818
|
-
* setCamera(myCamera);
|
|
819
|
-
*
|
|
820
|
-
* // Reset all transformations.
|
|
821
|
-
* resetMatrix();
|
|
822
|
-
*
|
|
823
|
-
* // Draw stuff...
|
|
824
|
-
*
|
|
825
|
-
* myBuffer.end();
|
|
826
|
-
* ```
|
|
827
|
-
*
|
|
828
|
-
* @returns {p5.Camera} new camera.
|
|
829
|
-
*
|
|
830
|
-
* @example
|
|
831
|
-
* <div>
|
|
832
|
-
* <code>
|
|
833
|
-
* // Double-click to toggle between cameras.
|
|
834
|
-
*
|
|
835
|
-
* let myBuffer;
|
|
836
|
-
* let cam1;
|
|
837
|
-
* let cam2;
|
|
838
|
-
* let usingCam1 = true;
|
|
839
|
-
*
|
|
840
|
-
* function setup() {
|
|
841
|
-
* createCanvas(100, 100, WEBGL);
|
|
842
|
-
*
|
|
843
|
-
* // Create a p5.Framebuffer object.
|
|
844
|
-
* myBuffer = createFramebuffer();
|
|
845
|
-
*
|
|
846
|
-
* // Create the cameras between begin() and end().
|
|
847
|
-
* myBuffer.begin();
|
|
848
|
-
*
|
|
849
|
-
* // Create the first camera.
|
|
850
|
-
* // Keep its default settings.
|
|
851
|
-
* cam1 = myBuffer.createCamera();
|
|
852
|
-
*
|
|
853
|
-
* // Create the second camera.
|
|
854
|
-
* // Place it at the top-left.
|
|
855
|
-
* // Point it at the origin.
|
|
856
|
-
* cam2 = myBuffer.createCamera();
|
|
857
|
-
* cam2.setPosition(400, -400, 800);
|
|
858
|
-
* cam2.lookAt(0, 0, 0);
|
|
859
|
-
*
|
|
860
|
-
* myBuffer.end();
|
|
861
|
-
*
|
|
862
|
-
* describe(
|
|
863
|
-
* 'A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.'
|
|
864
|
-
* );
|
|
865
|
-
* }
|
|
866
|
-
*
|
|
867
|
-
* function draw() {
|
|
868
|
-
* // Draw to the p5.Framebuffer object.
|
|
869
|
-
* myBuffer.begin();
|
|
870
|
-
* background(200);
|
|
871
|
-
*
|
|
872
|
-
* // Set the camera.
|
|
873
|
-
* if (usingCam1 === true) {
|
|
874
|
-
* setCamera(cam1);
|
|
875
|
-
* } else {
|
|
876
|
-
* setCamera(cam2);
|
|
877
|
-
* }
|
|
878
|
-
*
|
|
879
|
-
* // Reset all transformations.
|
|
880
|
-
* resetMatrix();
|
|
881
|
-
*
|
|
882
|
-
* // Draw the box.
|
|
883
|
-
* box();
|
|
884
|
-
*
|
|
885
|
-
* myBuffer.end();
|
|
886
|
-
*
|
|
887
|
-
* // Display the p5.Framebuffer object.
|
|
888
|
-
* image(myBuffer, -50, -50);
|
|
889
|
-
* }
|
|
890
|
-
*
|
|
891
|
-
* // Toggle the current camera when the user double-clicks.
|
|
892
|
-
* function doubleClicked() {
|
|
893
|
-
* if (usingCam1 === true) {
|
|
894
|
-
* usingCam1 = false;
|
|
895
|
-
* } else {
|
|
896
|
-
* usingCam1 = true;
|
|
897
|
-
* }
|
|
898
|
-
* }
|
|
899
|
-
* </code>
|
|
900
|
-
* </div>
|
|
901
|
-
*/
|
|
902
|
-
createCamera() {
|
|
903
|
-
const cam = new FramebufferCamera(this);
|
|
904
|
-
cam._computeCameraDefaultSettings();
|
|
905
|
-
cam._setDefaultCamera();
|
|
906
|
-
return cam;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
/**
|
|
910
|
-
* Given a raw texture wrapper, delete its stored texture from WebGL memory,
|
|
911
|
-
* and remove it from p5's list of active textures.
|
|
912
|
-
*
|
|
913
|
-
* @param {p5.FramebufferTexture} texture
|
|
914
|
-
* @private
|
|
915
|
-
*/
|
|
916
|
-
_deleteTexture(texture) {
|
|
917
|
-
const gl = this.gl;
|
|
918
|
-
gl.deleteTexture(texture.rawTexture());
|
|
919
|
-
|
|
920
|
-
this.renderer.textures.delete(texture);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
/**
|
|
924
|
-
* Deletes the framebuffer from GPU memory.
|
|
925
|
-
*
|
|
926
|
-
* Calling `myBuffer.remove()` frees the GPU memory used by the framebuffer.
|
|
927
|
-
* The framebuffer also uses a bit of memory on the CPU which can be freed
|
|
928
|
-
* like so:
|
|
929
|
-
*
|
|
930
|
-
* ```js
|
|
931
|
-
* // Delete the framebuffer from GPU memory.
|
|
932
|
-
* myBuffer.remove();
|
|
933
|
-
*
|
|
934
|
-
* // Delete the framebuffer from CPU memory.
|
|
935
|
-
* myBuffer = undefined;
|
|
936
|
-
* ```
|
|
937
|
-
*
|
|
938
|
-
* Note: All variables that reference the framebuffer must be assigned
|
|
939
|
-
* the value `undefined` to delete the framebuffer from CPU memory. If any
|
|
940
|
-
* variable still refers to the framebuffer, then it won't be garbage
|
|
941
|
-
* collected.
|
|
942
|
-
*
|
|
943
|
-
* @example
|
|
944
|
-
* <div>
|
|
945
|
-
* <code>
|
|
946
|
-
* // Double-click to remove the p5.Framebuffer object.
|
|
947
|
-
*
|
|
948
|
-
* let myBuffer;
|
|
949
|
-
*
|
|
950
|
-
* function setup() {
|
|
951
|
-
* createCanvas(100, 100, WEBGL);
|
|
952
|
-
*
|
|
953
|
-
* // Create an options object.
|
|
954
|
-
* let options = { width: 60, height: 60 };
|
|
955
|
-
*
|
|
956
|
-
* // Create a p5.Framebuffer object and
|
|
957
|
-
* // configure it using options.
|
|
958
|
-
* myBuffer = createFramebuffer(options);
|
|
959
|
-
*
|
|
960
|
-
* describe('A white circle at the center of a dark gray square disappears when the user double-clicks.');
|
|
961
|
-
* }
|
|
962
|
-
*
|
|
963
|
-
* function draw() {
|
|
964
|
-
* background(200);
|
|
965
|
-
*
|
|
966
|
-
* // Display the p5.Framebuffer object if
|
|
967
|
-
* // it's available.
|
|
968
|
-
* if (myBuffer) {
|
|
969
|
-
* // Draw to the p5.Framebuffer object.
|
|
970
|
-
* myBuffer.begin();
|
|
971
|
-
* background(100);
|
|
972
|
-
* circle(0, 0, 20);
|
|
973
|
-
* myBuffer.end();
|
|
974
|
-
*
|
|
975
|
-
* image(myBuffer, -30, -30);
|
|
976
|
-
* }
|
|
977
|
-
* }
|
|
978
|
-
*
|
|
979
|
-
* // Remove the p5.Framebuffer object when the
|
|
980
|
-
* // the user double-clicks.
|
|
981
|
-
* function doubleClicked() {
|
|
982
|
-
* // Delete the framebuffer from GPU memory.
|
|
983
|
-
* myBuffer.remove();
|
|
984
|
-
*
|
|
985
|
-
* // Delete the framebuffer from CPU memory.
|
|
986
|
-
* myBuffer = undefined;
|
|
987
|
-
* }
|
|
988
|
-
* </code>
|
|
989
|
-
* </div>
|
|
990
|
-
*/
|
|
991
|
-
remove() {
|
|
992
|
-
const gl = this.gl;
|
|
993
|
-
this._deleteTexture(this.color);
|
|
994
|
-
if (this.depth) this._deleteTexture(this.depth);
|
|
995
|
-
gl.deleteFramebuffer(this.framebuffer);
|
|
996
|
-
if (this.aaFramebuffer) {
|
|
997
|
-
gl.deleteFramebuffer(this.aaFramebuffer);
|
|
998
|
-
}
|
|
999
|
-
if (this.depthRenderbuffer) {
|
|
1000
|
-
gl.deleteRenderbuffer(this.depthRenderbuffer);
|
|
1001
|
-
}
|
|
1002
|
-
if (this.colorRenderbuffer) {
|
|
1003
|
-
gl.deleteRenderbuffer(this.colorRenderbuffer);
|
|
1004
|
-
}
|
|
1005
|
-
this.renderer.framebuffers.delete(this);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
/**
|
|
1009
|
-
* Begins drawing shapes to the framebuffer.
|
|
1010
|
-
*
|
|
1011
|
-
* `myBuffer.begin()` and <a href="#/p5.Framebuffer/end">myBuffer.end()</a>
|
|
1012
|
-
* allow shapes to be drawn to the framebuffer. `myBuffer.begin()` begins
|
|
1013
|
-
* drawing to the framebuffer and
|
|
1014
|
-
* <a href="#/p5.Framebuffer/end">myBuffer.end()</a> stops drawing to the
|
|
1015
|
-
* framebuffer. Changes won't be visible until the framebuffer is displayed
|
|
1016
|
-
* as an image or texture.
|
|
1017
|
-
*
|
|
1018
|
-
* @example
|
|
1019
|
-
* <div>
|
|
1020
|
-
* <code>
|
|
1021
|
-
* let myBuffer;
|
|
1022
|
-
*
|
|
1023
|
-
* function setup() {
|
|
1024
|
-
* createCanvas(100, 100, WEBGL);
|
|
1025
|
-
*
|
|
1026
|
-
* // Create a p5.Framebuffer object.
|
|
1027
|
-
* myBuffer = createFramebuffer();
|
|
1028
|
-
*
|
|
1029
|
-
* describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
|
|
1030
|
-
* }
|
|
1031
|
-
*
|
|
1032
|
-
* function draw() {
|
|
1033
|
-
* background(200);
|
|
1034
|
-
*
|
|
1035
|
-
* // Start drawing to the p5.Framebuffer object.
|
|
1036
|
-
* myBuffer.begin();
|
|
1037
|
-
*
|
|
1038
|
-
* background(50);
|
|
1039
|
-
* rotateY(frameCount * 0.01);
|
|
1040
|
-
* normalMaterial();
|
|
1041
|
-
* torus(30);
|
|
1042
|
-
*
|
|
1043
|
-
* // Stop drawing to the p5.Framebuffer object.
|
|
1044
|
-
* myBuffer.end();
|
|
1045
|
-
*
|
|
1046
|
-
* // Display the p5.Framebuffer object while
|
|
1047
|
-
* // the user presses the mouse.
|
|
1048
|
-
* if (mouseIsPressed === true) {
|
|
1049
|
-
* image(myBuffer, -50, -50);
|
|
1050
|
-
* }
|
|
1051
|
-
* }
|
|
1052
|
-
* </code>
|
|
1053
|
-
* </div>
|
|
1054
|
-
*/
|
|
1055
|
-
begin() {
|
|
1056
|
-
this.prevFramebuffer = this.renderer.activeFramebuffer();
|
|
1057
|
-
if (this.prevFramebuffer) {
|
|
1058
|
-
this.prevFramebuffer._beforeEnd();
|
|
1059
|
-
}
|
|
1060
|
-
this.renderer.activeFramebuffers.push(this);
|
|
1061
|
-
this._beforeBegin();
|
|
1062
|
-
this.renderer.push();
|
|
1063
|
-
// Apply the framebuffer's camera. This does almost what
|
|
1064
|
-
// RendererGL.reset() does, but this does not try to clear any buffers;
|
|
1065
|
-
// it only sets the camera.
|
|
1066
|
-
// this.renderer.setCamera(this.defaultCamera);
|
|
1067
|
-
this.renderer.states.setValue('curCamera', this.defaultCamera);
|
|
1068
|
-
// set the projection matrix (which is not normally updated each frame)
|
|
1069
|
-
this.renderer.states.setValue('uPMatrix', this.renderer.states.uPMatrix.clone());
|
|
1070
|
-
this.renderer.states.uPMatrix.set(this.defaultCamera.projMatrix);
|
|
1071
|
-
this.renderer.states.setValue('uViewMatrix', this.renderer.states.uViewMatrix.clone());
|
|
1072
|
-
this.renderer.states.uViewMatrix.set(this.defaultCamera.cameraMatrix);
|
|
1073
|
-
|
|
1074
|
-
this.renderer.resetMatrix();
|
|
1075
|
-
this.renderer.states.uViewMatrix
|
|
1076
|
-
.set(this.renderer.states.curCamera.cameraMatrix);
|
|
1077
|
-
this.renderer.states.uModelMatrix.reset();
|
|
1078
|
-
this.renderer._applyStencilTestIfClipping();
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
/**
|
|
1082
|
-
* When making a p5.Framebuffer active so that it may be drawn to, this method
|
|
1083
|
-
* returns the underlying WebGL framebuffer that needs to be active to
|
|
1084
|
-
* support this. Antialiased framebuffers first write to a multisampled
|
|
1085
|
-
* renderbuffer, while other framebuffers can write directly to their main
|
|
1086
|
-
* framebuffers.
|
|
1087
|
-
*
|
|
1088
|
-
* @private
|
|
1089
|
-
*/
|
|
1090
|
-
_framebufferToBind() {
|
|
1091
|
-
if (this.antialias) {
|
|
1092
|
-
// If antialiasing, draw to an antialiased renderbuffer rather
|
|
1093
|
-
// than directly to the texture. In end() we will copy from the
|
|
1094
|
-
// renderbuffer to the texture.
|
|
1095
|
-
return this.aaFramebuffer;
|
|
1096
|
-
} else {
|
|
1097
|
-
return this.framebuffer;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
/**
|
|
1102
|
-
* Ensures that the framebuffer is ready to be drawn to
|
|
1103
|
-
*
|
|
1104
|
-
* @private
|
|
1105
|
-
*/
|
|
1106
|
-
_beforeBegin() {
|
|
1107
|
-
const gl = this.gl;
|
|
1108
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebufferToBind());
|
|
1109
|
-
this.renderer.viewport(
|
|
1110
|
-
this.width * this.density,
|
|
1111
|
-
this.height * this.density
|
|
1112
|
-
);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
/**
|
|
1116
|
-
* Ensures that the framebuffer is ready to be read by other framebuffers.
|
|
1117
|
-
*
|
|
1118
|
-
* @private
|
|
1119
|
-
*/
|
|
1120
|
-
_beforeEnd() {
|
|
1121
|
-
if (this.antialias) {
|
|
1122
|
-
const gl = this.gl;
|
|
1123
|
-
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this.aaFramebuffer);
|
|
1124
|
-
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.framebuffer);
|
|
1125
|
-
const partsToCopy = [
|
|
1126
|
-
[gl.COLOR_BUFFER_BIT, this.colorP5Texture.glMagFilter]
|
|
1127
|
-
];
|
|
1128
|
-
if (this.useDepth) {
|
|
1129
|
-
partsToCopy.push(
|
|
1130
|
-
[gl.DEPTH_BUFFER_BIT, this.depthP5Texture.glMagFilter]
|
|
1131
|
-
);
|
|
1132
|
-
}
|
|
1133
|
-
for (const [flag, filter] of partsToCopy) {
|
|
1134
|
-
gl.blitFramebuffer(
|
|
1135
|
-
0, 0,
|
|
1136
|
-
this.width * this.density, this.height * this.density,
|
|
1137
|
-
0, 0,
|
|
1138
|
-
this.width * this.density, this.height * this.density,
|
|
1139
|
-
flag,
|
|
1140
|
-
filter
|
|
1141
|
-
);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
/**
|
|
1147
|
-
* Stops drawing shapes to the framebuffer.
|
|
1148
|
-
*
|
|
1149
|
-
* <a href="#/p5.Framebuffer/begin">myBuffer.begin()</a> and `myBuffer.end()`
|
|
1150
|
-
* allow shapes to be drawn to the framebuffer.
|
|
1151
|
-
* <a href="#/p5.Framebuffer/begin">myBuffer.begin()</a> begins drawing to
|
|
1152
|
-
* the framebuffer and `myBuffer.end()` stops drawing to the framebuffer.
|
|
1153
|
-
* Changes won't be visible until the framebuffer is displayed as an image
|
|
1154
|
-
* or texture.
|
|
1155
|
-
*
|
|
1156
|
-
* @example
|
|
1157
|
-
* <div>
|
|
1158
|
-
* <code>
|
|
1159
|
-
* let myBuffer;
|
|
1160
|
-
*
|
|
1161
|
-
* function setup() {
|
|
1162
|
-
* createCanvas(100, 100, WEBGL);
|
|
1163
|
-
*
|
|
1164
|
-
* // Create a p5.Framebuffer object.
|
|
1165
|
-
* myBuffer = createFramebuffer();
|
|
1166
|
-
*
|
|
1167
|
-
* describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
|
|
1168
|
-
* }
|
|
1169
|
-
*
|
|
1170
|
-
* function draw() {
|
|
1171
|
-
* background(200);
|
|
1172
|
-
*
|
|
1173
|
-
* // Start drawing to the p5.Framebuffer object.
|
|
1174
|
-
* myBuffer.begin();
|
|
1175
|
-
*
|
|
1176
|
-
* background(50);
|
|
1177
|
-
* rotateY(frameCount * 0.01);
|
|
1178
|
-
* normalMaterial();
|
|
1179
|
-
* torus(30);
|
|
1180
|
-
*
|
|
1181
|
-
* // Stop drawing to the p5.Framebuffer object.
|
|
1182
|
-
* myBuffer.end();
|
|
1183
|
-
*
|
|
1184
|
-
* // Display the p5.Framebuffer object while
|
|
1185
|
-
* // the user presses the mouse.
|
|
1186
|
-
* if (mouseIsPressed === true) {
|
|
1187
|
-
* image(myBuffer, -50, -50);
|
|
1188
|
-
* }
|
|
1189
|
-
* }
|
|
1190
|
-
* </code>
|
|
1191
|
-
* </div>
|
|
1192
|
-
*/
|
|
1193
|
-
end() {
|
|
1194
|
-
const gl = this.gl;
|
|
1195
|
-
this.renderer.pop();
|
|
1196
|
-
const fbo = this.renderer.activeFramebuffers.pop();
|
|
1197
|
-
if (fbo !== this) {
|
|
1198
|
-
throw new Error("It looks like you've called end() while another Framebuffer is active.");
|
|
1199
|
-
}
|
|
1200
|
-
this._beforeEnd();
|
|
1201
|
-
if (this.prevFramebuffer) {
|
|
1202
|
-
this.prevFramebuffer._beforeBegin();
|
|
1203
|
-
} else {
|
|
1204
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
1205
|
-
this.renderer.viewport(
|
|
1206
|
-
this.renderer._origViewport.width,
|
|
1207
|
-
this.renderer._origViewport.height
|
|
1208
|
-
);
|
|
1209
|
-
}
|
|
1210
|
-
this.renderer._applyStencilTestIfClipping();
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
/**
|
|
1214
|
-
* Draws to the framebuffer by calling a function that contains drawing
|
|
1215
|
-
* instructions.
|
|
1216
|
-
*
|
|
1217
|
-
* The parameter, `callback`, is a function with the drawing instructions
|
|
1218
|
-
* for the framebuffer. For example, calling `myBuffer.draw(myFunction)`
|
|
1219
|
-
* will call a function named `myFunction()` to draw to the framebuffer.
|
|
1220
|
-
* Doing so has the same effect as the following:
|
|
1221
|
-
*
|
|
1222
|
-
* ```js
|
|
1223
|
-
* myBuffer.begin();
|
|
1224
|
-
* myFunction();
|
|
1225
|
-
* myBuffer.end();
|
|
1226
|
-
* ```
|
|
1227
|
-
*
|
|
1228
|
-
* @param {Function} callback function that draws to the framebuffer.
|
|
1229
|
-
*
|
|
1230
|
-
* @example
|
|
1231
|
-
* <div>
|
|
1232
|
-
* <code>
|
|
1233
|
-
* // Click the canvas to display the framebuffer.
|
|
1234
|
-
*
|
|
1235
|
-
* let myBuffer;
|
|
1236
|
-
*
|
|
1237
|
-
* function setup() {
|
|
1238
|
-
* createCanvas(100, 100, WEBGL);
|
|
1239
|
-
*
|
|
1240
|
-
* // Create a p5.Framebuffer object.
|
|
1241
|
-
* myBuffer = createFramebuffer();
|
|
1242
|
-
*
|
|
1243
|
-
* describe('An empty gray canvas. The canvas gets darker and a rotating, multicolor torus appears while the user presses and holds the mouse.');
|
|
1244
|
-
* }
|
|
1245
|
-
*
|
|
1246
|
-
* function draw() {
|
|
1247
|
-
* background(200);
|
|
1248
|
-
*
|
|
1249
|
-
* // Draw to the p5.Framebuffer object.
|
|
1250
|
-
* myBuffer.draw(bagel);
|
|
1251
|
-
*
|
|
1252
|
-
* // Display the p5.Framebuffer object while
|
|
1253
|
-
* // the user presses the mouse.
|
|
1254
|
-
* if (mouseIsPressed === true) {
|
|
1255
|
-
* image(myBuffer, -50, -50);
|
|
1256
|
-
* }
|
|
1257
|
-
* }
|
|
1258
|
-
*
|
|
1259
|
-
* // Draw a rotating, multicolor torus.
|
|
1260
|
-
* function bagel() {
|
|
1261
|
-
* background(50);
|
|
1262
|
-
* rotateY(frameCount * 0.01);
|
|
1263
|
-
* normalMaterial();
|
|
1264
|
-
* torus(30);
|
|
1265
|
-
* }
|
|
1266
|
-
* </code>
|
|
1267
|
-
* </div>
|
|
1268
|
-
*/
|
|
1269
|
-
draw(callback) {
|
|
1270
|
-
this.begin();
|
|
1271
|
-
callback();
|
|
1272
|
-
this.end();
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
/**
|
|
1276
|
-
* Loads the current value of each pixel in the framebuffer into its
|
|
1277
|
-
* <a href="#/p5.Framebuffer/pixels">pixels</a> array.
|
|
1278
|
-
*
|
|
1279
|
-
* `myBuffer.loadPixels()` must be called before reading from or writing to
|
|
1280
|
-
* <a href="#/p5.Framebuffer/pixels">myBuffer.pixels</a>.
|
|
1281
|
-
*
|
|
1282
|
-
* @method loadPixels
|
|
1283
|
-
*
|
|
1284
|
-
* @example
|
|
1285
|
-
* <div>
|
|
1286
|
-
* <code>
|
|
1287
|
-
* function setup() {
|
|
1288
|
-
* createCanvas(100, 100, WEBGL);
|
|
1289
|
-
*
|
|
1290
|
-
* background(200);
|
|
1291
|
-
*
|
|
1292
|
-
* // Create a p5.Framebuffer object.
|
|
1293
|
-
* let myBuffer = createFramebuffer();
|
|
1294
|
-
*
|
|
1295
|
-
* // Load the pixels array.
|
|
1296
|
-
* myBuffer.loadPixels();
|
|
1297
|
-
*
|
|
1298
|
-
* // Get the number of pixels in the
|
|
1299
|
-
* // top half of the framebuffer.
|
|
1300
|
-
* let numPixels = myBuffer.pixels.length / 2;
|
|
1301
|
-
*
|
|
1302
|
-
* // Set the framebuffer's top half to pink.
|
|
1303
|
-
* for (let i = 0; i < numPixels; i += 4) {
|
|
1304
|
-
* myBuffer.pixels[i] = 255;
|
|
1305
|
-
* myBuffer.pixels[i + 1] = 102;
|
|
1306
|
-
* myBuffer.pixels[i + 2] = 204;
|
|
1307
|
-
* myBuffer.pixels[i + 3] = 255;
|
|
1308
|
-
* }
|
|
1309
|
-
*
|
|
1310
|
-
* // Update the pixels array.
|
|
1311
|
-
* myBuffer.updatePixels();
|
|
1312
|
-
*
|
|
1313
|
-
* // Draw the p5.Framebuffer object to the canvas.
|
|
1314
|
-
* image(myBuffer, -50, -50);
|
|
1315
|
-
*
|
|
1316
|
-
* describe('A pink rectangle above a gray rectangle.');
|
|
1317
|
-
* }
|
|
1318
|
-
* </code>
|
|
1319
|
-
* </div>
|
|
1320
|
-
*/
|
|
1321
|
-
loadPixels() {
|
|
1322
|
-
const gl = this.gl;
|
|
1323
|
-
const prevFramebuffer = this.renderer.activeFramebuffer();
|
|
1324
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
1325
|
-
const colorFormat = this._glColorFormat();
|
|
1326
|
-
this.pixels = readPixelsWebGL(
|
|
1327
|
-
this.pixels,
|
|
1328
|
-
gl,
|
|
1329
|
-
this.framebuffer,
|
|
1330
|
-
0,
|
|
1331
|
-
0,
|
|
1332
|
-
this.width * this.density,
|
|
1333
|
-
this.height * this.density,
|
|
1334
|
-
colorFormat.format,
|
|
1335
|
-
colorFormat.type
|
|
1336
|
-
);
|
|
1337
|
-
if (prevFramebuffer) {
|
|
1338
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer._framebufferToBind());
|
|
1339
|
-
} else {
|
|
1340
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
/**
|
|
1345
|
-
* Gets a pixel or a region of pixels from the framebuffer.
|
|
1346
|
-
*
|
|
1347
|
-
* `myBuffer.get()` is easy to use but it's not as fast as
|
|
1348
|
-
* <a href="#/p5.Framebuffer/pixels">myBuffer.pixels</a>. Use
|
|
1349
|
-
* <a href="#/p5.Framebuffer/pixels">myBuffer.pixels</a> to read many pixel
|
|
1350
|
-
* values.
|
|
1351
|
-
*
|
|
1352
|
-
* The version of `myBuffer.get()` with no parameters returns the entire
|
|
1353
|
-
* framebuffer as a a <a href="#/p5.Image">p5.Image</a> object.
|
|
1354
|
-
*
|
|
1355
|
-
* The version of `myBuffer.get()` with two parameters interprets them as
|
|
1356
|
-
* coordinates. It returns an array with the `[R, G, B, A]` values of the
|
|
1357
|
-
* pixel at the given point.
|
|
1358
|
-
*
|
|
1359
|
-
* The version of `myBuffer.get()` with four parameters interprets them as
|
|
1360
|
-
* coordinates and dimensions. It returns a subsection of the framebuffer as
|
|
1361
|
-
* a <a href="#/p5.Image">p5.Image</a> object. The first two parameters are
|
|
1362
|
-
* the coordinates for the upper-left corner of the subsection. The last two
|
|
1363
|
-
* parameters are the width and height of the subsection.
|
|
1364
|
-
*
|
|
1365
|
-
* @param {Number} x x-coordinate of the pixel. Defaults to 0.
|
|
1366
|
-
* @param {Number} y y-coordinate of the pixel. Defaults to 0.
|
|
1367
|
-
* @param {Number} w width of the subsection to be returned.
|
|
1368
|
-
* @param {Number} h height of the subsection to be returned.
|
|
1369
|
-
* @return {p5.Image} subsection as a <a href="#/p5.Image">p5.Image</a> object.
|
|
1370
|
-
*/
|
|
1371
|
-
/**
|
|
1372
|
-
* @return {p5.Image} entire framebuffer as a <a href="#/p5.Image">p5.Image</a> object.
|
|
1373
|
-
*/
|
|
1374
|
-
/**
|
|
1375
|
-
* @param {Number} x
|
|
1376
|
-
* @param {Number} y
|
|
1377
|
-
* @return {Number[]} color of the pixel at `(x, y)` as an array of color values `[R, G, B, A]`.
|
|
1378
|
-
*/
|
|
1379
|
-
get(x, y, w, h) {
|
|
1380
|
-
// p5._validateParameters('p5.Framebuffer.get', arguments);
|
|
1381
|
-
const colorFormat = this._glColorFormat();
|
|
1382
|
-
if (x === undefined && y === undefined) {
|
|
1383
|
-
x = 0;
|
|
1384
|
-
y = 0;
|
|
1385
|
-
w = this.width;
|
|
1386
|
-
h = this.height;
|
|
1387
|
-
} else if (w === undefined && h === undefined) {
|
|
1388
|
-
if (x < 0 || y < 0 || x >= this.width || y >= this.height) {
|
|
1389
|
-
console.warn(
|
|
1390
|
-
'The x and y values passed to p5.Framebuffer.get are outside of its range and will be clamped.'
|
|
1391
|
-
);
|
|
1392
|
-
x = constrain(x, 0, this.width - 1);
|
|
1393
|
-
y = constrain(y, 0, this.height - 1);
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
return readPixelWebGL(
|
|
1397
|
-
this.gl,
|
|
1398
|
-
this.framebuffer,
|
|
1399
|
-
x * this.density,
|
|
1400
|
-
y * this.density,
|
|
1401
|
-
colorFormat.format,
|
|
1402
|
-
colorFormat.type
|
|
1403
|
-
);
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
x = constrain(x, 0, this.width - 1);
|
|
1407
|
-
y = constrain(y, 0, this.height - 1);
|
|
1408
|
-
w = constrain(w, 1, this.width - x);
|
|
1409
|
-
h = constrain(h, 1, this.height - y);
|
|
1410
|
-
|
|
1411
|
-
const rawData = readPixelsWebGL(
|
|
1412
|
-
undefined,
|
|
1413
|
-
this.gl,
|
|
1414
|
-
this.framebuffer,
|
|
1415
|
-
x * this.density,
|
|
1416
|
-
y * this.density,
|
|
1417
|
-
w * this.density,
|
|
1418
|
-
h * this.density,
|
|
1419
|
-
colorFormat.format,
|
|
1420
|
-
colorFormat.type
|
|
1421
|
-
);
|
|
1422
|
-
// Framebuffer data might be either a Uint8Array or Float32Array
|
|
1423
|
-
// depending on its format, and it may or may not have an alpha channel.
|
|
1424
|
-
// To turn it into an image, we have to normalize the data into a
|
|
1425
|
-
// Uint8ClampedArray with alpha.
|
|
1426
|
-
const fullData = new Uint8ClampedArray(
|
|
1427
|
-
w * h * this.density * this.density * 4
|
|
1428
|
-
);
|
|
1429
|
-
|
|
1430
|
-
// Default channels that aren't in the framebuffer (e.g. alpha, if the
|
|
1431
|
-
// framebuffer is in RGB mode instead of RGBA) to 255
|
|
1432
|
-
fullData.fill(255);
|
|
1433
|
-
|
|
1434
|
-
const channels = colorFormat.type === this.gl.RGB ? 3 : 4;
|
|
1435
|
-
for (let y = 0; y < h * this.density; y++) {
|
|
1436
|
-
for (let x = 0; x < w * this.density; x++) {
|
|
1437
|
-
for (let channel = 0; channel < 4; channel++) {
|
|
1438
|
-
const idx = (y * w * this.density + x) * 4 + channel;
|
|
1439
|
-
if (channel < channels) {
|
|
1440
|
-
// Find the index of this pixel in `rawData`, which might have a
|
|
1441
|
-
// different number of channels
|
|
1442
|
-
const rawDataIdx = channels === 4
|
|
1443
|
-
? idx
|
|
1444
|
-
: (y * w * this.density + x) * channels + channel;
|
|
1445
|
-
fullData[idx] = rawData[rawDataIdx];
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
// Create an image from the data
|
|
1452
|
-
const region = new Image(w * this.density, h * this.density);
|
|
1453
|
-
region.imageData = region.canvas.getContext('2d').createImageData(
|
|
1454
|
-
region.width,
|
|
1455
|
-
region.height
|
|
1456
|
-
);
|
|
1457
|
-
region.imageData.data.set(fullData);
|
|
1458
|
-
region.pixels = region.imageData.data;
|
|
1459
|
-
region.updatePixels();
|
|
1460
|
-
if (this.density !== 1) {
|
|
1461
|
-
// TODO: support get() at a pixel density > 1
|
|
1462
|
-
region.resize(w, h);
|
|
1463
|
-
}
|
|
1464
|
-
return region;
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
/**
|
|
1468
|
-
* Updates the framebuffer with the RGBA values in the
|
|
1469
|
-
* <a href="#/p5.Framebuffer/pixels">pixels</a> array.
|
|
1470
|
-
*
|
|
1471
|
-
* `myBuffer.updatePixels()` only needs to be called after changing values
|
|
1472
|
-
* in the <a href="#/p5.Framebuffer/pixels">myBuffer.pixels</a> array. Such
|
|
1473
|
-
* changes can be made directly after calling
|
|
1474
|
-
* <a href="#/p5.Framebuffer/loadPixels">myBuffer.loadPixels()</a>.
|
|
1475
|
-
*
|
|
1476
|
-
* @method updatePixels
|
|
1477
|
-
*
|
|
1478
|
-
* @example
|
|
1479
|
-
* <div>
|
|
1480
|
-
* <code>
|
|
1481
|
-
* function setup() {
|
|
1482
|
-
* createCanvas(100, 100, WEBGL);
|
|
1483
|
-
*
|
|
1484
|
-
* background(200);
|
|
1485
|
-
*
|
|
1486
|
-
* // Create a p5.Framebuffer object.
|
|
1487
|
-
* let myBuffer = createFramebuffer();
|
|
1488
|
-
*
|
|
1489
|
-
* // Load the pixels array.
|
|
1490
|
-
* myBuffer.loadPixels();
|
|
1491
|
-
*
|
|
1492
|
-
* // Get the number of pixels in the
|
|
1493
|
-
* // top half of the framebuffer.
|
|
1494
|
-
* let numPixels = myBuffer.pixels.length / 2;
|
|
1495
|
-
*
|
|
1496
|
-
* // Set the framebuffer's top half to pink.
|
|
1497
|
-
* for (let i = 0; i < numPixels; i += 4) {
|
|
1498
|
-
* myBuffer.pixels[i] = 255;
|
|
1499
|
-
* myBuffer.pixels[i + 1] = 102;
|
|
1500
|
-
* myBuffer.pixels[i + 2] = 204;
|
|
1501
|
-
* myBuffer.pixels[i + 3] = 255;
|
|
1502
|
-
* }
|
|
1503
|
-
*
|
|
1504
|
-
* // Update the pixels array.
|
|
1505
|
-
* myBuffer.updatePixels();
|
|
1506
|
-
*
|
|
1507
|
-
* // Draw the p5.Framebuffer object to the canvas.
|
|
1508
|
-
* image(myBuffer, -50, -50);
|
|
1509
|
-
*
|
|
1510
|
-
* describe('A pink rectangle above a gray rectangle.');
|
|
1511
|
-
* }
|
|
1512
|
-
* </code>
|
|
1513
|
-
* </div>
|
|
1514
|
-
*/
|
|
1515
|
-
updatePixels() {
|
|
1516
|
-
const gl = this.gl;
|
|
1517
|
-
this.colorP5Texture.bindTexture();
|
|
1518
|
-
const colorFormat = this._glColorFormat();
|
|
1519
|
-
|
|
1520
|
-
const channels = colorFormat.format === gl.RGBA ? 4 : 3;
|
|
1521
|
-
const len =
|
|
1522
|
-
this.width * this.height * this.density * this.density * channels;
|
|
1523
|
-
const TypedArrayClass = colorFormat.type === gl.UNSIGNED_BYTE
|
|
1524
|
-
? Uint8Array
|
|
1525
|
-
: Float32Array;
|
|
1526
|
-
if (
|
|
1527
|
-
!(this.pixels instanceof TypedArrayClass) || this.pixels.length !== len
|
|
1528
|
-
) {
|
|
1529
|
-
throw new Error(
|
|
1530
|
-
'The pixels array has not been set correctly. Please call loadPixels() before updatePixels().'
|
|
1531
|
-
);
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
gl.texImage2D(
|
|
1535
|
-
gl.TEXTURE_2D,
|
|
1536
|
-
0,
|
|
1537
|
-
colorFormat.internalFormat,
|
|
1538
|
-
this.width * this.density,
|
|
1539
|
-
this.height * this.density,
|
|
1540
|
-
0,
|
|
1541
|
-
colorFormat.format,
|
|
1542
|
-
colorFormat.type,
|
|
1543
|
-
this.pixels
|
|
1544
|
-
);
|
|
1545
|
-
this.colorP5Texture.unbindTexture();
|
|
1546
|
-
|
|
1547
|
-
const prevFramebuffer = this.renderer.activeFramebuffer();
|
|
1548
|
-
if (this.antialias) {
|
|
1549
|
-
// We need to make sure the antialiased framebuffer also has the updated
|
|
1550
|
-
// pixels so that if more is drawn to it, it goes on top of the updated
|
|
1551
|
-
// pixels instead of replacing them.
|
|
1552
|
-
// We can't blit the framebuffer to the multisampled antialias
|
|
1553
|
-
// framebuffer to leave both in the same state, so instead we have
|
|
1554
|
-
// to use image() to put the framebuffer texture onto the antialiased
|
|
1555
|
-
// framebuffer.
|
|
1556
|
-
this.begin();
|
|
1557
|
-
this.renderer.push();
|
|
1558
|
-
// this.renderer.imageMode(constants.CENTER);
|
|
1559
|
-
this.renderer.states.setValue('imageMode', constants.CORNER);
|
|
1560
|
-
this.renderer.setCamera(this.filterCamera);
|
|
1561
|
-
this.renderer.resetMatrix();
|
|
1562
|
-
this.renderer.states.setValue('strokeColor', null);
|
|
1563
|
-
this.renderer.clear();
|
|
1564
|
-
this.renderer._drawingFilter = true;
|
|
1565
|
-
this.renderer.image(
|
|
1566
|
-
this,
|
|
1567
|
-
0, 0,
|
|
1568
|
-
this.width, this.height,
|
|
1569
|
-
-this.renderer.width / 2, -this.renderer.height / 2,
|
|
1570
|
-
this.renderer.width, this.renderer.height
|
|
1571
|
-
);
|
|
1572
|
-
this.renderer._drawingFilter = false;
|
|
1573
|
-
this.renderer.pop();
|
|
1574
|
-
if (this.useDepth) {
|
|
1575
|
-
gl.clearDepth(1);
|
|
1576
|
-
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
1577
|
-
}
|
|
1578
|
-
this.end();
|
|
1579
|
-
} else {
|
|
1580
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
|
|
1581
|
-
if (this.useDepth) {
|
|
1582
|
-
gl.clearDepth(1);
|
|
1583
|
-
gl.clear(gl.DEPTH_BUFFER_BIT);
|
|
1584
|
-
}
|
|
1585
|
-
if (prevFramebuffer) {
|
|
1586
|
-
gl.bindFramebuffer(
|
|
1587
|
-
gl.FRAMEBUFFER,
|
|
1588
|
-
prevFramebuffer._framebufferToBind()
|
|
1589
|
-
);
|
|
1590
|
-
} else {
|
|
1591
|
-
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
function framebuffer(p5, fn){
|
|
1598
|
-
/**
|
|
1599
|
-
* A <a href="#/p5.Camera">p5.Camera</a> attached to a
|
|
1600
|
-
* <a href="#/p5.Framebuffer">p5.Framebuffer</a>.
|
|
1601
|
-
*
|
|
1602
|
-
* @class p5.FramebufferCamera
|
|
1603
|
-
* @param {p5.Framebuffer} framebuffer The framebuffer this camera is
|
|
1604
|
-
* attached to
|
|
1605
|
-
* @private
|
|
1606
|
-
*/
|
|
1607
|
-
p5.FramebufferCamera = FramebufferCamera;
|
|
1608
|
-
|
|
1609
|
-
/**
|
|
1610
|
-
* A <a href="#/p5.Texture">p5.Texture</a> corresponding to a property of a
|
|
1611
|
-
* <a href="#/p5.Framebuffer">p5.Framebuffer</a>.
|
|
1612
|
-
*
|
|
1613
|
-
* @class p5.FramebufferTexture
|
|
1614
|
-
* @param {p5.Framebuffer} framebuffer The framebuffer represented by this
|
|
1615
|
-
* texture
|
|
1616
|
-
* @param {String} property The property of the framebuffer represented by
|
|
1617
|
-
* this texture, either `color` or `depth`
|
|
1618
|
-
* @private
|
|
1619
|
-
*/
|
|
1620
|
-
p5.FramebufferTexture = FramebufferTexture;
|
|
1621
|
-
|
|
1622
|
-
/**
|
|
1623
|
-
* A class to describe a high-performance drawing surface for textures.
|
|
1624
|
-
*
|
|
1625
|
-
* Each `p5.Framebuffer` object provides a dedicated drawing surface called
|
|
1626
|
-
* a *framebuffer*. They're similar to
|
|
1627
|
-
* <a href="#/p5.Graphics">p5.Graphics</a> objects but can run much faster.
|
|
1628
|
-
* Performance is improved because the framebuffer shares the same WebGL
|
|
1629
|
-
* context as the canvas used to create it.
|
|
1630
|
-
*
|
|
1631
|
-
* `p5.Framebuffer` objects have all the drawing features of the main
|
|
1632
|
-
* canvas. Drawing instructions meant for the framebuffer must be placed
|
|
1633
|
-
* between calls to
|
|
1634
|
-
* <a href="#/p5.Framebuffer/begin">myBuffer.begin()</a> and
|
|
1635
|
-
* <a href="#/p5.Framebuffer/end">myBuffer.end()</a>. The resulting image
|
|
1636
|
-
* can be applied as a texture by passing the `p5.Framebuffer` object to the
|
|
1637
|
-
* <a href="#/p5/texture">texture()</a> function, as in `texture(myBuffer)`.
|
|
1638
|
-
* It can also be displayed on the main canvas by passing it to the
|
|
1639
|
-
* <a href="#/p5/image">image()</a> function, as in `image(myBuffer, 0, 0)`.
|
|
1640
|
-
*
|
|
1641
|
-
* Note: <a href="#/p5/createFramebuffer">createFramebuffer()</a> is the
|
|
1642
|
-
* recommended way to create an instance of this class.
|
|
1643
|
-
*
|
|
1644
|
-
* @class p5.Framebuffer
|
|
1645
|
-
* @param {p5.Graphics|p5} target sketch instance or
|
|
1646
|
-
* <a href="#/p5.Graphics">p5.Graphics</a>
|
|
1647
|
-
* object.
|
|
1648
|
-
* @param {Object} [settings] configuration options.
|
|
1649
|
-
*/
|
|
1650
|
-
p5.Framebuffer = Framebuffer;
|
|
1651
|
-
|
|
1652
|
-
/**
|
|
1653
|
-
* An object that stores the framebuffer's color data.
|
|
1654
|
-
*
|
|
1655
|
-
* Each framebuffer uses a
|
|
1656
|
-
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture" target="_blank">WebGLTexture</a>
|
|
1657
|
-
* object internally to store its color data. The `myBuffer.color` property
|
|
1658
|
-
* makes it possible to pass this data directly to other functions. For
|
|
1659
|
-
* example, calling `texture(myBuffer.color)` or
|
|
1660
|
-
* `myShader.setUniform('colorTexture', myBuffer.color)` may be helpful for
|
|
1661
|
-
* advanced use cases.
|
|
1662
|
-
*
|
|
1663
|
-
* Note: By default, a framebuffer's y-coordinates are flipped compared to
|
|
1664
|
-
* images and videos. It's easy to flip a framebuffer's y-coordinates as
|
|
1665
|
-
* needed when applying it as a texture. For example, calling
|
|
1666
|
-
* `plane(myBuffer.width, -myBuffer.height)` will flip the framebuffer.
|
|
1667
|
-
*
|
|
1668
|
-
* @property {p5.FramebufferTexture} color
|
|
1669
|
-
* @for p5.Framebuffer
|
|
1670
|
-
*
|
|
1671
|
-
* @example
|
|
1672
|
-
* <div>
|
|
1673
|
-
* <code>
|
|
1674
|
-
* function setup() {
|
|
1675
|
-
* createCanvas(100, 100, WEBGL);
|
|
1676
|
-
*
|
|
1677
|
-
* background(200);
|
|
1678
|
-
*
|
|
1679
|
-
* // Create a p5.Framebuffer object.
|
|
1680
|
-
* let myBuffer = createFramebuffer();
|
|
1681
|
-
*
|
|
1682
|
-
* // Start drawing to the p5.Framebuffer object.
|
|
1683
|
-
* myBuffer.begin();
|
|
1684
|
-
*
|
|
1685
|
-
* triangle(-25, 25, 0, -25, 25, 25);
|
|
1686
|
-
*
|
|
1687
|
-
* // Stop drawing to the p5.Framebuffer object.
|
|
1688
|
-
* myBuffer.end();
|
|
1689
|
-
*
|
|
1690
|
-
* // Use the p5.Framebuffer object's WebGLTexture.
|
|
1691
|
-
* texture(myBuffer.color);
|
|
1692
|
-
*
|
|
1693
|
-
* // Style the plane.
|
|
1694
|
-
* noStroke();
|
|
1695
|
-
*
|
|
1696
|
-
* // Draw the plane.
|
|
1697
|
-
* plane(myBuffer.width, myBuffer.height);
|
|
1698
|
-
*
|
|
1699
|
-
* describe('A white triangle on a gray background.');
|
|
1700
|
-
* }
|
|
1701
|
-
* </code>
|
|
1702
|
-
* </div>
|
|
1703
|
-
*/
|
|
1704
|
-
|
|
1705
|
-
/**
|
|
1706
|
-
* An object that stores the framebuffer's depth data.
|
|
1707
|
-
*
|
|
1708
|
-
* Each framebuffer uses a
|
|
1709
|
-
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture" target="_blank">WebGLTexture</a>
|
|
1710
|
-
* object internally to store its depth data. The `myBuffer.depth` property
|
|
1711
|
-
* makes it possible to pass this data directly to other functions. For
|
|
1712
|
-
* example, calling `texture(myBuffer.depth)` or
|
|
1713
|
-
* `myShader.setUniform('depthTexture', myBuffer.depth)` may be helpful for
|
|
1714
|
-
* advanced use cases.
|
|
1715
|
-
*
|
|
1716
|
-
* Note: By default, a framebuffer's y-coordinates are flipped compared to
|
|
1717
|
-
* images and videos. It's easy to flip a framebuffer's y-coordinates as
|
|
1718
|
-
* needed when applying it as a texture. For example, calling
|
|
1719
|
-
* `plane(myBuffer.width, -myBuffer.height)` will flip the framebuffer.
|
|
1720
|
-
*
|
|
1721
|
-
* @property {p5.FramebufferTexture} depth
|
|
1722
|
-
* @for p5.Framebuffer
|
|
1723
|
-
*
|
|
1724
|
-
* @example
|
|
1725
|
-
* <div>
|
|
1726
|
-
* <code>
|
|
1727
|
-
* // Note: A "uniform" is a global variable within a shader program.
|
|
1728
|
-
*
|
|
1729
|
-
* // Create a string with the vertex shader program.
|
|
1730
|
-
* // The vertex shader is called for each vertex.
|
|
1731
|
-
* let vertSrc = `
|
|
1732
|
-
* precision highp float;
|
|
1733
|
-
* attribute vec3 aPosition;
|
|
1734
|
-
* attribute vec2 aTexCoord;
|
|
1735
|
-
* uniform mat4 uModelViewMatrix;
|
|
1736
|
-
* uniform mat4 uProjectionMatrix;
|
|
1737
|
-
* varying vec2 vTexCoord;
|
|
1738
|
-
*
|
|
1739
|
-
* void main() {
|
|
1740
|
-
* vec4 viewModelPosition = uModelViewMatrix * vec4(aPosition, 1.0);
|
|
1741
|
-
* gl_Position = uProjectionMatrix * viewModelPosition;
|
|
1742
|
-
* vTexCoord = aTexCoord;
|
|
1743
|
-
* }
|
|
1744
|
-
* `;
|
|
1745
|
-
*
|
|
1746
|
-
* // Create a string with the fragment shader program.
|
|
1747
|
-
* // The fragment shader is called for each pixel.
|
|
1748
|
-
* let fragSrc = `
|
|
1749
|
-
* precision highp float;
|
|
1750
|
-
* varying vec2 vTexCoord;
|
|
1751
|
-
* uniform sampler2D depth;
|
|
1752
|
-
*
|
|
1753
|
-
* void main() {
|
|
1754
|
-
* // Get the pixel's depth value.
|
|
1755
|
-
* float depthVal = texture2D(depth, vTexCoord).r;
|
|
1756
|
-
*
|
|
1757
|
-
* // Set the pixel's color based on its depth.
|
|
1758
|
-
* gl_FragColor = mix(
|
|
1759
|
-
* vec4(0., 0., 0., 1.),
|
|
1760
|
-
* vec4(1., 0., 1., 1.),
|
|
1761
|
-
* depthVal);
|
|
1762
|
-
* }
|
|
1763
|
-
* `;
|
|
1764
|
-
*
|
|
1765
|
-
* let myBuffer;
|
|
1766
|
-
* let myShader;
|
|
1767
|
-
*
|
|
1768
|
-
* function setup() {
|
|
1769
|
-
* createCanvas(100, 100, WEBGL);
|
|
1770
|
-
*
|
|
1771
|
-
* // Create a p5.Framebuffer object.
|
|
1772
|
-
* myBuffer = createFramebuffer();
|
|
1773
|
-
*
|
|
1774
|
-
* // Create a p5.Shader object.
|
|
1775
|
-
* myShader = createShader(vertSrc, fragSrc);
|
|
1776
|
-
*
|
|
1777
|
-
* // Compile and apply the shader.
|
|
1778
|
-
* shader(myShader);
|
|
1779
|
-
*
|
|
1780
|
-
* describe('The shadow of a box rotates slowly against a magenta background.');
|
|
1781
|
-
* }
|
|
1782
|
-
*
|
|
1783
|
-
* function draw() {
|
|
1784
|
-
* // Draw to the p5.Framebuffer object.
|
|
1785
|
-
* myBuffer.begin();
|
|
1786
|
-
* background(255);
|
|
1787
|
-
* rotateX(frameCount * 0.01);
|
|
1788
|
-
* box(20, 20, 80);
|
|
1789
|
-
* myBuffer.end();
|
|
1790
|
-
*
|
|
1791
|
-
* // Set the shader's depth uniform using
|
|
1792
|
-
* // the framebuffer's depth texture.
|
|
1793
|
-
* myShader.setUniform('depth', myBuffer.depth);
|
|
1794
|
-
*
|
|
1795
|
-
* // Style the plane.
|
|
1796
|
-
* noStroke();
|
|
1797
|
-
*
|
|
1798
|
-
* // Draw the plane.
|
|
1799
|
-
* plane(myBuffer.width, myBuffer.height);
|
|
1800
|
-
* }
|
|
1801
|
-
* </code>
|
|
1802
|
-
* </div>
|
|
1803
|
-
*/
|
|
1804
|
-
|
|
1805
|
-
/**
|
|
1806
|
-
* An array containing the color of each pixel in the framebuffer.
|
|
1807
|
-
*
|
|
1808
|
-
* <a href="#/p5.Framebuffer/loadPixels">myBuffer.loadPixels()</a> must be
|
|
1809
|
-
* called before accessing the `myBuffer.pixels` array.
|
|
1810
|
-
* <a href="#/p5.Framebuffer/updatePixels">myBuffer.updatePixels()</a>
|
|
1811
|
-
* must be called after any changes are made.
|
|
1812
|
-
*
|
|
1813
|
-
* Note: Updating pixels via this property is slower than drawing to the
|
|
1814
|
-
* framebuffer directly. Consider using a
|
|
1815
|
-
* <a href="#/p5.Shader">p5.Shader</a> object instead of looping over
|
|
1816
|
-
* `myBuffer.pixels`.
|
|
1817
|
-
*
|
|
1818
|
-
* @property {Number[]} pixels
|
|
1819
|
-
* @for p5.Framebuffer
|
|
1820
|
-
*
|
|
1821
|
-
* @example
|
|
1822
|
-
* <div>
|
|
1823
|
-
* <code>
|
|
1824
|
-
* function setup() {
|
|
1825
|
-
* createCanvas(100, 100, WEBGL);
|
|
1826
|
-
*
|
|
1827
|
-
* background(200);
|
|
1828
|
-
*
|
|
1829
|
-
* // Create a p5.Framebuffer object.
|
|
1830
|
-
* let myBuffer = createFramebuffer();
|
|
1831
|
-
*
|
|
1832
|
-
* // Load the pixels array.
|
|
1833
|
-
* myBuffer.loadPixels();
|
|
1834
|
-
*
|
|
1835
|
-
* // Get the number of pixels in the
|
|
1836
|
-
* // top half of the framebuffer.
|
|
1837
|
-
* let numPixels = myBuffer.pixels.length / 2;
|
|
1838
|
-
*
|
|
1839
|
-
* // Set the framebuffer's top half to pink.
|
|
1840
|
-
* for (let i = 0; i < numPixels; i += 4) {
|
|
1841
|
-
* myBuffer.pixels[i] = 255;
|
|
1842
|
-
* myBuffer.pixels[i + 1] = 102;
|
|
1843
|
-
* myBuffer.pixels[i + 2] = 204;
|
|
1844
|
-
* myBuffer.pixels[i + 3] = 255;
|
|
1845
|
-
* }
|
|
1846
|
-
*
|
|
1847
|
-
* // Update the pixels array.
|
|
1848
|
-
* myBuffer.updatePixels();
|
|
1849
|
-
*
|
|
1850
|
-
* // Draw the p5.Framebuffer object to the canvas.
|
|
1851
|
-
* image(myBuffer, -50, -50);
|
|
1852
|
-
*
|
|
1853
|
-
* describe('A pink rectangle above a gray rectangle.');
|
|
1854
|
-
* }
|
|
1855
|
-
* </code>
|
|
1856
|
-
* </div>
|
|
1857
|
-
*/
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
export default framebuffer;
|
|
1861
|
-
export { FramebufferTexture, FramebufferCamera, Framebuffer };
|
|
1862
|
-
|
|
1863
|
-
if(typeof p5 !== 'undefined'){
|
|
1864
|
-
framebuffer(p5, p5.prototype);
|
|
1865
|
-
}
|