three-base 2.0.188 → 2.0.190

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/ThreeBase.ts CHANGED
@@ -113,22 +113,24 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
113
113
  // (纹理被欠采样时的拍频,必须靠提高实际采样率才能消除)。
114
114
  // 1 = 关闭超采样(仅设备 pixelRatio);2 = 2 倍超采样(质量最高,片元开销 ×4,需强 GPU)。
115
115
  renderScale: number = 1;
116
- // 全局超采样/分辨率倍率(SSAA):base / 深度 / 法线 / 效果 mask / 屏幕合成 全部按
117
- // effectiveRatio × resolution 内部超采样渲染,终处理 FXAA 降采样输出到屏幕。
118
- // 全链路同倍率 → 各 RT 严格 1:1 对齐:几何锯齿、效果边缘锯齿、辉光漏光(深度 vs mask 错位)、
119
- // mask 合成 UV 缩放错位、SSAO 深度错位变黑 一并消除。
120
- // ⭐ 现行 4(2026-10-27 用户点名「只把超采样倍率改成 4」,setter 的夹上限也是 4):4 = 每维 4×、总像素 16×,
121
- // 全链路填充与全部 RT 显存同比涨,卡顿风险由用户自测裁决。
122
- // 上一档 1 出自 2026-10-21「有点卡,分辨率改成 1」;再早的默认 2 = 每维 2×、总像素 4×,
123
- // 当时量过:35 张 RT 显存约 1.1 GB(1920×1080 画布)。1 那档几何硬边锯齿只由终处理 FXAA 兜底。
124
- // 历史坑:baseRenderScale/auxRenderScale/effectRenderScale 三参各自为政,倍率不同步必错位,
125
- // 故统一为单参数 resolution 管全局。
116
+ // 超采样倍率(SSAA)**只作用于 baseRenderTarget**(2026-10-27 需求:「只有 baseRT 的超采样参数是 4,
117
+ // 其他的 RT 超采样参数是 1」):base 按 effectiveRatio × resolution 渲染,深度 / 法线 / 效果 mask /
118
+ // 屏幕合成一律按 effectiveRatio × auxResolution(= 1)渲染。
119
+ // ⚠️ 两条与「全链路同倍率」那版不同、看得见的后果:
120
+ // ① base(4×) → screen(1×) 那次拷贝是**降采样**(RT 不长 mipmap,线性滤波只取到 4 个纹素)
121
+ // ⇒ base 的超采样不会原样带到屏幕上,末端仍靠 FXAA 兜锯齿。
122
+ // ② 描边/发光/覆盖/磨砂的 mask 与 base 深度都从 4× 降到 1×,而它们要合成进 4× 的 base
123
+ // ⇒ 这些效果的边缘按 1× 精度放大,比同倍率那版软;UV 仍严格对齐(错位只发生在精度,不在位置)。
124
+ // 现行 resolution = 4(setter 夹的上限也是 4);上一档 1 出自 2026-10-21「有点卡,分辨率改成 1」。
125
+ // 历史:再早是 base / 深度 / 法线 / mask / 屏幕 **共用同一个 resolution**,2026-10-27 起拆成 base 与其余两档。
126
126
 
127
- // ⚠️ 必须用 getter/setter,不能是普通字段:所有 RT 尺寸只在 updateRenderTargetSizes()
128
- // 里按 effectiveRatio × resolution 计算,而该方法过去只被 setSize()(容器尺寸变化)调用
127
+ // ⚠️ 必须用 getter/setter,不能是普通字段:RT 尺寸只在 updateRenderTargetSizes()
128
+ // 里按 effectiveRatio × resolution(base)/ × auxResolution(其余)计算,而该方法过去只被 setSize()(容器尺寸变化)调用
129
129
  // → 运行时改 resolution 不会重建任何 RT,画面毫无变化(表现为"调分辨率没感觉")。
130
130
  // 故 setter 里改动倍率后立刻重建全部 RT。
131
131
  private _resolution: number = 4;
132
+ /** baseRenderTarget 之外**全部** RT 的倍率(深度 / 法线 / 各效果 mask / 屏幕合成);1 = 不超采样 */
133
+ auxResolution: number = 1;
132
134
 
133
135
  get resolution(): number {
134
136
  return this._resolution;
@@ -232,6 +234,9 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
232
234
  }
