From be19324799cb27085991bfa465c4aba0b4a1ea88 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Wed, 8 Nov 2023 18:56:50 -0500 Subject: [PATCH] Check for per vertex transparency when blending --- src/webgl/material.js | 7 ++++-- src/webgl/p5.Geometry.js | 32 ++++++++++++++++++++++++++++ src/webgl/p5.RendererGL.Immediate.js | 10 +++++++-- src/webgl/p5.RendererGL.Retained.js | 10 +++++++-- test/unit/webgl/p5.RendererGL.js | 24 +++++++++++++++++++++ 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/webgl/material.js b/src/webgl/material.js index 849e901a63..1b9a6b43d7 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -1177,14 +1177,17 @@ p5.prototype.shininess = function(shine) { * @private blends colors according to color components. * If alpha value is less than 1, or non-standard blendMode * we need to enable blending on our gl context. - * @param {Number[]} color [description] + * @param {Number[]} color The currently set color, with values in 0-1 range + * @param {Boolean} [hasTransparency] Whether the shape being drawn has other + * transparency internally, e.g. via vertex colors * @return {Number[]]} Normalized numbers array */ -p5.RendererGL.prototype._applyColorBlend = function(colors) { +p5.RendererGL.prototype._applyColorBlend = function(colors, hasTransparency) { const gl = this.GL; const isTexture = this.drawMode === constants.TEXTURE; const doBlend = + hasTransparency || this.userFillShader || this.userStrokeShader || this.userPointShader || diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 825fd27efc..a99b1dcd56 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -65,6 +65,9 @@ p5.Geometry = class Geometry { this.detailY = detailY !== undefined ? detailY : 1; this.dirtyFlags = {}; + this._hasFillTransparency = undefined; + this._hasStrokeTransparency = undefined; + if (callback instanceof Function) { callback.call(this); } @@ -72,6 +75,9 @@ p5.Geometry = class Geometry { } reset() { + this._hasFillTransparency = undefined; + this._hasStrokeTransparency = undefined; + this.lineVertices.clear(); this.lineTangentsIn.clear(); this.lineTangentsOut.clear(); @@ -87,6 +93,32 @@ p5.Geometry = class Geometry { this.dirtyFlags = {}; } + + hasFillTransparency() { + if (this._hasFillTransparency === undefined) { + this._hasFillTransparency = false; + for (let i = 0; i < this.vertexColors.length; i += 4) { + if (this.vertexColors[i + 3] < 1) { + this._hasFillTransparency = true; + break; + } + } + } + return this._hasFillTransparency; + } + hasStrokeTransparency() { + if (this._hasStrokeTransparency === undefined) { + this._hasStrokeTransparency = false; + for (let i = 0; i < this.lineVertexColors.length; i += 4) { + if (this.lineVertexColors[i + 3] < 1) { + this._hasStrokeTransparency = true; + break; + } + } + } + return this._hasStrokeTransparency; + } + /** * Removes the internal colors of p5.Geometry. * Using `clearColors()`, you can use `fill()` to supply new colors before drawing each shape. diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 2409dae3d6..39d72f4aad 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -518,7 +518,10 @@ p5.RendererGL.prototype._drawImmediateFill = function(count = 1) { } shader.disableRemainingAttributes(); - this._applyColorBlend(this.curFillColor); + this._applyColorBlend( + this.curFillColor, + this.immediateMode.geometry.hasFillTransparency() + ); if (count === 1) { gl.drawArrays( @@ -560,7 +563,10 @@ p5.RendererGL.prototype._drawImmediateStroke = function() { buff._prepareBuffer(this.immediateMode.geometry, shader); } shader.disableRemainingAttributes(); - this._applyColorBlend(this.curStrokeColor); + this._applyColorBlend( + this.curStrokeColor, + this.immediateMode.geometry.hasFillTransparency() + ); gl.drawArrays( gl.TRIANGLES, diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index d278d51428..9bd05739c8 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -143,7 +143,10 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { //vertex index buffer this._bindBuffer(geometry.indexBuffer, gl.ELEMENT_ARRAY_BUFFER); } - this._applyColorBlend(this.curFillColor); + this._applyColorBlend( + this.curFillColor, + geometry.model.hasFillTransparency() + ); this._drawElements(gl.TRIANGLES, gId); fillShader.unbindShader(); } @@ -156,7 +159,10 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { buff._prepareBuffer(geometry, strokeShader); } strokeShader.disableRemainingAttributes(); - this._applyColorBlend(this.curStrokeColor); + this._applyColorBlend( + this.curStrokeColor, + geometry.model.hasStrokeTransparency() + ); this._drawArrays(gl.TRIANGLES, gId); strokeShader.unbindShader(); } diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 216a89cd5e..1a98232445 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -1137,6 +1137,30 @@ suite('p5.RendererGL', function() { assert.deepEqual(myp5.get(16, 16), [255, 0, 255, 255]); done(); }); + + test('transparency works the same with per-vertex colors', function() { + myp5.createCanvas(20, 20, myp5.WEBGL); + myp5.noStroke(); + + function drawShapes() { + myp5.fill(255, 0, 0, 100); + myp5.rect(-10, -10, 15, 15); + myp5.fill(0, 0, 255, 100); + myp5.rect(-5, -5, 15, 15); + } + + drawShapes(); + myp5.loadPixels(); + const eachShapeResult = [...myp5.pixels]; + + myp5.clear(); + const shapes = myp5.buildGeometry(drawShapes); + myp5.model(shapes); + myp5.loadPixels(); + const singleShapeResult = [...myp5.pixels]; + + assert.deepEqual(eachShapeResult, singleShapeResult); + }); }); suite('BufferDef', function() {