import React from "react"; import {Button, InputNumber, message, Space, Switch, Tooltip} from "antd"; import {QuestionCircleOutlined} from "@ant-design/icons"; import axios from "axios"; import enhance from "../core/enhance.tsx"; import {col_style, row_style, setting_style} from "../Global.tsx"; export type Cesium_Graphics_Config = { // 显示 Cesium FPS/帧耗时诊断面板,不改变画质。 debugShowFramesPerSecond: boolean // 限制 Cesium 渲染循环目标帧率,降低上限可减少 GPU 占用。 targetFrameRate: number // 使用浏览器推荐分辨率,避免高 DPI 屏幕按物理像素渲染。 useBrowserRecommendedResolution: boolean // WebGL 内部渲染分辨率缩放,越低越省 GPU 但越模糊。 resolutionScale: number // MSAA 多重采样数量,1 表示关闭几何抗锯齿。 msaaSamples: number // FXAA 后处理抗锯齿,开销低但可能让文字和细线变软。 fxaa: boolean // 场景阴影开关,大量模型或地形开启后 GPU 开销明显。 shadows: boolean // 地球光照开关,开启后地表按光源方向产生明暗。 enableLighting: boolean // 真实日照模式,使用太阳方向、系统时间和动态大气表达昼夜状态。 solarLighting: boolean // 真实日照模式下的太阳光强度,只影响视觉明暗。 solarLightIntensity: number // 地球瓦片屏幕空间误差,值越大越快但地面细节越粗。 maximumScreenSpaceError: number // 仅 3D 模式加载 Terrarium 真实地形,2D/2.5D 不加载。 terrainEnabledIn3D: boolean // 地形垂直起伏倍率,只改变视觉起伏,不增加高程精度。 terrainExaggeration: number // 限制 Terrarium 地形最高请求层级,减少无效子瓦片请求。 terrainLimitMaximumLevel: boolean // 启用层级限制时允许请求的最高地形层级。 terrainMaximumLevel: number // 已解码地形高度瓦片缓存数量,越大越占内存但回看更快。 terrainCacheTiles: number } export const default_cesium_graphics_config: Cesium_Graphics_Config = { debugShowFramesPerSecond: true, targetFrameRate: 30, useBrowserRecommendedResolution: true, resolutionScale: 0.7, msaaSamples: 1, fxaa: false, shadows: false, enableLighting: false, solarLighting: false, solarLightIntensity: 1.5, maximumScreenSpaceError: 4, terrainEnabledIn3D: false, terrainExaggeration: 1.0, terrainLimitMaximumLevel: true, terrainMaximumLevel: 15, terrainCacheTiles: 128 }; export const cesium_graphics_config_event = "ecap_cesium_graphics_config_changed"; type Number_Config_Key = "targetFrameRate" | "resolutionScale" | "msaaSamples" | "solarLightIntensity" | "maximumScreenSpaceError" | "terrainExaggeration" | "terrainMaximumLevel" | "terrainCacheTiles" type Boolean_Config_Key = Exclude const graphics_presets: Record<"low" | "medium" | "high", Cesium_Graphics_Config> = { low: {...default_cesium_graphics_config}, medium: { debugShowFramesPerSecond: true, targetFrameRate: 45, useBrowserRecommendedResolution: true, resolutionScale: 0.85, msaaSamples: 1, fxaa: true, shadows: false, enableLighting: false, solarLighting: false, solarLightIntensity: 1.5, maximumScreenSpaceError: 3, terrainEnabledIn3D: true, terrainExaggeration: 1.0, terrainLimitMaximumLevel: true, terrainMaximumLevel: 15, terrainCacheTiles: 128 }, high: { debugShowFramesPerSecond: true, targetFrameRate: 60, useBrowserRecommendedResolution: false, resolutionScale: 1.0, msaaSamples: 4, fxaa: true, shadows: false, enableLighting: true, solarLighting: true, solarLightIntensity: 1.5, maximumScreenSpaceError: 2, terrainEnabledIn3D: true, terrainExaggeration: 1.0, terrainLimitMaximumLevel: true, terrainMaximumLevel: 17, terrainCacheTiles: 256 } }; const graphics_help: Record = { debugShowFramesPerSecond: ["显示 FPS、每帧耗时和诊断信息。", "只用于判断卡顿,基本不影响画质。"], targetFrameRate: ["限制 Cesium 渲染循环的目标帧率。", "越高越流畅,GPU 占用越高;集显建议 30。"], useBrowserRecommendedResolution: ["开启后按 CSS 像素渲染,忽略高 DPI 物理像素倍率。", "高 DPI 屏幕上通常能显著降低集显压力。"], resolutionScale: ["调整 WebGL 内部渲染分辨率。", "0.7 约等于渲染 49% 像素;越低越快但更模糊。"], msaaSamples: ["几何边缘多重采样抗锯齿。", "1 表示关闭;数值越高边缘越平滑,GPU 开销越大。"], fxaa: ["屏幕空间后处理抗锯齿。", "开销通常低于 MSAA,但可能让文字和细线变软。"], shadows: ["控制模型、Primitive 和地形阴影。", "画面更真实,但对大量模型和地形开销明显。"], enableLighting: ["根据场景光源给地球表面做明暗。", "这个开关不驱动系统时间;需要真实昼夜时打开“真实日照”。"], solarLighting: ["使用 Cesium 太阳方向、系统时间和动态大气表达昼夜。", "地球和地面对象保持地固坐标,不手动旋转地球;需要更真实的时间态势时开启。"], solarLightIntensity: ["真实日照模式下的太阳光强度。", "数值越大白天越亮、明暗对比越强;不会改变飞机、基站和轨迹数据。"], maximumScreenSpaceError: ["控制地球瓦片 LOD 的屏幕误差。", "数值越大越快但地面更粗糙;4 是集显平衡档。"], terrainEnabledIn3D: ["3D 模式下加载 Terrarium 真实高程地形。", "会增加网络、解码、CPU 和 GPU 压力;2D/2.5D 默认不加载。"], terrainExaggeration: ["控制地形垂直方向的视觉夸张倍率。", "1.0 为真实高度;0.0 压平地形;只改变视觉起伏,不增加高程精度,前端不限制范围。"], terrainLimitMaximumLevel: ["限制 Terrarium 地形继续请求更深层级。", "只限制高程层级,减少无意义子瓦片请求。"], terrainMaximumLevel: ["地形允许加载的最高瓦片层级。", "只有启用地形且限制层级时生效;越高越精细也越耗资源。"], terrainCacheTiles: ["内存中保留的已解码地形瓦片数量。", "越大回看越快但占用更多内存;过小会增加重复请求和解码。"] }; type Cesium_Graphics_Config_Json = { debug_show_frames_per_second: boolean target_frame_rate: number use_browser_recommended_resolution: boolean resolution_scale: number msaa_samples: number fxaa: boolean shadows: boolean enable_lighting: boolean solar_lighting: boolean solar_light_intensity: number maximum_screen_space_error: number terrain_enabled_in_3d: boolean terrain_exaggeration: number terrain_limit_maximum_level: boolean terrain_maximum_level: number terrain_cache_tiles: number } function number_value(value: unknown, fallback: number, min: number, max: number): number { const next = Number(value); if (!Number.isFinite(next)) return fallback; return Math.min(max, Math.max(min, next)); } function unrestricted_number_value(value: unknown, fallback: number): number { const next = Number(value); return Number.isFinite(next) ? next : fallback; } function boolean_value(value: unknown, fallback: boolean): boolean { return typeof value === "boolean" ? value : fallback; } export function normalize_cesium_graphics_config(data: Partial): Cesium_Graphics_Config { return { debugShowFramesPerSecond: boolean_value(data.debugShowFramesPerSecond, default_cesium_graphics_config.debugShowFramesPerSecond), targetFrameRate: number_value(data.targetFrameRate, default_cesium_graphics_config.targetFrameRate, 1, 120), useBrowserRecommendedResolution: boolean_value(data.useBrowserRecommendedResolution, default_cesium_graphics_config.useBrowserRecommendedResolution), resolutionScale: number_value(data.resolutionScale, default_cesium_graphics_config.resolutionScale, 0.2, 1.5), msaaSamples: number_value(data.msaaSamples, default_cesium_graphics_config.msaaSamples, 1, 8), fxaa: boolean_value(data.fxaa, default_cesium_graphics_config.fxaa), shadows: boolean_value(data.shadows, default_cesium_graphics_config.shadows), enableLighting: boolean_value(data.enableLighting, default_cesium_graphics_config.enableLighting), solarLighting: boolean_value(data.solarLighting, default_cesium_graphics_config.solarLighting), solarLightIntensity: number_value(data.solarLightIntensity, default_cesium_graphics_config.solarLightIntensity, 0, 10), maximumScreenSpaceError: number_value(data.maximumScreenSpaceError, default_cesium_graphics_config.maximumScreenSpaceError, 1, 16), terrainEnabledIn3D: boolean_value(data.terrainEnabledIn3D, default_cesium_graphics_config.terrainEnabledIn3D), terrainExaggeration: unrestricted_number_value(data.terrainExaggeration, default_cesium_graphics_config.terrainExaggeration), terrainLimitMaximumLevel: boolean_value(data.terrainLimitMaximumLevel, default_cesium_graphics_config.terrainLimitMaximumLevel), terrainMaximumLevel: number_value(data.terrainMaximumLevel, default_cesium_graphics_config.terrainMaximumLevel, 0, 24), terrainCacheTiles: Math.round(number_value(data.terrainCacheTiles, default_cesium_graphics_config.terrainCacheTiles, 16, 2048)) }; } function from_server_config(data: Partial): Cesium_Graphics_Config { return normalize_cesium_graphics_config({ debugShowFramesPerSecond: data.debug_show_frames_per_second, targetFrameRate: data.target_frame_rate, useBrowserRecommendedResolution: data.use_browser_recommended_resolution, resolutionScale: data.resolution_scale, msaaSamples: data.msaa_samples, fxaa: data.fxaa, shadows: data.shadows, enableLighting: data.enable_lighting, solarLighting: data.solar_lighting, solarLightIntensity: data.solar_light_intensity, maximumScreenSpaceError: data.maximum_screen_space_error, terrainEnabledIn3D: data.terrain_enabled_in_3d, terrainExaggeration: data.terrain_exaggeration, terrainLimitMaximumLevel: data.terrain_limit_maximum_level, terrainMaximumLevel: data.terrain_maximum_level, terrainCacheTiles: data.terrain_cache_tiles }); } function to_server_config(config: Cesium_Graphics_Config): Cesium_Graphics_Config_Json { const normalized = normalize_cesium_graphics_config(config); return { debug_show_frames_per_second: normalized.debugShowFramesPerSecond, target_frame_rate: normalized.targetFrameRate, use_browser_recommended_resolution: normalized.useBrowserRecommendedResolution, resolution_scale: normalized.resolutionScale, msaa_samples: normalized.msaaSamples, fxaa: normalized.fxaa, shadows: normalized.shadows, enable_lighting: normalized.enableLighting, solar_lighting: normalized.solarLighting, solar_light_intensity: normalized.solarLightIntensity, maximum_screen_space_error: normalized.maximumScreenSpaceError, terrain_enabled_in_3d: normalized.terrainEnabledIn3D, terrain_exaggeration: normalized.terrainExaggeration, terrain_limit_maximum_level: normalized.terrainLimitMaximumLevel, terrain_maximum_level: normalized.terrainMaximumLevel, terrain_cache_tiles: normalized.terrainCacheTiles }; } export async function load_cesium_graphics_config(): Promise { const response = await axios.get("/map/graphics"); return from_server_config(response.data); } export async function save_cesium_graphics_config(config: Cesium_Graphics_Config): Promise { const response = await axios.post("/map/graphics", to_server_config(config)); const normalized = from_server_config(response.data); emit_cesium_graphics_config(normalized); return normalized; } export function emit_cesium_graphics_config(config: Cesium_Graphics_Config) { const normalized = normalize_cesium_graphics_config(config); window.dispatchEvent(new CustomEvent(cesium_graphics_config_event, {detail: normalized})); } export class Cesium_Render_Settings extends enhance.Base { config: Cesium_Graphics_Config = {...default_cesium_graphics_config}; number_input_drafts: Partial> = {}; async on_mount() { this.config = await load_cesium_graphics_config(); this.number_input_drafts = {}; this.flush(); } number_input_value(key: Number_Config_Key): number | null { return Object.prototype.hasOwnProperty.call(this.number_input_drafts, key) ? this.number_input_drafts[key]! : this.config[key]; } set_number(key: Number_Config_Key, value: number | null) { this.number_input_drafts[key] = value; if (value === null) { this.flush(); return; } this.config = normalize_cesium_graphics_config({...this.config, [key]: value}); emit_cesium_graphics_config(this.config); this.flush(); } blur_number(key: Number_Config_Key) { if (Object.prototype.hasOwnProperty.call(this.number_input_drafts, key)) { delete this.number_input_drafts[key]; this.flush(); } } set_boolean(key: Boolean_Config_Key, value: boolean) { this.config = normalize_cesium_graphics_config({...this.config, [key]: value}); emit_cesium_graphics_config(this.config); this.flush(); } reset() { this.config = {...default_cesium_graphics_config}; this.number_input_drafts = {}; emit_cesium_graphics_config(this.config); this.flush(); } apply_preset(key: keyof typeof graphics_presets) { this.config = {...graphics_presets[key]}; this.number_input_drafts = {}; emit_cesium_graphics_config(this.config); this.flush(); } async save() { this.config = await save_cesium_graphics_config(this.config); this.number_input_drafts = {}; message.success("Cesium画质设置已保存"); this.flush(); } render_label(label: string, key: keyof Cesium_Graphics_Config) { return ( {label} {graphics_help[key].map((line) =>
{line}
)}}>
); } render_switch(label: string, key: Boolean_Config_Key) { return ( {this.render_label(label, key)} this.set_boolean(key, checked)} /> ); } render_number(label: string, key: Number_Config_Key, min: number, max: number, step: number) { return ( this.set_number(key, value)} onBlur={() => this.blur_number(key)} /> ); } render_terrain_exaggeration() { const disabled = !this.config.terrainEnabledIn3D; return ( {this.render_label("地形起伏倍率", "terrainExaggeration")} this.set_number("terrainExaggeration", value)} onBlur={() => this.blur_number("terrainExaggeration")} /> ); } render(props: any) { return (

Cesium画质设置

{this.render_switch("显示FPS", "debugShowFramesPerSecond")} {this.render_number("目标帧率", "targetFrameRate", 1, 120, 1)} {this.render_switch("浏览器推荐分辨率", "useBrowserRecommendedResolution")} {this.render_number("分辨率缩放", "resolutionScale", 0.2, 1.5, 0.1)} {this.render_number("MSAA采样", "msaaSamples", 1, 8, 1)} {this.render_switch("FXAA", "fxaa")} {this.render_switch("阴影", "shadows")} {this.render_switch("地球光照", "enableLighting")} {this.render_switch("真实日照", "solarLighting")} {this.render_number("太阳光强度", "solarLightIntensity", 0, 10, 0.1)} {this.render_number("地球细节误差", "maximumScreenSpaceError", 1, 16, 1)} {this.render_switch("3D启用地形", "terrainEnabledIn3D")} {this.render_terrain_exaggeration()} {this.render_switch("限制地形层级", "terrainLimitMaximumLevel")} {this.render_number("地形最大层级", "terrainMaximumLevel", 0, 24, 1)} {this.render_number("地形缓存瓦片数", "terrainCacheTiles", 16, 2048, 16)}
); } }