Skip to content

Commit 24f9fd8

Browse files
dcojgithub-actions[bot]
authored andcommitted
[GLJS-1720] Adds support for a dynamic and fixed density mapping when
compositing using the additive blend mode on line layers (internal-11706) GitOrigin-RevId: d475ab930ae63aecd2221c789f51914f6e8ad75f
1 parent 2e5ec18 commit 24f9fd8

29 files changed

Lines changed: 1113 additions & 96 deletions

3d-style/shaders/model.fragment.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ Material getPBRMaterial() {
283283
mat.metallic = v_roughness_metallic_emissive_alpha.y;
284284
mat.baseColor.w *= v_roughness_metallic_emissive_alpha.w;
285285
#endif
286-
#if defined(HAS_TEXTURE_u_metallicRoughnessTexture) && defined(HAS_ATTRIBUTE_a_uv_2f)
286+
#if defined(HAS_TEXTURE_u_metallicRoughnessTexture) && defined(HAS_ATTRIBUTE_a_uv_2f)
287287
vec4 mrSample = texture(u_metallicRoughnessTexture, uv_2f);
288288
mat.perceptualRoughness *= mrSample.g;
289289
mat.metallic *= mrSample.b;

debug/blend-mode.html

Lines changed: 178 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,29 @@
6363
margin-left: 5px;
6464
font-weight: normal;
6565
}
66+
67+
.section-divider {
68+
border: none;
69+
border-top: 2px solid #ccc;
70+
margin: 12px 0 10px;
71+
}
72+
73+
.section-title {
74+
font-weight: bold;
75+
font-size: 13px;
76+
margin-bottom: 8px;
77+
color: #333;
78+
text-transform: uppercase;
79+
letter-spacing: 0.5px;
80+
}
6681
</style>
6782
</head>
6883

6984
<body>
7085
<div id='map'></div>
7186
<div id='controls'>
87+
<div class="section-title">Layer 1 — Flight Lines</div>
88+
7289
<div class="control-group">
7390
<label>
7491
<input type="checkbox" id="blendModeEnabled" checked onchange="toggleBlendMode(this)">
@@ -80,22 +97,29 @@
8097
<label for="blendModeSelect">Blend Mode:</label>
8198
<select id="blendModeSelect" onchange="updateBlendMode()">
8299
<option value="multiply">multiply</option>
83-
<option value="additive">additive</option>
100+
<option value="additive" selected>additive</option>
84101
</select>
85102
</div>
86103

87104
<div class="control-group">
88105
<label for="opacitySlider">
89-
Opacity: <span class="slider-value" id="opacityValue">0.01</span>
106+
Opacity: <span class="slider-value" id="opacityValue">1.00</span>
90107
</label>
91-
<input type="range" id="opacitySlider" min="0" max="1" step="0.01" value="0.01" oninput="updateOpacity(this)">
108+
<input type="range" id="opacitySlider" min="0" max="1" step="0.001" value="1" oninput="updateOpacity(this)">
109+
</div>
110+
111+
<div class="control-group">
112+
<label for="additiveClampSlider">
113+
Additive Clamp: <span class="slider-value" id="additiveClampValue">0 (auto)</span>
114+
</label>
115+
<input type="range" id="additiveClampSlider" min="0" max="200" step="1" value="0" oninput="updateAdditiveClamp(this)">
92116
</div>
93117

94118
<div class="control-group">
95119
<label for="basemapSelect">Basemap & Line Color:</label>
96120
<select id="basemapSelect" onchange="updateBasemap()">
97121
<option value="light">Light (Blue Lines)</option>
98-
<option value="dark">Dark (Yellow Lines)</option>
122+
<option value="dark" selected>Dark (Yellow Lines)</option>
99123
</select>
100124
</div>
101125

