Skip to content

Commit d2851fe

Browse files
committed
use simple quad mesh for EPSG:4326 reprojection
1 parent 90ccff8 commit d2851fe

File tree

5 files changed

+219
-124
lines changed

5 files changed

+219
-124
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Simple pass-through module that sets geometry.uv to vTexCoord.
3+
* Used when no reprojection is needed.
4+
*/
5+
export const PassthroughUV = {
6+
name: "passthrough-uv",
7+
inject: {
8+
"fs:#main-start": /* glsl */ `
9+
geometry.uv = vTexCoord;
10+
`,
11+
},
12+
getUniforms: () => ({}),
13+
} as const;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Combined Reprojection + Texture Sampling Module for EPSG:4326 Source Data
3+
*
4+
* This module combines UV reprojection and texture sampling into a single
5+
* DECKGL_FILTER_COLOR hook to avoid issues with geometry.uv not persisting
6+
* between shader module injections.
7+
*/
8+
import type { Texture } from "@luma.gl/core";
9+
10+
export type ReprojectTextureProps = {
11+
/** The texture to sample */
12+
textureName: Texture;
13+
/** Latitude bounds [south, north] in degrees */
14+
latBounds: [number, number];
15+
/** Mercator Y bounds [north, south] in normalized coordinates */
16+
mercatorYBounds: [number, number];
17+
/** Whether row 0 of the texture is south (true) or north (false) */
18+
latIsAscending: boolean;
19+
/** Debug mode: 0=off, 1=show texV as grayscale, 2=show original vTexCoord.y */
20+
debugMode?: number;
21+
};
22+
23+
/** Module name - must match uniform block name */
24+
const MODULE_NAME = "reprojectTexture";
25+
26+
/** Uniform block for non-texture uniforms (luma.gl v9 pattern) */
27+
const uniformBlock = /* glsl */ `\
28+
uniform ${MODULE_NAME}Uniforms {
29+
vec2 latBounds;
30+
vec2 mercatorYBounds;
31+
int latIsAscending;
32+
int debugMode;
33+
} ${MODULE_NAME};
34+
`;
35+
36+
export const ReprojectTexture = {
37+
name: MODULE_NAME,
38+
fs: uniformBlock,
39+
inject: {
40+
"fs:#decl": /* glsl */ `
41+
const float PI_REPROJ = 3.14159265358979323846;
42+
43+
uniform sampler2D reprojectTexture_texture;
44+
45+
// Convert normalized Mercator Y [0,1] to latitude in degrees
46+
float mercatorYToLat_reproj(float mercY) {
47+
float t = PI_REPROJ * (1.0 - 2.0 * mercY);
48+
return degrees(atan(sinh(t)));
49+
}
50+
`,
51+
"fs:DECKGL_FILTER_COLOR": /* glsl */ `
52+
// Check if uniforms are set (mercatorYBounds will be [0,0] if not)
53+
// If not set, discard fragment to avoid flash during loading
54+
if (reprojectTexture.mercatorYBounds.x == 0.0 && reprojectTexture.mercatorYBounds.y == 0.0) {
55+
discard;
56+
}
57+
58+
// Get the original UV from geometry.uv (set to vTexCoord by base shader or passthrough)
59+
vec2 originalUV = geometry.uv;
60+
61+
// Interpolate Mercator Y based on original UV.y
62+
float currentMercY;
63+
if (reprojectTexture.latIsAscending == 1) {
64+
// UV.y: 0 at south, 1 at north
65+
currentMercY = mix(
66+
reprojectTexture.mercatorYBounds.y, // south (at y=0)
67+
reprojectTexture.mercatorYBounds.x, // north (at y=1)
68+
originalUV.y
69+
);
70+
} else {
71+
// UV.y: 0 at north, 1 at south
72+
currentMercY = mix(
73+
reprojectTexture.mercatorYBounds.x, // north (at y=0)
74+
reprojectTexture.mercatorYBounds.y, // south (at y=1)
75+
originalUV.y
76+
);
77+
}
78+
79+
// Convert Mercator Y to latitude
80+
float lat = mercatorYToLat_reproj(currentMercY);
81+
82+
// Map latitude to texture V
83+
float south = reprojectTexture.latBounds.x;
84+
float north = reprojectTexture.latBounds.y;
85+
float latRange = north - south;
86+
87+
float texV;
88+
if (reprojectTexture.latIsAscending == 1) {
89+
texV = (lat - south) / latRange;
90+
} else {
91+
texV = (north - lat) / latRange;
92+
}
93+
94+
// Sample texture with reprojected UV
95+
vec2 reprojectedUV = vec2(originalUV.x, texV);
96+
color = texture(reprojectTexture_texture, reprojectedUV);
97+
98+
// Debug modes
99+
if (reprojectTexture.debugMode == 1) {
100+
color = vec4(texV, texV, texV, 1.0);
101+
}
102+
if (reprojectTexture.debugMode == 2) {
103+
color = vec4(originalUV.y, originalUV.y, originalUV.y, 1.0);
104+
}
105+
`,
106+
},
107+
// Uniform types for luma.gl v9 (must match uniform block order)
108+
uniformTypes: {
109+
latBounds: "vec2<f32>",
110+
mercatorYBounds: "vec2<f32>",
111+
latIsAscending: "i32",
112+
debugMode: "i32",
113+
},
114+
getUniforms: (props: Partial<ReprojectTextureProps> = {}) => {
115+
return {
116+
// Texture is handled separately via setBindings
117+
reprojectTexture_texture: props.textureName,
118+
// Non-texture uniforms go to uniform block
119+
latBounds: props.latBounds ?? [0, 0],
120+
mercatorYBounds: props.mercatorYBounds ?? [0, 0],
121+
latIsAscending: props.latIsAscending ? 1 : 0,
122+
debugMode: props.debugMode ?? 0,
123+
};
124+
},
125+
} as const;

