three-base 2.0.172 → 2.0.175
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/FPScontrol.d.ts +15 -24
- package/FPScontrol.js +63 -87
- package/FPScontrol.ts +64 -88
- package/Ground1/Ground1.d.ts +4 -2
- package/Ground1/Ground1.js +77 -57
- package/Ground1/Ground1.ts +77 -57
- package/Mirror.js +0 -4
- package/Mirror.ts +0 -4
- package/MktMesh/MktMesh.d.ts +3 -2
- package/MktMesh/MktMesh.js +27 -8
- package/MktMesh/MktMesh.ts +24 -8
- package/MktMesh/mktMappingShader.d.ts +6 -5
- package/MktMesh/mktMappingShader.js +26 -19
- package/MktMesh/mktMappingShader.ts +28 -19
- package/MktMesh/mktShader.d.ts +127 -58
- package/MktMesh/mktShader.js +192 -117
- package/MktMesh/mktShader.ts +251 -127
- package/MktMesh/panoSlotRoster.d.ts +2 -2
- package/MktMesh/panoSlotRoster.js +9 -7
- package/MktMesh/panoSlotRoster.ts +9 -7
- package/MktMesh/panoTileStitcher.d.ts +20 -13
- package/MktMesh/panoTileStitcher.js +97 -76
- package/MktMesh/panoTileStitcher.ts +119 -90
- package/PipeMesh/PipeJoint.d.ts +22 -0
- package/PipeMesh/PipeJoint.js +33 -0
- package/PipeMesh/PipeJoint.ts +38 -0
- package/PipeMesh/PipeMesh.d.ts +94 -22
- package/PipeMesh/PipeMesh.js +192 -53
- package/PipeMesh/PipeMesh.ts +223 -76
- package/PipeMesh/PipeStraight.d.ts +19 -0
- package/PipeMesh/PipeStraight.js +34 -0
- package/PipeMesh/PipeStraight.ts +45 -0
- package/PipeMesh/axisSnap.d.ts +52 -0
- package/PipeMesh/axisSnap.js +52 -0
- package/PipeMesh/axisSnap.ts +93 -0
- package/PipeMesh/directRoute.d.ts +85 -0
- package/PipeMesh/directRoute.js +126 -0
- package/PipeMesh/directRoute.ts +190 -0
- package/PipeMesh/index.d.ts +6 -0
- package/PipeMesh/index.js +6 -0
- package/PipeMesh/index.ts +7 -1
- package/PipeMesh/pipeMaterial.d.ts +16 -0
- package/PipeMesh/pipeMaterial.js +23 -0
- package/PipeMesh/pipeMaterial.ts +26 -0
- package/ThreeBase.d.ts +1 -39
- package/ThreeBase.js +38 -147
- package/ThreeBase.ts +1176 -1302
- package/instanced/InstancedBatch.d.ts +14 -1
- package/instanced/InstancedBatch.js +53 -3
- package/instanced/InstancedBatch.ts +62 -3
- package/instanced/InstancedChild.d.ts +24 -0
- package/instanced/InstancedChild.js +40 -0
- package/instanced/InstancedChild.ts +41 -0
- package/instanced/InstancedGroup.d.ts +48 -0
- package/instanced/InstancedGroup.js +45 -1
- package/instanced/InstancedGroup.ts +85 -0
- package/package.json +2 -7
- package/pass/debugDepthState.d.ts +16 -8
- package/pass/debugDepthState.js +4 -0
- package/pass/debugDepthState.ts +27 -9
- package/pass/glow_pass.js +11 -94
- package/pass/glow_pass.ts +11 -97
- package/pass/ssao_pass.d.ts +9 -23
- package/pass/ssao_pass.js +41 -128
- package/pass/ssao_pass.ts +36 -166
- package/pass/stroke_pass.js +3 -0
- package/pass/stroke_pass.ts +4 -0
- package/shaders/debugDepthCompareShader.d.ts +1 -1
- package/shaders/debugDepthCompareShader.js +1 -1
- package/shaders/debugDepthCompareShader.ts +1 -1
- package/shaders/debugDepthJudgeShader.d.ts +1 -1
- package/shaders/debugDepthJudgeShader.js +1 -1
- package/shaders/debugDepthJudgeShader.ts +1 -1
- package/PipeMesh/PathGeometry.d.ts +0 -44
- package/PipeMesh/PathGeometry.js +0 -307
- package/PipeMesh/PathGeometry.ts +0 -343
- package/PipeMesh/PathPoint.d.ts +0 -17
- package/PipeMesh/PathPoint.js +0 -31
- package/PipeMesh/PathPoint.ts +0 -36
- package/PipeMesh/PathPointList.d.ts +0 -31
- package/PipeMesh/PathPointList.js +0 -209
- package/PipeMesh/PathPointList.ts +0 -227
- package/PipeMesh/PathTubeGeometry.d.ts +0 -25
- package/PipeMesh/PathTubeGeometry.js +0 -130
- package/PipeMesh/PathTubeGeometry.ts +0 -146
- package/WaterFlow/FloatingDuck.d.ts +0 -88
- package/WaterFlow/FloatingDuck.js +0 -307
- package/WaterFlow/FloatingDuck.ts +0 -445
- package/WaterFlow/GPUFluidSimulation.d.ts +0 -56
- package/WaterFlow/GPUFluidSimulation.js +0 -785
- package/WaterFlow/GPUFluidSimulation.ts +0 -827
- package/WaterFlow/ReservoirSystem.d.ts +0 -106
- package/WaterFlow/ReservoirSystem.js +0 -621
- package/WaterFlow/ReservoirSystem.ts +0 -754
- package/WaterFlow/SPHField.d.ts +0 -35
- package/WaterFlow/SPHField.js +0 -204
- package/WaterFlow/SPHField.ts +0 -228
- package/WaterFlow/ScreenSpaceFluidPass.d.ts +0 -69
- package/WaterFlow/ScreenSpaceFluidPass.js +0 -736
- package/WaterFlow/ScreenSpaceFluidPass.ts +0 -789
- package/WaterFlow/ShallowWaterSurface.d.ts +0 -45
- package/WaterFlow/ShallowWaterSurface.js +0 -441
- package/WaterFlow/ShallowWaterSurface.ts +0 -510
- package/WaterFlow/WaterFlow.d.ts +0 -87
- package/WaterFlow/WaterFlow.js +0 -171
- package/WaterFlow/WaterFlow.ts +0 -219
- package/WaterFlow/types.d.ts +0 -161
- package/WaterFlow/types.js +0 -1
- package/WaterFlow/types.ts +0 -189
- package/WaterFlow/waterFlowPass.d.ts +0 -3
- package/WaterFlow/waterFlowPass.js +0 -20
- package/WaterFlow/waterFlowPass.ts +0 -40
- package/shaders/debugExcludedShader.d.ts +0 -15
- package/shaders/debugExcludedShader.js +0 -48
- package/shaders/debugExcludedShader.ts +0 -50
package/FPScontrol.ts
CHANGED
|
@@ -6,12 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { BufferAttribute, Camera, Clock, Euler, EventDispatcher, Group, InstancedMesh, Intersection, Matrix3, Matrix4, Mesh, Object3D, Quaternion, Ray, Raycaster, Scene, Vector3 } from "three";
|
|
8
8
|
import { MathUtils } from "three";
|
|
9
|
-
//
|
|
10
|
-
// collisionOBB.ts 纯几何模块与其单测保留,但 FPScontrol 不再引用。)
|
|
9
|
+
// 碰撞完全由围绕玩家视点的射线完成;FPScontrol 不使用任何体积碰撞体。
|
|
11
10
|
const { degToRad, clamp } = MathUtils;
|
|
12
11
|
|
|
13
12
|
// 「矩形射线法」的水平 far 口径:斜弦画长 = R√2(四弦首尾闭合的唯一解,勿再改回)。
|
|
14
|
-
import { Capsule } from "three/examples/jsm/math/Capsule";
|
|
15
13
|
// 粗辅助线:WebGL 下 `LineBasicMaterial.linewidth` 恒被驱动钳成 1px(Chrome/ANGLE 一律 1px,
|
|
16
14
|
// 调大无效),要真的变粗必须走 `LineSegments2 + LineMaterial`(shader 里按像素铺quad)。
|
|
17
15
|
// 本仓库先例:src/views/home/homeRender.ts:71-73, 344-358。
|
|
@@ -263,7 +261,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
263
261
|
/** 是否允许跳跃(空格触发)。false 时按空格不跳。默认 true */
|
|
264
262
|
public jumpEnabled: boolean = true;
|
|
265
263
|
/** 跳跃初速度(垂直方向,m/s)。
|
|
266
|
-
* ⚠️ 2026-09-27 治「跳跃翻墙」:原 15(当时重力 30)→ 顶点 3.75m 远超墙高 2m
|
|
264
|
+
* ⚠️ 2026-09-27 治「跳跃翻墙」:原 15(当时重力 30)→ 顶点 3.75m 远超墙高 2m,跳起后视点越过墙顶
|
|
267
265
|
* 造成水平失明窗口,人带着水平速度飞越墙平面、落地在墙外侧被越推越远(探针实测 z=-187)。
|
|
268
266
|
* 定案 8 ⇒ 顶点 1.07m:仍可跳上 0.8m 高台,且 yBottom 恒低于「墙顶 2.0 − 步高 0.1」,
|
|
269
267
|
* 与主循环竖直门配合后失明窗口彻底消失。**顶点**才是防翻墙的那条线,不是初速度本身。
|
|
@@ -311,7 +309,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
311
309
|
// 位置修正沿该面法线一格一格推,每格 = min(RETREAT_SKIN, deficit) ⇒ 大欠距仍走整格,
|
|
312
310
|
// **收尾那一格按欠距截短** ⇒ 落点正好贴在包络边上(旧写法最后一格也推满 1cm → 过冲 → 来回甩)。
|
|
313
311
|
// 速度只削「往墙里」那一份(v −(v·n)n),切向保留 ⇒ 贴墙能滑,撞墙不再「加速→清零」锯齿。
|
|
314
|
-
// ⇒ 停位仍只由 **R** 一个数决定(R=1.06 → 沿法线 0.749~1.06m
|
|
312
|
+
// ⇒ 停位仍只由 **R** 一个数决定(R=1.06 → 沿法线 0.749~1.06m);射线包络决定视点与墙之间
|
|
315
313
|
// 还留 0.40~0.71m 空气隙,这是皇上 2026-09-06 终裁布局自带、且亲眼验收过的量,不要当 bug 修。
|
|
316
314
|
/**
|
|
317
315
|
* 单步最大位移(连续碰撞检测用):每帧位移会被切成若干 ≤ 本值的小步,逐步做碰撞检测。
|
|
@@ -324,8 +322,8 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
324
322
|
public MAX_STEP_DISTANCE: number = 0.1;
|
|
325
323
|
/** 位移细分的最大段数(防止极端速度/长卡顿把一帧切成几百步拖垮帧率) */
|
|
326
324
|
public MAX_STEP_COUNT: number = 32;
|
|
327
|
-
// 垂直碰撞范围 =
|
|
328
|
-
// =
|
|
325
|
+
// 垂直碰撞范围 = 从玩家视点到「该射线所保护的那一面」的距离
|
|
326
|
+
// = 视点到脚下射线保护面的距离 1.0(下方两个常量是**口径**,不是几何)
|
|
329
327
|
// 对应 xsh-react-three 中 collideVolume(=3)= 控制器到地面的距离
|
|
330
328
|
// xsh 中 far = collideVolume, 响应 = collideVolume - distance
|
|
331
329
|
// 此处 far = DOWN_RANGE/UP_RANGE + SKIN,响应 = DOWN_RANGE/UP_RANGE − distance
|
|
@@ -334,9 +332,9 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
334
332
|
// 我提醒过这一条常量身兼两职(探测长度 + 站位基准),他追选定案「**直接改站位基准**」,
|
|
335
333
|
// 所以拆成两条各自独立的常量,**不再**共用 VERTICAL_RANGE=1.0:
|
|
336
334
|
// · DOWN_RANGE = 1.5:下射线 far = 1.5+SKIN,响应 downPen = 1.5 − distance
|
|
337
|
-
// → 落地后**眼睛离地 1.5m
|
|
335
|
+
// → 落地后**眼睛离地 1.5m**。
|
|
338
336
|
// · UP_RANGE = 0.3:上射线 far = 0.3+SKIN,响应 upPen = 0.3 − distance
|
|
339
|
-
// → 头顶到面时**眼睛离顶 0.3m
|
|
337
|
+
// → 头顶到面时**眼睛离顶 0.3m**,由上射线阻止继续上移。
|
|
340
338
|
// 且 0.3~1.0m 之间的低矮障碍物从此探不到(上射线变短的直接后果)。
|
|
341
339
|
// ⚠️ 跳跃/重力/台阶吸附的其余口径一字未动,只换这两个基准数。
|
|
342
340
|
// ⭐ 2026-09-26 皇上旨「能调节的参数都做滚轮输入框」→ 两条改为公共可写(面板「参数旋钮」可拧),
|
|
@@ -451,14 +449,14 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
451
449
|
* 即 R 就是「玩家中心离墙多远开始判定碰撞」的半径。若 R 小于停位需求,人必须**先陷进墙里**
|
|
452
450
|
* 才会被射线检测到,随后每帧被位置修正弹回一点 → 皇上所见「侧撞弹很远」。
|
|
453
451
|
* 探针实测(`tmp/TMP_R_side_effect.mjs`,斜 45° 撞墙角 / 正对单墙同口径):
|
|
454
|
-
* R=0.30 → 停位垂距 **0.2128
|
|
452
|
+
* R=0.30 → 停位垂距 **0.2128**,射线包络过小,容易贴墙。
|
|
455
453
|
* R=0.50 → 0.3541(贴到墙面) 每帧 0.036
|
|
456
|
-
* R=0.60 → 0.4243
|
|
454
|
+
* R=0.60 → 0.4243。
|
|
457
455
|
* R=0.80 → 0.5448 ✓ / R=1.06 → 0.7247 ✓(每帧修正仅 0.002)
|
|
458
|
-
* ⇒ 取 **0.6
|
|
456
|
+
* ⇒ 取 **0.6**:为射线包络保留足够的贴墙间距。
|
|
459
457
|
* ⭐ 2026-10-08 皇上旨:下限放宽到 **0.2**、上限收到 **1.0**(编辑框范围 0.2~1.0)。
|
|
460
458
|
* ⚠️ R<0.6 仍会贴墙穿模(R=0.5 贴到墙面、R=0.3 明显穿模),皇上按需拧;下限放开是给窄通道/贴墙演示留余地。
|
|
461
|
-
* ⭐ 2026-10-11 A 档复核(tmp→test/fpsCollideFit.test.ts,正对 1m
|
|
459
|
+
* ⭐ 2026-10-11 A 档复核(tmp→test/fpsCollideFit.test.ts,正对 1m 墙):
|
|
462
460
|
* 停位现在**逐位等于 R**(眼停在面前 R 处 ⇒ 身体入墙深度 = 0.35 − R,一条直线):
|
|
463
461
|
* R=0.20 → 入墙 **0.1500m** R=0.30 → **0.0500m** R=0.35 → **0.0000m**
|
|
464
462
|
* R=0.50 / 0.60 / 1.00 → **0.0000m**(身体不出面)
|
|
@@ -647,19 +645,6 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
647
645
|
desc: "⭐ 2026-10-11 皇上 A 档改判:每轮**只沿「欠距最大那一面」的法线**推一格,且这一格取 min(本值, 该面欠距) ⇒ 大欠距仍是一格一格往外走(皇上 10-07「1 个单位 1 个单位的修正」),只有**收尾那一格按欠距截短**,落点贴在包络边上(旧写法每条命中射线各推满一格、最后一格也推满 ⇒ 过冲 ⇒ 贴墙来回甩 = 抖动)。" +
|
|
648
646
|
"⚠️ 上限以下别贪小:0.03/0.02/0.01 体检台判级完全相同,拧到 0.005 才有改善但「跳跃+冲刺+掉帧」那个现场开始穿墙(格长 = 每帧出水量)。迭代推到全部射线脱开为止",
|
|
649
647
|
},
|
|
650
|
-
{
|
|
651
|
-
group: "回弹与手感",
|
|
652
|
-
label: "每小帧轮数上限",
|
|
653
|
-
key: "ROUNDS_PER_SUBSTEP",
|
|
654
|
-
min: 1,
|
|
655
|
-
max: 500,
|
|
656
|
-
step: 1,
|
|
657
|
-
digits: 0,
|
|
658
|
-
unit: "轮",
|
|
659
|
-
desc: "⭐ 皇上 2026-10-07 裁决加:一个物理小帧里 while 最多走几轮就落地(默认 500 = 与「每帧迭代预算」同档 ⇒ 现状一字不变)。" +
|
|
660
|
-
"**这颗才是「一次修多少」的真闸门**:10-07 实测每次落地量 = 这一小帧往墙里挤进多深(2/8/10cm 都这么来),跟一格多远无关 ⇒ 要「一次只修几格、下一小帧再修」只能拧这里。" +
|
|
661
|
-
"⚠️ 拧小的代价:一秒能挤出去的总量变小,高速冲刺顶墙时「挤进来的比推出去的快」⇒ 越陷越深(格长拧到 0.005 那档体检台 40 号现场就判 ❌穿墙,同一个机理)。先拧着看手感,别把默认改小",
|
|
662
|
-
},
|
|
663
648
|
// ===== 移动与视角 =====
|
|
664
649
|
{
|
|
665
650
|
group: "移动与视角",
|
|
@@ -829,7 +814,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
829
814
|
// ⚠️ 只闸水平+竖向的**位置写入**;削速度、playerOnFloor、事件派发全部保留,
|
|
830
815
|
// 否则预览模式下重力会一帧帧攒着把人送到地心并触发掉出世界复位。
|
|
831
816
|
// ⭐⭐ 2026-09-26 皇上七旨「回弹预览开了, 就关不掉」⇒ 本字段改成**带暂存的访问器**:
|
|
832
|
-
// 开 =
|
|
817
|
+
// 开 = 记下当前视点位置;关 = **还原**那个位置 + 清掉水平速度。
|
|
833
818
|
// 病根(实测,墙面板块 x∈[5.0,6.0]、R=1.06):预览既然不写位置,人就照着自己的
|
|
834
819
|
// 行走速度(speed=6 ⇒ 终端 37.5m/s ⇒ 每帧 0.62m)一路**穿墙走出去**,120 帧后中心
|
|
835
820
|
// 已经在 x=138;这一路上只要停在任何一处「两面墙的票互相抵消」的几何里,位置修正就
|
|
@@ -837,15 +822,13 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
837
822
|
// deficit 恒为 0.5600、位移 0。看着就是「预览关了位置修正也不回来了」。
|
|
838
823
|
// ⇒ 预览是**观察**用的,那一段时间里的位移本来就不作数,关掉即回到打开前那一刻。
|
|
839
824
|
private _previewRetreat: boolean = false;
|
|
840
|
-
/**
|
|
841
|
-
private
|
|
842
|
-
private _obsSavedEnd: Vector3 = new Vector3();
|
|
825
|
+
/** 开启预览那一刻的玩家视点位置(关闭时还原) */
|
|
826
|
+
private _obsSavedPosition: Vector3 = new Vector3();
|
|
843
827
|
/** 是否已有可还原的暂存(没进过观察模式就没有,避免构造期赋值把人还原到原点) */
|
|
844
828
|
private _obsHasSave: boolean = false;
|
|
845
|
-
/** 进观察模式(回弹预览 /
|
|
829
|
+
/** 进观察模式(回弹预览 / 单步):记下当前视点位置,退出时还原 */
|
|
846
830
|
private _saveObsPos(): void {
|
|
847
|
-
this.
|
|
848
|
-
this._obsSavedEnd.copy(this.playerCollider.end);
|
|
831
|
+
this._obsSavedPosition.copy(this.playerPosition);
|
|
849
832
|
this._obsHasSave = true;
|
|
850
833
|
}
|
|
851
834
|
/**
|
|
@@ -855,9 +838,8 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
855
838
|
*/
|
|
856
839
|
private _restoreObsPos(): void {
|
|
857
840
|
if (!this._obsHasSave) return;
|
|
858
|
-
this.
|
|
859
|
-
this.
|
|
860
|
-
this.camera.position.copy(this.playerCollider.end);
|
|
841
|
+
this.playerPosition.copy(this._obsSavedPosition);
|
|
842
|
+
this.camera.position.copy(this.playerPosition);
|
|
861
843
|
this.playerVelocity.x = 0;
|
|
862
844
|
this.playerVelocity.z = 0;
|
|
863
845
|
this._obsHasSave = false;
|
|
@@ -875,8 +857,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
875
857
|
// xsh 的骨架只有一个 while(`FPSControls.tsx` 的 `computerPosition`):
|
|
876
858
|
// ① 在**候选位置**(真实位置 + 已累计的 positionIncremental)上打水平射线
|
|
877
859
|
// ② 有命中 ⇒ 沿「离开命中点」的方向推**一小格**(它写死 0.03)并续跑下一轮
|
|
878
|
-
// ③ 一轮下来没命中 ⇒ goOnComputer=false ⇒ 收工;整圈上限 = MAX_ITERATIONS(皇上 10-03 旨:500
|
|
879
|
-
// 单趟 while 另有「本轮数上限」闸门(常态帧 = ROUNDS_PER_SUBSTEP,皇上 10-07 裁决加,默认 500 同档 ⇒ 不变)
|
|
860
|
+
// ③ 一轮下来没命中 ⇒ goOnComputer=false ⇒ 收工;整圈上限 = MAX_ITERATIONS(皇上 10-03 旨:500)轮
|
|
880
861
|
// ④ 循环外**唯一一次** position.add(incremental)
|
|
881
862
|
// ⇒ 旧的「测试法逐级逼近」(六旨:1 → 0.1 → 0.01 三档、武装/判定/接受/撤销、每帧出水量 ≥
|
|
882
863
|
// 进水量那套配套保险丝)**整条删除**。位置仍是一格一格挪出去的(皇上要的一步一步看那个形态不变),
|
|
@@ -902,18 +883,6 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
902
883
|
private _tempEuler: Euler = new Euler(0, 0, 0, "YXZ");
|
|
903
884
|
// 最大迭代次数(皇上 2026-10-03 旨:「那计算次数改成不超过 500」⇒ 100 → 500)
|
|
904
885
|
public MAX_ITERATIONS: number = 500;
|
|
905
|
-
/**
|
|
906
|
-
* 一个**物理小帧**里 while 最多走几轮就落地(皇上 2026-10-07 裁决「加一个每小帧轮数上限」)。
|
|
907
|
-
* 默认 500 = 与 `MAX_ITERATIONS` 同档 ⇒ 现状一字不变;拧小才是他要的「一次只修几格、下一小帧再修」。
|
|
908
|
-
* ⭐ 为什么拧格长没用(10-07 实测):常态帧会一直迭代到「8 条射线全部脱开」才落地 ⇒
|
|
909
|
-
* **每次落地量 = 这一小帧往墙里挤进多深**,与一格多远无关(格长 0.03→0.01 判级一字未变)。
|
|
910
|
-
* 一格 = 一轮里每条命中各推的距离 ⇒ 要限制"一次修多少"只能限制**轮数**。
|
|
911
|
-
* ⚠️ 代价(不许瞒):出水量 = 轮数 × 每轮票数 × 格长,默认每帧 2 个小帧。拧到 1 轮 + 1cm 格长 ⇒
|
|
912
|
-
* 一秒最多挤出约 120cm 的量看着够,但高速冲刺顶墙时「这一帧挤进来的」可能比「两小帧推出去的」多
|
|
913
|
-
* ⇒ 越陷越深,格长拧到 0.005 那一档体检台 40 号现场就是这么判 **❌穿墙** 的。
|
|
914
|
-
* 所以默认不动,先让他在线上拧着看手感。
|
|
915
|
-
*/
|
|
916
|
-
public ROUNDS_PER_SUBSTEP: number = 500;
|
|
917
886
|
// ⚠️ 每帧碰撞迭代总预算(皇上 2026-09-03:墙角偶尔卡住,要求碰撞计算次数不超过 100;
|
|
918
887
|
// 2026-10-03 同一位皇上把这条上限抬到 500 —— 机制一字未改,只是额度变大):
|
|
919
888
|
// while 循环原本的上限 100 是「单次 playerCollisions」的,而一个 update() 帧内会调
|
|
@@ -941,6 +910,10 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
941
910
|
*/
|
|
942
911
|
private _fitNormal: Vector3 = new Vector3();
|
|
943
912
|
private _horizNormal: Vector3 = new Vector3();
|
|
913
|
+
/** 本子步实际推离过的不同墙面法线。墙角需要同时裁掉两个朝内速度分量,
|
|
914
|
+
* 不能只用最后一次命中的面,否则两个面会在相邻帧交替抢占速度闸门而抖动。 */
|
|
915
|
+
private _horizContactNormals: Vector3[] = [new Vector3(), new Vector3(), new Vector3(), new Vector3()];
|
|
916
|
+
private _horizContactCount: number = 0;
|
|
944
917
|
private _fitVec: Vector3 = new Vector3();
|
|
945
918
|
private _rhombusAxisX: Vector3 = new Vector3(1, 0, 0);
|
|
946
919
|
private _rhombusAxisZ: Vector3 = new Vector3(0, 0, 1);
|
|
@@ -1234,14 +1207,12 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1234
1207
|
resetPlayer(position: Vector3 = new Vector3(0, 0, 0)): void {
|
|
1235
1208
|
this.playerVelocity.set(0, 0, 0);
|
|
1236
1209
|
this.prePlayerVelocity.set(0, 0, 0);
|
|
1237
|
-
this.
|
|
1238
|
-
this.
|
|
1239
|
-
|
|
1240
|
-
this.camera.position.copy(this.playerCollider.end);
|
|
1241
|
-
// ⭐ 复位 = 换现场 ⇒ 把相机/胶囊位置基准挪到新位置。
|
|
1210
|
+
this.playerPosition.set(position.x, position.y + 1, position.z);
|
|
1211
|
+
this.camera.position.copy(this.playerPosition);
|
|
1212
|
+
// ⭐ 复位 = 换现场 ⇒ 把相机/视点位置基准挪到新位置。
|
|
1242
1213
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1214
|
+
/** 玩家视点。所有碰撞射线均以此点为局部原点。 */
|
|
1215
|
+
playerPosition: Vector3 = new Vector3(0, 1, 0);
|
|
1245
1216
|
// 时钟对象
|
|
1246
1217
|
clock: Clock = new Clock();
|
|
1247
1218
|
prePlayerVelocity: Vector3 = new Vector3();
|
|
@@ -1288,7 +1259,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1288
1259
|
* `depthWrite:true`(three-base/Ground1/Ground1.ts:107);哪天地板改回
|
|
1289
1260
|
* transparent + depthWrite:false,射线线会重新穿地浮出来。
|
|
1290
1261
|
* - 回弹方向线(黄→白)= **false**:「回弹向量线保持穿透显示」。
|
|
1291
|
-
*
|
|
1262
|
+
* 它是「本帧要看的向量」,起点在玩家位置、方向常指着墙里,开遮挡会被墙面
|
|
1292
1263
|
* 或墙面吃掉半截 → 观察目的直接落空,故保持常显在最前。
|
|
1293
1264
|
*/
|
|
1294
1265
|
private _createDebugLine(depthTest: boolean): LineSegments2 {
|
|
@@ -1356,7 +1327,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1356
1327
|
this._debugRayGroup = null;
|
|
1357
1328
|
}
|
|
1358
1329
|
init(canvas: HTMLCanvasElement): void {
|
|
1359
|
-
this.
|
|
1330
|
+
this.playerPosition.set(0, 1, 0);
|
|
1360
1331
|
document.addEventListener("keydown", this.keydown);
|
|
1361
1332
|
document.addEventListener("keyup", this.keyup);
|
|
1362
1333
|
canvas.addEventListener("pointerdown", this.mousedown);
|
|
@@ -1509,7 +1480,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1509
1480
|
this.playerCollisions(s === stepCount - 1);
|
|
1510
1481
|
}
|
|
1511
1482
|
// 设置相机位置(第一人称默认值)
|
|
1512
|
-
this.camera.position.copy(this.
|
|
1483
|
+
this.camera.position.copy(this.playerPosition);
|
|
1513
1484
|
// 第三人称:偏移相机位置 + 朝向第一人称坐标
|
|
1514
1485
|
if (this.thirdPerson) {
|
|
1515
1486
|
const cosPitch = Math.cos(this._thirdPersonAngleX);
|
|
@@ -1524,7 +1495,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1524
1495
|
Math.cos(this._thirdPersonAngleY) *
|
|
1525
1496
|
cosPitch *
|
|
1526
1497
|
this.thirdPersonDistance;
|
|
1527
|
-
this.camera.lookAt(this.
|
|
1498
|
+
this.camera.lookAt(this.playerPosition);
|
|
1528
1499
|
}
|
|
1529
1500
|
// dev 调试模式:在相机本地坐标系叠加偏移(向后上方)
|
|
1530
1501
|
if (this.dev && !this.thirdPerson) {
|
|
@@ -1543,7 +1514,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1543
1514
|
// xsh: direction prop → _localRayDirs(归一化)
|
|
1544
1515
|
// xsh: raycaster.far = getDirection().length() → HORIZONTAL_FAR / downFar / upFar
|
|
1545
1516
|
// xsh: Group3D rotation={x:-euler.x} → yawQuat(pitch 补偿)
|
|
1546
|
-
// xsh: getIntersetResult(positionIncremental) → worldPos =
|
|
1517
|
+
// xsh: getIntersetResult(positionIncremental) → worldPos = playerPosition + positionIncremental
|
|
1547
1518
|
//
|
|
1548
1519
|
// 现行 10 条布局逐条表 = 上方「★ 矩形射线法 ★」权威规格块(唯一数据源,不在这里重复一遍)。
|
|
1549
1520
|
/** 输出一条碰撞日志(唯一出口 = 浏览器控制台;皇上:「日志就是浏览器的控制台」) */
|
|
@@ -1579,18 +1550,16 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1579
1550
|
// · 竖向保留 faceUp / 天花板三道闸守卫(「掉出世界」血案修复)。
|
|
1580
1551
|
// · ⭐ 2026-10-11 A 档:**只沿「欠距最大那一面」的法线**推,且最后一格按欠距截短
|
|
1581
1552
|
// (xsh 是每条命中各推一整格 ⇒ 4 条命中同一面墙一轮推 4cm,退出时必过冲)。
|
|
1582
|
-
// 原 deficit / 票面合成(finalDir) / 逐格推离 /
|
|
1553
|
+
// 原 deficit / 票面合成(finalDir) / 逐格推离 / 白推保险丝等旧分支已拆除。
|
|
1583
1554
|
// ⚠️ 措辞校正(10-11):上面这句里「deficit」那半已随 A 档**回归**(`_horizFitDeficit()`:
|
|
1584
1555
|
// 沿命中面法线的菱形可达距离 R·max(|n·x̂|,|n·ẑ|) 减当前垂距);
|
|
1585
|
-
// 仍拆除的是「票面合成 finalDir /
|
|
1556
|
+
// 仍拆除的是「票面合成 finalDir / 白推保险丝」两件 —— 别照旧文案把它们捡回来。
|
|
1586
1557
|
const positionIncremental = this._tempVec4.set(0, 0, 0);
|
|
1587
1558
|
this._refreshInputDir(); // 台账 ix/iz 列还要用(与碰撞计算无关)
|
|
1588
1559
|
const recordDebug = (this.dev || this.thirdPerson) && isFinalStep;
|
|
1589
1560
|
let debugRecorded = !recordDebug;
|
|
1590
1561
|
this._horizHit = false;
|
|
1591
|
-
|
|
1592
|
-
// 常态帧用旋钮 ROUNDS_PER_SUBSTEP(默认 500 与 MAX_ITERATIONS 同档 ⇒ 现状完全不变)。
|
|
1593
|
-
const roundCap = this.ROUNDS_PER_SUBSTEP;
|
|
1562
|
+
this._horizContactCount = 0;
|
|
1594
1563
|
const downRange = this.DOWN_RANGE;
|
|
1595
1564
|
const upRange = this.UP_RANGE;
|
|
1596
1565
|
let i = 0;
|
|
@@ -1604,7 +1573,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1604
1573
|
// ⭐ 2026-10-02 模拟修正:真实 collider 此刻**还没被本步位移推过**,
|
|
1605
1574
|
// 所以锚点 = 真实位置 + 本步位移 =「这一步走完会站在哪儿」的**模拟位置**
|
|
1606
1575
|
// (数值上与旧写法「先 translate 再取 end」完全相同 ⇒ 几何、方向、测试基线都不变)。
|
|
1607
|
-
const basePos = this._tempVec3.copy(this.
|
|
1576
|
+
const basePos = this._tempVec3.copy(this.playerPosition).add(this._subStep);
|
|
1608
1577
|
// xsh: 射线原点 = 控制器位置 + positionIncremental(getIntersetResult(incremental) 语义)
|
|
1609
1578
|
const worldPos = this._candPos.copy(basePos).add(positionIncremental);
|
|
1610
1579
|
// xsh: Group3D rotation={x:-euler.x} 补偿 pitch → 只跟随 yaw
|
|
@@ -1664,17 +1633,21 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1664
1633
|
}
|
|
1665
1634
|
}
|
|
1666
1635
|
if (bestDeficit > 1e-6) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
//
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1636
|
+
// 大欠距逐格推离,直到所有水平射线都脱离碰撞面。
|
|
1637
|
+
positionIncremental.addScaledVector(this._horizNormal, Math.min(this.RETREAT_SKIN, bestDeficit));
|
|
1638
|
+
// 一面墙在多轮迭代里会重复命中;只保留方向不同的接触面。
|
|
1639
|
+
let known = false;
|
|
1640
|
+
for (let ni = 0; ni < this._horizContactCount; ni++) {
|
|
1641
|
+
if (this._horizContactNormals[ni].dot(this._horizNormal) > 0.999) {
|
|
1642
|
+
known = true;
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
if (!known && this._horizContactCount < this._horizContactNormals.length) {
|
|
1647
|
+
this._horizContactNormals[this._horizContactCount++].copy(this._horizNormal);
|
|
1648
|
+
}
|
|
1649
|
+
goOnComputer = true;
|
|
1650
|
+
this._horizHit = true;
|
|
1678
1651
|
}
|
|
1679
1652
|
// ========== 向下射线(索引 0 = upTodwomRay)==========
|
|
1680
1653
|
const downRay = this._rays[0];
|
|
@@ -1789,7 +1762,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1789
1762
|
// 第 11 帧眼落到板底之下 0.0135,上射线打到**板底(法线朝下)**,旧口径
|
|
1790
1763
|
// `positionIncremental.y -= upPen` 一帧把人往下砸 **0.9865m** → 眼落到 −2.0,
|
|
1791
1764
|
// 从此两条竖射线彻底够不着 → 自由落体到 **−13.161547** → 撞上 camera.y≤−10 的
|
|
1792
|
-
//
|
|
1765
|
+
// 掉出世界复位(玩家视点重置到 (0, 1, 10))→ 「陷进地面很长一段
|
|
1793
1766
|
// 距离然后凭空回到原点」。这就是皇上看到的现象。
|
|
1794
1767
|
// 新口径:**绝不往下顶**,一步顶到「脚底刚好落在这块面之上」(眼高 = 面 y +
|
|
1795
1768
|
// downRange + SKIN)。顶完眼只比板底高一个 SKIN → 下一轮迭代下射线立刻
|
|
@@ -1886,7 +1859,7 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1886
1859
|
this._subStep.z + corrZ
|
|
1887
1860
|
);
|
|
1888
1861
|
if (this._appliedMove.lengthSq() > 0) {
|
|
1889
|
-
this.
|
|
1862
|
+
this.playerPosition.add(this._appliedMove);
|
|
1890
1863
|
}
|
|
1891
1864
|
// 本步已消费:归零,面板那两颗直接调 playerCollisions 的按钮不会重复走一步
|
|
1892
1865
|
this._subStep.set(0, 0, 0);
|
|
@@ -1903,9 +1876,14 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1903
1876
|
// ⚠️ 方向与推离用的是**同一个** `_horizNormal`(水平半区那轮算好、就地复用),绝不另算一遍。
|
|
1904
1877
|
// ⚠️ 竖向 y 一字不动(重力/跳跃照旧);观察模式(回弹预览)不改速度,口径与 10-07 一致。
|
|
1905
1878
|
if (this._horizHit && !this.previewRetreat) {
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1879
|
+
// 墙角中两个约束面不一定在同一轮成为“欠距最大面”。依次投影两轮,
|
|
1880
|
+
// 让每个面都得到 v·n >= 0,同时保留不受任何墙面约束的切向速度。
|
|
1881
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
1882
|
+
for (let ni = 0; ni < this._horizContactCount; ni++) {
|
|
1883
|
+
const normal = this._horizContactNormals[ni];
|
|
1884
|
+
const into = this.playerVelocity.dot(normal);
|
|
1885
|
+
if (into < 0) this.playerVelocity.addScaledVector(normal, -into);
|
|
1886
|
+
}
|
|
1909
1887
|
}
|
|
1910
1888
|
}
|
|
1911
1889
|
// ⭐ 十旨:一帧一条的抖动台账。**只由子步循环的最后一步(isFinalStep)记** ——
|
|
@@ -1931,10 +1909,8 @@ export default class FPScontrol extends EventDispatcher<FPScontrolEventTypeMap>
|
|
|
1931
1909
|
// 越界传送
|
|
1932
1910
|
teleportPlayerIfOob = (): void => {
|
|
1933
1911
|
if (this.camera.position.y <= -10) {
|
|
1934
|
-
this.
|
|
1935
|
-
this.
|
|
1936
|
-
this.playerCollider.radius = 0.35;
|
|
1937
|
-
this.camera.position.copy(this.playerCollider.end);
|
|
1912
|
+
this.playerPosition.set(0, 1, 10);
|
|
1913
|
+
this.camera.position.copy(this.playerPosition);
|
|
1938
1914
|
}
|
|
1939
1915
|
};
|
|
1940
1916
|
// 更新
|
package/Ground1/Ground1.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Matrix4, Mesh, PlaneGeometry, ShaderMaterial } from "three";
|
|
2
|
-
/**
|
|
1
|
+
import { Clock, BufferGeometry, Camera, Group, Material, Matrix4, Mesh, PlaneGeometry, Scene, ShaderMaterial, WebGLRenderer } from "three";
|
|
2
|
+
/** 装饰地板(科技感格子贴图 + 紫色扩散动画,半透明可透视外观) */
|
|
3
3
|
export default class Ground1 extends Mesh {
|
|
4
4
|
tempMatrix: Matrix4;
|
|
5
5
|
constructor();
|
|
6
6
|
renderOrder: number;
|
|
7
7
|
geometry: PlaneGeometry;
|
|
8
8
|
setSize(width: number, height: number): void;
|
|
9
|
+
clock: Clock;
|
|
10
|
+
onBeforeRender: (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void;
|
|
9
11
|
material: ShaderMaterial;
|
|
10
12
|
}
|
package/Ground1/Ground1.js
CHANGED
|
@@ -2,44 +2,39 @@
|
|
|
2
2
|
* @Author: xiaosihan
|
|
3
3
|
* @Date: 2022-09-20 23:06:00
|
|
4
4
|
* @Last Modified by: xiaosihan
|
|
5
|
-
* @Last Modified time:
|
|
5
|
+
* @Last Modified time: 2026-10-12 00:00:00
|
|
6
6
|
*
|
|
7
|
-
* ⭐ 2026-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* → `transparent: false`、alpha 恒 1(不透明)。
|
|
7
|
+
* ⭐ 装饰地板 = 科技感格子贴图 + 紫色扩散动画(2026-10-12 皇上旨「整条退回旧科技感地板」+「装饰地板需要透明」)。
|
|
8
|
+
* 历史:2026-09-20 皇上曾两旨把它改成「随机渐变的深灰、不透明」,理由是每帧 3 次贴图采样 +
|
|
9
|
+
* 平铺无各向异性 → 摩尔纹,且是 home 页卡顿元凶之一。**当年那个摩尔纹根因现在已经在
|
|
10
|
+
* `threeLoader.getTexture` 里根治了**(anisotropy=16 + generateMipmaps + LinearMipmapLinear),
|
|
11
|
+
* 所以贴图路线的重画成本比 09-20 那版低一档,本次按皇上旨意恢复旧外观。
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
* -
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 正是靠本地板「不透明 + depthWrite:true」才挡得住埋进地里的那半截线;**8/9 两条回弹向量线
|
|
19
|
-
* 保持穿透显示**(关深度测试 + renderOrder:9999),照旧常显在最前。
|
|
20
|
-
* ⚠️ 若哪天把本地板改回 transparent + depthWrite:false,射线辅助线会重新穿地浮出来。
|
|
13
|
+
* ⭐ 2026-10-12 皇上旨「装饰地板需要透明」⇒ 恢复旧版的**外观**口径(逐字照抄 09-20 之前的写法):
|
|
14
|
+
* - `transparent: true` + 径向 alpha 淡出 `mapColor.a = 1 - dist(center)*2`(中心实、外缘全透)。
|
|
15
|
+
* - **但 `depthWrite: true` 保留**(旧版本来就是 transparent + depthWrite:true):主渲染的深度缓冲
|
|
16
|
+
* 照写 ⇒ FPScontrol 那 10 条射线辅助线仍被地板挡住埋进地里的那半截(09-25 定案口径不受影响)。
|
|
21
17
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* 磨砂会被裁掉。见 three-base/pass/ssao_pass.ts 文件头。
|
|
28
|
-
* - ShaderMaterial 必须含 logdepthbuf chunk;linear 输出必须 `#include <colorspace_fragment>`;
|
|
29
|
-
* 否则末端 sRGB 编码会二次编码 → 发白。
|
|
18
|
+
* ⭐ 2026-10-18 皇上旨「所有的对象都要进入到深度图里面了,不需要用参数过滤掉了」⇒ 当年为「可透视
|
|
19
|
+
* 地板」加的 `userData.aoFloor`(把地板从后处理的可见深度图里排除)连同 ssao_pass 里那整条过滤判据
|
|
20
|
+
* 一并删除。现在地板两张深度图都写 ⇒ 透过地板看到的实体,其描边/辉光/覆盖色/磨砂会被地板裁掉,
|
|
21
|
+
* 包括外缘 alpha≈0 那圈「看得见空、看不见地板」的区域。判据出处 three-base/pass/ssao_pass.ts 文件头,
|
|
22
|
+
* 护栏 test/depthDebug.test.js J 组。
|
|
30
23
|
*/
|
|
31
|
-
import { Color, DoubleSide, Matrix4, Mesh, PlaneGeometry, ShaderChunk, ShaderMaterial } from "three";
|
|
24
|
+
import { Clock, Color, DoubleSide, Matrix4, Mesh, PlaneGeometry, ShaderChunk, ShaderMaterial, Vector2 } from "three";
|
|
32
25
|
import { MathUtils } from "three";
|
|
33
26
|
const { degToRad } = MathUtils;
|
|
34
|
-
|
|
27
|
+
import floorMap1PNG from "./floorMap1.png";
|
|
28
|
+
import floorMap2PNG from "./floorMap2.png";
|
|
29
|
+
import floorMap3PNG from "./floorMap3.png";
|
|
30
|
+
import threeLoader from "../threeLoader";
|
|
31
|
+
/** 装饰地板(科技感格子贴图 + 紫色扩散动画,半透明可透视外观) */
|
|
35
32
|
export default class Ground1 extends Mesh {
|
|
36
33
|
tempMatrix = new Matrix4();
|
|
37
34
|
constructor() {
|
|
38
35
|
super();
|
|
39
36
|
// 标记为自己参与 FPS 碰撞检测
|
|
40
37
|
this.userData.fpsCollision = true;
|
|
41
|
-
// ⚠️ 这里原来还有 `this.userData.aoFloor = true;`(把本地板从可见深度图里排除的特例标记),
|
|
42
|
-
// 2026-10-11 皇上旨「物体的深度都要进深度图」⇒ 连同 ssao_pass 的特例分支一起整条删除。
|
|
43
38
|
}
|
|
44
39
|
renderOrder = -1;
|
|
45
40
|
geometry = (() => {
|
|
@@ -51,14 +46,22 @@ export default class Ground1 extends Mesh {
|
|
|
51
46
|
setSize(width, height) {
|
|
52
47
|
this.scale.set(width, 1, height);
|
|
53
48
|
}
|
|
49
|
+
clock = new Clock();
|
|
50
|
+
onBeforeRender = (renderer, scene, camera, geometry, material, group) => {
|
|
51
|
+
const elapsedTime = this.clock.getElapsedTime();
|
|
52
|
+
this.material.uniforms.speed.value = (elapsedTime * 0.1) % 0.6;
|
|
53
|
+
this.material.uniforms.speed2.value = ((elapsedTime * 0.1) % 0.6) - 0.1;
|
|
54
|
+
};
|
|
54
55
|
material = new ShaderMaterial({
|
|
55
56
|
uniforms: {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
size: { value: new Vector2(10, 10) }, // 格子平铺格数
|
|
58
|
+
speed: { value: 0 }, // 扩散值0-1
|
|
59
|
+
speed2: { value: 0 }, // 扩散值0-1
|
|
60
|
+
speedColor: { value: new Color("#8D108E") }, // 格子扩散颜色
|
|
61
|
+
lineColor: { value: new Color("#263C56") }, // 格子线的颜色
|
|
62
|
+
map1: { value: threeLoader.getTexture(floorMap1PNG) }, // 贴图纹理
|
|
63
|
+
map2: { value: threeLoader.getTexture(floorMap2PNG) }, // 贴图纹理
|
|
64
|
+
map3: { value: threeLoader.getTexture(floorMap3PNG) }, // 贴图纹理
|
|
62
65
|
},
|
|
63
66
|
vertexShader: `
|
|
64
67
|
${ShaderChunk.common}
|
|
@@ -72,42 +75,59 @@ export default class Ground1 extends Mesh {
|
|
|
72
75
|
`,
|
|
73
76
|
fragmentShader: `
|
|
74
77
|
${ShaderChunk.logdepthbuf_pars_fragment}
|
|
75
|
-
uniform
|
|
76
|
-
uniform
|
|
77
|
-
uniform
|
|
78
|
+
uniform sampler2D map1;
|
|
79
|
+
uniform sampler2D map2;
|
|
80
|
+
uniform sampler2D map3;
|
|
81
|
+
uniform vec2 size;
|
|
82
|
+
uniform vec3 lineColor;
|
|
83
|
+
uniform float speed;
|
|
84
|
+
uniform float speed2;
|
|
85
|
+
uniform vec3 speedColor;
|
|
78
86
|
varying vec2 vUv;
|
|
87
|
+
void main() {
|
|
79
88
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
83
|
-
}
|
|
84
|
-
float valueNoise(vec2 p) {
|
|
85
|
-
vec2 i = floor(p);
|
|
86
|
-
vec2 f = fract(p);
|
|
87
|
-
vec2 u = f * f * (3.0 - 2.0 * f); // smoothstep 插值 → 柔和渐变,不是硬格子
|
|
88
|
-
return mix(mix(hash21(i), hash21(i + vec2(1.0, 0.0)), u.x),
|
|
89
|
-
mix(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(1.0, 1.0)), u.x), u.y);
|
|
90
|
-
}
|
|
89
|
+
// 中心点
|
|
90
|
+
vec2 center = vec2(0.5,0.5);
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
vec4 map1Color = texture2D(map1, fract( vUv * size) );
|
|
93
|
+
|
|
94
|
+
// 格子中心渐变颜色
|
|
95
|
+
if(distance( vUv , center ) < speed){
|
|
96
|
+
map1Color = mix(map1Color, vec4(speedColor, 1.0), clamp((1.0 - (distance(distance( vUv , center ), speed) * 8.0) ) * map1Color.a, 0.0, 1.0 ) );
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
vec4 map2Color = texture2D(map2, fract( vUv * size) );
|
|
100
|
+
|
|
101
|
+
vec4 map3Color = texture2D(map3, fract( vUv * size) );
|
|
102
|
+
|
|
103
|
+
// 正方形装饰中心渐变颜色
|
|
104
|
+
if(distance( vUv , center ) < speed2){
|
|
105
|
+
map3Color = mix(map3Color, map3Color * 3.0, clamp((1.0 - (distance(distance( vUv , center ), speed) * 4.0) ) * map3Color.a, 0.0, 1.0 ) );
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
vec4 mapColor = mix(map1Color, map2Color, map2Color.a);
|
|
109
|
+
|
|
110
|
+
mapColor = mix(mapColor, map3Color, map3Color.a);
|
|
111
|
+
|
|
112
|
+
//添加背景色
|
|
113
|
+
mapColor = mix(mapColor, vec4(lineColor.rgb, 1.0 ), 1.0 - mapColor.a);
|
|
114
|
+
|
|
115
|
+
// 设置透明度(中心实、外缘全透的径向淡出 —— 500×500 大地板的硬边靠它消失)
|
|
116
|
+
mapColor.a = 1.0 - (distance( vUv , center ) * 2.0);
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
float edge = 1.0 - smoothstep(0.34, 0.5, distance(vUv, vec2(0.5, 0.5)));
|
|
100
|
-
col *= mix(0.72, 1.0, edge);
|
|
118
|
+
gl_FragColor = mapColor;
|
|
101
119
|
|
|
102
|
-
gl_FragColor = vec4(col, 1.0); // 不透明
|
|
103
120
|
${ShaderChunk.logdepthbuf_fragment}
|
|
104
121
|
#include <colorspace_fragment>
|
|
105
122
|
}
|
|
106
123
|
`,
|
|
107
124
|
depthTest: true,
|
|
108
125
|
depthWrite: true,
|
|
109
|
-
// ⭐ 皇上 2026-
|
|
110
|
-
|
|
126
|
+
// ⭐ 皇上 2026-10-12 旨「装饰地板需要透明」:恢复旧版 transparent:true(半透可透视)。
|
|
127
|
+
// ⚠️ depthWrite 保持 true —— 主渲染的深度缓冲照写,FPScontrol 的 10 条射线辅助线才挡得住
|
|
128
|
+
// 埋进地里的那半截(09-25 定案)。后处理特效的遮挡判定走 baseDepthTexture,2026-10-18
|
|
129
|
+
// 皇上旨「所有的对象都要进入到深度图里面了」⇒ 地板也写进那张图,会裁掉身后特效。
|
|
130
|
+
transparent: true,
|
|
111
131
|
side: DoubleSide,
|
|
112
132
|
});
|
|
113
133
|
}
|