Ticket: 06 — Three.js 雾 shader 可复用面与 Cesium 深度互通可行性
Date: 2026-09-18
Scope: decision facts only. No production cockpit code was modified; no build/dev/serve was run (repo rule). Engine versions verified locally: cesium@1.145.0(node_modules/cesium/package.json:3)、three@0.180.0(node_modules/three/package.json:3)。npm 包布局:cesium 包是空壳,真实渲染器源码在 node_modules/@cesium/engine/Source/(node_modules/cesium/Source/Cesium.js:8 起全部 export { … } from '@cesium/engine')。
@takram/three-clouds 的 haze 是一段与渲染器无关的闭式光学厚度积分(密度沿高度指数衰减 + 沿视线解析积分,源自 iq 的 fog 公式)。密度曲线、积分公式、双 HG 相位函数、射线-椭球求交都是纯数学,可原样搬进 Cesium PostProcessStage;PostProcessStage 自带 colorTexture/depthTexture/v_textureCoordinates,且 Cesium 自动注入 czm_inverseProjection、czm_eyeHeight、czm_ellipsoidRadii 等自动 uniform,几乎一一对应 Three 侧手动供给的矩阵 uniform。必须重写的是:深度采样约定(.r+log 解码 → czm_readDepth)、EffectComposer 多目标渲染管线、阴影长度/Beer shadow map、以及全部 LUT 纹理(transmittance/scattering/irradiance)——后两者是体积云/大气散射专用的,纯雾不需要。关键数值事实:默认 hazeDensityScale=3e-5、hazeExponent=1e-3 在县城尺度(相机高度 200 m 量级)等效于每米消光 ≈1.2×10⁻⁵,50% 起雾距离 ≈ 56 km——比 07 票的 1,651 m 预算弱约 30 倍,直接搬默认值不可用,必须重调参。Context.depthTexture 是布尔能力探测(是否支持 WEBGL_depth_texture),不是纹理对象,且不在随包 Cesium.d.ts;真正的场景深度纹理是打包 DEPTH_STENCIL / UNSIGNED_INT_24_8 的 D24S8 纹理(FramebufferManager 创建)。Two 个 WebGL context 之间不能直接共享纹理对象,所以只有两条路:共用 Cesium 的私有 GL context(上游明确不支持,issue #648 自 2013 年开票至今 open)或每帧 readPixels 回读(管线停顿 + 24 bit 量化 + 动态近远平面归一化)。社区实测还报告了俯视/仰视时遮挡错乱(same-canvas 线程),而驾驶舱主视角正是近俯视。该路径 = 驾驶舱引入第二套渲染器接管合成,与 ADR 0004「Cesium 为主、Three 为增强、不接管 Cesium 场景」冲突,按票面约束,若甲方坚持此路线须先推翻/修订 ADR 0004 并交回甲方裁定;本报告不提出实施建议。来源对象:src/plugins/useThreeGeospatial/lib/clouds(即 npm 包 @takram/three-clouds@0.2.1,lib/clouds/package.json:2)+ lib/atmosphere(@takram/three-atmosphere@0.11.1)。雾霾主实现是 clouds.frag 的 approximateHaze()(src/plugins/useThreeGeospatial/lib/clouds/src/shaders/clouds.frag:653-690),开关是 CloudsMaterial 的 @define('HAZE') haze(lib/clouds/src/CloudsMaterial.ts:438-439),README 明确标注它「inexpensive and recommended to keep enabled」(lib/clouds/README.md:589-595)。
密度是相机高度处的指数函数,两个参数各管一件事:
float modulation = remapClamped(coverage, 0.2, 0.4); // clouds.frag:662
float density = modulation * hazeDensityScale * exp(-cameraHeight * hazeExponent); // clouds.frag:666
if (density < 1e-7) { return vec4(0.0); } // 高空看太空时防伪影 // clouds.frag:667-669
hazeDensityScale = 3e-5:整体密度倍率,越大越浓(默认值在 CloudsMaterial.ts:251、README 定义在 README.md:938-944)。hazeExponent = 1e-3:密度随海拔的指数衰减率,越小越贴地、越大越往高空铺(默认值在 CloudsMaterial.ts:252、README 定义在 README.md:946-952)。cameraHeight 是相机相对椭球的大地高(不是视空间坐标):CloudsMaterial.copyCameraSettings() 里 geodeticScratch.setFromECEF(cameraPositionECEF).height(CloudsMaterial.ts:363-370)。coverage(云覆盖率,默认 0.3,uniforms.ts:54)经 remapClamped(coverage, 0.2, 0.4) 映射成 0..1 的调制系数——纯雾场景等于常数倍率(math.glsl:78-80 给出 remapClamped 的 saturate((x-min1)/(max1-min1)) 定义)。沿视线的光学厚度是解析积分(注释引 iq 的 fog 文章,clouds.frag:671-672):
float angle = max(dot(normalize(rayOrigin), rayDirection), 1e-5); // clouds.frag:673
float exponent = angle * hazeExponent; // clouds.frag:674
float expTerm = 1.0 - exp(-maxRayDistance * exponent); // clouds.frag:676
float linearTerm = density / hazeExponent / angle; // clouds.frag:678
float opticalDepth = expTerm * linearTerm; // clouds.frag:679
即 OD = (d₀/β/cosθ)·(1 − exp(−L·β·cosθ)),其中 d₀ 为相机高度密度、β = hazeExponent、θ 为「地心向外方向」与视线的夹角。注意 max(…, 1e-5) 这个钳制:视线朝下(θ>90°,cosθ<0,即一切俯视/平视视角)时公式被钳到 1e-5 分支,`expTerm ≈ L·1e-8`、`linearTerm ≈ d₀·1e8`,两者相乘退化为 **OD ≈ d₀·L —— 线性距离雾**(每米消光 = `modulation·hazeDensityScale·exp(−h·hazeExponent)`)。指数增长分支只在仰视(cosθ>0)时启用。县城尺度下 d₀≈1.2×10⁻⁵ m⁻¹(h=200 m、coverage=0.3),50% 起雾 ≈ 56 km;即默认参数在县域尺度约等于无雾,07 要的「1.6 km 内足够陡」需要把密度抬约 30 倍(h=10 m 时 ≈29×,h=200 m 时 ≈35×)。
approximateHaze 返回 vec4(inscatter, saturate(1-exp(-opticalDepth)))(clouds.frag:689),合成在两步完成:
color = color * (1.0 - haze.a) + haze;(clouds.frag:988)cloudsEffect.frag:7-9):outputColor.rgb = inputColor.rgb * (1.0 - clouds.a) + clouds.rgb;合并即标准雾公式 C_out = C_in·exp(−OD) + inscatter。内散射色不是常量,而是:
vec3 irradiance = sunIrradiance * phaseFunction(cosTheta); // clouds.frag:684
irradiance += skyIrradiance * RECIPROCAL_PI4 * skyIrradianceScale; // clouds.frag:685
vec3 inscatter = albedo * irradiance * saturate(1.0 - exp(-effectiveOpticalDepth)); // clouds.frag:686
sunIrradiance/skyIrradiance 由 GetSunAndSkyIrradiance(clouds.vert:34-53)从预计算 LUT 求出——太阳辐照 = 透射率 LUT 查询(functions.glsl:410-419、GetTransmittanceToSun 在 functions.glsl:139-148),天空辐照 = irradiance LUT 查询(functions.glsl:218-221)。LUT 尺寸与格式:transmittance 2D 256×64、scattering 3D 32×128×32×8、irradiance 2D 64×16(lib/atmosphere/src/constants.ts:1-13),以 EXR/半浮点或 .bin 加载(PrecomputedTexturesLoader.ts:42-51, 97-135)。大气参数默认值在 AtmosphereParameters.ts:45-58(Rayleigh (0.005802, 0.013558, 0.0331)、Mie 0.003996、g=0.8、bottomRadius 6360 km/topRadius 6420 km)。
两条配色路线的可搬性差别:
AerialPerspectiveEffect 路线(aerialPerspectiveEffect.frag:249-340):对每个片元用 GetSkyRadianceToPoint(functions.glsl:329-408)查 transmittance + 3D scattering LUT 得到光谱透射率与内散射——完全太阳/大气耦合,LUT 不搬进来就没法用。opticalDepth/expTerm/linearTerm 只含 d₀、β、cosθ、L 四个量,把 inscatter 换成一个常量雾色(或 czm_sunDirectionEC 的简易相位着色),公式照常成立。这正是可搬给 07 的最小形态。cosTheta = dot(sunDirection, rayDirection)(clouds.frag:828),双 lobe Henyey-Greenstein(clouds.frag:291-336),宏常量 SCATTER_ANISOTROPY_1=0.7、_2=-0.2、_MIX=0.5(CloudsMaterial.ts:459-466)。mu_s = dot(camera, sun_direction)/r,functions.glsl:350)。shadowExpTerm 用 shadowLength(来自 SHADOW_LENGTH pass 的 Beer 阴影长度,clouds.frag:677、680)削减雾的内散射——无云/无光轴时不参与。#04121f 或灰白霾),搬进 PostProcessStage 后不需要任何太阳纹理,最多用 Cesium 现成的 czm_sunDirectionEC/czm_sunDirectionWC(AutomaticUniforms.js:1255-1286)做个可选的方向性微调。雾的「距离」是沿视线的世界距离,来源是射线-椭球求交,不是深度缓冲:
cameraNear(把 near 平面距离当射线起点,clouds.frag:982)。getHazeRayNearFar(clouds.frag:786-804):raySphereIntersections() 对 bottomRadius(地表球)与 bottomRadius+vec4(0,minHeight,maxHeight,shadowTopHeight)(云层球壳)求交;命中地面取第一交点、否则取云顶第二交点。getRayDistanceToScene(clouds.frag:806-814)读深度纹理 → reverseLogDepth 反解 log 深度 → perspectiveDepthToViewZ(three 的 packing chunk,node_modules/three/src/renderers/shaders/ShaderChunk/packing.glsl.js:96)得视空间 z → -viewZ / dot(rayDirection, vCameraDirection) 换算成沿射线的距离。射线方向本身是每像素反投影到 ECEF:clouds.vert:58-64 用 inverseProjectionMatrix、inverseViewMatrix、inverseEllipsoidMatrix 把全屏四边形顶点反解成 ECEF 射线。到 Cesium 的对应物:czm_inverseProjection(AutomaticUniforms.js:524-532)+ czm_viewRotation(AutomaticUniforms.js:352-362)+ 椭球参数 czm_ellipsoidRadii(AutomaticUniforms.js:1903-1914),相机高度直接用 czm_eyeHeight(AutomaticUniforms.js:1003)——无需手工上传任何矩阵。
| 项 | 判定 | 依据 |
|---|---|---|
密度曲线 d₀·exp(−h·β) |
纯数学可搬 | clouds.frag:666;参数仅两个标量 |
| 闭式 OD 积分(iq 公式 + cosθ 钳制) | 纯数学可搬 | clouds.frag:671-680;只依赖标量 |
混合公式 C·(1−a)+inscatter |
纯数学可搬 | clouds.frag:988、cloudsEffect.frag:7-9;PostProcessStage 一次全屏 pass 内直接写 out_FragColor |
| 距离口径(世界射线长) | 结构同构需改写 | 深度→距离换算与射线-椭球求交要换 Cesium 方言(czm_readDepth、czm_inverseProjection、czm_ellipsoidRadii),公式本体不变 |
| 太阳耦合(相位+辐照) | 部分可搬/部分不可搬 | HG 相位函数纯数学可搬(clouds.frag:291-336);LUT 辐照(transmittance/scattering/irradiance 三张纹理)不可搬,须换常量雾色或 Cesium 大气 uniform |
| uniform 供给 | 结构同构需改写 | Three 侧手工上传 viewMatrix/inverseProjectionMatrix/inverseViewMatrix/cameraNear/cameraFar/cameraHeight(CloudsMaterial.ts:194-201, 319-358)→ Cesium 自动 uniform 直接对应(见 §2) |
| EffectComposer / renderTarget 管线 | 强耦合 Three 不可搬 | MRT(layout(location=0..2) 输出颜色/速度/阴影长度,clouds.frag:80-84)、temporal upscale、reprojection(CloudsMaterial.ts:322-354)全部丢弃 |
| 深度纹理采样 | 结构同构需改写 | Three:DEPTH_PACKING==3201 时 unpackRGBAToDepth 否则 .r(clouds.frag:86-92,3201=RGBADepthPacking、3200=BasicDepthPacking,node_modules/three/src/constants.js:1199-1207);Cesium:czm_readDepth(depthTexture, uv) = .r + log 解码(readDepth.glsl:1-4、reverseLogDepth.glsl:1-11) |
| GLSL 方言 | 结构同构需改写 | 见 §2 |
| 坐标系 | 结构同构需改写(低风险) | 雾公式本身用 ECEF 相对坐标(clouds.vert:58-64 已把 Three 世界坐标转 ECEF),与 Cesium 的 ECEF 世界/眼坐标天然同族;Three Y-up 只是外层包装 |
| 对数深度 | 结构同构需改写 | Three USE_LOGDEPTHBUF 编码(logdepthbuf_fragment.glsl.js:2-8:gl_FragDepth = log2(vFragDepth)*logDepthBufFC*0.5)与 Cesium 的 log 深度编码/解码(reverseLogDepth.glsl)是两套公式;Cesium 侧 PostProcessStage 会自动拿到 LOG_DEPTH define 并已替你解码(§2) |
GLSL3(CloudsMaterial.ts:159),layout(location=N) in/out 显式声明(clouds.vert:23、clouds.frag:80-84),WebGL2 前缀里 #define texture2D texture(node_modules/three/src/renderers/webgl/WebGLProgram.js:864-878)。Cesium 侧写「GLSL1 风格 + czm_* 内建」即可,ShaderSource 在 WebGL2 下自动补 #version 300 es(ShaderSource.js:296-301),WebGL1 下自动降级(demodernizeShader);输出变量必须叫 out_FragColor,v_textureCoordinates 由用户自己声明(PostProcessStage 官方文档 示例)。precision highp float(clouds.frag:1);Cesium 会剥掉用户 precision 声明(ShaderSource.js:206-210)并注入 GL_FRAGMENT_PRECISION_HIGH 分支的 highp/mediump 兜底(ShaderSource.js:229-243)——不写反而更省事。viewMatrix/inverseViewMatrix/inverseProjectionMatrix/cameraNear/cameraFar(CloudsMaterial.ts:319-358);postprocessing 库的 EffectMaterial 额外内建 cameraNear(默认 0.3)/cameraFar(默认 1000)/resolution/texelSize(EffectMaterial.js 源码)。Cesium 对应物全自动:czm_projection/czm_inverseProjection(AutomaticUniforms.js:499-509, 524-532)、czm_viewRotation(:352-362)、czm_viewport(:64)、czm_eyeHeight(:1003)、czm_entireFrustum(:1137)、czm_currentFrustum(:1152-1160)、czm_sunDirectionEC/WC(:1264-1286)、czm_ellipsoidRadii/InverseRadii(:1903-1914);用户自定义 uniform 走 stage.uniforms(常量或每帧函数,PostProcessStage.js:406-466)。PostProcessStage 默认即获得 sampler2D colorTexture + sampler2D depthTexture + vec2 v_textureCoordinates(PostProcessStage.js:32-33、uniform 注入在 :520-530),深度是场景渲染输出的 D24S8 深度-模板纹理(FramebufferManager.js:189-196 创建,Scene.js:4170-4173 传入后处理)。stage 声明了 depthTexture 但设备不支持时整级自动禁用(PostProcessStage.js:397-404)——即 07 想要的能力探测已有现成实现。读深度用 czm_readDepth(depthTexture, v_textureCoordinates),它在 LOG_DEPTH 下自动做 log 反解(readDepth.glsl:1-4、reverseLogDepth.glsl:1-11);LOG_DEPTH define 由场景状态自动注入(PostProcessStage.js:876-887, 588-591),Scene.logarithmicDepthBuffer 在 1.145 默认 true(Scene.js:195 + Scene.defaultLogDepthBuffer = true 在 Scene.js:798,仅要求 context.fragmentDepth)。normalize(rayOrigin),clouds.frag:673),相机高度是椭球大地高(CloudsMaterial.ts:363-370)。Cesium 侧 czm_eyeHeight 正是「相机相对椭球的高度」(AutomaticUniforms.js:1003 及其文档),语义完全一致;无需理会 Three Y-up / Cesium Z-up 的差异——那是 Three 局部场景的包装,雾公式早已通过 inverseEllipsoidMatrix 转到 ECEF(clouds.vert:58-64)。cloudsResolve.frag:190-197)、reprojection 速度(clouds.frag:936-942)、BSM 级联阴影、STBN 抖动。雾只需要「一遍全屏 pass + 深度附件」;深度附件在启用 post-process 时由引擎白送(Scene.js:4160-4175)。Context.depthTexture 是布尔,真正的深度纹理是 D24S8Context.depthTexture 是 boolean 能力标志(是否支持 WEBGL_depth_texture):实现见 Context.js:148-151(构造期查扩展)、:714-725(getter return this._depthTexture || this._webgl2)。它不是纹理对象——票面「Context.depthTexture 能否作为 Three 的深度输入」的前提本身需要修正。grep depthTexture 仅命中 Cesium.d.ts:43388、:43429 两处 PostProcessStage 的 JSDoc 注释,Context 类无此成员(引用 04 结论并给出本文件证据)。FramebufferManager:PixelFormat.DEPTH_STENCIL + PixelDatatype.UNSIGNED_INT_24_8(FramebufferManager.js:189-196),即 24 bit 深度 + 8 bit 模板打包;另有一个纯深度 DEPTH_COMPONENT/UNSIGNED_INT 变体供「仅深度」framebuffer(FramebufferManager.js:217-226)。主场景走 getDepthStencilTexture()(Scene.js:4170-4173)。默认 MSAA=4(Scene.js:118, 253),多采样时深度渲染进 renderbuffer、再解析进纹理。DepthTexture 默认 UnsignedIntType + DepthFormat,也支持 DepthStencilFormat(node_modules/three/src/textures/DepthTexture.js:28-38);WebGL2 下内部格式映射 DEPTH24_STENCIL8/DEPTH_COMPONENT24/DEPTH_COMPONENT32F/DEPTH_COMPONENT16(WebGLTextures.js:232-270)。格式上相容(D24S8 正是 Three DepthStencilFormat+UnsignedInt248Type 的映射目标),采样 .x 即深度。WebGLRenderer 是 WebGL2-only(WebGLRenderer.js:58-61「WebGL 1 is not supported since r163」)且接受外部 context 参数(WebGLRenderer.js:73-74, 97-111)——理论上唯一零拷贝路线是把 Cesium 的私有 context(scene.context._gl,非公开 API)交给 Three,而 Cesium 官方明确不支持与其他引擎共享 context(issue #648:自 2013 年 open,「Cesium 需要在 Scene.render 调用之外不持有 context」,至今未做)。另一条路是 readPixels 回读 + 重上传,见 §3.4。Cesium 侧全部有公开 API:camera.lookAtTransform 的 HeadingPitchRange 语义(heading 从 y 轴向 x 轴、正 pitch 在参考平面下方,Camera.js:2393-2396;实现 offsetFromHeadingPitchRange 在 Camera.js:2360-2389)、camera.viewMatrix/inverseViewMatrix(Camera.js:891-907)、frustum.near/far/fovy/aspectRatio。Three 侧 PerspectiveCamera 由 fov/aspect/near/far 构造;Matrix4.elements 列主序(node_modules/three/src/math/Matrix4.js:78),与 Cesium 列主序一致。
每帧同步面清单(双 canvas 官方方案,Cesium 官方博客「Integrating Cesium with Three.js」):
positionWC(ECEF)→ Three 世界坐标(官方博客方案里直接复制进 Three 的局部 ECEF 场景,需做 Y-up/Z-up 轴交换,博客原文注明「Three.js renders z-up and Cesium renders y-up」);或转成东北天局部系(仓库已有 Transforms.eastNorthUpToFixedFrame 实践,src/modules/cockpit/services/cesiumEngine.js:236-238)。camera.quaternion(轴交换 + 用 ENU 帧矩阵变换);社区 Babylon 移植版给出了同款换算(same-canvas 线程第 5 楼)。frustum.fovy(仓库已有 fovY() 读法,cesiumEngine.js:313-315)、aspect、near。far 是坑:Cesium 每帧动态重算 near/far(社区实测「相机一动深度值就抖」,需用 uniformState.currentFrustum 归一化,how-to-get-depth-texture-properly 线程第 4 楼)——Three 侧固定 near/far 的逆投影会对不上。useDefaultRenderLoop=false + 每帧 viewer.render() 后接 three.renderer.render()(官方博客模式);resize 时序同步 viewer.resize() 与 renderer.setSize。代码量估计:矩阵/参数同步函数约 60–100 行(含 ENU 轴交换与 quaternion 换算);但同步相机只是热身,深度的语义对齐才是主体(§3.3-3.5)。
Scene.js:195, 798):存的是 log2(depthFromNear+1)/czm_log2FarDepthFromNearPlusOne 编码值;后处理里 czm_readDepth 经 czm_reverseLogDepth 反解回透视 [0,1] 深度(reverseLogDepth.glsl:1-11)。若直接拿原始纹理值喂 Three,必须自己重新线性化,且近远平面取 czm_currentFrustum(动态)。gl_FragDepth = log2(vFragDepth)*logDepthBufFC*0.5(logdepthbuf_fragment.glsl.js:2-8)——与 Cesium 的编码不是同一公式,双向转换都要写。map.md:47-49 基准):24 bit 定点 log 深度在 1 m–100 km 跨 5 个数量级时量化步长尚可(log 分布把精度摊匀),真正的精度杀手是回读路径:readPixels 只能整帧回读,D24S8 的深度要么以 DEPTH_COMPONENT→UNSIGNED_INT 回读(WebGL2 允许的组合受 规范 §4.3.2 限制,深度-模板附件不是所有组合都合法),要么按 Cesium 现成的 GlobeDepth copyDepth 机制先把深度打进 RGBA 色纹(GlobeDepth.js:47 的 _copyDepthFramebuffer、:177-193 的 copy 命令,用 czm_packDepth 的 4×8bit 编码,packDepth.glsl:10-18)再以 UNSIGNED_BYTE 回读——多一遍全屏 pass + 上传,且 8bit 分量编码在 100 km 量程上有可见带化风险。z-fighting 风险在县城尺度低,量化/带化风险中。readPixels 是阻塞管线停顿操作,无论读多少都强制 GPU→CPU 同步(Stack Overflow 实测 10 ms 级、Khronos 论坛)。resolutionScale 之类近似会进一步破坏深度对齐。_gl,上游不支持(issue #648 自 2013 年 open);同 canvas 混渲会互相踩 GL 状态,社区要手动 gl.frontFace(gl.CCW) 复位(same-canvas 线程第 2 楼),Cesium 无官方状态复位 API。useGlobeDepthFramebuffer/OIT/MSAA/是否有 3D Tiles 而变——社区实测同一取法「加了 OSM Buildings 深度才平滑、否则抖」(how-to-get-depth-texture-properly 线程第 1/4 楼)。map.md:49)正中此失败面。uniformState.globeDepthTexture/scene.context._gl 均不在 d.ts、无兼容承诺(社区全靠它取纹理,线程)。WebGLRenderer.js:58-61),该组合在 WebGL1 设备直接不可用;Cesium 的 depthTexture 在 WebGL1 依赖扩展(Context.js:148-151)。评级:极难。格式与语义对齐每一条都能做,但上下文隔离这一条把整条路线逼到「私有 API 单 context 拼装」或「每帧读回」两条悬崖边上,且社区实测在俯视下失败——与本驾驶舱主视角正交冲突。
若被迫实施的最小可行草图(一段话,不是实施建议):单 canvas 单 context(把 Cesium 的私有 WebGL2 context 交给 THREE.WebGLRenderer({canvas, context})),Cesium 侧 useDefaultRenderLoop=false;每帧 scene.render() 后,在 Cesium 的 postRender 钩子用 GlobeDepth 的 copyDepth 机制把 log 深度打包进 RGBA 色纹,readPixels(UNSIGNED_BYTE)回读并按 uniformState.currentFrustum 归一化、反解 log 深度后以 DataTexture 上传给 Three 的雾 shader,Three 相机按 §3.2 清单逐帧同步;全程用 context.depthTexture 与 WebGL2 探测包裹,拿不到就退化。该草图未经任何实现验证,且明确与 ADR 0004 冲突:它把深度合成从 Cesium 手里拿出来、让驾驶舱场景由两套渲染器共同决定遮挡(等于让第二套渲染器接管合成),超出「Three 只做重点管线/局部构件增强」的边界;同时让大型插件能力进入驾驶舱主渲染循环,与 ADR 0007「大型插件不作为驾驶舱主体依赖」相抵。按票面约束(issues/06:22),此路线须先推翻/修订 ADR 0004 并交回甲方裁定,不得直接建议实施。
OD = (d₀/β/cosθ)·(1−exp(−L·β·cosθ))(clouds.frag:673-679)直接搬;但在俯视/平视钳制分支退化为 OD ≈ d₀·L 的线性距离雾,默认参数(3e-5 / 1e-3,coverage 调制 0.5)50% 起雾 ≈ 56 km(h=200 m)——07 的 1,651 m 预算要求密度抬 ≈30×,或换更陡的曲线族。这个「俯视自动退化为各向同性线性雾」的性质恰好适配 07 的自洽约束(issues/07:18 要求各向同性且俯视看得到 15% 县城)。PostProcessStage 版可用 czm_readDepth 取视空间深度再乘 czm_inverseProjection 反解,或按 §1.4 的射线-椭球求交复刻——两者 07 二选一,都与「相机→片元距离」语义一致。#04121f vs 灰白霾是 07 的产品取舍,本票只确认数学上常量雾色即成立)。可选:用 czm_sunDirectionEC 做简易相位着色。PostProcessStage 有现成的能力探测(PostProcessStage.js:397-404)与 enabled 开关(PostProcessStage.js:182),可支撑 07 的按需挂载/高度带门控;深度附件在启用后处理时由引擎白送(Scene.js:4160-4175),开销 = 一遍全屏 pass + 打包深度已有的附件。Context.depthTexture 不在 d.ts(半公开 API),07 须做能力探测 + 优雅降级(拿不到深度时退化为无雾或常量混色)——已有现成 _isSupported 行为可参考。src/plugins/useThreeGeospatial/lib/clouds/src/shaders/clouds.frag、clouds.vert、cloudsEffect.frag、cloudsResolve.frag、parameters.glsl、types.glsl、clouds.glsl(仓库内,行号见正文)src/plugins/useThreeGeospatial/lib/clouds/src/CloudsMaterial.ts、CloudsEffect.ts、PassBase.ts、uniforms.ts、constants.ts、qualityPresets.tssrc/plugins/useThreeGeospatial/lib/clouds/README.md(haze 参数文档)src/plugins/useThreeGeospatial/lib/atmosphere/src/AerialPerspectiveEffect.ts、AtmosphereParameters.ts、PrecomputedTexturesLoader.ts、constants.ts、shaders/functions.glsl、shaders/aerialPerspectiveEffect.fragsrc/plugins/useThreeGeospatial/lib/effects/src/DepthEffect.ts、src/shaders/depthEffect.fragnode_modules/@takram/three-geospatial/src/shaders/depth.glsl、math.glsl、transform.glsl、packing.glslnode_modules/@cesium/engine/Source/Renderer/Context.js、FramebufferManager.js、ShaderSource.js、AutomaticUniforms.js、UniformState.jsnode_modules/@cesium/engine/Source/Scene/PostProcessStage.js、PostProcessStageCollection.js、Scene.js、Camera.js、GlobeDepth.js、Core/PerspectiveFrustum.jsnode_modules/@cesium/engine/Source/Shaders/Builtin/Functions/readDepth.glsl、reverseLogDepth.glsl、packDepth.glsl、unpackDepth.js、windowToEyeCoordinates.glslnode_modules/cesium/Source/Cesium.d.ts(depthTexture 仅出现在注释:43388、43429)node_modules/cesium/package.json、node_modules/three/package.jsonnode_modules/three/src/textures/DepthTexture.js、src/renderers/WebGLRenderer.js、src/renderers/webgl/WebGLTextures.js、WebGLProgram.js、WebGLCapabilities.js、src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl.js、packing.glsl.js、src/math/Matrix4.js、src/constants.js