@@ -104,16 +128,59 @@
104128
<input type="text" id="geojsonUrl" value="https://docs.mapbox.com/mapbox-gl-js/assets/flights-200k.geojson">
105129
<button onclick="loadGeoJSON()" style="margin-top: 5px; width: 100%; padding: 4px;">Load GeoJSON</button>
106130
</div>
131+
132+
<hr class="section-divider">
133+
<div class="section-title">Layer 2 — Star Shape</div>
134+
135+
<div class="control-group">
136+
<label>
137+
<input type="checkbox" id="polygonBlendModeEnabled" checked onchange="togglePolygonBlendMode(this)">
138+
Enable Blend Mode
139+
</label>
140+
</div>
141+
142+
<div class="control-group">
143+
<label for="polygonBlendModeSelect">Blend Mode:</label>
144+
<select id="polygonBlendModeSelect" onchange="updatePolygonBlendMode()">
145+
<option value="multiply">multiply</option>
146+
<option value="additive">additive</option>
147+
</select>
148+
</div>
149+
150+
<div class="control-group">
151+
<label for="polygonColorSelect">Line Color:</label>
152+
<select id="polygonColorSelect" onchange="updatePolygonColor()">
153+
<option value="#0080ff">Blue</option>
154+
<option value="#ff4400">Red</option>
155+
<option value="#00cc44">Green</option>
156+
<option value="#ffcc00">Yellow</option>
157+
<option value="#aa00ff">Purple</option>
158+
</select>
159+
</div>
160+
161+
<div class="control-group">
162+
<label for="maineOpacitySlider">
163+
Opacity: <span class="slider-value" id="maineOpacityValue">0.00</span>
164+
</label>
165+
<input type="range" id="maineOpacitySlider" min="0" max="1" step="0.01" value="0"
166+
oninput="updateStarOpacity(this)">
167+
</div>
107168
</div>
108169

109170
<script type="module">
110171
import mapboxgl from '../dist/esm-dev/mapbox-gl.js';
111172
import { getAccessToken } from './access_token_generated.js';
112173
mapboxgl.accessToken = getAccessToken();
113174

114-
let currentOpacity = 0.01;
115-
let currentBlendMode = 'multiply';
175+
let currentOpacity = 1.0;
176+
let currentBlendMode = 'additive';
116177
let blendModeEnabled = true;
178+
let additiveClamp = 0;
179+
180+
let polygonBlendModeEnabled = true;
181+
let polygonBlendMode = 'multiply';
182+
let polygonLineColor = '#0080ff';
183+
let starOpacity = 0;
117184

118185
const basemapConfig = {
119186
light: {
@@ -126,7 +193,31 @@
126193
}
127194
};
128195

129-
let currentBasemap = 'light';
196+
let currentBasemap = 'dark';
197+
198+
// Abstract 6-pointed star centred on Munich [11.576, 48.137]
199+
const cx = 11.576, cy = 48.137;
200+
const starCoordinates = [
201+
[cx + 0.0, cy + 16.0],
202+
[cx + 5.0, cy + 9.0],
203+
[cx + 16.0, cy + 9.0],
204+
[cx + 7.0, cy + 1.0],
205+
[cx + 11.0, cy - 10.0],
206+
[cx + 0.0, cy - 4.0],
207+
[cx - 11.0, cy - 10.0],
208+
[cx - 7.0, cy + 1.0],
209+
[cx - 16.0, cy + 9.0],
210+
[cx - 5.0, cy + 9.0],
211+
[cx + 0.0, cy + 16.0]
212+
];
213+
214+
const starGeoJSON = {
215+
type: 'Feature',
216+
geometry: {
217+
type: 'Polygon',
218+
coordinates: [starCoordinates]
219+
}
220+
};
130221

131222
const map = window.map = new mapboxgl.Map({
132223
container: 'map',
@@ -135,7 +226,7 @@
135226
imports: [
136227
{
137228
id: 'basemap',
138-
url: basemapConfig.light.url
229+
url: basemapConfig.dark.url
139230
}
140231
],
141232
version: 8,
@@ -144,22 +235,38 @@
144235
type: 'geojson',
145236
data: document.getElementById('geojsonUrl').value,
146237
},
238+
'star': {
239+
type: 'geojson',
240+
data: starGeoJSON
241+
}
147242
},
148243
layers: [
244+
{
245+
id: 'star-outline',
246+
type: 'line',
247+
source: 'star',
248+
paint: {
249+
'line-color': polygonLineColor,
250+
'line-width': 200,
251+
'line-opacity': starOpacity,
252+
'line-blend-mode': polygonBlendMode
253+
}
254+
},
149255
{
150256
id: 'overlay',
151257
type: 'line',
152258
source: 'overlay',
153259
paint: {
154-
'line-color': `rgba(${basemapConfig.light.lineColor.join(',')}, ${currentOpacity})`,
260+
'line-color': `rgba(${basemapConfig.dark.lineColor.join(',')}, ${currentOpacity})`,
155261
'line-blend-mode': currentBlendMode,
262+
'line-blend-additive-clamp': additiveClamp,
156263
'line-width': 1
157264
}
158265
}
159266
]
160267
},
161-
center: [-73.985, 40.748],
162-
zoom: 6,
268+
center: [cx, cy],
269+
zoom: 4,
163270
pitch: 0,
164271
hash: true
165272
});
@@ -172,10 +279,17 @@
172279
function updateLayer() {
173280
if (map.getLayer('overlay')) {
174281
map.setPaintProperty('overlay', 'line-color', getLineColor());
175-
map.setPaintProperty('overlay', 'line-blend-mode', blendModeEnabled ? currentBlendMode : undefined);
282+
map.setPaintProperty('overlay', 'line-blend-mode', blendModeEnabled ? currentBlendMode : 'default');
283+
map.setPaintProperty('overlay', 'line-blend-additive-clamp', additiveClamp);
176284
}
177285
}
178286