233
235
  const w = Math.max(1, Math.round(this.size.x * this.effectiveRatio * this._resolution));
234
236
  const h = Math.max(1, Math.round(this.size.y * this.effectiveRatio * this._resolution));
237
+ // 除 base 之外的 RT 走 auxResolution(= 1):屏幕合成目标不跟着 base 超采样
238
+ const aw = Math.max(1, Math.round(this.size.x * this.effectiveRatio * this.auxResolution));
239
+ const ah = Math.max(1, Math.round(this.size.y * this.effectiveRatio * this.auxResolution));
235
240
  const opts = {
236
241
  minFilter: this._rtOptions.filter,
237
242
  magFilter: this._rtOptions.filter,
@@ -244,24 +249,23 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
244
249
  this.baseRenderTarget = new WebGLRenderTarget(w, h, { ...opts });
245
250
  this.baseRenderTarget.texture.colorSpace = this._rtOptions.colorSpace;
246
251
  this.screenRenderTarget.dispose();
247
- this.screenRenderTarget = new WebGLRenderTarget(w, h, { ...opts });
252
+ this.screenRenderTarget = new WebGLRenderTarget(aw, ah, { ...opts });
248
253
  this.screenRenderTarget.texture.colorSpace = this._rtOptions.colorSpace;
249
254
  this.needRender = true;
250
255
  }
251
256
 
252
- // 效果 mask 像素比硬上限:避免高 dpr 屏(如 2×)上 effectiveRatio×resolution 爆到 4×,
253
- // 让效果 mask RT(含辉光多次高斯迭代)填充量失控。经验值 2×:相对设备 dpr 输出足够平滑,
254
- // 且把"效果 mask 超采样"的填充量控制在场景同级的 2× 以内(原本 3× 在高 dpr 屏是"还是卡"的高嫌疑点)。
257
+ // 效果 mask 像素比硬上限(历史旋钮,当前渲染链未再引用):避免高 dpr 屏(如 2×)上
258
+ // effectiveRatio×resolution 爆到 4×,让效果 mask RT(含辉光多次高斯迭代)填充量失控。
259
+ // 2026-10-27 起 mask/深度/法线/屏幕全部按 auxResolution(= 1)走,超采样只留在 base 一张。
255
260
  effectMaxPixelRatio: number = 2;
256
- // 超采样倍率已统一为上方 resolution 字段(base / 深度 / 法线 / mask / 屏幕全部取它)。
257
- // 辅助通道(深度/法线)分辨率系数。
261
+ // 辅助通道(深度/法线)与各效果 mask、屏幕合成的倍率见下方 auxResolution 字段(= 1,不超采样)。
258
262
  // 关键:描边/发光/辉光/毛玻璃都拿 baseDepthTexture 做遮挡比对(deathTestShader 以全分辨率
259
263
  // UV 采样半分辨率深度,并与全分辨率物体深度做硬阈值比较)。半分辨率深度(Nearest 滤波)被
260
264
  // 放大采样后与锐利的物体深度在边缘逐像素翻转 discard,产生边缘摩尔纹/锯齿——这是这 4 个特效
261
- // 共同的摩尔纹根因。故深度必须与 mask 同分辨率(全分辨率),1:1 采样才无放大错位。
262
- // 法线仅 SSAO 使用(首页默认关闭、不渲染),全分辨率无额外开销。
263
- // 之前为"不卡"降到 0.5 反而引入摩尔纹,已恢复为 1。
264
- // 辅助通道(深度/法线)倍率已并入 resolution,不再单独设 auxRenderScale。
265
+ // 共同的摩尔纹根因。故深度与 mask 必须同倍率(都取 auxResolution),彼此 1:1 采样才无放大错位。
266
+ // 法线仅 SSAO 使用(首页默认关闭、不渲染),无额外开销。
267
+ // 之前为"不卡"把深度单独降到 0.5 反而引入摩尔纹,已恢复为 1。
268
+ // 辅助通道(深度/法线)倍率 = auxResolution,与 base 的 resolution 分离(2026-10-27 需求)。
265
269
  // 实际帧缓冲像素比 = min( max(devicePixelRatio, minDevicePixelRatio) * renderScale, maxPixelRatio )
