# AI 生成 3D 场景拆解 03：太阳神庙

规范网页：https://devtookit.com/article/16

作者：Revio

发布时间：2026-10-13T09:39:55Z

更新时间：2026-10-13T09:39:55Z

摘要：学习 Techartist 的太阳神庙，拆解程序化建筑与砂岩地形、TSL 材质、太阳盘光效、建筑开合与祭司骨骼动画、镜头和光照，并将可复用的建模与动作编排方法补进 Skill。

---

![太阳神庙闭合状态，中央阶梯、柱廊、太阳盘与砂岩山体构成完整场景](/articles/sun-temple/hero.jpg)

[在本站体验太阳神庙 Demo](https://devtookit.com/demos/sun-temple) · [原作者 GitHub 仓库](https://github.com/iamtechartist/sun-temple)

Sun Temple 是一座嵌在砂岩山壁前的风格化神庙。打开场景后，我们可以旋转和缩放视角，也可以切换主镜头、正立面与高处视角。点击底部的 Open Solar Court，两侧柱廊向外移动，中央阶梯抬成平台，太阳盘升起并逐渐发光，一位祭司从门内走出，停在庭院里挥手。再次点击按钮，她先转身返回，建筑随后恢复闭合。

本篇参考学习的 Demo 来自 **Techartist（GitHub 用户 iamtechartist）**，原地址为 [github.com/iamtechartist/sun-temple](https://github.com/iamtechartist/sun-temple)。场景设计和原始实现归原作者，项目采用 MIT 许可证，Copyright (c) 2026 Techartist。本文由 **Revio** 整理，配图来自本次运行 Demo 的实际截图。

前两篇分别拆解了海湾中的水体和山谷中的铁路。这一篇我们把注意力放在建筑与动作编排上：神庙怎样用简单几何搭出来，砂岩怎样在没有石材贴图的情况下保持统一质感，建筑、光效和人物又怎样配合完成一次开启仪式。原仓库没有说明作者使用了哪些 AI 工具；文末的 Skill 是我们从源码中整理出的复用方法。

## 技术栈

项目把页面、样式、场景代码和两份人物资产集中在一个 `index.html` 中，使用原生 JavaScript ES Modules，通过 import map 引入 **Three.js r185（0.185.0）**。本篇固定分析提交 [c247c0fd](https://github.com/iamtechartist/sun-temple/tree/c247c0fda9e7ead65361a49742f8a6da2dabf155)，以下实现都可以在这个版本的 [index.html](https://github.com/iamtechartist/sun-temple/blob/c247c0fda9e7ead65361a49742f8a6da2dabf155/index.html) 中找到。

| 部分 | 实际使用的技术 | 作用 |
| --- | --- | --- |
| 渲染 | WebGPURenderer、PCF 阴影、ACES 色调映射 | 绘制场景与统一输出亮度 |
| 材质 | TSL、MeshStandardNodeMaterial、MeshBasicNodeMaterial | 砂岩、天空、太阳盘与透明光效 |
| 几何 | BufferGeometry、RoundedBoxGeometry、LatheGeometry、CylinderGeometry、TorusGeometry | 山体、建筑、柱头、雕像和太阳盘 |
| 重复部件 | InstancedMesh、共享几何 | 台阶、柱子与装饰的批量绘制 |
| 人物 | FBXLoader、SkinnedMesh、AnimationMixer | 加载角色、播放行走和挥手动作 |
| 动作编排 | 归一化进度、分段缓动、状态机 | 同步建筑、太阳盘与人物 |
| 交互 | OrbitControls、HTML 按钮 | 旋转、缩放、视角切换与庭院开合 |

TSL 用 JavaScript 节点表达材质计算。比如 `positionWorld` 提供世界坐标，`normalWorld` 提供表面法线，`sin`、`mix` 和 `smoothstep` 组合成纹理与遮罩，最后交给 Three.js 生成着色器。它仍然是在 GPU 上逐顶点或逐片元计算，只是我们组织公式的方式与直接书写 GLSL 不同。

`WebGPURenderer` 优先使用 WebGPU，Three.js 在不能创建 WebGPU 后端时具有 WebGL 2 回退路径；项目本身没有提供后端切换按钮，也没有单独写两套材质。本站版本将原 import map 中的 CDN 地址换成本地依赖，保留 r185、原场景逻辑和内嵌 FBX 内容，方便直接访问。

## 程序化生成

这个项目采用了混合方式。神庙、台阶、山壁、沙地、太阳盘、天空和太阳光效都由代码构造，砂岩颜色与太阳纹理由材质公式计算。**祭司则来自仓库附带的 walking.fbx 与 waving.fbx，人物网格、骨骼、贴图和动作不是现场生成的。**

两份 FBX 也以 Base64 形式内嵌在 HTML 中。本次逐字节核对，它们分别与仓库中的两个独立文件完全相同。运行时会先读取内嵌数据，解码为 ArrayBuffer，再交给 FBXLoader 解析；只有内嵌内容缺失时，才会尝试备用模型地址。人物的颜色贴图同样嵌在 FBX 内部。

我们用同版本 FBXLoader 解析了这两份资产：每份包含一个蒙皮网格、24 根骨骼和一个材质，几何为 9,215 个三角形。行走片段约 1.03 秒，挥手片段约 4.10 秒。解析时用占位 Texture 跳过图片解码，这些数字用于确认文件结构，真实贴图与画面则另在浏览器中检查。

建筑布局也不是任意随机生成。柱子的位置、门框层数、山体体块和沙丘位置都有明确参数，左右主体通过对称循环构造。固定种子噪声负责岩壁与地表的细节变化。我们可以把它理解为一座由参数搭建、可重复生成的指定场景：修改宽度、间距或材料参数，就能调整同一种建筑语言。

## 构图

神庙的视觉中心在中央门洞和上方太阳盘。阶梯从画面前方指向入口，两侧柱廊提供横向展开的尺度，背后的山壁把视线收回建筑。人物走出后，她的身高成为尺度参照，让太阳盘和建筑更显高大。

![开启后的正立面，柱廊让出中央入口，祭司站在抬升的平台上](/articles/sun-temple/facade.jpg)

从正立面看，构图依赖中轴对称与重复门框；主镜头略偏侧面，则能看见台基的厚度、台阶的斜坡和柱子的前后关系。高处视角用于查看庭院、山体和平台的连接。它们是为同一座建筑选出的三种观察方式。

造型中有纸莎草式柱头、安卡符号和太阳盘等古埃及意象。这里适合学习其比例和层次组织，源码并没有声明这是某一座历史神庙的精确复原。

## 神庙

### 台基与墙体

建筑首先用台基、平台、塔门、上层楼廊和屋顶这些大体块确定轮廓。`makeFrustum` 用上下两层共八个顶点生成截头体：上下面尺寸不同，侧面因此带有收分，塔门和基础就不会像直接堆叠的直立方盒。

常规石块由 `addBlock` 创建。它复用一份单位 RoundedBoxGeometry，再通过位置、旋转和缩放得到梁、墙、石框与栏杆。共用基础几何可以降低重复创建网格数据的成本，小倒角也让石块边缘更容易接住光。

不过单位几何经过非等比缩放，倒角宽度也会随轴向改变。如果我们要复用到近距离建筑展示，就需要按部件尺寸生成倒角，或显式控制实际倒角半径；这份 Demo 主要依靠整体构图和光照，采用了更简单的处理。

基础的楔形几何还为前方斜面留出了顶点控制，中央升降区域后面会直接修改其中两个顶点。这里的建模表示方式与动画需求从一开始就是对应的。

### 柱子与檐口

左右柱廊各有五根柱子。柱脚、柱身和环状束带主要用 CylinderGeometry，柱身有轻微锥度。复杂一点的柱头采用 LatheGeometry：先写一条从下向上的半径轮廓，再绕竖直轴旋转。

```text
轮廓点：(r_i, y_i)
旋转表面：P(i, θ) = (r_i × sin θ, y_i, r_i × cos θ)
```

`r_i` 决定这一高度的宽窄，`y_i` 决定高度。轮廓向外展开再收束，就能形成花苞或纸莎草束状的柱头；底部收窄、中部鼓起，也能得到瓶身、基座和装饰雕像。项目柱头使用 20 个旋转分段，门口的简化雕像使用 18 个分段。

檐口采用另一种方式。`makeProfileXGeometry` 在 yz 平面写出有台阶与外挑的截面，复制到左右两端，再用侧面连接，最后用 ShapeUtils.triangulateShape 封住端面。也就是“固定轮廓 + 沿 x 方向拉长”。这样可以一次生成有层次的长檐口，无须给每个截面台阶单独加一排方块。

这些重复结构按几何和材质组合成 InstancedMesh。柱廊展开时移动的是整组柱廊，柱头、梁、檐口和装饰仍保持彼此的相对位置。

### 门洞与装饰

中央入口共有五层逐渐缩小的门框，沿深度方向排列。每层由左右石柱和顶部横梁组成，门洞自然留在中间，因此不需要先建实体墙，再用布尔运算挖洞。重复框架带来明确的透视节奏，入口看起来比一个平面上的黑矩形更深。

最深处放置暗色面板，地面也使用压暗的材质。它负责遮住场景末端并提示室内空间；这里没有完整建模整座神庙的内部房间。我们在正面构图中需要的是可信的入口深度，五层门框与暗部已经足以支撑这一观察范围。

塔门浮雕也采用层次组合：先放深色底板，再加浅色边框、圆盘和几组竖向符号。安卡装饰由环、竖杆和横杆组成。它们是固定图形的几何拼装，没有象形文字生成器，也没有浮雕贴图或通用雕刻算法。

### 台阶

前方阶梯分为左右固定区与中央活动区。34 排台阶中，左右一共 68 个实例保持原来的阶梯形态，中央 34 个实例参与抬升。关闭时每排有自己的高度；开启时，它们的中心逐步汇到同一个高度。

```text
y_step(i, p) = lerp(y_closed(i), 12.47, p)
y_foundation_front(p) = lerp(0.35, 12.72, p)
```

`p` 是经过分段缓动的台阶进度。中央台阶厚度为 0.56，所以完全开启时顶面约为 12.75；每排深度为 1.86，略大于排间距，抬平后相邻台阶有覆盖，组成连续平台。地基前方的两个顶点同步抬高，避免台阶下方出现空隙。

每帧先更新实例矩阵，再标记 `instanceMatrix.needsUpdate`；基础网格顶点改变后则重新计算法线。整个过程中都沿用原网格与材质，没有在某个时刻突然切换成另一套平台模型。

![高处视角下的开启状态，中央平台和两侧固定台阶的关系更清楚](/articles/sun-temple/elevated.jpg)

## 地形

### 沙地

沙地是宽 300、深 255、细分 100×86 的网格。基础高度上叠加两组方向不同的正弦起伏和四层 value noise，形成低矮、连续的沙面。

value noise 的做法是先在整数网格上生成散列值，再对相邻四个角插值。源码的散列核心可以写为：

```text
hash(x,z,seed) = fract(sin(127.1x + 311.7z + 74.7seed) × 43758.5453123)
f(t) = t²(3 - 2t)
noise = 四个角的 hash，按 f(fract(x))、f(fract(z)) 做双线性插值
```

`fract` 取小数部分，使输出落在 0 到 1。直接插值会在网格边界留下明显的斜率变化，三次平滑函数让过渡更柔和。`fbm` 再叠加四个尺度：初始振幅 0.56，每层乘 0.47，频率每层乘 2.02。大尺度决定缓慢起伏，小尺度补细节。

建筑附近还需要更平整的基础，所以作者使用一个四次幂指数遮罩降低地表波动：

```text
M(x,z) = exp(-(x/74)^4 - ((z-10)/82)^4)
h(x,z) = -1.35 + (wind(x,z) + noise(x,z)) × (1 - 0.76M)
```

遮罩中心在神庙附近，向外逐渐减弱。它让建筑区域的起伏降到原来的约 24%，而不是硬切成一块完全平坦的矩形。这个高度场用于沙地本身，项目没有另外提供通用的建筑落脚采样接口。

局部堆沙由六个单独的网格补上。每个网格把平面位置映射成归一化坐标 u、v，用椭圆范围控制沙堆轮廓：

```text
falloff = max(0, 1 - u² - v²)
crest = 0.86 + 0.22 × exp(-((u + skew × v) × 1.8)²)
h = -0.78 + falloff^1.6 × height × crest
```

`height` 控制沙堆高度，`skew` 让隆起的脊线偏斜，`falloff` 让边缘回落。椭圆外的网格没有被裁掉，而是保留在基底高度。地面的大尺度起伏与局部沙堆分开，便于把积沙放在台基前和山壁旁。

### 山壁

山壁由四块实体状网格组合：背部大山体、左右斜放的侧翼，以及向上叠起的顶部岩块。每块都生成前后两层顶点、各自的面片和周边封口。它们有厚度与侧面，可以经受当前相机范围内的侧向观察。

顶边由低频 fbm 改变高度，正面则叠加三种细节：噪声产生不规则起伏，随高度变化的正弦条带表现岩层，窄而高的峰值形成横向岩架。

```text
shelf(y) = max(0, sin(0.41y + 0.73seed))^13 × 1.45
```

13 次幂把正弦的宽峰压成较窄的凸起，岩层因此能在几何上形成突出边缘。两条竖向沟槽则用负高斯函数向内凹陷：

```text
gully(x,y) = -depth × exp(-((x - center(y))/width)²)
```

沟槽中心随高度缓慢摆动，宽度控制凹槽范围，深度控制切入岩面的距离。作者为每块岩体设置不同的固定种子、尺寸与偏移，使它们有共同的砂岩语言，又不完全重复。

![高处闭合场景，四块砂岩山体包围建筑，前方保留低矮的沙地和局部堆沙](/articles/sun-temple/terrain.jpg)

这里没有进行侵蚀模拟，也没有把神庙从山体里做布尔切割。视觉上的“岩壁神庙”由建筑靠在山壁前、岩体包围两侧、色彩与纹理相互协调形成。真正改变轮廓的是山壁几何，微小颗粒则交给下面的材质处理。

## 砂岩材质

`makeSandstone` 给神庙、岩壁和沙地提供同一套材质计算，不同对象换用不同的色板、尺度与凹陷参数。基础色是暖棕，混入赭石、浅沙色和深褐色，粗糙度限制在 0.72 到 0.98，金属度为 0。

纹理从世界坐标 `positionWorld` 出发，因此相邻石块可以读到连续的岩层，不必先制作 UV。低频正弦控制大片色差，中频条带表现沉积层，散列颗粒形成少量深色斑点。条带中的一项是：

```text
B = 0.5 + 0.5 × sin(0.43y + 1.8sin(0.018x) + 0.7sin(0.021z))
```

主变化沿 y 方向，小幅 x、z 扰动让条带不至于完全笔直。这里的 `x,y,z` 已经乘过该材质的尺度参数。不同表面使用相同公式，所以颜色相近的建筑与山体能保持连贯。

材质还读取法线。朝上的表面多混一点浅色，朝向太阳的表面增加暖色与亮度，凹陷区域根据显式 `recess` 参数压暗。这些是作者写进材质的调色规则，之后仍由标准材质接受实际光照。凹陷参数不是屏幕空间 AO，也没有通过射线计算遮挡。

颗粒散列 `fract(sin(dot(p,k)) × c)` 与沙地的平滑 value noise 不同，它没有网格插值，更适合制造细碎色差。它与细条带也参与粗糙度计算，让表面各处对光的响应略有差别。

这一套砂岩没有法线贴图、凹凸贴图或材质位移。细节改变的是颜色与粗糙度，真正的岩架、沟槽和倒角仍由几何承担。我们复用时应该先保证大轮廓，再增加材质层次。

世界坐标纹理也有一个需要留意的结果：柱廊移动时，石块会经过世界中的纹理场，条带不会完全固定在石块表面。如果需要长期观察运动部件，可对它们改用局部坐标，并明确保存纹理尺度。

## 太阳盘

### 几何结构

太阳盘本身是一件有厚度的石雕。主体是半径 7.3、厚度 1.18 的圆柱，外围有 TorusGeometry 做出的圆环，中间有凸出的圆柱轴心，周围再布置 12 根放射状石条。整组太阳盘沿石质导轨从高度 66.7 升到 79，同时绕正面的轴旋转。

圆柱默认沿局部 y 轴延伸，作者把它绕 x 轴旋转了 π/2，使圆盘正面朝向神庙前方。因此圆柱材质里的径向距离用局部 x、z 计算，而透明光晕面片使用局部 x、y。我们提取着色器时需要保留这个坐标区别，否则圆形遮罩会变成错误的条带。

### 表面纹理

太阳盘从砂岩状态过渡到燃烧状态，使用 `solarIgnition` 这个 0 到 1 的 uniform 控制。盘面、圆环、轴心和石条使用同一类材质，只改变发光强度、中心遮罩与时间偏移。

火焰纹理由几组随时间变化的正弦函数组合。下面是对应源码的标量形式，坐标来自材质局部位置，t 是共享时间加上部件的相位偏移：

```text
A = 0.5 + 0.5sin(1.34x + 0.72z + 2.15t)
B = 0.5 + 0.5sin(-0.63x + 1.91z - 1.47t + 2.4A)
C = 0.5 + 0.5sin(2.72x + 2.1sin(1.18z - 0.92t))
F = clamp(0.46A + 0.38B + 0.30C, 0, 1)^3.2
```

A、B 的方向和速度不同，B 又受 A 扰动，C 增加局部交错。3.2 次幂压低中间亮度，保留少量强亮区，所以盘面上能看到游动的亮丝，而不是整块均匀发橙。

颜色从暗红、红到橙色混合，黄色只占有限比例，中心区域额外加亮。`ignition` 同时控制砂岩色与火焰色的混合、发光值和粗糙度：

```text
color = mix(dormantStone, fireColor, ignition)
emissive = fireColor × ignition × glowStrength
           × (0.945 + 0.055sin(2.4t)) × (0.42 + 0.48F)
roughness = mix(0.82, 0.38, ignition)
```

材质从粗糙石面变成更亮、更平滑的发光表面，脉冲幅度只有 5.5%，整体亮度不会剧烈跳动。这些计算是对火焰外观的表达，没有流体网格、温度场或燃烧模拟。

### 光晕与火舌

![正立面截图中的太阳盘局部，盘面、环形光晕和放射火舌共同形成发光轮廓](/articles/sun-temple/sun-disc.jpg)

太阳盘周围的光晕来自两张透明平面，尺寸分别为 36 和 31。平面材质先按到中心的距离 r 定义一个圆环：

```text
outside = smoothstep(6.55, 7.34, r)
falloff = 1 - smoothstep(7.5, 15.8, r)
alpha ∝ ignition² × outside × falloff
```

`outside` 压掉盘面内部，`falloff` 让外缘逐渐透明，两个遮罩相乘得到环形范围。再加入随时间移动的径向波纹、斜向扰动和小幅闪烁，光环就有了层次。透明度使用 `ignition²`，点火初期更克制，后期才明显扩散。

外侧还有 22 片放射状火舌。每片仅用五个顶点、三个三角形形成窄长轮廓，按照圆周分布，并根据固定正弦值给出不同长度。它们复用三份材质，材质沿局部 y 方向控制根部到尖端的衰减，再用时间项改变亮度，看起来像能量向外流动。

两层光晕与火舌采用 AdditiveBlending、双面渲染，并关闭深度写入。加法混合让重叠区域更亮，透明外缘不会留下实体方框；关闭深度写入则避免透明面提前挡住后续光效。作者还为三组效果设置了 12、13、14 的 `renderOrder`，保持绘制次序。它们仍然接受深度测试，可以被前方实体遮挡。

光晕和火舌分别以很小的角速度转动，有的方向相反，让几层细节不完全同步。它们固定在太阳盘所在的平面，并没有每帧转向相机。因此这套效果与当前以建筑正面为主的观察范围是配合的；若扩展为自由绕行，需要补上侧面体积或重新设计光效。

### 环境照明

太阳盘前方还放了一盏橙色 PointLight，跟随盘组移动。它的强度由点火进度控制，再叠加两组小幅周期变化：

```text
I = ignition × (155 + 18sin(2.65t) + 7sin(5.7t))
```

发光材质让盘面自身变亮，透明面片扩大屏幕上的亮轮廓，点光源则照亮附近建筑。三者各负责一个部分，不能相互替代。

项目没有使用 Bloom 后处理，也没有体积光步进。我们看到的柔亮边缘是已经建好的光效几何在相机中叠加出来的。复用这套做法时，可以把主体、外轮廓与环境照明分别调节，比一开始就把发光值拉得很高更容易控制画面。

## 建筑动画

![太阳庭院完全开启后的主镜头，太阳盘升起发光，祭司出现在中央平台](/articles/sun-temple/court-open.jpg)

### 开启顺序

建筑动作由一个总进度 `transformation.value` 控制，范围是 0 到 1。点击按钮只改变目标值，每帧按 `delta / 6.5` 推进或退回，所以建筑开启的设计时长为 6.5 秒。各部件从总进度里取自己的时间区间。

| 部分 | 总进度区间 | 开启时的约定时间 | 动作 |
| --- | --- | --- | --- |
| 柱廊 | 0～0.92 | 0～5.98 秒 | 左右整组各外移 16 |
| 中央阶梯 | 0.015～0.87 | 0.10～5.66 秒 | 抬平台阶与前方基础 |
| 塔门 | 0.03～0.90 | 0.20～5.85 秒 | 左右各外移 5.5 |
| 太阳盘 | 0.08～0.94 | 0.52～6.11 秒 | 上升 12.3 并旋转 |
| 点火 | 0.62～0.985 | 4.03～6.40 秒 | 渐变材质、光晕与点光源 |
| 祭司出场 | 总进度达到 0.72 | 约 4.68 秒开始 | 从门内走向庭院 |

时间来自进度乘以 6.5 的换算，假设动画时间正常推进。祭司走出还需要 6 秒，所以从点击开启到她站定约为 10.68 秒，不能把建筑完成的 6.5 秒当作整段演出的时长。

这些阶段有重叠：柱廊还在移动，太阳盘已经升起；盘面开始点火时，人物准备出场。于是我们看到一段连续动作，而不是六个互不相关的开关。

### 缓动

`heavySegment` 先把总进度映射到部件的局部进度，再使用五次平滑函数：

```text
u = clamp((value - start) / (end - start), 0, 1)
E(u) = 6u^5 - 15u^4 + 10u^3
```

E 在两端的一阶、二阶导数都为零，因此位移能平稳起步、平稳停止。对较重的石块来说，这种变化比线性插值更符合我们对缓慢启动与收住动作的预期。

最后 22% 的进度还叠加一个很小的衰减振荡：

```text
q = (u - 0.78) / 0.22
E += 0.012 × sin(2πq) × (1-q)²   // 仅在 0.78 < u < 1 时加入
```

它让部件在终点附近轻微回摆，幅度随 q 接近 1 而消失。这是作者设计的动作曲线，没有质量、阻尼或弹簧的物理求解。

打开过程中即使再次点击关闭，各部件仍从当前 `value` 计算位姿，可以沿原来的形变关系退回。关闭时如果人物已走出，情况则更复杂，需要下面的角色状态机先完成返回。

### 返回与中断

每帧更新总进度前，源码检查：目标是否为关闭，祭司是否就绪，以及她的路径进度是否大于零。只要人物还在外面，建筑进度就暂时保持不动。她回到门内并隐藏后，建筑才开始收拢。

这个检查解决了平台、人物和建筑之间的依赖。它不需要给每块石头安排碰撞体，也能让这段预设动作保持合理的先后关系。我们实际运行了开启、完整关闭，以及返回刚开始时再次开启：按钮切换正常，重新开启后人物与平台继续保持在当前演出中，没有重载场景。

太阳盘在总进度超过 0.9 时，还会积累一小段持续旋转。这个角度保存在 `discSpin` 中，关闭时没有清零；共享时间 `solarClock` 也一直前进。因此关闭再打开并不是把每个细节都复原到同一帧，材质流动和累计转角可以继续延续。

## 祭司

### 模型与骨骼动画

人物由 walking FBX 的根对象进入场景，缩放为 0.052，并放在中央入口。waving FBX 提供挥手 AnimationClip，但第二个角色根对象没有加入场景。两个片段交给同一个 AnimationMixer，绑定到正在显示的角色上。

这依赖两份资产中的骨骼命名与动画绑定兼容。源码没有执行一套通用重定向算法，也没有自动修复不同骨架的比例。如果换人物，我们需要先检查动作轨道能否找到对应骨骼，并核对朝向、单位和站立高度。

行走和挥手都设置为无限循环，播放速度分别是 1.14 和 0.94。走到目标位置时，挥手动作重置并用约 0.72 秒淡入，行走动作淡出；开始返回时，再切回行走。`AnimationAction` 的权重过渡让两段姿态有一段混合，不会在切换时直接跳帧。

载入时作者还把角色贴图设为 sRGB，启用投射和接收阴影，并关闭网格的视锥剔除，防止骨骼动作超出静态包围盒时角色突然消失。角色材质被统一设为不透明并写入深度；如果我们替换成带透明发片的角色，这项统一设置需要重新评估。

两份 FBX 都触发了“顶点超过四个蒙皮权重”的加载器警告。r185 的 FBXLoader 选择其中较大的四项，角色网格再归一化权重。原页面暂时过滤了这条警告，它只是隐藏提示，并没有增加蒙皮影响数量。复用时应该检查实际变形效果，必要时在导出阶段整理权重。

### 路径与转身

骨骼动画负责腿、手臂和身体姿态，人物在庭院里的整体位置则由代码控制。路径进度 s 从 0 增到 1，根对象沿中央直线移动：

```text
z(s) = lerp(-36, 1.5, s)
d = clamp((s - 0.12) / 0.46, 0, 1)
y(s) = lerp(16.2, 13.53, d²(3-2d))
```

她从较高的门内位置走到庭院，竖直方向用平滑函数下降。这里没有 Root Motion、导航网格、逐脚 IK 或实时地面射线检测。角色位置与当前平台是作者按这条演出路线协调好的。

出门用 6 秒，返回用 5.6 秒。源码没有根据脚步长度反推整体速度，因此替换动作或修改距离后可能出现滑步。更稳妥的复用方式是同时调整路径长度、动作速度与行程时间，再观察脚是否贴着平台移动。

角色状态包括隐藏、走出、挥手、转向门内、走回与重新转向外侧。关闭时先留出约 0.68 秒转身，再往回走；返回途中重新开启，则给约 0.42 秒转向，然后从当前路径进度继续走出。

转身首先把角度差归到最短方向，再按时间进行指数平滑：

```text
α = 1 - exp(-λ × delta)
heading_next = heading + shortestAngle(target - heading) × α
```

这样不会因为从 359° 转到 1° 而绕行几乎一整圈。按 delta 计算也让不同帧率下的转向速度更接近一致。到达入口后角色隐藏，两个动作暂停，建筑便可以继续关闭。

## 镜头

项目使用 41° 视野的透视相机，近裁剪面为 0.2，远裁剪面为 560。三个预设如下，坐标与场景共用同一单位：

| 镜头 | 相机位置 | 观察目标 |
| --- | --- | --- |
| Hero | (84, 38, 152) | (0, 31, -27) |
| Façade | (0, 39, 156) | (0, 33, -35) |
| Elevated | (102, 98, 128) | (0, 31, -24) |

切换时直接复制位置和目标，没有平滑飞行或自动运镜。Reset 返回 Hero 视角，**不会关闭庭院或重置动画**。拖动相机时，预设按钮的选中状态被清除，表示我们已经离开固定镜头。

OrbitControls 禁止平移，缩放距离限制在 44 到 200；方位角限制在 -1.12 到 1.12 弧度，约为正面左右各 64°，俯仰范围也避免相机钻到地下。这个范围保住了入口层次、暗面板和太阳盘平面光效所依赖的观察角度。

窄屏处理主要针对按钮、安全区和 FPS 的位置。相机只更新 aspect，并没有按建筑包围盒重新计算距离。本次 390×844 预览中按钮可用，建筑两侧会被裁切，中央入口仍能看到。如果我们要把它做成面向手机的完整场景，可按主体包围盒分别计算横向和竖向的取景距离，并在缩放上限内给出窄屏镜头。

## 光照与天空

主光是偏暖的 DirectionalLight，强度为 4.1，从神庙左前上方照射，产生柱子、檐口与山壁的大块阴影。半球光用偏冷天空色和暖棕地面色提供整体明暗，低强度 AmbientLight 与另一盏冷色方向光抬起背光面的细节。

主光使用 4096×4096 阴影图，相机范围明确包住建筑与部分周边山体，配合 `bias` 和 `normalBias` 减少阴影自交伪影。阴影框越大，同样分辨率下每个区域分到的像素越少；这里是围绕主场景调出的尺寸，复用时应重新检查覆盖范围。

天空是一个半径 360、从内部观看的球体。材质根据视线高度混合暖色地平线和偏蓝灰的高空，再用与太阳方向的点积生成亮区：

```text
glow = max(dot(skyDirection, sunDirection), 0)^54
core = max(dot(skyDirection, sunDirection), 0)^420
```

54 次幂形成较宽的暖色晕圈，420 次幂缩成更小的亮核。天空亮区与砂岩材质的朝向调色共用 `SUN_VECTOR`。实际方向光另用灯的位置与目标计算方向，两者都指向左前上方，但并不精确同向；如果我们需要严格一致，应由同一参数生成天空、材质与灯光方向。场景没有载入 HDR 环境图，也没有昼夜、天气或雾的切换。

风格化主要来自这些具体选择：暖砂岩大色块、低饱和的冷色阴影、明确的直射光、克制的石面颗粒、几何化装饰，以及中央太阳盘的橙红光效。ACESFilmicToneMapping 配合 1.2 的曝光，把普通石面和强亮光效放进同一画面；色调映射负责最终亮度压缩，不会自动替我们生成额外的光晕。

## 性能与加载

项目复用了基础石块几何，并把同类台阶、柱子与装饰放进实例网格，减少重复对象的绘制调用。动态中央台阶更新 34 个实例矩阵，也比每帧重新创建 34 份几何更合适。像素比上限为 1.6，控制高密度屏幕上的渲染像素量。

太阳光效的几何成本比较小：两张平面、22 片三角形火舌和少量石雕。透明区域依然会产生叠加绘制成本，但它没有额外的 Bloom 模糊链或离屏反射 pass。人物则保留完整蒙皮网格，主光阴影图也较大，这些都需要结合实际设备再评估。

加载方面，原 `index.html` 约 16.7 MB，其中主要是内嵌 Base64 的两份 FBX。Base64 把二进制转成文字，会增加未压缩体积；运行时还要解码字节、解析模型和图片。虽然挥手文件的网格没有加入场景，解析它时仍会创建第二套模型与材质。这种单文件分发很直接，如果继续产品化，可考虑保留一份角色网格，再单独保存兼容动作，改为外部二进制资产并按需加载。

还有两处可以继续优化：中央台阶与地基在静止时也会被更新，基础法线反复重算；主光的阴影没有按建筑动作是否变化单独控制刷新。复用到静态占比较大的展示场景时，可以给变形与阴影增加更新标记，再确认人物与光效的动态阴影需求。

更新循环把 delta 限制在最多 0.05 秒，避免切回页面时一次跳过太多动画。但它没有补上被截断的时间，也没有固定步长子步，所以低于 20 FPS 时演出可能比实际时间更慢。页面上的 FPS 是短时间窗口内的帧数统计，不能据此承诺所有设备的表现。本次浏览器运行和窄屏检查已完成，真实手机设备与强制 WebGL 2 后端的画面尚未单独测试。

## 学习总结

这座神庙最值得我们带走的，是几何、材质和行为之间明确的对应关系。轮廓决定用截头体、旋转曲面还是挤出；台阶的实例矩阵与基础顶点一起响应同一个进度；发光表面、透明轮廓和环境光分别负责各自的画面效果；角色先返回，平台才能关闭。每个模块都不复杂，组合时的规则让它们成为完整场景。

我们把可复用内容补进了 **procedural-3d-scene Skill**。入口保存通用工作步骤，太阳神庙的参数、公式和近似放在独立案例中，前两篇的海湾与铁路案例继续保留。

| 本篇方法 | 在 Skill 中复用的内容 | 使用时需要检查 |
| --- | --- | --- |
| 对称神庙与重复门框 | 主体轮廓、二维截面、旋转曲面与实例化 | 门洞净空、部件连接和倒角比例 |
| 地表平整遮罩与岩壁沟槽 | 大形状、局部遮罩、分层噪声与封口 | 建筑基础、边界和实际几何法线 |
| 同一套砂岩节点材质 | 大色差、结构纹理、颗粒与粗糙度分层 | 世界坐标与运动部件局部坐标 |
| 太阳盘光效 | 主体发光、透明轮廓、环境照明分开控制 | 透明排序、深度和侧面观察 |
| 开启演出 | 总进度映射到各模块区间，加入缓动与轻微回摆 | 开启、中断、反向与终点位姿 |
| 人物与平台同步 | 状态机、返回守卫、路径位移与动作混合 | 骨架兼容、滑步和脚下支撑 |
| 单文件人物资产 | 记录导入资产、动画和许可证边界 | 包体、重复资源与真实来源 |

[下载本篇更新后的完整 Skill](https://devtookit.com/articles/sun-temple/procedural-3d-scene.zip) · [查看 Skill 入口](https://devtookit.com/articles/sun-temple/procedural-3d-scene/SKILL.md) · [查看太阳神庙案例](https://devtookit.com/articles/sun-temple/procedural-3d-scene/references/sun-temple.md)

安装后，我们可以把场景目标、资产限制与验收状态一起交给 AI，而不是只要求“做一个类似神庙的场景”。例如：

```text
使用 $procedural-3d-scene，阅读 Sun Temple 案例。

制作一个可交互的沙漠遗迹：中轴对称，两组柱廊可以展开，
中央阶梯抬成平台，上方能量盘在动作后半段逐渐点亮。
建筑、地形与材质由代码生成，人物允许使用我提供的骨骼资产，
请明确记录角色网格、动作与贴图的来源和许可证。

先用灰盒确认关闭、完全开启和窄屏三种构图，再添加装饰。
用一个总进度组织建筑动作，用人物状态机处理走出与返回。
关闭时必须等人物回到入口，中途重新开启应从当前位置继续。
能量盘分别实现表面发光、透明轮廓与附近照明，
不要在没有对应实现时把它描述成 Bloom 或体积光。

交付参数表、可运行场景、来源清单和实际截图。
检查完整开合、开启途中关闭、返回途中开启、镜头切换与窄屏；
检查动作过渡、平台支撑、透明排序和静止状态的更新成本。
```

Skill 中保留的是我们已经理解的建模与编排方法。下一次换成遗迹、机械庭院或舞台装置时，可以调整对象与参数，继续沿用这些空间和时间关系；原作者的场景设计、代码与资产仍需保留原有出处。
