mapboxgl-custom-webgl-layer

Build, debug, and optimize mapboxgl `type: 'custom'` layers using raw WebGL. Covers shader setup, buffer pipelines, matrix usage, animation uniforms, precision-safe coordinate handling (RTC/local origin), and multiple geometry types (lines, polygons, points/icons). Use for custom GIS overlays and for fixing jitter, tearing, flicker, or empty rendering issues.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "mapboxgl-custom-webgl-layer" with this command: npx skills add xiaxiangfeng/mapboxgl-custom-webgl-layer/xiaxiangfeng-mapboxgl-custom-webgl-layer-mapboxgl-custom-webgl-layer

Mapbox GL Custom WebGL Layer

Implement custom Mapbox GL layers with deterministic WebGL lifecycle, stable geometry generation, and precision-safe rendering. Follow the workflow, rules, and checklists below.

CustomLayerInterface Contract

A custom layer object must conform to the Mapbox CustomLayerInterface:

Property / MethodRequiredDescription
idUnique layer identifier
typeMust be 'custom'
renderingMode'2d' (default, no depth) or '3d' (shares depth buffer)
onAdd(map, gl)Called once when layer is added. Initialize shaders, buffers, uniforms here.
render(gl, matrix)Called every frame. Draw geometry here. Do not assume GL state except blend/depth.
prerender(gl, matrix)Called before render if the layer needs to draw to a texture/FBO first.
onRemove(map, gl)Called when layer is removed. Delete buffers, programs, textures here.

Critical API Notes

  1. Mapbox uses premultiplied alpha blending. The expected blend state is gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA). Fragment shaders must output premultiplied colors: gl_FragColor = vec4(color.rgb * color.a, color.a).
  2. Do NOT assume any GL state in render except that blending and depth are configured for compositing. Set all other state explicitly.
  3. Call map.triggerRepaint() only when continuous animation is needed.
  4. Handle webglcontextlost / webglcontextrestored events to recreate GPU resources gracefully.

Workflow

  1. Normalize input data into explicit LineString / MultiLineString / Polygon / MultiPolygon / Point primitives.
  2. Construct a custom layer object with id, type: 'custom', renderingMode, onAdd, render, and onRemove.
  3. In onAdd: compile/link shaders, cache attribute/uniform locations, allocate buffers, register context-loss listeners.
  4. Build geometry on CPU: convert coordinates → Mercator → local offsets. Upload typed arrays with gl.bufferData.
  5. In render: compute originClip on CPU, set uniforms (u_matrix, u_originClip, time, colors), bind buffers, set GL state, draw.
  6. Trigger repaint only when animation is active.
  7. In onRemove: delete buffers, program, textures; remove event listeners.

Data Preprocessing

  1. Parse GeoJSON features, flatten Multi* geometries into individual primitives.
  2. Convert [lng, lat] to Mercator using mapboxgl.MercatorCoordinate.fromLngLat(lngLat).
  3. Use MercatorCoordinate.meterInMercatorCoordinateUnits() to convert metric widths/offsets to Mercator space.
  4. Remove consecutive duplicate points (dist < epsilon).
  5. Optionally simplify dense polylines (Douglas-Peucker) for performance.

Precision Rules (RTC Pipeline)

  1. Keep geographic coordinates (lng/lat) on CPU only.
  2. Convert to MercatorCoordinate on CPU — values range [0, 1].
  3. Pick one local origin (originMercator) per geometry batch (e.g. centroid or first point).
  4. Store vertex positions as local offsets: local = mercator - originMercator.
  5. Per frame on CPU: originClip = matrix * vec4(originMercator.x, originMercator.y, 0, 1).
  6. In vertex shader: offsetClip = matrix * vec4(a_pos, 0, 0) (note w=0), then gl_Position = originClip + offsetClip.
  7. Never add large world coordinates and small offsets in GPU math.
  8. Use highp in vertex shader. Use fragment precision fallback guard:
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

Reference: precision-playbook.md

Geometry Rules

