s3m-three 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anim/Animator.d.ts +111 -0
- package/dist/anim/Animator.d.ts.map +1 -0
- package/dist/anim/Animator.js +382 -0
- package/dist/anim/Animator.js.map +1 -0
- package/dist/anim/Path.d.ts +30 -0
- package/dist/anim/Path.d.ts.map +1 -0
- package/dist/anim/Path.js +100 -0
- package/dist/anim/Path.js.map +1 -0
- package/dist/anim/easing.d.ts +70 -0
- package/dist/anim/easing.d.ts.map +1 -0
- package/dist/anim/easing.js +170 -0
- package/dist/anim/easing.js.map +1 -0
- package/dist/anim/index.d.ts +6 -0
- package/dist/anim/index.d.ts.map +1 -0
- package/dist/anim/index.js +4 -0
- package/dist/anim/index.js.map +1 -0
- package/dist/browser/s3m-three.iife.ext-three.js +218 -39
- package/dist/browser/s3m-three.iife.js +218 -39
- package/dist/core/types.d.ts +20 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/render/EdgeLines.d.ts +68 -14
- package/dist/render/EdgeLines.d.ts.map +1 -1
- package/dist/render/EdgeLines.js +125 -8
- package/dist/render/EdgeLines.js.map +1 -1
- package/dist/render/FeatureStyleManager.d.ts +62 -1
- package/dist/render/FeatureStyleManager.d.ts.map +1 -1
- package/dist/render/FeatureStyleManager.js +331 -60
- package/dist/render/FeatureStyleManager.js.map +1 -1
- package/dist/render/MergeVertices.d.ts +7 -0
- package/dist/render/MergeVertices.d.ts.map +1 -1
- package/dist/render/MergeVertices.js +10 -1
- package/dist/render/MergeVertices.js.map +1 -1
- package/dist/render/OrthoProjection.d.ts +6 -0
- package/dist/render/OrthoProjection.d.ts.map +1 -1
- package/dist/render/OrthoProjection.js +48 -0
- package/dist/render/OrthoProjection.js.map +1 -1
- package/dist/render/S3MViewer.d.ts +235 -9
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +880 -68
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.d.ts +28 -0
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +190 -21
- package/dist/render/StyleMaterial.js.map +1 -1
- package/dist/tileset/S3MTileset.d.ts.map +1 -1
- package/dist/tileset/S3MTileset.js +3 -1
- package/dist/tileset/S3MTileset.js.map +1 -1
- package/docs/ANIMATION.md +534 -0
- package/docs/API.md +2 -0
- package/package.json +1 -1
- package/src/anim/Animator.ts +460 -0
- package/src/anim/Path.ts +107 -0
- package/src/anim/easing.ts +195 -0
- package/src/anim/index.ts +10 -0
- package/src/core/types.ts +10 -0
- package/src/index.ts +16 -0
- package/src/render/EdgeLines.ts +161 -15
- package/src/render/FeatureStyleManager.ts +414 -65
- package/src/render/MergeVertices.ts +11 -1
- package/src/render/OrthoProjection.ts +52 -0
- package/src/render/S3MViewer.ts +1012 -64
- package/src/render/StyleMaterial.ts +211 -22
- package/src/tileset/S3MTileset.ts +3 -1
|
@@ -115,8 +115,23 @@ export interface S3MStyle {
|
|
|
115
115
|
roofThreshold?: number;
|
|
116
116
|
/** 是否与模型原色贴图叠加混合(0 = 纯色覆盖,1 = 叠乘原色贴图) */
|
|
117
117
|
texMix?: number;
|
|
118
|
+
/**
|
|
119
|
+
* 基础漫反射底色增益 / 直通自发光微调(0~1,默认 0.0,建议开启 TOP_SIDE 时可设 0.15~0.3)。
|
|
120
|
+
* 绕过光照夹角直接补偿表面暗部底色,让 TOP_SIDE / 自定义等模式下的背光阴影面保持通透、不至于过黑。
|
|
121
|
+
*/
|
|
122
|
+
ambientGain?: number;
|
|
118
123
|
/** 半透明时是否保持深度写入(默认 true,保持写入可彻底遮挡背面与内部棱线,避免背面透出) */
|
|
119
124
|
depthWrite?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* 新瓦片首次出现时的淡入时长(毫秒,0 = 关闭)。
|
|
127
|
+
*
|
|
128
|
+
* 只影响「刚加载进来」的瓦片,已经显示的不会被重播。LOD 细化换瓦片时也会淡入,
|
|
129
|
+
* 视觉上比「啪」一下换掉柔和得多。
|
|
130
|
+
*
|
|
131
|
+
* 注意代价:淡入期间该瓦片会进透明渲染队列(混合 + 前后排序),首次加载
|
|
132
|
+
* 几百块瓦片同时淡入时会有一小段额外开销,所以默认关闭、按需开启。
|
|
133
|
+
*/
|
|
134
|
+
fadeIn?: number;
|
|
120
135
|
}
|
|
121
136
|
|
|
122
137
|
export const DEFAULT_STYLE: S3MStyle = {
|
|
@@ -147,7 +162,9 @@ export const DEFAULT_STYLE: S3MStyle = {
|
|
|
147
162
|
edgeThreshold: 55,
|
|
148
163
|
roofThreshold: 0.65,
|
|
149
164
|
texMix: 0,
|
|
165
|
+
ambientGain: 0,
|
|
150
166
|
depthWrite: true,
|
|
167
|
+
fadeIn: 0,
|
|
151
168
|
};
|
|
152
169
|
|
|
153
170
|
export interface StyledMaterial extends THREE.MeshStandardMaterial {
|
|
@@ -155,15 +172,24 @@ export interface StyledMaterial extends THREE.MeshStandardMaterial {
|
|
|
155
172
|
uSelBatch: { value: number };
|
|
156
173
|
uSelWhole: { value: number };
|
|
157
174
|
uBatchTex: { value: THREE.Texture };
|
|
175
|
+
uBatchTex2: { value: THREE.Texture };
|
|
158
176
|
uBatchTexWidth: { value: number };
|
|
159
177
|
uBatchTexActive: { value: number };
|
|
178
|
+
/** 单块瓦片的淡入系数(1 = 完全显示) */
|
|
179
|
+
uTileFade: { value: number };
|
|
160
180
|
isStyled: true;
|
|
161
181
|
[key: string]: unknown;
|
|
162
182
|
};
|
|
163
183
|
}
|
|
164
184
|
|
|
165
|
-
const DUMMY_DATA = new Float32Array([
|
|
166
|
-
|
|
185
|
+
const DUMMY_DATA = new Float32Array([
|
|
186
|
+
-1, -1, -1, 0, // Row 0: color rgb, elevation/hidden
|
|
187
|
+
0, 0, 0, 0, // Row 1: translation xyz, opacity
|
|
188
|
+
1, 0, 0, 0, // Row 2: rot row 0, pivot.x
|
|
189
|
+
0, 1, 0, 0, // Row 3: rot row 1, pivot.y
|
|
190
|
+
0, 0, 1, 0, // Row 4: rot row 2, pivot.z
|
|
191
|
+
]);
|
|
192
|
+
export const DUMMY_BATCH_TEX = new THREE.DataTexture(DUMMY_DATA, 1, 5, THREE.RGBAFormat, THREE.FloatType);
|
|
167
193
|
DUMMY_BATCH_TEX.minFilter = THREE.NearestFilter;
|
|
168
194
|
DUMMY_BATCH_TEX.magFilter = THREE.NearestFilter;
|
|
169
195
|
DUMMY_BATCH_TEX.needsUpdate = true;
|
|
@@ -205,9 +231,27 @@ export function createSharedUniforms(): StyleUniforms {
|
|
|
205
231
|
uRoofThreshold: { value: 0.65 },
|
|
206
232
|
/** 与模型原色贴图混合强度:0 = 纯色覆盖,1 = 叠乘原色贴图 */
|
|
207
233
|
uTexMix: { value: 0 },
|
|
234
|
+
/** 基础底色增益 / 暗部补偿(0~1,默认 0.0) */
|
|
235
|
+
uAmbientGain: { value: 0 },
|
|
236
|
+
/** 双纹理交叉淡化插值系数(0 = 纯 uBatchTex, 1 = 纯 uBatchTex2) */
|
|
237
|
+
uBatchMix: { value: 0 },
|
|
238
|
+
/**
|
|
239
|
+
* 图层整体透明度:在片元里乘进 diffuseColor.a。
|
|
240
|
+
*
|
|
241
|
+
* 为什么不做成 `material.opacity`:那是逐材质写入,几百块瓦片就是 O(meshes),
|
|
242
|
+
* 而且跨 0.999 改 `material.transparent` 会换 program(`opaque` 参与缓存键)。
|
|
243
|
+
* 放到共享 uniform 里,透明度动画就退化成一次 float 赋值。
|
|
244
|
+
*/
|
|
245
|
+
uOpacity: { value: 1 },
|
|
246
|
+
/** 棱线淡入淡出乘子:setEdges 过渡期间 0→1,新插入的瓦片自动继承 */
|
|
247
|
+
uEdgeFade: { value: 1 },
|
|
248
|
+
/**
|
|
249
|
+
* 整体压暗乘子:`mode` 这类不可插值的切换用它做 fade-through
|
|
250
|
+
* (压暗 → 换模式 → 亮回来),比硬切自然得多。
|
|
251
|
+
*/
|
|
252
|
+
uFade: { value: 1 },
|
|
208
253
|
};
|
|
209
254
|
}
|
|
210
|
-
|
|
211
255
|
const VERT_PARS = /* glsl */ `
|
|
212
256
|
attribute float aBatchId;
|
|
213
257
|
attribute vec3 aColor;
|
|
@@ -229,6 +273,8 @@ const VERT_PARS = /* glsl */ `
|
|
|
229
273
|
uniform float uSelWhole;
|
|
230
274
|
uniform float uSelElevation;
|
|
231
275
|
uniform sampler2D uBatchTex;
|
|
276
|
+
uniform sampler2D uBatchTex2;
|
|
277
|
+
uniform float uBatchMix;
|
|
232
278
|
uniform float uBatchTexWidth;
|
|
233
279
|
uniform int uBatchTexActive;
|
|
234
280
|
varying float vBatchId;
|
|
@@ -237,6 +283,11 @@ const VERT_PARS = /* glsl */ `
|
|
|
237
283
|
varying vec3 vWorldNormal;
|
|
238
284
|
varying vec3 vWorldPos;
|
|
239
285
|
|
|
286
|
+
// 跨 chunk 共享的顶点姿态变量(在 beginnormal_vertex 建立,begin_vertex / worldpos 使用)
|
|
287
|
+
vec3 s3mCustomOffset;
|
|
288
|
+
mat3 s3mRot;
|
|
289
|
+
vec3 s3mPivot;
|
|
290
|
+
|
|
240
291
|
/** 当前图元所属单体的控制位:实例化取实例属性,其余取逐顶点属性 */
|
|
241
292
|
float s3mBatchRaw() {
|
|
242
293
|
#ifdef USE_INSTANCING
|
|
@@ -245,27 +296,85 @@ const VERT_PARS = /* glsl */ `
|
|
|
245
296
|
return aBatchId;
|
|
246
297
|
#endif
|
|
247
298
|
}
|
|
299
|
+
|
|
300
|
+
vec4 s3mSampleBatch( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
301
|
+
vec4 vA = texture2D( texA, uv );
|
|
302
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
303
|
+
vec4 vB = texture2D( texB, uv );
|
|
304
|
+
return mix( vA, vB, mixVal );
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
vec4 s3mSampleBatchRow0( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
308
|
+
vec4 vA = texture2D( texA, uv );
|
|
309
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
310
|
+
vec4 vB = texture2D( texB, uv );
|
|
311
|
+
vec4 res = mix( vA, vB, mixVal );
|
|
312
|
+
if ( vA.r < 0.0 && vB.r >= 0.0 ) {
|
|
313
|
+
res.rgb = vB.rgb;
|
|
314
|
+
} else if ( vA.r >= 0.0 && vB.r < 0.0 ) {
|
|
315
|
+
res.rgb = vA.rgb;
|
|
316
|
+
}
|
|
317
|
+
if ( vA.a < -0.5 || vB.a < -0.5 ) {
|
|
318
|
+
res.a = ( mixVal >= 0.5 ) ? vB.a : vA.a;
|
|
319
|
+
}
|
|
320
|
+
return res;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
vec4 s3mSampleBatchRow1( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
324
|
+
vec4 vA = texture2D( texA, uv );
|
|
325
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
326
|
+
vec4 vB = texture2D( texB, uv );
|
|
327
|
+
vec4 res = mix( vA, vB, mixVal );
|
|
328
|
+
if ( vA.a == 0.0 && vB.a > 0.0 ) {
|
|
329
|
+
res.a = mix( 1.0, vB.a, mixVal );
|
|
330
|
+
} else if ( vA.a > 0.0 && vB.a == 0.0 ) {
|
|
331
|
+
res.a = mix( vA.a, 1.0, mixVal );
|
|
332
|
+
}
|
|
333
|
+
return res;
|
|
334
|
+
}
|
|
248
335
|
`;
|
|
249
336
|
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
337
|
+
const VERT_SETUP = /* glsl */ `
|
|
338
|
+
s3mCustomOffset = vec3( 0.0 );
|
|
339
|
+
s3mRot = mat3( 1.0 );
|
|
340
|
+
s3mPivot = vec3( 0.0 );
|
|
341
|
+
float s3mBatchSetup = s3mBatchRaw();
|
|
342
|
+
if ( uBatchTexActive > 0 && s3mBatchSetup >= 0.0 ) {
|
|
343
|
+
float s3mU = ( s3mBatchSetup + 0.5 ) / uBatchTexWidth;
|
|
344
|
+
vec4 bState = s3mSampleBatchRow0( uBatchTex, uBatchTex2, vec2( s3mU, 0.1 ), uBatchMix );
|
|
345
|
+
if ( bState.a >= -0.5 ) {
|
|
346
|
+
vec4 bOffset = s3mSampleBatchRow1( uBatchTex, uBatchTex2, vec2( s3mU, 0.3 ), uBatchMix );
|
|
259
347
|
s3mCustomOffset = bOffset.xyz;
|
|
260
348
|
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
261
349
|
s3mCustomOffset.z = bState.a;
|
|
262
350
|
}
|
|
351
|
+
vec4 r2 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.5 ), uBatchMix );
|
|
352
|
+
vec4 r3 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.7 ), uBatchMix );
|
|
353
|
+
vec4 r4 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.9 ), uBatchMix );
|
|
354
|
+
s3mRot = mat3(
|
|
355
|
+
vec3( r2.x, r3.x, r4.x ),
|
|
356
|
+
vec3( r2.y, r3.y, r4.y ),
|
|
357
|
+
vec3( r2.z, r3.z, r4.z )
|
|
358
|
+
);
|
|
359
|
+
s3mPivot = vec3( r2.w, r3.w, r4.w );
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
objectNormal = normalize( s3mRot * objectNormal );
|
|
363
|
+
`;
|
|
364
|
+
|
|
365
|
+
const VERT_ELEVATION = /* glsl */ `
|
|
366
|
+
float s3mBatchElev = s3mBatchRaw();
|
|
367
|
+
if ( uBatchTexActive > 0 && s3mBatchElev >= 0.0 ) {
|
|
368
|
+
float s3mU = ( s3mBatchElev + 0.5 ) / uBatchTexWidth;
|
|
369
|
+
vec4 bState = s3mSampleBatchRow0( uBatchTex, uBatchTex2, vec2( s3mU, 0.1 ), uBatchMix );
|
|
370
|
+
if ( bState.a < -0.5 ) {
|
|
371
|
+
transformed = vec3( 0.0 );
|
|
263
372
|
}
|
|
264
373
|
}
|
|
265
374
|
|
|
266
|
-
float s3mSelElev = max( step( 0.0, uSelBatch ) * step( abs(
|
|
267
|
-
//
|
|
268
|
-
transformed
|
|
375
|
+
float s3mSelElev = max( step( 0.0, uSelBatch ) * step( abs( s3mBatchElev - uSelBatch ), 0.5 ), uSelWhole );
|
|
376
|
+
// 刚体旋转以 s3mPivot 为中心,再叠加自定义平移与选中拔高
|
|
377
|
+
transformed = s3mRot * ( transformed - s3mPivot ) + s3mPivot + s3mCustomOffset;
|
|
269
378
|
transformed.z += s3mSelElev * uSelElevation;
|
|
270
379
|
`;
|
|
271
380
|
|
|
@@ -293,7 +402,7 @@ const VERT_WORLD = /* glsl */ `
|
|
|
293
402
|
#else
|
|
294
403
|
mat3 s3mNormMat = mat3( modelMatrix );
|
|
295
404
|
#endif
|
|
296
|
-
vWorldNormal = normalize( s3mNormMat * normal );
|
|
405
|
+
vWorldNormal = normalize( s3mNormMat * ( s3mRot * normal ) );
|
|
297
406
|
`;
|
|
298
407
|
|
|
299
408
|
const FRAG_PARS = /* glsl */ `
|
|
@@ -376,6 +485,8 @@ const FRAG_PARS = /* glsl */ `
|
|
|
376
485
|
uniform float uSelEmissive;
|
|
377
486
|
uniform float uDimFactor;
|
|
378
487
|
uniform sampler2D uBatchTex;
|
|
488
|
+
uniform sampler2D uBatchTex2;
|
|
489
|
+
uniform float uBatchMix;
|
|
379
490
|
uniform float uBatchTexWidth;
|
|
380
491
|
uniform int uBatchTexActive;
|
|
381
492
|
uniform float uFilterMinZ;
|
|
@@ -391,12 +502,45 @@ const FRAG_PARS = /* glsl */ `
|
|
|
391
502
|
uniform float uSelWhole;
|
|
392
503
|
uniform float uRoofThreshold;
|
|
393
504
|
uniform float uTexMix;
|
|
505
|
+
uniform float uAmbientGain;
|
|
506
|
+
uniform float uOpacity;
|
|
507
|
+
uniform float uTileFade;
|
|
508
|
+
uniform float uFade;
|
|
394
509
|
varying float vBatchId;
|
|
395
510
|
varying vec3 vVertColor;
|
|
396
511
|
varying float vWorldZ;
|
|
397
512
|
varying vec3 vWorldNormal;
|
|
398
513
|
varying vec3 vWorldPos;
|
|
399
514
|
|
|
515
|
+
vec4 s3mSampleBatchRow0( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
516
|
+
vec4 vA = texture2D( texA, uv );
|
|
517
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
518
|
+
vec4 vB = texture2D( texB, uv );
|
|
519
|
+
vec4 res = mix( vA, vB, mixVal );
|
|
520
|
+
if ( vA.r < 0.0 && vB.r >= 0.0 ) {
|
|
521
|
+
res.rgb = vB.rgb;
|
|
522
|
+
} else if ( vA.r >= 0.0 && vB.r < 0.0 ) {
|
|
523
|
+
res.rgb = vA.rgb;
|
|
524
|
+
}
|
|
525
|
+
if ( vA.a < -0.5 || vB.a < -0.5 ) {
|
|
526
|
+
res.a = ( mixVal >= 0.5 ) ? vB.a : vA.a;
|
|
527
|
+
}
|
|
528
|
+
return res;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
vec4 s3mSampleBatchRow1( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
532
|
+
vec4 vA = texture2D( texA, uv );
|
|
533
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
534
|
+
vec4 vB = texture2D( texB, uv );
|
|
535
|
+
vec4 res = mix( vA, vB, mixVal );
|
|
536
|
+
if ( vA.a == 0.0 && vB.a > 0.0 ) {
|
|
537
|
+
res.a = mix( 1.0, vB.a, mixVal );
|
|
538
|
+
} else if ( vA.a > 0.0 && vB.a == 0.0 ) {
|
|
539
|
+
res.a = mix( vA.a, 1.0, mixVal );
|
|
540
|
+
}
|
|
541
|
+
return res;
|
|
542
|
+
}
|
|
543
|
+
|
|
400
544
|
float s3mHash( float p ) {
|
|
401
545
|
p = fract( p * 0.1031 );
|
|
402
546
|
p *= p + 33.33;
|
|
@@ -430,6 +574,8 @@ const FRAG_PARS = /* glsl */ `
|
|
|
430
574
|
const FRAG_COLOR = /* glsl */ `
|
|
431
575
|
float s3mH = clamp( ( vWorldZ - uHeightMin ) / max( uHeightMax - uHeightMin, 0.001 ), 0.0, 1.0 );
|
|
432
576
|
vec3 s3mBase = diffuseColor.rgb; // 已含 diffuse × map
|
|
577
|
+
// 图层整体透明度(共享 uniform)+ 单瓦片淡入系数(逐材质),逐帧改都不产生重编译
|
|
578
|
+
diffuseColor.a *= uOpacity * uTileFade;
|
|
433
579
|
// 所有按单体取值(配色 / 状态纹理 / 选中判定)都走取整后的 id,避免插值噪声
|
|
434
580
|
float s3mBid = s3mBatchId();
|
|
435
581
|
|
|
@@ -490,7 +636,8 @@ const FRAG_COLOR = /* glsl */ `
|
|
|
490
636
|
bool hasCustomColor = false;
|
|
491
637
|
if ( uBatchTexActive > 0 && s3mBid >= 0.0 ) {
|
|
492
638
|
// 用取整后的 id 采样:否则逐像素的插值误差会让纹素在边界上来回跳,同样出彩色噪点
|
|
493
|
-
|
|
639
|
+
float s3mU = ( s3mBid + 0.5 ) / uBatchTexWidth;
|
|
640
|
+
vec4 bState = s3mSampleBatchRow0( uBatchTex, uBatchTex2, vec2( s3mU, 0.1 ), uBatchMix );
|
|
494
641
|
if ( bState.a < -0.5 ) {
|
|
495
642
|
discard; // 属于被过滤隐藏的单体
|
|
496
643
|
}
|
|
@@ -501,7 +648,7 @@ const FRAG_COLOR = /* glsl */ `
|
|
|
501
648
|
}
|
|
502
649
|
// 单体独立透明度(状态纹理第 2 行 alpha,0 = 无覆盖)。
|
|
503
650
|
// 与全局 uOpacity(diffuseColor.a 初值)和图集贴图自带 alpha 逐级叠乘
|
|
504
|
-
vec4 bOffset =
|
|
651
|
+
vec4 bOffset = s3mSampleBatchRow1( uBatchTex, uBatchTex2, vec2( s3mU, 0.3 ), uBatchMix );
|
|
505
652
|
if ( bOffset.a > 0.0 ) {
|
|
506
653
|
diffuseColor.a *= bOffset.a;
|
|
507
654
|
}
|
|
@@ -531,6 +678,10 @@ const FRAG_COLOR = /* glsl */ `
|
|
|
531
678
|
} else {
|
|
532
679
|
diffuseColor.rgb *= mix( 1.0, uDimFactor, uDim );
|
|
533
680
|
}
|
|
681
|
+
|
|
682
|
+
// 压暗乘子放在最后:mode 分支会整体覆写 diffuseColor.rgb,
|
|
683
|
+
// 放在前面会被覆盖掉。setMode 的 fade-through 就靠它。
|
|
684
|
+
diffuseColor.rgb *= uFade;
|
|
534
685
|
`;
|
|
535
686
|
|
|
536
687
|
const FRAG_EMISSIVE = /* glsl */ `
|
|
@@ -539,6 +690,10 @@ const FRAG_EMISSIVE = /* glsl */ `
|
|
|
539
690
|
if ( uSelMode != 3 ) {
|
|
540
691
|
totalEmissiveRadiance += uSelColor * s3mSelE * uSelMix * ( uSelEmissive * 0.4 );
|
|
541
692
|
}
|
|
693
|
+
// 直通自发光微调 / 环境光底色提升:补偿背光阴影面(尤其 TOP_SIDE 立面),避免光照阴影面过暗
|
|
694
|
+
if ( uAmbientGain > 0.001 ) {
|
|
695
|
+
totalEmissiveRadiance += diffuseColor.rgb * uAmbientGain;
|
|
696
|
+
}
|
|
542
697
|
`;
|
|
543
698
|
|
|
544
699
|
const FRAG_BAKE = /* glsl */ `
|
|
@@ -594,8 +749,11 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
594
749
|
// 没有单体化信息的几何(batchId = -1)只能整块选中
|
|
595
750
|
const uSelWhole = { value: 0 };
|
|
596
751
|
const uBatchTex = { value: DUMMY_BATCH_TEX };
|
|
752
|
+
const uBatchTex2 = { value: DUMMY_BATCH_TEX };
|
|
597
753
|
const uBatchTexWidth = { value: 1.0 };
|
|
598
754
|
const uBatchTexActive = { value: 0 };
|
|
755
|
+
/** 单块瓦片的淡入系数:逐材质独享(每块网格一份材质),默认 1 = 完全显示 */
|
|
756
|
+
const uTileFade = { value: 1 };
|
|
599
757
|
|
|
600
758
|
// 图集材质:只有几何确实带了 aAtlasRect 才开这个宏,否则缺属性会采到 (0,0) 的默认值
|
|
601
759
|
const atlas = !!opts.atlas && !!opts.map;
|
|
@@ -611,8 +769,10 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
611
769
|
uSelBatch,
|
|
612
770
|
uSelWhole,
|
|
613
771
|
uBatchTex,
|
|
772
|
+
uBatchTex2,
|
|
614
773
|
uBatchTexWidth,
|
|
615
774
|
uBatchTexActive,
|
|
775
|
+
uTileFade,
|
|
616
776
|
uSecMap: { value: secMap },
|
|
617
777
|
uAtlasDim,
|
|
618
778
|
uAtlasDim1,
|
|
@@ -620,6 +780,7 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
620
780
|
|
|
621
781
|
shader.vertexShader = shader.vertexShader
|
|
622
782
|
.replace('#include <common>', `#include <common>\n${VERT_PARS}`)
|
|
783
|
+
.replace('#include <beginnormal_vertex>', `#include <beginnormal_vertex>\n${VERT_SETUP}`)
|
|
623
784
|
.replace('#include <begin_vertex>', `#include <begin_vertex>\n${VERT_ELEVATION}`)
|
|
624
785
|
.replace('#include <worldpos_vertex>', VERT_WORLD);
|
|
625
786
|
|
|
@@ -654,8 +815,10 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
654
815
|
mat.userData.uSelBatch = uSelBatch;
|
|
655
816
|
mat.userData.uSelWhole = uSelWhole;
|
|
656
817
|
mat.userData.uBatchTex = uBatchTex;
|
|
818
|
+
mat.userData.uBatchTex2 = uBatchTex2;
|
|
657
819
|
mat.userData.uBatchTexWidth = uBatchTexWidth;
|
|
658
820
|
mat.userData.uBatchTexActive = uBatchTexActive;
|
|
821
|
+
mat.userData.uTileFade = uTileFade;
|
|
659
822
|
mat.userData.isStyled = true;
|
|
660
823
|
return mat;
|
|
661
824
|
}
|
|
@@ -669,26 +832,48 @@ const OUTLINE_VERT = /* glsl */ `
|
|
|
669
832
|
uniform float uWidth;
|
|
670
833
|
uniform float uElevation;
|
|
671
834
|
uniform sampler2D uBatchTex;
|
|
835
|
+
uniform sampler2D uBatchTex2;
|
|
836
|
+
uniform float uBatchMix;
|
|
672
837
|
uniform float uBatchTexWidth;
|
|
673
838
|
uniform int uBatchTexActive;
|
|
674
839
|
varying float vSel;
|
|
840
|
+
|
|
841
|
+
vec4 s3mSampleBatch( sampler2D texA, sampler2D texB, vec2 uv, float mixVal ) {
|
|
842
|
+
vec4 vA = texture2D( texA, uv );
|
|
843
|
+
if ( mixVal <= 0.0001 ) return vA;
|
|
844
|
+
vec4 vB = texture2D( texB, uv );
|
|
845
|
+
return mix( vA, vB, mixVal );
|
|
846
|
+
}
|
|
847
|
+
|
|
675
848
|
void main() {
|
|
676
849
|
vSel = max( step( 0.0, uSelBatch ) * step( abs( aBatchId - uSelBatch ), 0.5 ), uSelWhole );
|
|
677
850
|
vec3 s3mCustomOffset = vec3( 0.0 );
|
|
851
|
+
mat3 s3mRot = mat3( 1.0 );
|
|
852
|
+
vec3 s3mPivot = vec3( 0.0 );
|
|
678
853
|
if ( uBatchTexActive > 0 && aBatchId >= 0.0 ) {
|
|
679
|
-
|
|
854
|
+
float s3mU = ( aBatchId + 0.5 ) / uBatchTexWidth;
|
|
855
|
+
vec4 bState = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.1 ), uBatchMix );
|
|
680
856
|
if ( bState.a < -0.5 ) {
|
|
681
857
|
vSel = 0.0; // 过滤隐藏的单体不显示描边
|
|
682
858
|
} else {
|
|
683
|
-
vec4 bOffset =
|
|
859
|
+
vec4 bOffset = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.3 ), uBatchMix );
|
|
684
860
|
s3mCustomOffset = bOffset.xyz;
|
|
685
861
|
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
686
862
|
s3mCustomOffset.z = bState.a;
|
|
687
863
|
}
|
|
864
|
+
vec4 r2 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.5 ), uBatchMix );
|
|
865
|
+
vec4 r3 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.7 ), uBatchMix );
|
|
866
|
+
vec4 r4 = s3mSampleBatch( uBatchTex, uBatchTex2, vec2( s3mU, 0.9 ), uBatchMix );
|
|
867
|
+
s3mRot = mat3(
|
|
868
|
+
vec3( r2.x, r3.x, r4.x ),
|
|
869
|
+
vec3( r2.y, r3.y, r4.y ),
|
|
870
|
+
vec3( r2.z, r3.z, r4.z )
|
|
871
|
+
);
|
|
872
|
+
s3mPivot = vec3( r2.w, r3.w, r4.w );
|
|
688
873
|
}
|
|
689
874
|
}
|
|
690
|
-
vec3 p = position + normal * uWidth * vSel;
|
|
691
|
-
p
|
|
875
|
+
vec3 p = position + ( s3mRot * normal ) * uWidth * vSel;
|
|
876
|
+
p = s3mRot * ( p - s3mPivot ) + s3mPivot + s3mCustomOffset;
|
|
692
877
|
p.z += vSel * uElevation;
|
|
693
878
|
gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
|
|
694
879
|
}
|
|
@@ -710,6 +895,8 @@ export interface OutlineMaterial extends THREE.ShaderMaterial {
|
|
|
710
895
|
uWidth: { value: number };
|
|
711
896
|
uElevation: { value: number };
|
|
712
897
|
uBatchTex: { value: THREE.Texture };
|
|
898
|
+
uBatchTex2: { value: THREE.Texture };
|
|
899
|
+
uBatchMix: { value: number };
|
|
713
900
|
uBatchTexWidth: { value: number };
|
|
714
901
|
uBatchTexActive: { value: number };
|
|
715
902
|
uColor: { value: THREE.Color };
|
|
@@ -725,6 +912,8 @@ export function createOutlineMaterial(shared: StyleUniforms, initialWidth = 0.9)
|
|
|
725
912
|
uWidth: { value: initialWidth },
|
|
726
913
|
uElevation: { value: 0 },
|
|
727
914
|
uBatchTex: { value: DUMMY_BATCH_TEX },
|
|
915
|
+
uBatchTex2: { value: DUMMY_BATCH_TEX },
|
|
916
|
+
uBatchMix: shared.uBatchMix ?? { value: 0 },
|
|
728
917
|
uBatchTexWidth: { value: 1.0 },
|
|
729
918
|
uBatchTexActive: { value: 0 },
|
|
730
919
|
uColor: { value: shared.uSelColor.value.clone() },
|
|
@@ -1178,7 +1178,9 @@ export class S3MTileset {
|
|
|
1178
1178
|
if (inRange) node.lastSeen = this._frame;
|
|
1179
1179
|
|
|
1180
1180
|
const childUrls = node.childUrls ?? (node.childUrl ? [node.childUrl] : []);
|
|
1181
|
-
|
|
1181
|
+
// 加入 8% 迟滞区间(Hysteresis Band),防止相机在 LOD 临界距离处发生父子瓦片逐帧来回横跳闪烁
|
|
1182
|
+
const hFactor = node.needRefine ? (byDistance ? 1.08 : 0.92) : 1.0;
|
|
1183
|
+
const needRefine = childUrls.length > 0 && (byDistance ? metric < threshold * hFactor : metric > threshold * hFactor);
|
|
1182
1184
|
node.needRefine = needRefine;
|
|
1183
1185
|
|
|
1184
1186
|
const dbgSeen = ((globalThis as { __S3M_LOD_SEEN?: Set<string> }).__S3M_LOD_SEEN ??= new Set<string>());
|