alpha blending fix in scatterplots (#2033)

* explicitly specify alpha blending; rename NaN flag to accurately describe function

* fix incorret dest alpha blending function
This commit is contained in:
Bruce Martin
2021-02-05 09:34:37 -08:00
committed by GitHub
parent d821f0eac9
commit 3aef21f76c
5 changed files with 59 additions and 28 deletions
+16 -6
View File
@@ -14,7 +14,7 @@ export default function drawPointsRegl(regl) {
uniform float nPoints; uniform float nPoints;
uniform float minViewportDimension; uniform float minViewportDimension;
varying vec4 fragColor; varying lowp vec4 fragColor;
const float zBottom = 0.99; const float zBottom = 0.99;
const float zMiddle = 0.; const float zMiddle = 0.;
@@ -27,23 +27,23 @@ export default function drawPointsRegl(regl) {
${glPointSize} ${glPointSize}
void main() { void main() {
bool isNaN, isSelected, isHighlight; bool isBackground, isSelected, isHighlight;
getFlags(flag, isNaN, isSelected, isHighlight); getFlags(flag, isBackground, isSelected, isHighlight);
float size = pointSize(nPoints, minViewportDimension, isSelected, isHighlight); float size = pointSize(nPoints, minViewportDimension, isSelected, isHighlight);
gl_PointSize = size * pow(distance, 0.5); gl_PointSize = size * pow(distance, 0.5);
float z = isNaN ? zBottom : (isHighlight ? zTop : zMiddle); float z = isBackground ? zBottom : (isHighlight ? zTop : zMiddle);
vec3 xy = projView * vec3(position, 1.); vec3 xy = projView * vec3(position, 1.);
gl_Position = vec4(xy.xy, z, 1.); gl_Position = vec4(xy.xy, z, 1.);
float alpha = isNaN ? 0.9 : 1.0; float alpha = isBackground ? 0.9 : 1.0;
fragColor = vec4(color, alpha); fragColor = vec4(color, alpha);
}`, }`,
frag: ` frag: `
precision mediump float; precision mediump float;
varying vec4 fragColor; varying lowp vec4 fragColor;
void main() { void main() {
if (length(gl_PointCoord.xy - 0.5) > 0.5) { if (length(gl_PointCoord.xy - 0.5) > 0.5) {
discard; discard;
@@ -67,5 +67,15 @@ export default function drawPointsRegl(regl) {
count: regl.prop("count"), count: regl.prop("count"),
primitive: "points", primitive: "points",
blend: {
enable: true,
func: {
srcRGB: "src alpha",
srcAlpha: 1,
dstRGB: 0,
dstAlpha: "zero",
},
},
}); });
} }
+9 -6
View File
@@ -22,6 +22,12 @@ import CentroidLabels from "./overlays/centroidLabels";
import actions from "../../actions"; import actions from "../../actions";
import renderThrottle from "../../util/renderThrottle"; import renderThrottle from "../../util/renderThrottle";
import {
flagBackground,
flagSelected,
flagHighlight,
} from "../../util/glHelpers";
/* /*
Simple 2D transforms control all point painting. There are three: Simple 2D transforms control all point painting. There are three:
* model - convert from underlying per-point coordinate to a layout. * model - convert from underlying per-point coordinate to a layout.
@@ -62,10 +68,6 @@ function createModelTF() {
return m; return m;
} }
const flagSelected = 1;
const flagNaN = 2;
const flagHighlight = 4;
@connect((state) => ({ @connect((state) => ({
annoMatrix: state.annoMatrix, annoMatrix: state.annoMatrix,
crossfilter: state.obsCrossfilter, crossfilter: state.obsCrossfilter,
@@ -159,8 +161,9 @@ class Graph extends React.Component {
const flags = new Float32Array(nObs); const flags = new Float32Array(nObs);
if (colorByData) { if (colorByData) {
for (let i = 0, len = flags.length; i < len; i += 1) { for (let i = 0, len = flags.length; i < len; i += 1) {
if (!Number.isFinite(colorByData[i])) { const val = colorByData[i];
flags[i] = flagNaN; if (typeof val === "number" && !Number.isFinite(val)) {
flags[i] = flagBackground;
} }
} }
} }
@@ -13,7 +13,7 @@ export default function drawPointsRegl(regl) {
uniform float nPoints; uniform float nPoints;
uniform float minViewportDimension; uniform float minViewportDimension;
varying vec4 fragColor; varying lowp vec4 fragColor;
const float zBottom = 0.99; const float zBottom = 0.99;
const float zMiddle = 0.; const float zMiddle = 0.;
@@ -26,22 +26,22 @@ export default function drawPointsRegl(regl) {
${glPointSize} ${glPointSize}
void main() { void main() {
bool isNaN, isSelected, isHighlight; bool isBackground, isSelected, isHighlight;
getFlags(flag, isNaN, isSelected, isHighlight); getFlags(flag, isBackground, isSelected, isHighlight);
gl_PointSize = pointSize(nPoints, minViewportDimension, isSelected, isHighlight); gl_PointSize = pointSize(nPoints, minViewportDimension, isSelected, isHighlight);
float z = isNaN ? zBottom : (isHighlight ? zTop : zMiddle); float z = isBackground ? zBottom : (isHighlight ? zTop : zMiddle);
vec3 xy = projection * vec3(position, 1.); vec3 xy = projection * vec3(position, 1.);
gl_Position = vec4(xy.xy, z, 1.); gl_Position = vec4(xy.xy, z, 1.);
float alpha = isNaN ? 0.9 : 1.0; float alpha = isBackground ? 0.9 : 1.0;
fragColor = vec4(color, alpha); fragColor = vec4(color, alpha);
}`, }`,
frag: ` frag: `
precision mediump float; precision mediump float;
varying vec4 fragColor; varying lowp vec4 fragColor;
void main() { void main() {
if (length(gl_PointCoord.xy - 0.5) > 0.5) { if (length(gl_PointCoord.xy - 0.5) > 0.5) {
discard; discard;
@@ -64,5 +64,15 @@ export default function drawPointsRegl(regl) {
count: regl.prop("count"), count: regl.prop("count"),
primitive: "points", primitive: "points",
blend: {
enable: true,
func: {
srcRGB: "src alpha",
srcAlpha: 1,
dstRGB: 0,
dstAlpha: "zero",
},
},
}); });
} }
@@ -16,10 +16,11 @@ import {
createColorQuery, createColorQuery,
} from "../../util/stateManager/colorHelpers"; } from "../../util/stateManager/colorHelpers";
import renderThrottle from "../../util/renderThrottle"; import renderThrottle from "../../util/renderThrottle";
import {
const flagSelected = 1; flagBackground,
const flagNaN = 2; flagSelected,
const flagHighlight = 4; flagHighlight,
} from "../../util/glHelpers";
function createProjectionTF(viewportWidth, viewportHeight) { function createProjectionTF(viewportWidth, viewportHeight) {
/* /*
@@ -135,8 +136,9 @@ class Scatterplot extends React.PureComponent {
const flags = new Float32Array(nObs); const flags = new Float32Array(nObs);
if (colorByData) { if (colorByData) {
for (let i = 0, len = flags.length; i < len; i += 1) { for (let i = 0, len = flags.length; i < len; i += 1) {
if (!Number.isFinite(colorByData[i])) { const val = colorByData[i];
flags[i] = flagNaN; if (typeof val === "number" && !Number.isFinite(val)) {
flags[i] = flagBackground;
} }
} }
} }
+10 -4
View File
@@ -8,18 +8,24 @@ PointFlags:
We want a bitmask-like flag structure, but due to webgl limitations We want a bitmask-like flag structure, but due to webgl limitations
must emulate it with floats. must emulate it with floats.
Supported flags are:
selected: the point is currently selected
highlight: the point is currently highlighted
background: the point is background information
*/ */
// for JS // for JS
export const flagSelected = 1; export const flagSelected = 1;
export const flagNaN = 2; export const flagBackground = 2;
export const flagHighlight = 4; export const flagHighlight = 4;
// for GLSL // for GLSL
export const glPointFlags = ` export const glPointFlags = `
const float flagSelected = 1.; const float flagSelected = 1.;
const float flagNaN = 2.; const float flagBackground = 2.;
const float flagHighlight = 4.; const float flagHighlight = 4.;
bool isLowBitSet(float f) { bool isLowBitSet(float f) {
@@ -32,12 +38,12 @@ export const glPointFlags = `
} }
void getFlags(in float flag, void getFlags(in float flag,
out bool isNaN, out bool isBackground,
out bool isSelected, out bool isSelected,
out bool isHighlight) { out bool isHighlight) {
isSelected = isLowBitSet(flag); isSelected = isLowBitSet(flag);
flag = shiftRightOne(flag); flag = shiftRightOne(flag);
isNaN = isLowBitSet(flag); isBackground = isLowBitSet(flag);
flag = shiftRightOne(flag); flag = shiftRightOne(flag);
isHighlight = isLowBitSet(flag); isHighlight = isLowBitSet(flag);
} }