266
270
  get effectiveRatio(): number {
267
271
  return Math.min(Math.max(window.devicePixelRatio, this.minDevicePixelRatio) * this.renderScale, this.maxPixelRatio);
@@ -552,7 +556,8 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
552
556
  this.orthographicCamera.right = width / height;
553
557
  this.renderer.setPixelRatio(Math.max(window.devicePixelRatio, this.minDevicePixelRatio)),
554
558
  this.renderer.setSize(width, height);
555
- // 所有 RT 尺寸按 effectiveRatio × resolution 重建(容器尺寸变化与 resolution 变化共用)
559
+ // base 按 effectiveRatio × resolution、其余 RT 按 effectiveRatio × auxResolution 重建
560
+ // (容器尺寸变化与 resolution 变化共用)
556
561
  this.updateRenderTargetSizes(width, height);
557
562
  this.size.set(width, height);
558
563
  setTimeout(() => {
@@ -564,7 +569,8 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
564
569
  }
565
570
 
566
571
  /**
567
- * 按「容器尺寸 × effectiveRatio × resolution」重建所有渲染目标尺寸。
572
+ * 按「容器尺寸 × effectiveRatio」重建所有渲染目标尺寸:base 再乘 resolution,
573
+ * 其余(深度 / 法线 / mask / 屏幕合成)再乘 auxResolution。
568
574
  * 由 setSize()(容器尺寸变化)与 resolution setter(运行时调倍率)共用 ——
569
575
  * 关键:resolution 是运行时可调的性能/质量旋钮,改完必须立刻重建 RT,
570
576
  * 否则倍率变了而 RT 尺寸没变,画面毫无变化(历史坑:"调分辨率没感觉")。
@@ -573,18 +579,21 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
573
579
  // 防御 0 尺寸:RT 不接受 0,否则 WebGL 报 framebuffer incomplete
574
580
  const w = Math.max(1, Math.round(width * this.effectiveRatio * this._resolution));
575
581
  const h = Math.max(1, Math.round(height * this.effectiveRatio * this._resolution));
582
+ const aw = Math.max(1, Math.round(width * this.effectiveRatio * this.auxResolution));
583
+ const ah = Math.max(1, Math.round(height * this.effectiveRatio * this.auxResolution));
576
584
 
577
- // 主画面:超采样渲染,纹理摩尔纹在此被根治
585
+ // 主画面:只有这一张吃 resolution 超采样(纹理摩尔纹在此被根治)
578
586
  this.baseRenderTarget.setSize(w, h);
579
- // 深度/法线辅助通道:与 base 同倍率,保证 depthTest 1:1 无漏光、SSAO 深度不黑。
587
+ // 深度/法线辅助通道:按 auxResolution(= 1)。与 base 之间**倍率不同但 UV 一致**,
588
+ // 遮挡判定读的是归一化 UV ⇒ 位置不错位,只是精度按 1× 走(2026-10-27 需求点名要的这一档)。
580
589
  // 其 depthTexture(baseDepthTexture) 尺寸由 renderer 在 setRenderTarget 时
581
590
  // 强制同步为 depthRenderTarget 尺寸,下面再手动对齐一次避免首帧抖动。
582
- this.depthRenderTarget.setSize(w, h);
583
- this.normalRenderTarget.setSize(w, h);
584
- // 屏幕合成目标:同样跟随 resolution,全链路 1:1 对齐
585
- this.screenRenderTarget.setSize(w, h);
586
- this.baseDepthTexture.image.width = w;
587
- this.baseDepthTexture.image.height = h;
591
+ this.depthRenderTarget.setSize(aw, ah);
592
+ this.normalRenderTarget.setSize(aw, ah);
593
+ // 屏幕合成目标:同样按 auxResolution,与 base 之间的那次拷贝是降采样
594
+ this.screenRenderTarget.setSize(aw, ah);
595
+ this.baseDepthTexture.image.width = aw;
596
+ this.baseDepthTexture.image.height = ah;
588
597
  // 立即渲染一帧,避免静态场景下改了倍率却停在旧画面
589
598
  this.needRender = true;
590
599
  }
@@ -997,10 +1006,10 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
997
1006
  renderVisibleDepthPass(this.renderer, camera, this.scene, this.depthRenderTarget);
998
1007
  // 切回 baseRenderTarget,后处理 pass 需要把效果叠加到颜色渲染目标上
999
1008
  this.renderer.setRenderTarget(this.baseRenderTarget);
1000
- // 效果 mask 灰度图尺寸 = effectiveRatio × resolution,与 baseRenderTarget / baseDepthTexture 同倍率,
1001
- // 全链路 1:1 对齐(mask 跟随 resolution,深度也跟随 resolution,辉光漏光/mask 合成 UV 错位均消除)。
1002
- // 几何/效果边缘抗锯齿由「baseRenderTarget/screenRenderTarget 超采样(resolution) + 终处理 FXAA 降采样」承担。
1003
- const dpr = this.effectiveRatio * this.resolution;
1009
+ // 效果 mask 灰度图尺寸 = effectiveRatio × auxResolution(= 1),与 baseDepthTexture 同倍率;
1010
+ // baseRenderTarget 是唯一的超采样档(resolution),故 mask/深度 → base 的合成是「1× 精度放大到 4×」。
1011
+ // 位置靠归一化 UV 对齐,不会偏移;只是描边/发光/覆盖/磨砂的边缘精度按 1× 走(2026-10-27 需求)。
1012
+ const dpr = this.effectiveRatio * this.auxResolution;
1004
1013
  // SSAO 后处理(基于深度计算遮蔽,乘法叠加):数据准备 + 计算全在 ssao_pass 内部,
1005
1014
  // 结果输出到「调用时绑定的渲染目标」。
1006
1015
  // ⚠️ 顺序(2026-09-14 皇上旨):SSAO 必须排在对象级特效(描边/发光/bloom/覆盖/磨砂)之前——
@@ -1011,18 +1020,18 @@ export default class ThreeBase extends EventDispatcher<ThreeBaseEventTypeMap> {
1011
1020
  // 后处理特效渲染(描边,发光,颜色覆盖,bloom,磨砂玻璃):叠加到 baseRenderTarget(此时已含 SSAO)
1012
1021
  this.scene.traverseVisible((child: Object3D) => {
1013
1022
  if (child instanceof Mesh || child instanceof Line || child instanceof Points) {
1014
- stroke_pass(this.renderer, camera, child, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1015
- glow_pass(this.renderer, camera, child, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1016
- bloom_pass(this.renderer, camera, child, this.baseRenderTarget, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1023
+ stroke_pass(this.renderer, camera, child, this.baseDepthTexture, dpr);
1024
+ glow_pass(this.renderer, camera, child, this.baseDepthTexture, dpr);
1025
+ bloom_pass(this.renderer, camera, child, this.baseRenderTarget, this.baseDepthTexture, dpr);
1017
1026
  }
1018
1027
  });
1019
1028
  // 自定义描边对象(如 InstancedChild 的选择代理):不在 scene 内,单独在效果阶段描边。
1020
1029
  // 代理用实例自身的 matrixWorld + 同款 geometry/material 重建,故其深度/灰度与真实实例严格对齐,
1021
1030
  // 并复用同一张 baseDepthTexture 做遮挡判定(被其他物体挡住的部分不描边)。
1022
1031
  for (const so of this._strokeObjects) {
1023
- stroke_pass(this.renderer, camera, so, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1024
- glow_pass(this.renderer, camera, so, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1025
- bloom_pass(this.renderer, camera, so, this.baseRenderTarget, this.baseDepthTexture, this.effectiveRatio * this.resolution);
1032
+ stroke_pass(this.renderer, camera, so, this.baseDepthTexture, dpr);
1033
+ glow_pass(this.renderer, camera, so, this.baseDepthTexture, dpr);
1034
+ bloom_pass(this.renderer, camera, so, this.baseRenderTarget, this.baseDepthTexture, dpr);
1026
1035
  }
1027
1036
  // 把 base(含 SSAO)+描边/发光/bloom 合成结果拷到屏幕合成目标,后续 颜色叠加/磨砂玻璃
1028
1037
  // 在此目标上叠加;最后统一跑一次 FXAA 终处理输出到屏幕,覆盖全部后处理的锯齿/摩尔纹。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-base",
3
- "version": "2.0.188",
3
+ "version": "2.0.190",
4
4
  "description": "A Three.js extension library providing common 3D components, animations, materials, and utilities",
5
5
  "author": "xiaosihan",
6
6
  "publishConfig": {