packages/deck.gl-raster/src/mesh-layer/mesh-layer-fragment.glsl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ in vec4 vColor;
2121
out vec4 fragColor;
2222
2323
void main(void) {
24-
geometry.uv = vTexCoord;
24+
// geometry.uv is set by shader modules (PassthroughUV or Reproject4326)
25+
// at fs:#main-start injection point, before this code runs.
2526
2627
vec3 normal;
2728
if (simpleMesh.flatShading) {

packages/deck.gl-raster/src/mesh-layer/mesh-layer.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,34 @@ export class MeshTextureLayer extends SimpleMeshLayer<
4040
}
4141

4242
override draw(opts: any): void {
43-
const shaderProps: { [x: string]: Partial<Record<string, unknown>> } = {};
43+
// Build props object keyed by module name for shaderInputs
44+
// With proper uniformTypes, setProps should handle non-texture uniforms
45+
const shaderProps: Record<string, Record<string, unknown>> = {};
4446
for (const m of this.props.renderPipeline) {
45-
// Props should be keyed by module name
4647
shaderProps[m.module.name] = m.props || {};
4748
}
4849

49-
for (const m of super.getModels()) {
50-
m.shaderInputs.setProps(shaderProps);
50+
// Collect texture bindings from modules (textures can't go in uniform blocks)
51+
const textureBindings: Record<string, unknown> = {};
52+
for (const m of this.props.renderPipeline) {
53+
if (m.module.getUniforms && m.props) {
54+
const moduleUniforms = m.module.getUniforms(m.props);
55+
for (const [key, value] of Object.entries(moduleUniforms)) {
56+
if (value && typeof value === "object" && "handle" in value) {
57+
textureBindings[key] = value;
58+
}
59+
}
60+
}
61+
}
62+
63+
for (const model of super.getModels()) {
64+
// setProps should handle uniform block values via uniformTypes
65+
model.shaderInputs.setProps(shaderProps);
66+
67+
// Textures need to be set via bindings (can't be in uniform blocks)
68+
if (Object.keys(textureBindings).length > 0) {
69+
model.setBindings(textureBindings as Record<string, any>);
70+
}
5171
}
5272

5373
super.draw(opts);

0 commit comments

Comments
 (0)