s3m-three 0.1.0 → 0.2.0
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/LICENSE +202 -202
- package/dist/render/DatasetManager.d.ts +10 -3
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +14 -5
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/EdgeLines.js +46 -46
- package/dist/render/GroundManager.d.ts +9 -0
- package/dist/render/GroundManager.d.ts.map +1 -1
- package/dist/render/GroundManager.js +13 -1
- package/dist/render/GroundManager.js.map +1 -1
- package/dist/render/Picker.js +66 -66
- package/dist/render/S3MViewer.d.ts +11 -3
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +18 -3
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.js +265 -265
- package/dist/vendor/meshopt_decoder.module.js +118 -118
- package/docs/API.md +20 -0
- package/package.json +1 -1
- package/src/core/BinaryReader.ts +116 -116
- package/src/core/DXTTextureDecode.ts +260 -260
- package/src/core/FeatureInfo.ts +150 -150
- package/src/core/MeshOpt.ts +401 -401
- package/src/core/S3MBParser.ts +590 -590
- package/src/core/types.ts +222 -222
- package/src/render/DatasetManager.ts +17 -6
- package/src/render/EdgeLines.ts +343 -343
- package/src/render/GeometryBuilder.ts +840 -840
- package/src/render/GroundManager.ts +14 -1
- package/src/render/Picker.ts +293 -293
- package/src/render/S3MViewer.ts +18 -3
- package/src/render/StyleMaterial.ts +602 -602
- package/src/render/TextureFactory.ts +134 -134
- package/src/tileset/S3MTileset.ts +552 -552
- package/src/vendor/meshopt_decoder.module.d.ts +31 -31
- package/src/vendor/meshopt_decoder.module.js +118 -118
- package/dist/render/CoordinateHelper.d.ts +0 -90
- package/dist/render/CoordinateHelper.d.ts.map +0 -1
- package/dist/render/CoordinateHelper.js +0 -85
- package/dist/render/CoordinateHelper.js.map +0 -1
- package/dist/render/S3MLayer.d.ts +0 -457
- package/dist/render/S3MLayer.d.ts.map +0 -1
- package/dist/render/S3MLayer.js +0 -1860
- package/dist/render/S3MLayer.js.map +0 -1
|
@@ -123,239 +123,239 @@ export function createSharedUniforms() {
|
|
|
123
123
|
uTexMix: { value: 0 },
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
|
-
const VERT_PARS = /* glsl */ `
|
|
127
|
-
attribute float aBatchId;
|
|
128
|
-
attribute vec3 aColor;
|
|
129
|
-
// 实例化几何没有逐顶点 batchId:顶点是 base mesh 的,单体粒度落在实例上
|
|
130
|
-
#ifdef USE_INSTANCING
|
|
131
|
-
attribute float aInstanceBatchId;
|
|
132
|
-
#endif
|
|
133
|
-
uniform float uSelBatch;
|
|
134
|
-
uniform float uSelWhole;
|
|
135
|
-
uniform float uSelElevation;
|
|
136
|
-
uniform sampler2D uBatchTex;
|
|
137
|
-
uniform float uBatchTexWidth;
|
|
138
|
-
uniform int uBatchTexActive;
|
|
139
|
-
varying float vBatchId;
|
|
140
|
-
varying vec3 vVertColor;
|
|
141
|
-
varying float vWorldZ;
|
|
142
|
-
varying vec3 vWorldNormal;
|
|
143
|
-
varying vec3 vWorldPos;
|
|
144
|
-
|
|
145
|
-
/** 当前图元所属单体的控制位:实例化取实例属性,其余取逐顶点属性 */
|
|
146
|
-
float s3mBatchRaw() {
|
|
147
|
-
#ifdef USE_INSTANCING
|
|
148
|
-
return aInstanceBatchId;
|
|
149
|
-
#else
|
|
150
|
-
return aBatchId;
|
|
151
|
-
#endif
|
|
152
|
-
}
|
|
126
|
+
const VERT_PARS = /* glsl */ `
|
|
127
|
+
attribute float aBatchId;
|
|
128
|
+
attribute vec3 aColor;
|
|
129
|
+
// 实例化几何没有逐顶点 batchId:顶点是 base mesh 的,单体粒度落在实例上
|
|
130
|
+
#ifdef USE_INSTANCING
|
|
131
|
+
attribute float aInstanceBatchId;
|
|
132
|
+
#endif
|
|
133
|
+
uniform float uSelBatch;
|
|
134
|
+
uniform float uSelWhole;
|
|
135
|
+
uniform float uSelElevation;
|
|
136
|
+
uniform sampler2D uBatchTex;
|
|
137
|
+
uniform float uBatchTexWidth;
|
|
138
|
+
uniform int uBatchTexActive;
|
|
139
|
+
varying float vBatchId;
|
|
140
|
+
varying vec3 vVertColor;
|
|
141
|
+
varying float vWorldZ;
|
|
142
|
+
varying vec3 vWorldNormal;
|
|
143
|
+
varying vec3 vWorldPos;
|
|
144
|
+
|
|
145
|
+
/** 当前图元所属单体的控制位:实例化取实例属性,其余取逐顶点属性 */
|
|
146
|
+
float s3mBatchRaw() {
|
|
147
|
+
#ifdef USE_INSTANCING
|
|
148
|
+
return aInstanceBatchId;
|
|
149
|
+
#else
|
|
150
|
+
return aBatchId;
|
|
151
|
+
#endif
|
|
152
|
+
}
|
|
153
153
|
`;
|
|
154
|
-
const VERT_ELEVATION = /* glsl */ `
|
|
155
|
-
float s3mBatch = s3mBatchRaw();
|
|
156
|
-
vec3 s3mCustomOffset = vec3( 0.0 );
|
|
157
|
-
if ( uBatchTexActive > 0 && s3mBatch >= 0.0 ) {
|
|
158
|
-
vec4 bState = texture2D( uBatchTex, vec2( ( s3mBatch + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
159
|
-
if ( bState.a < -0.5 ) {
|
|
160
|
-
transformed = vec3( 0.0 );
|
|
161
|
-
} else {
|
|
162
|
-
vec4 bOffset = texture2D( uBatchTex, vec2( ( s3mBatch + 0.5 ) / uBatchTexWidth, 0.75 ) );
|
|
163
|
-
s3mCustomOffset = bOffset.xyz;
|
|
164
|
-
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
165
|
-
s3mCustomOffset.z = bState.a;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
float s3mSelElev = max( step( 0.0, uSelBatch ) * step( abs( s3mBatch - uSelBatch ), 0.5 ), uSelWhole );
|
|
171
|
-
// 实例化时这是局部坐标,实例矩阵只含平移 + 绕 z 旋转(白模摆位),所以偏移仍沿局部轴生效
|
|
172
|
-
transformed += s3mCustomOffset;
|
|
173
|
-
transformed.z += s3mSelElev * uSelElevation;
|
|
154
|
+
const VERT_ELEVATION = /* glsl */ `
|
|
155
|
+
float s3mBatch = s3mBatchRaw();
|
|
156
|
+
vec3 s3mCustomOffset = vec3( 0.0 );
|
|
157
|
+
if ( uBatchTexActive > 0 && s3mBatch >= 0.0 ) {
|
|
158
|
+
vec4 bState = texture2D( uBatchTex, vec2( ( s3mBatch + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
159
|
+
if ( bState.a < -0.5 ) {
|
|
160
|
+
transformed = vec3( 0.0 );
|
|
161
|
+
} else {
|
|
162
|
+
vec4 bOffset = texture2D( uBatchTex, vec2( ( s3mBatch + 0.5 ) / uBatchTexWidth, 0.75 ) );
|
|
163
|
+
s3mCustomOffset = bOffset.xyz;
|
|
164
|
+
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
165
|
+
s3mCustomOffset.z = bState.a;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
float s3mSelElev = max( step( 0.0, uSelBatch ) * step( abs( s3mBatch - uSelBatch ), 0.5 ), uSelWhole );
|
|
171
|
+
// 实例化时这是局部坐标,实例矩阵只含平移 + 绕 z 旋转(白模摆位),所以偏移仍沿局部轴生效
|
|
172
|
+
transformed += s3mCustomOffset;
|
|
173
|
+
transformed.z += s3mSelElev * uSelElevation;
|
|
174
174
|
`;
|
|
175
|
-
const VERT_WORLD = /* glsl */ `
|
|
176
|
-
vec4 s3mWorld = vec4( transformed, 1.0 );
|
|
177
|
-
#ifdef USE_INSTANCING
|
|
178
|
-
s3mWorld = instanceMatrix * s3mWorld;
|
|
179
|
-
#endif
|
|
180
|
-
s3mWorld = modelMatrix * s3mWorld;
|
|
181
|
-
vWorldZ = s3mWorld.z;
|
|
182
|
-
vWorldPos = s3mWorld.xyz;
|
|
183
|
-
vBatchId = s3mBatchRaw();
|
|
184
|
-
vVertColor = aColor;
|
|
185
|
-
|
|
186
|
-
#ifdef USE_INSTANCING
|
|
187
|
-
mat3 s3mNormMat = mat3( modelMatrix ) * mat3( instanceMatrix );
|
|
188
|
-
#else
|
|
189
|
-
mat3 s3mNormMat = mat3( modelMatrix );
|
|
190
|
-
#endif
|
|
191
|
-
vWorldNormal = normalize( s3mNormMat * normal );
|
|
175
|
+
const VERT_WORLD = /* glsl */ `
|
|
176
|
+
vec4 s3mWorld = vec4( transformed, 1.0 );
|
|
177
|
+
#ifdef USE_INSTANCING
|
|
178
|
+
s3mWorld = instanceMatrix * s3mWorld;
|
|
179
|
+
#endif
|
|
180
|
+
s3mWorld = modelMatrix * s3mWorld;
|
|
181
|
+
vWorldZ = s3mWorld.z;
|
|
182
|
+
vWorldPos = s3mWorld.xyz;
|
|
183
|
+
vBatchId = s3mBatchRaw();
|
|
184
|
+
vVertColor = aColor;
|
|
185
|
+
|
|
186
|
+
#ifdef USE_INSTANCING
|
|
187
|
+
mat3 s3mNormMat = mat3( modelMatrix ) * mat3( instanceMatrix );
|
|
188
|
+
#else
|
|
189
|
+
mat3 s3mNormMat = mat3( modelMatrix );
|
|
190
|
+
#endif
|
|
191
|
+
vWorldNormal = normalize( s3mNormMat * normal );
|
|
192
192
|
`;
|
|
193
|
-
const FRAG_PARS = /* glsl */ `
|
|
194
|
-
uniform int uMode;
|
|
195
|
-
uniform vec3 uColorA;
|
|
196
|
-
uniform vec3 uColorB;
|
|
197
|
-
uniform vec3 uColorC;
|
|
198
|
-
uniform vec3 uSelColor;
|
|
199
|
-
uniform int uSelMode;
|
|
200
|
-
uniform float uSelOpacity;
|
|
201
|
-
uniform float uSelEmissive;
|
|
202
|
-
uniform float uDimFactor;
|
|
203
|
-
uniform sampler2D uBatchTex;
|
|
204
|
-
uniform float uBatchTexWidth;
|
|
205
|
-
uniform int uBatchTexActive;
|
|
206
|
-
uniform float uFilterMinZ;
|
|
207
|
-
uniform float uFilterMaxZ;
|
|
208
|
-
uniform int uFilterHeightActive;
|
|
209
|
-
uniform float uTime;
|
|
210
|
-
uniform float uHeightMin;
|
|
211
|
-
uniform float uHeightMax;
|
|
212
|
-
uniform float uFloorHeight;
|
|
213
|
-
uniform float uDim;
|
|
214
|
-
uniform float uSelMix;
|
|
215
|
-
uniform float uSelBatch;
|
|
216
|
-
uniform float uSelWhole;
|
|
217
|
-
uniform float uRoofThreshold;
|
|
218
|
-
uniform float uTexMix;
|
|
219
|
-
varying float vBatchId;
|
|
220
|
-
varying vec3 vVertColor;
|
|
221
|
-
varying float vWorldZ;
|
|
222
|
-
varying vec3 vWorldNormal;
|
|
223
|
-
varying vec3 vWorldPos;
|
|
224
|
-
|
|
225
|
-
float s3mHash( float p ) {
|
|
226
|
-
p = fract( p * 0.1031 );
|
|
227
|
-
p *= p + 33.33;
|
|
228
|
-
p *= p + p;
|
|
229
|
-
return fract( p );
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* 取整后的 batchId。
|
|
234
|
-
*
|
|
235
|
-
* vBatchId 是 varying:GPU 按像素做透视校正插值,即便一个三角形三个顶点的
|
|
236
|
-
* aBatchId 完全相等,三次乘加各自的舍入误差也不会正好抵消,结果会带
|
|
237
|
-
* 1e-7 量级的相对误差,而且逐像素不同(不同 GPU / 驱动还不一样)。
|
|
238
|
-
*
|
|
239
|
-
* 这个误差本身无害,但后面的哈希是混沌放大器:p → fract(p*0.1031) →
|
|
240
|
-
* p*(p+33.33) → p*2p,每一级都把误差放大几十倍,三级下来 1e-7 就能放大到 O(1)。
|
|
241
|
-
* 实测 batchId≈4.2 万时,1e-7 的抖动会让哈希输出在 0.20~0.78 之间乱跳,
|
|
242
|
-
* 直接表现为整屏彩色噪点。所以凡是要「按 id 取值」的地方都必须先取整。
|
|
243
|
-
*/
|
|
244
|
-
float s3mBatchId() {
|
|
245
|
-
return floor( vBatchId + 0.5 );
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
vec3 s3mHsv( float h, float s, float v ) {
|
|
249
|
-
vec3 k = vec3( 1.0, 2.0 / 3.0, 1.0 / 3.0 );
|
|
250
|
-
vec3 p = abs( fract( vec3( h ) + k.xyz ) * 6.0 - vec3( 3.0 ) );
|
|
251
|
-
return v * mix( vec3( 1.0 ), clamp( p - vec3( 1.0 ), 0.0, 1.0 ), s );
|
|
252
|
-
}
|
|
193
|
+
const FRAG_PARS = /* glsl */ `
|
|
194
|
+
uniform int uMode;
|
|
195
|
+
uniform vec3 uColorA;
|
|
196
|
+
uniform vec3 uColorB;
|
|
197
|
+
uniform vec3 uColorC;
|
|
198
|
+
uniform vec3 uSelColor;
|
|
199
|
+
uniform int uSelMode;
|
|
200
|
+
uniform float uSelOpacity;
|
|
201
|
+
uniform float uSelEmissive;
|
|
202
|
+
uniform float uDimFactor;
|
|
203
|
+
uniform sampler2D uBatchTex;
|
|
204
|
+
uniform float uBatchTexWidth;
|
|
205
|
+
uniform int uBatchTexActive;
|
|
206
|
+
uniform float uFilterMinZ;
|
|
207
|
+
uniform float uFilterMaxZ;
|
|
208
|
+
uniform int uFilterHeightActive;
|
|
209
|
+
uniform float uTime;
|
|
210
|
+
uniform float uHeightMin;
|
|
211
|
+
uniform float uHeightMax;
|
|
212
|
+
uniform float uFloorHeight;
|
|
213
|
+
uniform float uDim;
|
|
214
|
+
uniform float uSelMix;
|
|
215
|
+
uniform float uSelBatch;
|
|
216
|
+
uniform float uSelWhole;
|
|
217
|
+
uniform float uRoofThreshold;
|
|
218
|
+
uniform float uTexMix;
|
|
219
|
+
varying float vBatchId;
|
|
220
|
+
varying vec3 vVertColor;
|
|
221
|
+
varying float vWorldZ;
|
|
222
|
+
varying vec3 vWorldNormal;
|
|
223
|
+
varying vec3 vWorldPos;
|
|
224
|
+
|
|
225
|
+
float s3mHash( float p ) {
|
|
226
|
+
p = fract( p * 0.1031 );
|
|
227
|
+
p *= p + 33.33;
|
|
228
|
+
p *= p + p;
|
|
229
|
+
return fract( p );
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 取整后的 batchId。
|
|
234
|
+
*
|
|
235
|
+
* vBatchId 是 varying:GPU 按像素做透视校正插值,即便一个三角形三个顶点的
|
|
236
|
+
* aBatchId 完全相等,三次乘加各自的舍入误差也不会正好抵消,结果会带
|
|
237
|
+
* 1e-7 量级的相对误差,而且逐像素不同(不同 GPU / 驱动还不一样)。
|
|
238
|
+
*
|
|
239
|
+
* 这个误差本身无害,但后面的哈希是混沌放大器:p → fract(p*0.1031) →
|
|
240
|
+
* p*(p+33.33) → p*2p,每一级都把误差放大几十倍,三级下来 1e-7 就能放大到 O(1)。
|
|
241
|
+
* 实测 batchId≈4.2 万时,1e-7 的抖动会让哈希输出在 0.20~0.78 之间乱跳,
|
|
242
|
+
* 直接表现为整屏彩色噪点。所以凡是要「按 id 取值」的地方都必须先取整。
|
|
243
|
+
*/
|
|
244
|
+
float s3mBatchId() {
|
|
245
|
+
return floor( vBatchId + 0.5 );
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
vec3 s3mHsv( float h, float s, float v ) {
|
|
249
|
+
vec3 k = vec3( 1.0, 2.0 / 3.0, 1.0 / 3.0 );
|
|
250
|
+
vec3 p = abs( fract( vec3( h ) + k.xyz ) * 6.0 - vec3( 3.0 ) );
|
|
251
|
+
return v * mix( vec3( 1.0 ), clamp( p - vec3( 1.0 ), 0.0, 1.0 ), s );
|
|
252
|
+
}
|
|
253
253
|
`;
|
|
254
|
-
const FRAG_COLOR = /* glsl */ `
|
|
255
|
-
float s3mH = clamp( ( vWorldZ - uHeightMin ) / max( uHeightMax - uHeightMin, 0.001 ), 0.0, 1.0 );
|
|
256
|
-
vec3 s3mBase = diffuseColor.rgb; // 已含 diffuse × map
|
|
257
|
-
// 所有按单体取值(配色 / 状态纹理 / 选中判定)都走取整后的 id,避免插值噪声
|
|
258
|
-
float s3mBid = s3mBatchId();
|
|
259
|
-
|
|
260
|
-
// 法线:优先使用顶点插值法线;若退化或为平面着色,使用屏幕导数精确计算真实几何法线
|
|
261
|
-
vec3 s3mNorm = length( vWorldNormal ) > 0.1 ? normalize( vWorldNormal ) : normalize( cross( dFdx( vWorldPos ), dFdy( vWorldPos ) ) );
|
|
262
|
-
|
|
263
|
-
if ( uMode == 0 ) {
|
|
264
|
-
// 白膜:丢掉贴图,只留光照
|
|
265
|
-
diffuseColor.rgb = uColorA;
|
|
266
|
-
} else if ( uMode == 1 ) {
|
|
267
|
-
// 原色:贴图 × 顶点色
|
|
268
|
-
diffuseColor.rgb = s3mBase * vVertColor;
|
|
269
|
-
} else if ( uMode == 2 ) {
|
|
270
|
-
diffuseColor.rgb = uColorA;
|
|
271
|
-
} else if ( uMode == 3 ) {
|
|
272
|
-
// 按高度连续渐变
|
|
273
|
-
diffuseColor.rgb = mix( uColorA, uColorB, s3mH );
|
|
274
|
-
} else if ( uMode == 4 ) {
|
|
275
|
-
// 按楼层:先算楼层数再归一化,形成分层色带
|
|
276
|
-
float floors = floor( ( vWorldZ - uHeightMin ) / max( uFloorHeight, 0.5 ) );
|
|
277
|
-
float total = max( floor( ( uHeightMax - uHeightMin ) / max( uFloorHeight, 0.5 ) ), 1.0 );
|
|
278
|
-
diffuseColor.rgb = mix( uColorA, uColorB, floors / total );
|
|
279
|
-
} else if ( uMode == 5 ) {
|
|
280
|
-
// 按单体:同一个 batchId 同色,相邻单体色相拉开(必须用取整后的 id,见 s3mBatchId)
|
|
281
|
-
diffuseColor.rgb = s3mHsv( s3mHash( s3mBid * 7.31 + 1.0 ), 0.52, 0.95 );
|
|
282
|
-
} else if ( uMode == 6 ) {
|
|
283
|
-
// 动态扫描:底色按高度渐变,一条亮带自下而上循环
|
|
284
|
-
float scan = fract( uTime * 0.18 );
|
|
285
|
-
float glow = smoothstep( 0.10, 0.0, abs( s3mH - scan ) );
|
|
286
|
-
diffuseColor.rgb = mix( uColorA, uColorB, s3mH ) + uColorC * glow * 1.2;
|
|
287
|
-
} else if ( uMode == 7 ) {
|
|
288
|
-
// 顶面/侧面分色:uColorA 为顶面(屋顶),uColorB 为侧面(立面)
|
|
289
|
-
// 局部 ENU 坐标系以 Z 为朝天方向,abs(s3mNorm.z) 接近 1 为顶面/底面,接近 0 为立面
|
|
290
|
-
float th = uRoofThreshold > 0.0 ? uRoofThreshold : 0.65;
|
|
291
|
-
float isRoof = smoothstep( th - 0.08, th + 0.08, abs( s3mNorm.z ) );
|
|
292
|
-
diffuseColor.rgb = mix( uColorB, uColorA, isRoof );
|
|
293
|
-
} else {
|
|
294
|
-
// 自定义复合模式:顶面使用 uColorA,侧面叠加高度渐变(uColorB 到 uColorC)
|
|
295
|
-
float th = uRoofThreshold > 0.0 ? uRoofThreshold : 0.65;
|
|
296
|
-
float isRoof = smoothstep( th - 0.08, th + 0.08, abs( s3mNorm.z ) );
|
|
297
|
-
vec3 sideGrad = mix( uColorB, uColorC, s3mH );
|
|
298
|
-
diffuseColor.rgb = mix( sideGrad, uColorA, isRoof );
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// 叠加在原色贴图上:保留窗框、玻璃幕墙、材质纹理与顶点色
|
|
302
|
-
if ( uMode != 1 && uTexMix > 0.001 ) {
|
|
303
|
-
vec3 origTex = s3mBase * vVertColor;
|
|
304
|
-
vec3 overlay = origTex * diffuseColor.rgb * 1.35;
|
|
305
|
-
diffuseColor.rgb = mix( diffuseColor.rgb, overlay, uTexMix );
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// 1. 高度范围过滤
|
|
309
|
-
if ( uFilterHeightActive > 0 ) {
|
|
310
|
-
if ( vWorldZ < uFilterMinZ || vWorldZ > uFilterMaxZ ) discard;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// 2. 单体状态过滤(隐藏)与单体独立改色
|
|
314
|
-
bool hasCustomColor = false;
|
|
315
|
-
if ( uBatchTexActive > 0 && s3mBid >= 0.0 ) {
|
|
316
|
-
// 用取整后的 id 采样:否则逐像素的插值误差会让纹素在边界上来回跳,同样出彩色噪点
|
|
317
|
-
vec4 bState = texture2D( uBatchTex, vec2( ( s3mBid + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
318
|
-
if ( bState.a < -0.5 ) {
|
|
319
|
-
discard; // 属于被过滤隐藏的单体
|
|
320
|
-
}
|
|
321
|
-
if ( bState.r >= 0.0 ) {
|
|
322
|
-
// 单体独立自定义颜色
|
|
323
|
-
diffuseColor.rgb = bState.rgb;
|
|
324
|
-
hasCustomColor = true;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// 3. 选中单体高亮 + 其余压暗
|
|
329
|
-
float s3mSel = max( step( 0.0, uSelBatch ) * step( abs( s3mBid - uSelBatch ), 0.5 ), uSelWhole );
|
|
330
|
-
if ( s3mSel > 0.5 && uSelMix > 0.001 ) {
|
|
331
|
-
if ( hasCustomColor ) {
|
|
332
|
-
// 若当前建筑已有用户固定的独立颜色,选中时保留该颜色,仅做轻微高亮叠色提示
|
|
333
|
-
if ( uSelMode == 0 || uSelMode == 1 ) {
|
|
334
|
-
diffuseColor.rgb = mix( diffuseColor.rgb, uSelColor, uSelOpacity * 0.35 );
|
|
335
|
-
}
|
|
336
|
-
} else {
|
|
337
|
-
if ( uSelMode == 0 ) {
|
|
338
|
-
// 纯色覆盖(经典纯高亮色)
|
|
339
|
-
diffuseColor.rgb = uSelColor;
|
|
340
|
-
} else if ( uSelMode == 1 ) {
|
|
341
|
-
// 叠加原图(半透明混色,保留纹理贴图)
|
|
342
|
-
diffuseColor.rgb = mix( diffuseColor.rgb, uSelColor, uSelOpacity );
|
|
343
|
-
} else if ( uSelMode == 2 ) {
|
|
344
|
-
// 叠乘模式(明暗对比强)
|
|
345
|
-
diffuseColor.rgb = mix( diffuseColor.rgb, diffuseColor.rgb * uSelColor * 1.5, uSelOpacity );
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
// uSelMode == 3 为 glowOnly,保持 diffuseColor.rgb 不变
|
|
349
|
-
} else {
|
|
350
|
-
diffuseColor.rgb *= mix( 1.0, uDimFactor, uDim );
|
|
351
|
-
}
|
|
254
|
+
const FRAG_COLOR = /* glsl */ `
|
|
255
|
+
float s3mH = clamp( ( vWorldZ - uHeightMin ) / max( uHeightMax - uHeightMin, 0.001 ), 0.0, 1.0 );
|
|
256
|
+
vec3 s3mBase = diffuseColor.rgb; // 已含 diffuse × map
|
|
257
|
+
// 所有按单体取值(配色 / 状态纹理 / 选中判定)都走取整后的 id,避免插值噪声
|
|
258
|
+
float s3mBid = s3mBatchId();
|
|
259
|
+
|
|
260
|
+
// 法线:优先使用顶点插值法线;若退化或为平面着色,使用屏幕导数精确计算真实几何法线
|
|
261
|
+
vec3 s3mNorm = length( vWorldNormal ) > 0.1 ? normalize( vWorldNormal ) : normalize( cross( dFdx( vWorldPos ), dFdy( vWorldPos ) ) );
|
|
262
|
+
|
|
263
|
+
if ( uMode == 0 ) {
|
|
264
|
+
// 白膜:丢掉贴图,只留光照
|
|
265
|
+
diffuseColor.rgb = uColorA;
|
|
266
|
+
} else if ( uMode == 1 ) {
|
|
267
|
+
// 原色:贴图 × 顶点色
|
|
268
|
+
diffuseColor.rgb = s3mBase * vVertColor;
|
|
269
|
+
} else if ( uMode == 2 ) {
|
|
270
|
+
diffuseColor.rgb = uColorA;
|
|
271
|
+
} else if ( uMode == 3 ) {
|
|
272
|
+
// 按高度连续渐变
|
|
273
|
+
diffuseColor.rgb = mix( uColorA, uColorB, s3mH );
|
|
274
|
+
} else if ( uMode == 4 ) {
|
|
275
|
+
// 按楼层:先算楼层数再归一化,形成分层色带
|
|
276
|
+
float floors = floor( ( vWorldZ - uHeightMin ) / max( uFloorHeight, 0.5 ) );
|
|
277
|
+
float total = max( floor( ( uHeightMax - uHeightMin ) / max( uFloorHeight, 0.5 ) ), 1.0 );
|
|
278
|
+
diffuseColor.rgb = mix( uColorA, uColorB, floors / total );
|
|
279
|
+
} else if ( uMode == 5 ) {
|
|
280
|
+
// 按单体:同一个 batchId 同色,相邻单体色相拉开(必须用取整后的 id,见 s3mBatchId)
|
|
281
|
+
diffuseColor.rgb = s3mHsv( s3mHash( s3mBid * 7.31 + 1.0 ), 0.52, 0.95 );
|
|
282
|
+
} else if ( uMode == 6 ) {
|
|
283
|
+
// 动态扫描:底色按高度渐变,一条亮带自下而上循环
|
|
284
|
+
float scan = fract( uTime * 0.18 );
|
|
285
|
+
float glow = smoothstep( 0.10, 0.0, abs( s3mH - scan ) );
|
|
286
|
+
diffuseColor.rgb = mix( uColorA, uColorB, s3mH ) + uColorC * glow * 1.2;
|
|
287
|
+
} else if ( uMode == 7 ) {
|
|
288
|
+
// 顶面/侧面分色:uColorA 为顶面(屋顶),uColorB 为侧面(立面)
|
|
289
|
+
// 局部 ENU 坐标系以 Z 为朝天方向,abs(s3mNorm.z) 接近 1 为顶面/底面,接近 0 为立面
|
|
290
|
+
float th = uRoofThreshold > 0.0 ? uRoofThreshold : 0.65;
|
|
291
|
+
float isRoof = smoothstep( th - 0.08, th + 0.08, abs( s3mNorm.z ) );
|
|
292
|
+
diffuseColor.rgb = mix( uColorB, uColorA, isRoof );
|
|
293
|
+
} else {
|
|
294
|
+
// 自定义复合模式:顶面使用 uColorA,侧面叠加高度渐变(uColorB 到 uColorC)
|
|
295
|
+
float th = uRoofThreshold > 0.0 ? uRoofThreshold : 0.65;
|
|
296
|
+
float isRoof = smoothstep( th - 0.08, th + 0.08, abs( s3mNorm.z ) );
|
|
297
|
+
vec3 sideGrad = mix( uColorB, uColorC, s3mH );
|
|
298
|
+
diffuseColor.rgb = mix( sideGrad, uColorA, isRoof );
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 叠加在原色贴图上:保留窗框、玻璃幕墙、材质纹理与顶点色
|
|
302
|
+
if ( uMode != 1 && uTexMix > 0.001 ) {
|
|
303
|
+
vec3 origTex = s3mBase * vVertColor;
|
|
304
|
+
vec3 overlay = origTex * diffuseColor.rgb * 1.35;
|
|
305
|
+
diffuseColor.rgb = mix( diffuseColor.rgb, overlay, uTexMix );
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 1. 高度范围过滤
|
|
309
|
+
if ( uFilterHeightActive > 0 ) {
|
|
310
|
+
if ( vWorldZ < uFilterMinZ || vWorldZ > uFilterMaxZ ) discard;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 2. 单体状态过滤(隐藏)与单体独立改色
|
|
314
|
+
bool hasCustomColor = false;
|
|
315
|
+
if ( uBatchTexActive > 0 && s3mBid >= 0.0 ) {
|
|
316
|
+
// 用取整后的 id 采样:否则逐像素的插值误差会让纹素在边界上来回跳,同样出彩色噪点
|
|
317
|
+
vec4 bState = texture2D( uBatchTex, vec2( ( s3mBid + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
318
|
+
if ( bState.a < -0.5 ) {
|
|
319
|
+
discard; // 属于被过滤隐藏的单体
|
|
320
|
+
}
|
|
321
|
+
if ( bState.r >= 0.0 ) {
|
|
322
|
+
// 单体独立自定义颜色
|
|
323
|
+
diffuseColor.rgb = bState.rgb;
|
|
324
|
+
hasCustomColor = true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 3. 选中单体高亮 + 其余压暗
|
|
329
|
+
float s3mSel = max( step( 0.0, uSelBatch ) * step( abs( s3mBid - uSelBatch ), 0.5 ), uSelWhole );
|
|
330
|
+
if ( s3mSel > 0.5 && uSelMix > 0.001 ) {
|
|
331
|
+
if ( hasCustomColor ) {
|
|
332
|
+
// 若当前建筑已有用户固定的独立颜色,选中时保留该颜色,仅做轻微高亮叠色提示
|
|
333
|
+
if ( uSelMode == 0 || uSelMode == 1 ) {
|
|
334
|
+
diffuseColor.rgb = mix( diffuseColor.rgb, uSelColor, uSelOpacity * 0.35 );
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
if ( uSelMode == 0 ) {
|
|
338
|
+
// 纯色覆盖(经典纯高亮色)
|
|
339
|
+
diffuseColor.rgb = uSelColor;
|
|
340
|
+
} else if ( uSelMode == 1 ) {
|
|
341
|
+
// 叠加原图(半透明混色,保留纹理贴图)
|
|
342
|
+
diffuseColor.rgb = mix( diffuseColor.rgb, uSelColor, uSelOpacity );
|
|
343
|
+
} else if ( uSelMode == 2 ) {
|
|
344
|
+
// 叠乘模式(明暗对比强)
|
|
345
|
+
diffuseColor.rgb = mix( diffuseColor.rgb, diffuseColor.rgb * uSelColor * 1.5, uSelOpacity );
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// uSelMode == 3 为 glowOnly,保持 diffuseColor.rgb 不变
|
|
349
|
+
} else {
|
|
350
|
+
diffuseColor.rgb *= mix( 1.0, uDimFactor, uDim );
|
|
351
|
+
}
|
|
352
352
|
`;
|
|
353
|
-
const FRAG_EMISSIVE = /* glsl */ `
|
|
354
|
-
float s3mSelE = max( step( 0.0, uSelBatch ) * step( abs( s3mBatchId() - uSelBatch ), 0.5 ), uSelWhole );
|
|
355
|
-
// glowOnly 模式下表面不自发光,只保留描边边框;其他模式下适度发光
|
|
356
|
-
if ( uSelMode != 3 ) {
|
|
357
|
-
totalEmissiveRadiance += uSelColor * s3mSelE * uSelMix * ( uSelEmissive * 0.4 );
|
|
358
|
-
}
|
|
353
|
+
const FRAG_EMISSIVE = /* glsl */ `
|
|
354
|
+
float s3mSelE = max( step( 0.0, uSelBatch ) * step( abs( s3mBatchId() - uSelBatch ), 0.5 ), uSelWhole );
|
|
355
|
+
// glowOnly 模式下表面不自发光,只保留描边边框;其他模式下适度发光
|
|
356
|
+
if ( uSelMode != 3 ) {
|
|
357
|
+
totalEmissiveRadiance += uSelColor * s3mSelE * uSelMix * ( uSelEmissive * 0.4 );
|
|
358
|
+
}
|
|
359
359
|
`;
|
|
360
360
|
/**
|
|
361
361
|
* 创建一个带样式的 PBR 材质。
|
|
@@ -413,44 +413,44 @@ export function createStyledMaterial(opts) {
|
|
|
413
413
|
return mat;
|
|
414
414
|
}
|
|
415
415
|
/* ------------------------------------------------------------------ 选中描边 */
|
|
416
|
-
const OUTLINE_VERT = /* glsl */ `
|
|
417
|
-
attribute float aBatchId;
|
|
418
|
-
uniform float uSelBatch;
|
|
419
|
-
uniform float uSelWhole;
|
|
420
|
-
uniform float uWidth;
|
|
421
|
-
uniform float uElevation;
|
|
422
|
-
uniform sampler2D uBatchTex;
|
|
423
|
-
uniform float uBatchTexWidth;
|
|
424
|
-
uniform int uBatchTexActive;
|
|
425
|
-
varying float vSel;
|
|
426
|
-
void main() {
|
|
427
|
-
vSel = max( step( 0.0, uSelBatch ) * step( abs( aBatchId - uSelBatch ), 0.5 ), uSelWhole );
|
|
428
|
-
vec3 s3mCustomOffset = vec3( 0.0 );
|
|
429
|
-
if ( uBatchTexActive > 0 && aBatchId >= 0.0 ) {
|
|
430
|
-
vec4 bState = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
431
|
-
if ( bState.a < -0.5 ) {
|
|
432
|
-
vSel = 0.0; // 过滤隐藏的单体不显示描边
|
|
433
|
-
} else {
|
|
434
|
-
vec4 bOffset = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.75 ) );
|
|
435
|
-
s3mCustomOffset = bOffset.xyz;
|
|
436
|
-
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
437
|
-
s3mCustomOffset.z = bState.a;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
vec3 p = position + normal * uWidth * vSel;
|
|
442
|
-
p += s3mCustomOffset;
|
|
443
|
-
p.z += vSel * uElevation;
|
|
444
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
|
|
445
|
-
}
|
|
416
|
+
const OUTLINE_VERT = /* glsl */ `
|
|
417
|
+
attribute float aBatchId;
|
|
418
|
+
uniform float uSelBatch;
|
|
419
|
+
uniform float uSelWhole;
|
|
420
|
+
uniform float uWidth;
|
|
421
|
+
uniform float uElevation;
|
|
422
|
+
uniform sampler2D uBatchTex;
|
|
423
|
+
uniform float uBatchTexWidth;
|
|
424
|
+
uniform int uBatchTexActive;
|
|
425
|
+
varying float vSel;
|
|
426
|
+
void main() {
|
|
427
|
+
vSel = max( step( 0.0, uSelBatch ) * step( abs( aBatchId - uSelBatch ), 0.5 ), uSelWhole );
|
|
428
|
+
vec3 s3mCustomOffset = vec3( 0.0 );
|
|
429
|
+
if ( uBatchTexActive > 0 && aBatchId >= 0.0 ) {
|
|
430
|
+
vec4 bState = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.25 ) );
|
|
431
|
+
if ( bState.a < -0.5 ) {
|
|
432
|
+
vSel = 0.0; // 过滤隐藏的单体不显示描边
|
|
433
|
+
} else {
|
|
434
|
+
vec4 bOffset = texture2D( uBatchTex, vec2( ( aBatchId + 0.5 ) / uBatchTexWidth, 0.75 ) );
|
|
435
|
+
s3mCustomOffset = bOffset.xyz;
|
|
436
|
+
if ( s3mCustomOffset.z == 0.0 && bState.a > 0.0 ) {
|
|
437
|
+
s3mCustomOffset.z = bState.a;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
vec3 p = position + normal * uWidth * vSel;
|
|
442
|
+
p += s3mCustomOffset;
|
|
443
|
+
p.z += vSel * uElevation;
|
|
444
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
|
|
445
|
+
}
|
|
446
446
|
`;
|
|
447
|
-
const OUTLINE_FRAG = /* glsl */ `
|
|
448
|
-
uniform vec3 uColor;
|
|
449
|
-
varying float vSel;
|
|
450
|
-
void main() {
|
|
451
|
-
if ( vSel < 0.5 ) discard;
|
|
452
|
-
gl_FragColor = vec4( uColor, 1.0 );
|
|
453
|
-
}
|
|
447
|
+
const OUTLINE_FRAG = /* glsl */ `
|
|
448
|
+
uniform vec3 uColor;
|
|
449
|
+
varying float vSel;
|
|
450
|
+
void main() {
|
|
451
|
+
if ( vSel < 0.5 ) discard;
|
|
452
|
+
gl_FragColor = vec4( uColor, 1.0 );
|
|
453
|
+
}
|
|
454
454
|
`;
|
|
455
455
|
/** 选中单体的描边材质(背面外扩法) */
|
|
456
456
|
export function createOutlineMaterial(shared, initialWidth = 0.9) {
|