287+
function updateAdditiveClamp(slider) {
288+
additiveClamp = parseFloat(slider.value);
289+
document.getElementById('additiveClampValue').textContent = additiveClamp === 0 ? '0 (auto)' : additiveClamp.toFixed(0);
290+
updateLayer();
291+
}
292+
179293
function updateOpacity(slider) {
180294
currentOpacity = parseFloat(slider.value);
181295
document.getElementById('opacityValue').textContent = currentOpacity.toFixed(2);
@@ -210,15 +324,31 @@
210324
type: 'geojson',
211325
data: document.getElementById('geojsonUrl').value,
212326
},
327+
'star': {
328+
type: 'geojson',
329+
data: starGeoJSON
330+
}
213331
},
214332
layers: [
333+
{
334+
id: 'star-outline',
335+
type: 'line',
336+
source: 'star',
337+
paint: {
338+
'line-color': polygonLineColor,
339+
'line-width': 200,
340+
'line-opacity': starOpacity,
341+
'line-blend-mode': polygonBlendModeEnabled ? polygonBlendMode : 'default'
342+
}
343+
},
215344
{
216345
id: 'overlay',
217346
type: 'line',
218347
source: 'overlay',
219348
paint: {
220349
'line-color': getLineColor(),
221-
'line-blend-mode': blendModeEnabled ? currentBlendMode : 'normal',
350+
'line-blend-mode': blendModeEnabled ? currentBlendMode : 'default',
351+
'line-blend-additive-clamp': additiveClamp,
222352
'line-width': 1
223353
}
224354
}
@@ -235,11 +365,46 @@
235365
}
236366
}
237367

368+
function updateStarOpacity(slider) {
369+
starOpacity = parseFloat(slider.value);
370+
document.getElementById('maineOpacityValue').textContent = starOpacity.toFixed(2);
371+
if (map.getLayer('star-outline')) {
372+
map.setPaintProperty('star-outline', 'line-opacity', starOpacity);
373+
}
374+
}
375+
376+
function togglePolygonBlendMode(checkbox) {
377+
polygonBlendModeEnabled = checkbox.checked;
378+
document.getElementById('polygonBlendModeSelect').disabled = !polygonBlendModeEnabled;
379+
if (map.getLayer('star-outline')) {
380+
map.setPaintProperty('star-outline', 'line-blend-mode', polygonBlendModeEnabled ? polygonBlendMode : 'default');
381+
}
382+
}
383+
384+
function updatePolygonBlendMode() {
385+
polygonBlendMode = document.getElementById('polygonBlendModeSelect').value;
386+
if (map.getLayer('star-outline') && polygonBlendModeEnabled) {
387+
map.setPaintProperty('star-outline', 'line-blend-mode', polygonBlendMode);
388+
}
389+
}
390+
391+
function updatePolygonColor() {
392+
polygonLineColor = document.getElementById('polygonColorSelect').value;
393+
if (map.getLayer('star-outline')) {
394+
map.setPaintProperty('star-outline', 'line-color', polygonLineColor);
395+
}
396+
}
397+
238398
window.updateOpacity = updateOpacity;
399+
window.updateAdditiveClamp = updateAdditiveClamp;
239400
window.updateBlendMode = updateBlendMode;
240401
window.toggleBlendMode = toggleBlendMode;
241402
window.updateBasemap = updateBasemap;
242403
window.loadGeoJSON = loadGeoJSON;
404+
window.updateStarOpacity = updateStarOpacity;
405+
window.togglePolygonBlendMode = togglePolygonBlendMode;
406+
window.updatePolygonBlendMode = updatePolygonBlendMode;
407+
window.updatePolygonColor = updatePolygonColor;
243408
</script>
244409
</body>
245410

