Skip to content

Commit eabe2a2

Browse files
committed
fix: "SubsurfaceViewer: colorAbove/colorBelow/colorNaN possibly not working? #2479"
1 parent 92d2add commit eabe2a2

File tree

5 files changed

+241
-52
lines changed

5 files changed

+241
-52
lines changed

example-data/deckgl-map.json

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
437720,
4040
6481113
4141
],
42+
"hillshading": true,
4243
"colorMapName": "Rainbow",
4344
"valueRange": [
4445
2782,
@@ -49,21 +50,6 @@
4950
3513
5051
]
5152
},
52-
{
53-
"@@type": "Hillshading2DLayer",
54-
"bounds": [
55-
432205,
56-
6475078,
57-
437720,
58-
6481113
59-
],
60-
"valueRange": [
61-
2782,
62-
3513
63-
],
64-
"rotDeg": 0,
65-
"image": "@@#resources.depthMap"
66-
},
6753
{
6854
"@@type": "WellsLayer",
6955
"data": "@@#resources.wellsData",

typescript/packages/subsurface-viewer/src/layers/colormap/colormap.fs.glsl.ts

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ out vec4 fragColor;
1010
uniform sampler2D bitmapTexture; // Property map
1111
uniform sampler2D colormap;
1212
13+
// Compute the normal value for every pixel, based on the current value and two values aroud it.
14+
vec3 normal(float val) {
15+
vec2 dr = 1.0 / map.bitmapResolution;
16+
17+
float valueRangeSize = map.valueRangeMax - map.valueRangeMin;
18+
float p0 = valueRangeSize * val;
19+
20+
float px = valueRangeSize * decode_rgb2float(texture(bitmapTexture, vTexCoord + vec2(1.0, 0.0) / map.bitmapResolution).rgb);
21+
float py = valueRangeSize * decode_rgb2float(texture(bitmapTexture, vTexCoord + vec2(0.0, 1.0) / map.bitmapResolution).rgb);
22+
vec3 dx = vec3(1.0, 0.0, px - p0);
23+
vec3 dy = vec3(0.0, 1.0, py - p0);
24+
25+
return normalize(cross(dx, dy));
26+
}
27+
28+
// Compute how much a pixel is in the shadow based on its normal and where the light comes from.
29+
float shadow(vec3 normal) {
30+
vec3 lightDirection = vec3(1.0, 1.0, 1.0);
31+
float ambientLightIntensity = 0.5;
32+
float diffuseLightIntensity = 0.5;
33+
float diffuse = diffuseLightIntensity * dot(normal, normalize(lightDirection));
34+
return clamp(ambientLightIntensity + diffuse, 0.0, 1.0);
35+
}
36+
1337
void main(void) {
1438
vec4 bitmapColor = texture(bitmapTexture, vTexCoord);
1539
@@ -20,18 +44,52 @@ void main(void) {
2044
}
2145
2246
// Decode the RGB value into a float. See decoder.fs.glsl for more details.
23-
float val = decode_rgb2float(bitmapColor.rgb);
24-
// The resulting val will be in [0, 1] interval, so we can use it directly as a texture coord
25-
// to sample from the colormap.
26-
// 0 => Leftmost color in the colormap, 1 => rightmost color, linearly interpolated in between.
27-
vec4 color = texture(colormap, vec2(val, 0.5));
28-
29-
// The final pixel opacity is the combination of the user provided image-wide opacity,
30-
// the colormap opacity at the sampled pixel and the property map opacity of the sampled pixel.
31-
fragColor = vec4(color.rgb, color.a * bitmapColor.a * layer.opacity);
32-
33-
// Support for existing functionality that comes from the BitmapLayer, such as desaturate, tintColor etc.
34-
// See https://deck.gl/docs/api-reference/layers/bitmap-layer#render-options for more details.
47+
float val = decode_rgb2float(bitmapColor.rgb); // The resulting val will be in [0, 1] interval.
48+
float property = val * (map.valueRangeMax - map.valueRangeMin) + map.valueRangeMin;
49+
50+
// Compute the color from the colormap.
51+
vec4 color = vec4(1.0, 1.0, 1.0, 1.0);
52+
float x = (property - map.colorMapRangeMin) / (map.colorMapRangeMax - map.colorMapRangeMin);
53+
if (x < 0.0 || x > 1.0) {
54+
// Out of range. Use clampcolor.
55+
if (map.isClampColor) {
56+
color = vec4(map.colorMapClampColor.rgb, 1.0);
57+
}
58+
else if (map.isColorMapClampColorTransparent) {
59+
discard;
60+
return;
61+
}
62+
else {
63+
// Use min/max color to clamp.
64+
x = max(0.0, x);
65+
x = min(1.0, x);
66+
67+
color = texture(colormap, vec2(x, 0.5));
68+
}
69+
}
70+
else {
71+
color = texture(colormap, vec2(x, 0.5));
72+
}
73+
fragColor = vec4(color.rgb, color.a * bitmapColor.a * layer.opacity);
74+
75+
76+
// Hillshading.
77+
if (map.hillshading) {
78+
// Compute the shadow value, how dark a pixel will be, 1 is in complete shadow, 0 is in complete light.
79+
float shadow = shadow(normal(val));
80+
fragColor = vec4(fragColor.rgb * shadow, fragColor.a);
81+
}
82+
83+
84+
// Contour lines.
85+
if (map.contours) {
86+
float x = (property - map.contourReferencePoint) / map.contourInterval;
87+
float f = fract(x);
88+
float df = fwidth(x);
89+
float c = smoothstep(df * 1.0, df * 2.0, f); // smootstep from/to no of pixels distance from contour line. // keep:
90+
fragColor = fragColor * vec4(c, c, c, 1.0);
91+
}
92+
3593
geometry.uv = vTexCoord;
3694
DECKGL_FILTER_COLOR(fragColor, geometry);
3795
}

typescript/packages/subsurface-viewer/src/layers/colormap/colormapLayer.ts

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@ import type { LayerProps, PickingInfo } from "@deck.gl/core";
22
import { project32 } from "@deck.gl/core";
33
import type { BitmapLayerPickingInfo, BitmapLayerProps } from "@deck.gl/layers";
44
import { BitmapLayer } from "@deck.gl/layers";
5-
6-
import type { ShaderModule } from "@luma.gl/shadertools";
7-
import type { Model } from "@luma.gl/engine";
8-
9-
import type { colorTablesArray } from "@emerson-eps/color-tables/";
10-
import { getRgbData } from "@emerson-eps/color-tables";
11-
5+
import type { Color, LayerProps, PickingInfo } from "@deck.gl/core";
6+
import { project32 } from "@deck.gl/core";
127
import type {
138
ColorMapFunctionType,
149
LayerPickInfo,
@@ -20,16 +15,17 @@ import { decodeRGB, type ValueDecoder } from "../utils/propertyMapTools";
2015
import fsColormap from "./colormap.fs.glsl";
2116
import type { DeckGLLayerContext } from "../../components/Map";
2217
import type { ContinuousLegendDataType } from "../../components/ColorLegend";
18+
import type { ShaderModule } from "@luma.gl/shadertools";
19+
import type { Model } from "@luma.gl/engine";
20+
import type { Texture } from "@luma.gl/core";
2321

2422
function getImageData(
2523
colorMapName: string,
2624
colorTables: colorTablesArray,
2725
colorMapFunction?: ColorMapFunctionType
2826
) {
2927
const isColorMapFunctionDefined = typeof colorMapFunction !== "undefined";
30-
3128
const data = new Uint8Array(256 * 3);
32-
3329
for (let i = 0; i < 256; i++) {
3430
const value = i / 255.0;
3531
const rgb = isColorMapFunctionDefined
@@ -93,6 +89,25 @@ export interface ColormapLayerProps
9389
// Rotates image around bounds upper left corner counterclockwise in degrees.
9490
rotDeg: number;
9591

92+
// If true, draw contour lines. Default false.
93+
contours: boolean;
94+
95+
// If true, apply hillshading. Default false.
96+
hillshading: boolean;
97+
98+
// Contour reference height. Default 0.
99+
contourReferencePoint: number;
100+
101+
// Height between contour lines. Default 50.
102+
contourInterval: number;
103+
104+
/** Clamp colormap to this color at ends.
105+
* Given as array of three values (r,g,b) e.g: [255, 0, 0]
106+
* If not set or set to true, it will clamp to color map min and max values.
107+
* If set to false the clamp color will be completely transparent.
108+
*/
109+
colorMapClampColor: Color | undefined | boolean;
110+
96111
// Non public properties:
97112
reportBoundingBox?: React.Dispatch<ReportBoundingBoxAction>;
98113
}
@@ -114,13 +129,13 @@ const defaultProps = {
114129
},
115130
rotDeg: 0,
116131
colorMapName: "Rainbow",
132+
contours: false,
133+
hillshading: false,
134+
contourReferencePoint: 0,
135+
contourInterval: 50, // 50 meter by default
117136
};
118137

119138
export default class ColormapLayer extends BitmapLayer<ColormapLayerProps> {
120-
initializeState(): void {
121-
super.initializeState();
122-
}
123-
124139
setShaderModuleProps(
125140
...props: Parameters<Model["shaderInputs"]["setProps"]>
126141
): void {
@@ -161,7 +176,7 @@ export default class ColormapLayer extends BitmapLayer<ColormapLayerProps> {
161176
const valueRangeMin = this.props.valueRange[0] ?? 0.0;
162177
const valueRangeMax = this.props.valueRange[1] ?? 1.0;
163178

164-
// If specified color map will extend from colorMapRangeMin to colorMapRangeMax.
179+
// If specified, color map will extend from colorMapRangeMin to colorMapRangeMax.
165180
// Otherwise it will extend from valueRangeMin to valueRangeMax.
166181
const colorMapRangeMin = this.props.colorMapRange?.[0] ?? valueRangeMin;
167182
const colorMapRangeMax = this.props.colorMapRange?.[1] ?? valueRangeMax;
@@ -179,13 +194,48 @@ export default class ColormapLayer extends BitmapLayer<ColormapLayerProps> {
179194

180195
this.state.model?.setBindings({ colormap });
181196

197+
const bitmapResolution = this.props.image
198+
? [
199+
(this.props.image as Texture).width,
200+
(this.props.image as Texture).height,
201+
]
202+
: [1, 1];
203+
204+
const contours = this.props.contours;
205+
const hillshading = this.props.hillshading;
206+
207+
const contourReferencePoint = this.props.contourReferencePoint;
208+
const contourInterval = this.props.contourInterval;
209+
210+
const isClampColor: boolean =
211+
this.props.colorMapClampColor !== undefined &&
212+
this.props.colorMapClampColor !== true &&
213+
this.props.colorMapClampColor !== false;
214+
let colorMapClampColor = isClampColor
215+
? this.props.colorMapClampColor
216+
: [0, 0, 0];
217+
218+
colorMapClampColor = (colorMapClampColor as Color).map(
219+
(x) => (x ?? 0) / 255
220+
);
221+
222+
const isColorMapClampColorTransparent: boolean =
223+
(this.props.colorMapClampColor as boolean) === false;
224+
182225
super.setShaderModuleProps({
183226
map: {
184227
valueRangeMin,
185228
valueRangeMax,
186229
colorMapRangeMin,
187230
colorMapRangeMax,
188-
231+
contours,
232+
hillshading,
233+
bitmapResolution,
234+
contourReferencePoint,
235+
contourInterval,
236+
isClampColor,
237+
colorMapClampColor,
238+
isColorMapClampColorTransparent,
189239
rgbScaler: this.props.valueDecoder.rgbScaler,
190240
floatScaler: this.props.valueDecoder.floatScaler,
191241
offset: this.props.valueDecoder.offset,
@@ -220,6 +270,10 @@ export default class ColormapLayer extends BitmapLayer<ColormapLayerProps> {
220270
// We just need to decode that RGB color into a property float value.
221271
const val = decodeRGB(info.color, mergedDecoder, this.props.valueRange);
222272

273+
if (info.coordinate) {
274+
info.coordinate[2] = NaN; // disable z value in 2D map;
275+
}
276+
223277
return {
224278
...info,
225279
// Picking color doesn't represent object index in this layer.
@@ -258,7 +312,14 @@ uniform mapUniforms {
258312
float valueRangeMax;
259313
float colorMapRangeMin;
260314
float colorMapRangeMax;
261-
315+
vec2 bitmapResolution;
316+
bool contours;
317+
bool hillshading;
318+
float contourReferencePoint;
319+
float contourInterval;
320+
bool isClampColor;
321+
vec3 colorMapClampColor;
322+
bool isColorMapClampColorTransparent;
262323
vec3 rgbScaler; // r, g and b multipliers
263324
float floatScaler; // value multiplier
264325
float offset; // translation of the r, g, b sum
@@ -274,13 +335,7 @@ float decode_rgb2float(vec3 rgb) {
274335
value = floor(value / map.step + 0.5) * map.step;
275336
}
276337
277-
// If colorMapRangeMin/Max specified, color map will span this interval.
278-
float x = value * (map.valueRangeMax - map.valueRangeMin) + map.valueRangeMin;
279-
x = (x - map.colorMapRangeMin) / (map.colorMapRangeMax - map.colorMapRangeMin);
280-
x = max(0.0, x);
281-
x = min(1.0, x);
282-
283-
return x;
338+
return value;
284339
}
285340
`;
286341

@@ -289,7 +344,14 @@ type Map2DUniformsType = {
289344
valueRangeMax: number;
290345
colorMapRangeMin: number;
291346
colorMapRangeMax: number;
292-
347+
bitmapResolution: [number, number];
348+
contours: boolean;
349+
hillshading: boolean;
350+
contourReferencePoint: number;
351+
contourInterval: number;
352+
isClampColor: boolean;
353+
colorMapClampColor: [number, number, number];
354+
isColorMapClampColorTransparent: boolean;
293355
rgbScaler: [number, number, number];
294356
floatScaler: number;
295357
offset: number;
@@ -306,7 +368,14 @@ const map2DUniforms = {
306368
valueRangeMax: "f32",
307369
colorMapRangeMin: "f32",
308370
colorMapRangeMax: "f32",
309-
371+
bitmapResolution: "vec2<f32>",
372+
contours: "u32",
373+
hillshading: "u32",
374+
contourReferencePoint: "f32",
375+
contourInterval: "f32",
376+
isClampColor: "u32",
377+
colorMapClampColor: "vec3<f32>",
378+
isColorMapClampColorTransparent: "u32",
310379
rgbScaler: "vec3<f32>",
311380
floatScaler: "f32",
312381
offset: "f32",

0 commit comments

Comments
 (0)