diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index fa67acc34d..6894b35329 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -23,7 +23,6 @@ const defineStrokeJoinEnum = function (key, val) { STROKE_JOIN_ENUM[constants[key]] = val; }; - // Define constants in line shaders for each type of cap/join, and also record // the values in JS objects defineStrokeCapEnum('ROUND', 0); @@ -81,9 +80,18 @@ const defaultShaders = { lineDefs + readFileSync(join(__dirname, '/shaders/line.frag'), 'utf-8'), pointVert: readFileSync(join(__dirname, '/shaders/point.vert'), 'utf-8'), pointFrag: readFileSync(join(__dirname, '/shaders/point.frag'), 'utf-8'), - imageLightVert: readFileSync(join(__dirname, '/shaders/imageLight.vert'), 'utf-8'), - imageLightDiffusedFrag: readFileSync(join(__dirname, '/shaders/imageLightDiffused.frag'), 'utf-8'), - imageLightSpecularFrag: readFileSync(join(__dirname, '/shaders/imageLightSpecular.frag'), 'utf-8') + imageLightVert: readFileSync( + join(__dirname, '/shaders/imageLight.vert'), + 'utf-8' + ), + imageLightDiffusedFrag: readFileSync( + join(__dirname, '/shaders/imageLightDiffused.frag'), + 'utf-8' + ), + imageLightSpecularFrag: readFileSync( + join(__dirname, '/shaders/imageLightSpecular.frag'), + 'utf-8' + ) }; let sphereMapping = defaultShaders.sphereMappingFrag; for (const key in defaultShaders) { @@ -91,24 +99,43 @@ for (const key in defaultShaders) { } const filterShaderFrags = { - [constants.GRAY]: - readFileSync(join(__dirname, '/shaders/filters/gray.frag'), 'utf-8'), - [constants.ERODE]: - readFileSync(join(__dirname, '/shaders/filters/erode.frag'), 'utf-8'), - [constants.DILATE]: - readFileSync(join(__dirname, '/shaders/filters/dilate.frag'), 'utf-8'), - [constants.BLUR]: - readFileSync(join(__dirname, '/shaders/filters/blur.frag'), 'utf-8'), - [constants.POSTERIZE]: - readFileSync(join(__dirname, '/shaders/filters/posterize.frag'), 'utf-8'), - [constants.OPAQUE]: - readFileSync(join(__dirname, '/shaders/filters/opaque.frag'), 'utf-8'), - [constants.INVERT]: - readFileSync(join(__dirname, '/shaders/filters/invert.frag'), 'utf-8'), - [constants.THRESHOLD]: - readFileSync(join(__dirname, '/shaders/filters/threshold.frag'), 'utf-8') + [constants.GRAY]: readFileSync( + join(__dirname, '/shaders/filters/gray.frag'), + 'utf-8' + ), + [constants.ERODE]: readFileSync( + join(__dirname, '/shaders/filters/erode.frag'), + 'utf-8' + ), + [constants.DILATE]: readFileSync( + join(__dirname, '/shaders/filters/dilate.frag'), + 'utf-8' + ), + [constants.BLUR]: readFileSync( + join(__dirname, '/shaders/filters/blur.frag'), + 'utf-8' + ), + [constants.POSTERIZE]: readFileSync( + join(__dirname, '/shaders/filters/posterize.frag'), + 'utf-8' + ), + [constants.OPAQUE]: readFileSync( + join(__dirname, '/shaders/filters/opaque.frag'), + 'utf-8' + ), + [constants.INVERT]: readFileSync( + join(__dirname, '/shaders/filters/invert.frag'), + 'utf-8' + ), + [constants.THRESHOLD]: readFileSync( + join(__dirname, '/shaders/filters/threshold.frag'), + 'utf-8' + ) }; -const filterShaderVert = readFileSync(join(__dirname, '/shaders/filters/default.vert'), 'utf-8'); +const filterShaderVert = readFileSync( + join(__dirname, '/shaders/filters/default.vert'), + 'utf-8' +); /** * @module Rendering @@ -268,7 +295,7 @@ p5.prototype.setAttributes = function (key, value) { if (typeof this._glAttributes === 'undefined') { console.log( 'You are trying to use setAttributes on a p5.Graphics object ' + - 'that does not use a WEBGL renderer.' + 'that does not use a WEBGL renderer.' ); return; } @@ -300,7 +327,7 @@ p5.prototype.setAttributes = function (key, value) { if (this._renderer.retainedMode.geometry.hasOwnProperty(x)) { p5._friendlyError( 'Sorry, Could not set the attributes, you need to call setAttributes() ' + - 'before calling the other drawing methods in setup()' + 'before calling the other drawing methods in setup()' ); return; } @@ -358,7 +385,7 @@ export function readPixelsWebGL( gl.readPixels( x, - flipY ? (flipY - y - height) : y, + flipY ? flipY - y - height : y, width, height, format, @@ -397,15 +424,7 @@ export function readPixelsWebGL( * @param {Number|undefined} flipY If provided, the total height with which to flip the y axis about * @returns {Number[]} pixels The channel data for the pixel at that location */ -export function readPixelWebGL( - gl, - framebuffer, - x, - y, - format, - type, - flipY -) { +export function readPixelWebGL(gl, framebuffer, x, y, format, type, flipY) { // Record the currently bound framebuffer so we can go back to it after, and // bind the framebuffer we want to read from const prevFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); @@ -415,11 +434,7 @@ export function readPixelWebGL( const TypedArrayClass = type === gl.UNSIGNED_BYTE ? Uint8Array : Float32Array; const pixels = new TypedArrayClass(channels); - gl.readPixels( - x, flipY ? (flipY - y - 1) : y, 1, 1, - format, type, - pixels - ); + gl.readPixels(x, flipY ? flipY - y - 1 : y, 1, 1, format, type, pixels); // Re-bind whatever was previously bound gl.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer); @@ -512,7 +527,6 @@ p5.RendererGL = class RendererGL extends p5.Renderer { } this._isBlending = false; - this._hasSetAmbient = false; this._useSpecularMaterial = false; this._useEmissiveMaterial = false; @@ -533,9 +547,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer { this.quadraticAttenuation = 0; /** - * model view, projection, & normal - * matrices - */ + * model view, projection, & normal + * matrices + */ this.uModelMatrix = new p5.Matrix(); this.uViewMatrix = new p5.Matrix(); this.uMVMatrix = new p5.Matrix(); @@ -585,23 +599,94 @@ p5.RendererGL = class RendererGL extends p5.Renderer { geometry: {}, buffers: { stroke: [ - new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this), + new p5.RenderBuffer( + 4, + 'lineVertexColors', + 'lineColorBuffer', + 'aVertexColor', + this + ), + new p5.RenderBuffer( + 3, + 'lineVertices', + 'lineVerticesBuffer', + 'aPosition', + this + ), + new p5.RenderBuffer( + 3, + 'lineTangentsIn', + 'lineTangentsInBuffer', + 'aTangentIn', + this + ), + new p5.RenderBuffer( + 3, + 'lineTangentsOut', + 'lineTangentsOutBuffer', + 'aTangentOut', + this + ), new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) ], fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), + new p5.RenderBuffer( + 3, + 'vertices', + 'vertexBuffer', + 'aPosition', + this, + this._vToNArray + ), + new p5.RenderBuffer( + 3, + 'vertexNormals', + 'normalBuffer', + 'aNormal', + this, + this._vToNArray + ), + new p5.RenderBuffer( + 4, + 'vertexColors', + 'colorBuffer', + 'aVertexColor', + this + ), + new p5.RenderBuffer( + 3, + 'vertexAmbients', + 'ambientBuffer', + 'aAmbientColor', + this + ), //new BufferDef(3, 'vertexSpeculars', 'specularBuffer', 'aSpecularColor'), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + new p5.RenderBuffer( + 2, + 'uvs', + 'uvBuffer', + 'aTexCoord', + this, + this._flatten + ) ], text: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + new p5.RenderBuffer( + 3, + 'vertices', + 'vertexBuffer', + 'aPosition', + this, + this._vToNArray + ), + new p5.RenderBuffer( + 2, + 'uvs', + 'uvBuffer', + 'aTexCoord', + this, + this._flatten + ) ] } }; @@ -617,17 +702,74 @@ p5.RendererGL = class RendererGL extends p5.Renderer { _curveVertex: [], buffers: { fill: [ - new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), - new p5.RenderBuffer(3, 'vertexNormals', 'normalBuffer', 'aNormal', this, this._vToNArray), - new p5.RenderBuffer(4, 'vertexColors', 'colorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'vertexAmbients', 'ambientBuffer', 'aAmbientColor', this), - new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) + new p5.RenderBuffer( + 3, + 'vertices', + 'vertexBuffer', + 'aPosition', + this, + this._vToNArray + ), + new p5.RenderBuffer( + 3, + 'vertexNormals', + 'normalBuffer', + 'aNormal', + this, + this._vToNArray + ), + new p5.RenderBuffer( + 4, + 'vertexColors', + 'colorBuffer', + 'aVertexColor', + this + ), + new p5.RenderBuffer( + 3, + 'vertexAmbients', + 'ambientBuffer', + 'aAmbientColor', + this + ), + new p5.RenderBuffer( + 2, + 'uvs', + 'uvBuffer', + 'aTexCoord', + this, + this._flatten + ) ], stroke: [ - new p5.RenderBuffer(4, 'lineVertexColors', 'lineColorBuffer', 'aVertexColor', this), - new p5.RenderBuffer(3, 'lineVertices', 'lineVerticesBuffer', 'aPosition', this), - new p5.RenderBuffer(3, 'lineTangentsIn', 'lineTangentsInBuffer', 'aTangentIn', this), - new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this), + new p5.RenderBuffer( + 4, + 'lineVertexColors', + 'lineColorBuffer', + 'aVertexColor', + this + ), + new p5.RenderBuffer( + 3, + 'lineVertices', + 'lineVerticesBuffer', + 'aPosition', + this + ), + new p5.RenderBuffer( + 3, + 'lineTangentsIn', + 'lineTangentsInBuffer', + 'aTangentIn', + this + ), + new p5.RenderBuffer( + 3, + 'lineTangentsOut', + 'lineTangentsOutBuffer', + 'aTangentOut', + this + ), new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) ], point: this.GL.createBuffer() @@ -680,21 +822,23 @@ p5.RendererGL = class RendererGL extends p5.Renderer { } /** - * Starts creating a new p5.Geometry. Subsequent shapes drawn will be added - * to the geometry and then returned when - * endGeometry() is called. One can also use - * buildGeometry() to pass a function that - * draws shapes. - * - * If you need to draw complex shapes every frame which don't change over time, - * combining them upfront with `beginGeometry()` and `endGeometry()` and then - * drawing that will run faster than repeatedly drawing the individual pieces. - * - * @method beginGeometry + * Starts creating a new p5.Geometry. Subsequent shapes drawn will be added + * to the geometry and then returned when + * endGeometry() is called. One can also use + * buildGeometry() to pass a function that + * draws shapes. + * + * If you need to draw complex shapes every frame which don't change over time, + * combining them upfront with `beginGeometry()` and `endGeometry()` and then + * drawing that will run faster than repeatedly drawing the individual pieces. + * + * @method beginGeometry */ beginGeometry() { if (this.geometryBuilder) { - throw new Error('It looks like `beginGeometry()` is being called while another p5.Geometry is already being build.'); + throw new Error( + 'It looks like `beginGeometry()` is being called while another p5.Geometry is already being build.' + ); } this.geometryBuilder = new GeometryBuilder(this); } @@ -710,7 +854,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer { */ endGeometry() { if (!this.geometryBuilder) { - throw new Error('Make sure you call beginGeometry() before endGeometry()!'); + throw new Error( + 'Make sure you call beginGeometry() before endGeometry()!' + ); } const geometry = this.geometryBuilder.finish(); this.geometryBuilder = undefined; @@ -769,11 +915,14 @@ p5.RendererGL = class RendererGL extends p5.Renderer { _initContext() { if (this._pInst._glAttributes.version !== 1) { // Unless WebGL1 is explicitly asked for, try to create a WebGL2 context - this.drawingContext = - this.canvas.getContext('webgl2', this._pInst._glAttributes); + this.drawingContext = this.canvas.getContext( + 'webgl2', + this._pInst._glAttributes + ); } - this.webglVersion = - this.drawingContext ? constants.WEBGL2 : constants.WEBGL; + this.webglVersion = this.drawingContext + ? constants.WEBGL2 + : constants.WEBGL; // If this is the main canvas, make sure the global `webglVersion` is set this._pInst._setProperty('webglVersion', this.webglVersion); if (!this.drawingContext) { @@ -816,12 +965,8 @@ p5.RendererGL = class RendererGL extends p5.Renderer { maxAllowedPixelDimensions = Math.floor( maxTextureSize / this.pixelDensity() ); - let adjustedWidth = Math.min( - width, maxAllowedPixelDimensions - ); - let adjustedHeight = Math.min( - height, maxAllowedPixelDimensions - ); + let adjustedWidth = Math.min(width, maxAllowedPixelDimensions); + let adjustedHeight = Math.min(height, maxAllowedPixelDimensions); if (adjustedWidth !== width || adjustedHeight !== height) { console.warn( @@ -889,10 +1034,9 @@ p5.RendererGL = class RendererGL extends p5.Renderer { } } - /** - * @class p5.RendererGL - */ + * @class p5.RendererGL + */ _update() { // reset model view and apply initial camera transform // (containing only look at info; no projection). @@ -931,9 +1075,31 @@ p5.RendererGL = class RendererGL extends p5.Renderer { } /** - * [background description] - */ + * [background description] + */ background(...args) { + // Handle case where background is an image/graphics/media/texture + if ( + args.length === 1 && + (args[0] instanceof p5.Image || + args[0] instanceof p5.Graphics || + args[0] instanceof p5.MediaElement || + args[0] instanceof p5.Texture || + args[0] instanceof p5.Framebuffer) + ) { + const img = args[0]; + this.clear(); // wipe previous frame + + // reset camera so background image fills canvas + this._pInst.push(); + this._setDefaultCamera(); + this._pInst.imageMode(this._pInst.CORNER); + this._pInst.image(img, 0, 0, this.width, this.height); + this._pInst.pop(); + return; + } + + // Otherwise, treat args as color input const _col = this._pInst.color(...args); const _r = _col.levels[0] / 255; const _g = _col.levels[1] / 255; @@ -946,37 +1112,37 @@ p5.RendererGL = class RendererGL extends p5.Renderer { // COLOR ////////////////////////////////////////////// /** - * Basic fill material for geometry with a given color - * @method fill - * @class p5.RendererGL - * @param {Number|Number[]|String|p5.Color} v1 gray value, - * red or hue value (depending on the current color mode), - * or color Array, or CSS color string - * @param {Number} [v2] green or saturation value - * @param {Number} [v3] blue or brightness value - * @param {Number} [a] opacity - * @chainable - * @example - *
- * function setup() {
- * createCanvas(200, 200, WEBGL);
- * }
- *
- * function draw() {
- * background(0);
- * noStroke();
- * fill(100, 100, 240);
- * rotateX(frameCount * 0.01);
- * rotateY(frameCount * 0.01);
- * box(75, 75, 75);
- * }
- *
- *
+ * function setup() {
+ * createCanvas(200, 200, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * noStroke();
+ * fill(100, 100, 240);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(75, 75, 75);
+ * }
+ *
+ *
- * function setup() {
- * createCanvas(200, 200, WEBGL);
- * }
- *
- * function draw() {
- * background(0);
- * stroke(240, 150, 150);
- * fill(100, 100, 240);
- * rotateX(frameCount * 0.01);
- * rotateY(frameCount * 0.01);
- * box(75, 75, 75);
- * }
- *
- *
+ * function setup() {
+ * createCanvas(200, 200, WEBGL);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * stroke(240, 150, 150);
+ * fill(100, 100, 240);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(75, 75, 75);
+ * }
+ *
+ *
- * function setup() {
- * createCanvas(200, 400, WEBGL);
- * setAttributes('antialias', true);
- * }
- *
- * function draw() {
- * background(0);
- * noStroke();
- * translate(0, -100, 0);
- * stroke(240, 150, 150);
- * fill(100, 100, 240);
- * push();
- * strokeWeight(8);
- * rotateX(frameCount * 0.01);
- * rotateY(frameCount * 0.01);
- * sphere(75);
- * pop();
- * push();
- * translate(0, 200, 0);
- * strokeWeight(1);
- * rotateX(frameCount * 0.01);
- * rotateY(frameCount * 0.01);
- * sphere(75);
- * pop();
- * }
- *
- *
+ * function setup() {
+ * createCanvas(200, 400, WEBGL);
+ * setAttributes('antialias', true);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * noStroke();
+ * translate(0, -100, 0);
+ * stroke(240, 150, 150);
+ * fill(100, 100, 240);
+ * push();
+ * strokeWeight(8);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * sphere(75);
+ * pop();
+ * push();
+ * translate(0, 200, 0);
+ * strokeWeight(1);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * sphere(75);
+ * pop();
+ * }
+ *
+ *