src/gl/color_mode.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const ONE = 0x0001;
77
export const SRC_ALPHA = 0x0302;
88
export const ONE_MINUS_SRC_ALPHA = 0x0303;
99
export const DST_COLOR = 0x0306;
10+
export const ONE_MINUS_DST_ALPHA = 0x0305;
11+
export const DST_ALPHA = 0x0304;
1012

1113
export default class ColorMode {
1214
blendFunction: BlendFuncType;
@@ -30,6 +32,8 @@ export default class ColorMode {
3032
static multiply: Readonly<ColorMode>;
3133
static multiplyAccumulateAlpha: Readonly<ColorMode>;
3234
static additive: Readonly<ColorMode>;
35+
static additiveAlphaWeighted: Readonly<ColorMode>;
36+
static additiveAlphaWeightedUnboundedAlpha: Readonly<ColorMode>;
3337
}
3438

3539
ColorMode.Replace = [ONE, ZERO, ONE, ZERO];
@@ -41,3 +45,5 @@ ColorMode.alphaBlendedNonPremultiplied = new ColorMode([SRC_ALPHA, ONE_MINUS_SRC
4145
ColorMode.multiply = new ColorMode([DST_COLOR, ZERO, DST_COLOR, ZERO], Color.transparent, [true, true, true, true]);
4246
ColorMode.multiplyAccumulateAlpha = new ColorMode([DST_COLOR, ZERO, ONE, ONE_MINUS_SRC_ALPHA], Color.transparent, [true, true, true, true]);
4347
ColorMode.additive = new ColorMode([ONE, ONE, ONE, ONE], Color.transparent, [true, true, true, true]);
48+
ColorMode.additiveAlphaWeighted = new ColorMode([SRC_ALPHA, ONE, ONE_MINUS_DST_ALPHA, ONE], Color.transparent, [true, true, true, true]);
49+
ColorMode.additiveAlphaWeightedUnboundedAlpha = new ColorMode([SRC_ALPHA, ONE, ONE, ONE], Color.transparent, [true, true, true, true]);

src/gl/context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class Context {
7777
// eslint-disable-next-line camelcase
7878
extRenderToTextureHalfFloat: EXT_color_buffer_half_float;
7979
// eslint-disable-next-line camelcase
80+
extColorBufferFloat: EXT_color_buffer_float;
81+
// eslint-disable-next-line camelcase
8082
extDebugRendererInfo: WEBGL_debug_renderer_info;
8183
extTimerQuery: {
8284
/* EXT_disjoint_timer_query is not yet available as a TypeScript type */
@@ -159,6 +161,7 @@ class Context {
159161
this.extTextureFloatLinear = gl.getExtension('OES_texture_float_linear');
160162
}
161163
this.extRenderToTextureHalfFloat = gl.getExtension('EXT_color_buffer_half_float');
164+
this.extColorBufferFloat = gl.getExtension('EXT_color_buffer_float');
162165

163166
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
164167
this.extTimerQuery = gl.getExtension('EXT_disjoint_timer_query_webgl2');

src/gl/framebuffer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Framebuffer {
1010
context: Context;
1111
width: number;
1212
height: number;
13+
format: TextureFormat;
1314
framebuffer: WebGLFramebuffer;
1415
colorAttachment0: ColorAttachment;
1516
colorAttachment1: ColorAttachment;
@@ -65,11 +66,12 @@ class Framebuffer {
6566
existingFbo.destroy();
6667
}
6768

68-
const format: TextureFormat = context.extRenderToTextureHalfFloat ? context.gl.RGBA16F : context.gl.RGBA8;
69+
const format: TextureFormat = (context.extRenderToTextureHalfFloat || context.extColorBufferFloat) ? context.gl.RGBA16F : context.gl.RGBA8;
6970
const texture = new Texture(context, {width, height, data: null}, format);
7071
texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
7172

7273
const fbo = context.createFramebuffer(width, height, 1, null);
74+
fbo.format = format;
7375
fbo.colorAttachment0.set(texture.texture);
7476

7577
if (attachStencil) {

0 commit comments

Comments
 (0)