Lines / Path Ribbons

  1. Sample centerline in input order (do not reorder points).
  2. Compute direction from neighboring samples and derive perpendicular normals.
  3. Use miter join with clamp (miterLimit ≈ 2.0) and bevel fallback on sharp turns.
  4. Build triangle strip or indexed triangle list deterministically.
  5. For animated patterns, compute cumulative lineDistance on CPU and pass as attribute.
  6. Rebuild buffers only when geometry-affecting props change (data, width, offset, join style).

Polygons

  1. Flatten rings from GeoJSON: outer ring + holes.
  2. Triangulate using earcut (or equivalent): earcut(flatCoords, holeIndices, dimensions).
  3. Convert resulting vertex positions to Mercator local offsets (same RTC pipeline).
  4. Use gl.drawElements(gl.TRIANGLES, ...) with the earcut index buffer.
  5. For extruded polygons (3D), add height attribute and adjust renderingMode: '3d'.

Points / Icons / Symbols

  1. For each point, emit a billboard quad (4 vertices, 2 triangles) centered at the point.
  2. Pass quad corner offsets as attributes (e.g. [-1,-1], [1,-1], [1,1], [-1,1]).
  3. In vertex shader, scale quad offsets by desired pixel size / zoom factor.
  4. Use texture atlas or SDF (Signed Distance Field) for icon/glyph rendering.
  5. For large point counts, consider instancing (ANGLE_instanced_arrays extension).

Reference: common-patterns.md

Render-State Rules

  1. Set blend state explicitly: gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) (premultiplied alpha).
  2. Disable depth test for pure 2D overlay layers unless 3D ordering is required.
  3. Rebind attribute pointers explicitly every frame — do not rely on preserved state.
  4. Avoid hidden global state assumptions between custom layers.
  5. Restore any modified GL state if the layer modifies non-standard state (stencil, scissor, etc.).
  6. Keep renderingMode aligned with intent: '2d' for overlays, '3d' for depth interaction.

Performance Rules

  1. Use gl.STATIC_DRAW for geometry that rarely changes; gl.DYNAMIC_DRAW for frequently updated data.
  2. Avoid rebuilding geometry or re-uploading buffers every frame.
  3. Batch multiple features into a single draw call when possible.
  4. Minimize shader uniform uploads — cache values and skip if unchanged.
  5. Use Uint16Array for index buffers when vertex count ≤ 65535; Uint32Array with OES_element_index_uint extension otherwise.
  6. Consider using VAO (OES_vertex_array_object) to reduce per-frame attribute setup cost.

Debug Sequence

  1. Check layer is actually added: map.getLayer(id).
  2. Check shader compile/link logs before geometry debugging.
  3. Log indexCount / vertexCount; if zero, fix data normalization or geometry build.
  4. If geometry exists but nothing renders, verify attribute locations are not -1 and draw count > 0.
  5. If geometry renders but flickers/jitters, verify RTC pipeline and w=0 local transform.
  6. If colors appear too bright or too dark, check premultiplied alpha output.
  7. If spikes or self-intersections appear, tune join strategy (reduce miterLimit, use bevel fallback).
  8. If visuals disappear on some GPUs, check precision declarations and fragment fallback.
  9. If layer disappears after tab switch, handle webglcontextrestored event.

Reference: troubleshooting-checklist.md

Output Contract

  1. Return implementation changes with concrete file paths and line-level rationale.
  2. Explain precision decisions explicitly when touching vertex transforms.
  3. Explain blending mode choice (premultiplied alpha).
  4. Add a short validation checklist: render visible, zoom/pan stability, no shader errors, context-loss recovery.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

Pantry

Pantry — a fast home management tool. Log anything, find it later, export when needed.

Registry SourceRecently Updated
General

Milestone

A focused utility tools tool built for Milestone. Log entries, review trends, and export reports — all locally.

Registry SourceRecently Updated
General

Dingtalk Connector Guide

钉钉机器人接入指南 - OpenClaw 连接钉钉完整教程。适合:中国企业用户、钉钉开发者。

Registry SourceRecently Updated