Skip to content

Fix coplanar strokes in WebGL mode not respecting depth + pixels[] being flipped #5981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 16 additions & 15 deletions src/webgl/p5.RendererGL.Retained.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ p5.RendererGL.prototype.drawBuffers = function(gId) {
const gl = this.GL;
const geometry = this.retainedMode.geometry[gId];

if (this._doFill) {
this._useVertexColor = (geometry.model.vertexColors.length > 0);
const fillShader = this._getRetainedFillShader();
this._setFillUniforms(fillShader);
for (const buff of this.retainedMode.buffers.fill) {
buff._prepareBuffer(geometry, fillShader);
}
if (geometry.indexBuffer) {
//vertex index buffer
this._bindBuffer(geometry.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
}
this._applyColorBlend(this.curFillColor);
this._drawElements(gl.TRIANGLES, gId);
fillShader.unbindShader();
}

if (this._doStroke && geometry.lineVertexCount > 0) {
const faceCullingEnabled = gl.isEnabled(gl.CULL_FACE);
// Prevent strokes from getting removed by culling
Expand All @@ -136,21 +152,6 @@ p5.RendererGL.prototype.drawBuffers = function(gId) {
strokeShader.unbindShader();
}

if (this._doFill) {
this._useVertexColor = (geometry.model.vertexColors.length > 0);
const fillShader = this._getRetainedFillShader();
this._setFillUniforms(fillShader);
for (const buff of this.retainedMode.buffers.fill) {
buff._prepareBuffer(geometry, fillShader);
}
if (geometry.indexBuffer) {
//vertex index buffer
this._bindBuffer(geometry.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
}
this._applyColorBlend(this.curFillColor);
this._drawElements(gl.TRIANGLES, gId);
fillShader.unbindShader();
}
return this;
};

Expand Down
16 changes: 15 additions & 1 deletion src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ p5.RendererGL.prototype._getPixel = function(x, y) {
let imageData, index;
imageData = new Uint8Array(4);
this.drawingContext.readPixels(
x, y, 1, 1,
x, this.GL.drawingBufferHeight - y - 1, 1, 1,
this.drawingContext.RGBA, this.drawingContext.UNSIGNED_BYTE,
imageData
);
Expand Down Expand Up @@ -901,6 +901,20 @@ p5.RendererGL.prototype.loadPixels = function() {
this.GL.RGBA, this.GL.UNSIGNED_BYTE,
pixels
);

// WebGL pixels are inverted compared to 2D pixels, so we have to flip
// the resulting rows. Adapted from https://stackoverflow.com/a/41973289
const width = this.GL.drawingBufferWidth;
const height = this.GL.drawingBufferHeight;
const halfHeight = Math.floor(height / 2);
const tmpRow = new Uint8Array(width * 4);
for (let y = 0; y < halfHeight; y++) {
const topOffset = y * width * 4;
const bottomOffset = (height - y - 1) * width * 4;
tmpRow.set(pixels.subarray(topOffset, topOffset + width * 4));
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + width * 4);
pixels.set(tmpRow, bottomOffset);
}
};

//////////////////////////////////////////////
Expand Down
19 changes: 14 additions & 5 deletions src/webgl/shaders/line.vert
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ vec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) {
}

void main() {
// using a scale <1 moves the lines towards the camera
// in order to prevent popping effects due to half of
// the line disappearing behind the geometry faces.
vec3 scale = vec3(0.9995);

// Caps have one of either the in or out tangent set to 0
vCap = (aTangentIn == vec3(0.)) != (aTangentOut == (vec3(0.)))
? 1. : 0.;
Expand All @@ -85,6 +80,20 @@ void main() {
vec4 posqIn = uModelViewMatrix * (aPosition + vec4(aTangentIn, 0));
vec4 posqOut = uModelViewMatrix * (aPosition + vec4(aTangentOut, 0));

float facingCamera = pow(
// The world space tangent's z value is 0 if it's facing the camera
abs(normalize(posqIn-posp).z),

// Using pow() here to ramp `facingCamera` up from 0 to 1 really quickly
// so most lines get scaled and don't get clipped
0.25
);

// using a scale <1 moves the lines towards the camera
// in order to prevent popping effects due to half of
// the line disappearing behind the geometry faces.
float scale = mix(1., 0.995, facingCamera);

// Moving vertices slightly toward the camera
// to avoid depth-fighting with the fill triangles.
// Discussed here:
Expand Down
38 changes: 32 additions & 6 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ suite('p5.RendererGL', function() {
);
done();
});

test('coplanar strokes match 2D', function(done) {
const getColors = function(mode) {
myp5.createCanvas(20, 20, mode);
myp5.pixelDensity(1);
myp5.background(255);
myp5.strokeCap(myp5.SQUARE);
myp5.strokeJoin(myp5.MITER);
if (mode === myp5.WEBGL) {
myp5.translate(-myp5.width/2, -myp5.height/2);
}
myp5.stroke('black');
myp5.strokeWeight(4);
myp5.fill('red');
myp5.rect(10, 10, 15, 15);
myp5.fill('blue');
myp5.rect(0, 0, 15, 15);
myp5.loadPixels();
console.log([...myp5.pixels]);
return [...myp5.pixels];
};

assert.deepEqual(getColors(myp5.P2D), getColors(myp5.WEBGL));
done();
});
});

suite('text shader', function() {
Expand Down Expand Up @@ -1189,7 +1214,7 @@ suite('p5.RendererGL', function() {
renderer.bezierVertex(128, -128, 128, 128, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(128, 128), [255, 129, 129, 255]);
assert.deepEqual(myp5.get(128, 127), [255, 129, 129, 255]);

done();
});
Expand All @@ -1210,7 +1235,7 @@ suite('p5.RendererGL', function() {
renderer.bezierVertex(128, -128, 128, 128, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(190, 128), [255, 128, 128, 255]);
assert.deepEqual(myp5.get(190, 127), [255, 128, 128, 255]);

done();
});
Expand All @@ -1229,7 +1254,7 @@ suite('p5.RendererGL', function() {
renderer.quadraticVertex(256, 0, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(128, 128), [255, 128, 128, 255]);
assert.deepEqual(myp5.get(128, 127), [255, 128, 128, 255]);

done();
});
Expand All @@ -1250,7 +1275,7 @@ suite('p5.RendererGL', function() {
renderer.quadraticVertex(256, 0, -128, 128);
renderer.endShape();

assert.deepEqual(myp5.get(190, 128), [255, 128, 128, 255]);
assert.deepEqual(myp5.get(190, 127), [255, 128, 128, 255]);

done();
});
Expand Down Expand Up @@ -1298,7 +1323,7 @@ suite('p5.RendererGL', function() {
myp5.model(myGeom);

assert.equal(renderer._useLineColor, true);
assert.deepEqual(myp5.get(128, 0), [127, 0, 128, 255]);
assert.deepEqual(myp5.get(128, 255), [127, 0, 128, 255]);
done();
});

Expand All @@ -1319,7 +1344,7 @@ suite('p5.RendererGL', function() {
myp5.endShape(myp5.CLOSE);

assert.equal(renderer._useLineColor, true);
assert.deepEqual(myp5.get(128, 0), [127, 0, 128, 255]);
assert.deepEqual(myp5.get(128, 255), [127, 0, 128, 255]);
done();
});
});
Expand Down Expand Up @@ -1482,6 +1507,7 @@ suite('p5.RendererGL', function() {
});

myp5.fill(255);
myp5.noStroke();
myp5.directionalLight(255, 255, 255, 0, 0, -1);

myp5.triangle(-8, -8, 8, -8, 8, 8);
Expand Down