developmentseed / developmentseed/deck.gl-raster
Raster imagery darkened by scene LightingEffect; no consumer control over mesh material/lighting
- Dominant language
- TypeScript
- Stars
- 228
- Forks
- 29
- Avg merge
- 12h 27m
- Merged PRs (30d)
- 4
Description
## Problem
Raster imagery rendered through `RasterLayer`/`MeshTextureLayer` (and therefore `COGLayer`) is uniformly darkened whenever the deck.gl scene has a `LightingEffect` with ambient intensity < 1 — a common setup when the same scene lights 3D extrusions. Rendered pixels come out at roughly `fileValue × ambientIntensity` (we measured 40% and 60% in two views), while `BitmapLayer`-based imagery in the same scene shows true values. Downloading the GeoTIFF shows correct pixels; only the on-screen render is dark.
## Root cause
The vendored mesh fragment shader (`packages/deck.gl-raster/src/mesh-layer/mesh-layer-fragment.glsl.ts`) runs the sampled color through phong lighting:
```glsl
vec3 lightColor = lighting_getLightColor(color.rgb, cameraPosition, position_commonspace.xyz, normal);
fragColor = vec4(lightColor, color.a * layer.opacity);
```
`MeshTextureLayer`'s default `material: { ambient: 1.0, diffuse: 0.0, ... }` intends to neutralize lighting, but the phong ambient term is `material.ambient × ambientLight.color × ambientLight.intensity × surfaceColor` — it's only neutral when the scene's ambient intensity is exactly 1.0.
There is also no consumer-facing escape hatch: the mesh layer sits behind `COGLayer → TileLayer → RasterLayer → MeshTextureLayer`, the `TileLayer` is constructed bare, and per-tile sublayer ids are dynamic, so `_subLayerProps` can't reach it with a `material` override.
## Related deck.gl v9 regression (affects the fix design)
Even where `material` *can* be passed (directly to `MeshTextureLayer`), deck.gl's documented `material: false` ("disable lighting") is silently broken in v9:
- `LightingEffect.getShaderModuleProps()` forwards the layer's raw `material` prop as the `phongMaterial` module props (`@deck.gl/core` `effects/lighting/lighting-effect.ts`).
- luma.gl's `ShaderInputs.setProps()` coerces falsy module props to `{}`:
```js
const moduleProps = props[moduleName] || {};
```
so `false` → `{}` → `getUniforms({})` → the **default lit** phong material (`ambient 0.35, diffuse 0.6`) — arguably darker still.
The unlit escape hatch exists in the shader (`if (material.unlit) return surfaceColor;` in `@luma.gl/shadertools` phong module); it's just unreachable via `material: false` in v9.
## Proposal
Keep the current default (no visual change for existing consumers), and expose deck.gl's standard `material` prop on `RasterTileLayer` and `RasterLayer`, forwarded conditionally down to `MeshTextureLayer` (following the existing `debugOpacity` pattern; forwarded only when set so the current default is preserved). `COGLayer` extends `RasterTileLayer`, so it inherits the prop with zero deck.gl-geotiff changes.
To make `material: false` actually mean "unlit" despite the v9 regression above, `MeshTextureLayer.draw()` (which already sets per-frame shader module props after the effect's props are applied) translates a falsy material into `phongMaterial: { unlit: true }`.
Result: `new COGLayer({ ..., material: false })` renders file values verbatim — parity with `BitmapLayer`.
We considered a raster-specific boolean (e.g. `lighting: false`) instead; `material` follows the `SimpleMeshLayer` convention and gives custom-material control for free, but happy to rename if you prefer a raster-specific term.
**PR incoming from us implementing the above.**
## Workaround (0.7.x)
pnpm patch on `dist/mesh-layer/mesh-layer-fragment.glsl.js` replacing the lit `fragColor` with `vec4(color.rgb, color.a * layer.opacity)`.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with packages/deck.gl-raster/src/mesh-layer/mesh-layer-fragment.glsl.ts and MeshTextureLayer.draw(), then trace material and debugOpacity forwarding through RasterLayer, RasterTileLayer, COGLayer, and TileLayer. Review @deck.gl/core effects/lighting/lighting-effect.ts and the luma.gl phong module behavior. Done means existing defaults remain unchanged and material: false produces raster pixels matching the source values under a LightingEffect.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- data-visualization, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100