news 2026/9/18 11:52:02

Unity 3D+C#满族刺绣虚拟展馆交互漫游实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity 3D+C#满族刺绣虚拟展馆交互漫游实战

做非遗主题虚拟展馆的这几年,我手上这个满族刺绣展馆算是真正跑通的一个。前两版都死在中途:第一版把全部精力砸在模型精度上,观众戴着设备进门三分钟就开始眩晕;第二版交互做得花哨,可展柜里的绣片看上去像贴了张A4打印纸,一点丝线的光泽都没有。到第三版我才想明白一件事——虚拟展馆不是"把展厅搬到电脑里",而是"把看展这件事重新设计一遍"。

这套基于 Unity + 3D + C# 的满族刺绣文化主题虚拟展馆交互漫游系统,核心就是三件事:让观众能自然地走、能看得清绣品的针脚、能随手点开每一件绣品背后的故事。技术栈上用的是 Unity 2022 LTS,C# 写全部交互逻辑,3D 资产走 Blender + 摄影测量结合的管线,最终同时发布到 PC 端和 WebGL 网页端,并留了一体机 VR 的降级分支。如果你是有 C# 基础想找完整落地路径的开发者,或者是做数字展陈、非遗数字化方向的在校同学,这篇内容能给你省掉不少重复摸索的时间——下面讲的每一条参数和坑,都是我自己在项目里踩出来的。

1. 这个展馆为什么最终落在 Unity 上

1.1 三种技术路线的实际对比

立项初期我认真比过三条路:纯 Web 前端(Three.js)、Unreal、Unity。最后选 Unity,不是因为 Unity 最强,而是因为它的"综合性价比"在这个体量的项目里最合适。

纯 Web 方案的优势是传播成本极低,一个链接就能打开,不用装任何东西。但它的问题在于光照和材质的上限太低——满族刺绣最抓人的是丝线的方向性反光和绣片在展柜玻璃后的层次感,Three.js 要用自写着色器去补这些东西,工作量会直接翻倍。而且一旦展品数量上到五六十件、场景三角面数上百万,浏览器的内存和帧率就很难看。

Unreal 的渲染效果确实降维打击,Nanite 和 Lumen 能直接省掉大量烘焙工作。但代价是构建体积大、上手曲线陡,团队里如果只有我一个人能改材质,后期维护会变成单点依赖。而且 WebGL 方向的导出体验,说实话不如下面要讲的 Unity 方案顺手。

Unity 卡在中间:URP 管线足够把绣片质感做出来,C# 的开发效率高,一套工程可以同时导出桌面端、WebGL 和 Android 一体机,Asset Store 上现成的交互组件也能省时间。对于一个三到六个月周期、两三个人规模的项目,这是最不容易翻车的选择。

对比维度Three.jsUnrealUnity
传播门槛最低,开链接即用高,需安装中,WebGL 可开链接
材质表现上限中,需自写着色器极高高,URP/HDRP 可调
大场景性能受浏览器内存限制最好好,可控
多端发布仅 Web桌面/主机为主桌面/Web/移动/一体机
团队上手成本前端基础即可较高中,C# 友好

1.2 渲染管线选 URP 而不是 Built-in

管线选择这一步很多人会犹豫,我的结论是:只要你不是必须依赖某些只在 Built-in 下能跑的老插件,就选 URP。理由有三个。

第一,URP 的 Shader Graph 让我能快速试出一种"丝线反光"的自定义材质,不用去啃手写 HLSL。做法是把各向异性高光近似成一张方向性遮罩乘以高光强度,虽然不如 HDRP 的物理各向异性精确,但在展柜玻璃的反射叠加下,视觉上已经足够以假乱真。

第二,URP 的渲染特性(Renderer Feature)体系很干净,我想加一个"选中展品描边"的后处理效果,只需要挂一个 Renderer Feature,比在 Built-in 里改 CommandBuffer 舒服太多。

第三,移动端和一体机方向的兼容性更好,URP 的移动端预设基本可以直接用。

需要提醒的是,Unity 2022 里 URP 的版本号是跟着编辑器走的,别去 Package Manager 里乱升版本,我见过有人把 URP 升到高版本后所有材质集体变粉红,回退花了一下午。

2. 空间尺度、动线与第一人称控制器

2.1 用"米"当单位,先把展馆量出来

虚拟展馆最容易犯的错,是把尺寸随手填。Unity 默认 1 个单位是 1 米,这是个必须遵守的约定,一旦破掉,后面所有物理、光照、阴影距离都会跟着歪。

我的做法是先按真实展馆画平面图:主展厅开间 12 米 × 18 米,层高 4.2 米;过渡廊道宽 2.4 米;展柜高度 1.1 米,柜体进深 0.6 米。观众视点高度设为 1.65 米——这个数字很关键,1.6 偏矮会有"蹲着看展"的压抑感,1.75 又会让人觉得自己在飘。我最后在 1.62 到 1.68 之间试了三轮,1.65 是最自然的。

展品间距也要按观看距离算。人在静止观看时的舒适距离大约是展品对角线长度的 1.5 到 2 倍。一件 0.4 米宽的绣片,最佳观看距离在 0.8 到 1.0 米之间。这就要求相邻展柜的间距不能小于 1.8 米,否则两个人同时看展会互相挡视线。这些数字看着琐碎,但它们是整个漫游体感的地基。

提示:建模阶段就把所有物体按 1 单位 = 1 米导入,并在 Blender 里先 Apply Scale。导入 Unity 后再放大 100 倍的做法,会让烘焙和物理全部出问题。

2.2 CharacterController 的六个参数分别管什么

第一人称漫游我一开始想用 Rigidbody 加力去做,结果在楼梯和门槛上一直卡。后来换成 CharacterController,问题基本消失。它的参数不多,但每一个都直接影响手感。

Slope Limit 我设成 45 度,这是控制器能爬上去的最大坡度;展馆里有几级缓坡台阶,正好卡在 30 度以内很安全。Step Offset 设 0.3,代表能自动迈过 30 厘米的台阶,展馆门槛高度是 12 厘米,绰绰有余。Skin Width 建议设成 Radius 的 10%,我用的是 Radius 0.3、Skin Width 0.03,这个值太小会导致穿墙抖动,太大又会让人浮在地面上。

Center 这个参数经常被忽略,它决定了胶囊体的中心相对物体原点的偏移。我把 Center 的 y 设成 0.9,让胶囊底部正好贴地,相机挂在胶囊顶部往下一点的位置。

移动速度上,观展不是打游戏,走路速度 2.2 米/秒就够了,奔跑给到 4.0 米/秒。加速度用插值而不是直接赋值,否则起步和停下会有明显的顿挫感。

using UnityEngine; using UnityEngine.InputSystem; [RequireComponent(typeof(CharacterController))] public class VisitorController : MonoBehaviour { [Header("移动")] public float walkSpeed = 2.2f; public float runSpeed = 4.0f; public float acceleration = 12f; [Header("视角")] public Transform cameraPivot; public float mouseSensitivity = 0.12f; public float pitchLimit = 75f; [Header("物理")] public float gravity = -18f; private CharacterController cc; private Vector3 horizontalVelocity; private float verticalVelocity; private float pitch; void Awake() => cc = GetComponent<CharacterController>(); void Update() { Look(); Move(); } void Look() { Vector2 delta = Mouse.current.delta.ReadValue() * mouseSensitivity; transform.Rotate(Vector3.up, delta.x); pitch = Mathf.Clamp(pitch - delta.y, -pitchLimit, pitchLimit); cameraPivot.localEulerAngles = new Vector3(pitch, 0f, 0f); } void Move() { var kb = Keyboard.current; Vector2 input = Vector2.zero; if (kb != null) { if (kb.wKey.isPressed) input.y += 1f; if (kb.sKey.isPressed) input.y -= 1f; if (kb.dKey.isPressed) input.x += 1f; if (kb.aKey.isPressed) input.x -= 1f; } input = Vector2.ClampMagnitude(input, 1f); float target = kb != null && kb.leftShiftKey.isPressed ? runSpeed : walkSpeed; Vector3 desired = (transform.right * input.x + transform.forward * input.y) * target; horizontalVelocity = Vector3.Lerp(horizontalVelocity, desired, acceleration * Time.deltaTime); if (cc.isGrounded && verticalVelocity < 0f) verticalVelocity = -2f; verticalVelocity += gravity * Time.deltaTime; cc.Move((horizontalVelocity + Vector3.up * verticalVelocity) * Time.deltaTime); } }

2.3 动线设计比场景好看更重要

展馆做出来好不好用,一半看动线。我的做法是在平面图上先画一条"必看主线":入口 → 序厅(满族刺绣的历史脉络)→ 针法区(平针、锁绣、打籽、补绣的对比展示)→ 纹样区(枕顶、荷包、云肩、幔帐上的蝴蝶、石榴、盘长纹)→ 互动区(观众可以自己"配线配色")→ 出口。

这条主线的宽度留 2.6 米,支线岔路做窄一点(1.8 米),用地面材质和灯光亮度做隐形引导——主线上地面亮一点、顶灯密一点,观众会下意识往亮的地方走。这个技巧比放箭头指示牌优雅得多,而且不破坏展厅的整体氛围。

另一个细节是"回望视野"。观众走完主线回头时,应该能看到一个有记忆点的画面,比如一面通高的绣片墙或者一个旋转的纹样装置。这个位置我放了一面 6 米高的幔帐复原模型,配顶部洗墙灯,几乎所有人走到出口都会回头拍一张。

3. 刺绣展品从实物到引擎的资产管线

3.1 什么该建模,什么该用贴图解决

一开始我想把所有展品都做成高精度模型,结果第一件荷包就建了两天,三角面数到了八万,摆进展厅里和其他展品风格完全不统一。后来我定了一条规则:只有"观众会绕着看"的展品才做完整三维,其余一律用底板 + 法线贴图做浮雕式呈现。

具体分三档:

  • A 档(完整三维):荷包、香囊、小件佩饰,这类是立体的,观众会想看侧面和背面。单件控制在 15000 到 30000 三角面。
  • B 档(半立体):云肩、枕顶、袖口镶边,做成微起伏的曲面片,起伏靠法线贴图提供。单件 2000 到 5000 三角面。
  • C 档(平面板):大面积的幔帐、旗袍面料,直接一张贴图贴在平面上,靠材质球做出织物质感。单件 200 面以内。

这个分档让总面数从 180 万压到 60 万左右,帧率直接翻倍,而观众几乎看不出区别——因为看展时的注意力集中在图案和针脚上,而不是几何精度。

3.2 绣线质感不靠分辨率,靠这几张图

把贴图分辨率从 1024 提到 4096,绣片的观感提升其实很有限。真正让它"活过来"的是这几张图的配合。

Albedo(底色)是最基础的一张,但要注意:刺绣的颜色是有层次的,同一块红色在平针区偏亮、在打籽区偏暗。如果直接拿照片当 Albedo,会因为拍摄时的环境光被"烤"进去,导致在引擎里怎么打光都发灰。我的做法是在修图软件里先做一次去光照处理,把明暗关系剥掉,只留色相和彩度信息,再重新在引擎里打光。

Normal Map(法线贴图)是绣线立体感的核心。这里有个取巧办法:把绣线走向用一张灰度图表示出来(亮的地方是线脊、暗的地方是线沟),再转成法线贴图。这样每一根线的凸起方向都是对的,侧光一打就会出现真实的丝线反光带。

Roughness(粗糙度)必须做变化。真实的绣线在不同角度下粗糙度不同,如果全图给一个常数,材质就会显得像塑料。我的做法是把法线贴图的灰度反过来当粗糙度遮罩用——线脊处粗糙度低一点(0.25 左右),线沟处高一点(0.6 左右),效果立刻上一个台阶。

Ambient Occlusion(AO)用减法烘焙或者直接在引擎里烘,主要压住针脚之间的缝隙,让绣片有"厚度"。

贴图尺寸建议关键设置
Albedo2048×2048去光照处理,sRGB 勾选
Normal2048×2048导入类型选 Normal Map,不勾 sRGB
Roughness1024×1024单通道打包,不勾 sRGB
AO1024×1024单通道,不勾 sRGB
遮罩图1024×1024把 Roughness/Metallic/AO 打包进 R/G/B 通道

3.3 资产导入 Unity 的规范化清单

资产一多,导入设置如果不统一,后面调材质会疯掉。我给自己定了一份检查表,每次导入必过一遍。

  • Model 标签页:Scale Factor 保持 1,Convert Units 勾选,Generate Colliders 只给需要交互的物体勾。
  • 法线方向:Import Normals 用默认,别选 Calculate,否则硬边会糊。
  • Read/Write Enabled 默认关闭,除非确实要在代码里改 Mesh,开着会白占内存。
  • Mesh Compression 展品类选 Off 或 Low,压缩过头会让绣片边缘出现锯齿。
  • 材质 Material Creation Mode 选 None,手动绑材质,避免一堆自动生成的重复材质球。
  • 所有展品统一挂在Exhibits目录下,命名前缀带展品编号,方便批量脚本处理。

4. C# 交互层:射线拾取与数据驱动

4.1 为什么展品数据要走 ScriptableObject

最早我把展品名称、简介、针法这些信息直接写在挂在物体上的 MonoBehaviour 里。做到第 20 件的时候问题来了:改一个字段要挨个点开对象,而且美术一旦重新导模型,挂的脚本就全丢了。

换成 ScriptableObject 之后,所有展品信息变成独立的 .asset 文件,放在Assets/Data/Exhibits下。好处很直接:可以用编辑器脚本批量改,可以导出成 Excel 做校,模型替换不影响数据,而且这些数据在运行时是共享引用,几十件展品只占一份内存。

using UnityEngine; [CreateAssetMenu(fileName = "Exhibit_", menuName = "展馆/展品数据")] public class ExhibitData : ScriptableObject { [Header("基础信息")] public string exhibitId; public string displayName; [TextArea(2, 4)] public string summary; [Header("工艺信息")] public string stitchPattern; public string motif; public string eraAndRegion; [Header("展示资源")] public Sprite thumbnail; public AudioClip narration; public GameObject highDetailPrefab; [Header("聚焦参数")] public Vector3 focusOffset = new Vector3(0f, 1.5f, 0f); public float focusDistance = 1.2f; }

再写一个继承 ScriptableObject 的"数据总表",把所有展品按区域分组,方便做导览模式和统计。

4.2 一条射线搞定拾取与反馈

拾取系统我用接口 + 射线探针的组合。先定一个IInteractable接口,所有可交互物体实现它;再写一个探针脚本,每帧从相机中心发一条射线,检测到新目标就切换焦点。

交互距离我定的是 2.8 米。这个数字不是随便来的:观众站在展柜前,眼睛到展品的距离大约 1.2 到 1.5 米,考虑到展柜玻璃会挡住一部分,探针真正打到的是展柜内的碰撞体,所以要留出余量。最开始我设 5 米,结果站在大厅中央就能隔空点亮三件展品,非常出戏。

public interface IInteractable { string Prompt { get; } void OnFocusEnter(); void OnFocusExit(); void OnInteract(); }
using UnityEngine; public class InteractionProbe : MonoBehaviour { public Camera viewCamera; public float reach = 2.8f; public LayerMask interactMask; public KeyCode interactKey = KeyCode.E; private IInteractable current; void Update() { IInteractable hit = Probe(); if (!ReferenceEquals(hit, current)) { if (current != null) current.OnFocusExit(); current = hit; if (current != null) current.OnFocusEnter(); } if (current != null && Input.GetKeyDown(interactKey)) current.OnInteract(); } IInteractable Probe() { Ray ray = new Ray(viewCamera.transform.position, viewCamera.transform.forward); if (Physics.Raycast(ray, out RaycastHit info, reach, interactMask, QueryTriggerInteraction.Collide)) return info.collider.GetComponentInParent<IInteractable>(); return null; } }

有个坑必须提:GetComponentInParentGetComponent更适合展品,因为展品的碰撞体往往挂在子物体上(比如荷包的碰撞体是简化过的盒子),接口实现在父物体上。用GetComponent会返回 null,调试时会让人怀疑射线根本没打中。

4.3 焦点高亮与信息面板的三个细节

高亮我试过三种方案。最简单的是改材质 Emission,缺点是会污染原始材质,多个展品共享材质时会串色。第二种是描边后处理,效果最好但需要额外 Renderer Feature,移动端开销大。最后我用的是"叠加一层高亮壳":在展品位置放一个略大一圈的复制体,材质用纯色 + 半透明,只在高亮时启用。

这个方案的优势是零后处理开销,缺点是展品形状复杂时复制体面数高。我折中了一下,高亮壳用的是展品的低模版本,面数只有原模型的十分之一,视觉上完全够用。

信息面板用 TextMeshPro + 世界空间 Canvas。这里有三个细节:

第一,面板朝向。不要让它永远面向相机(Billboard),那样观众绕着看的时候面板会跟着转,很晕。我改成面板固定在展品旁边,只在触发时用一个 0.25 秒的缓动从展品位置滑出来。

第二,文字长度。满族刺绣的工艺介绍很容易写长,面板上超过 6 行就没人看了。我的做法是面板只显示名称、针法、年代三行,详细内容放在"展开"按钮后面。

第三,音频。每件展品配一段 40 到 60 秒的讲解音频,触发时播放。这里要注意音频源的位置——如果挂在展品上,观众走远后声音会衰减到听不见,体验很割裂。我改成播放时把 AudioSource 挂到相机下的一个空物体上,音量恒定。

5. 光照烘焙与阴影调试:绣片能不能立住看这一步

5.1 Mixed Lighting 的配置思路

展馆里百分之九十的物体是静止的,所以光照烘焙是最优解。我把 Lighting Mode 设成 Baked Indirect,天空盒和主方向光设为 Mixed,其余补光全部 Baked。

关键参数是 Lightmap Resolution。展品区的绣片墙我用了 40 texels/unit,普通墙面 15 就够,地面 8。分辨率不是越高越好,一个 12×18 米的展厅如果全用 40,烘焙一次要四十分钟,而且光照贴图内存能吃到 800MB。分区设置能把烘焙时间压到十二分钟左右。

Lightmap Padding 设 4 像素,防止相邻展品的贴图边缘互相渗色。这个值设小了会出现"隔壁展柜的暖黄光渗到我这块绣片上"的问题,非常难排查。

还有一点:所有需要烘焙的物体必须勾 Static,包括展柜玻璃。玻璃如果没勾 Static,它就不会参与间接光计算,展柜内部会黑成一片。

提示:展品替换后要重新烘焙。如果只是替换了同尺寸模型的 Mesh,可以不勾 Static 让它在运行时用 Light Probe 接收环境光,代价是少了自身的间接光反弹。

5.2 阴影穿透、漏光和 peter panning 的排查顺序

阴影问题我遇到过三种,排查顺序建议固定下来,别乱试参数。

第一种是 shadow acne(自阴影条纹),表现是物体表面出现一条条明暗相间的斜纹。原因是阴影贴图的分辨率和深度偏移不匹配。解决顺序:先降 Bias(从 0.05 降到 0.02),再降 Normal Bias(从 0.4 降到 0.15),最后才考虑提高 Shadow Resolution。我一般把方向光的 Shadow Resolution 定在 Medium(2048),Bias 0.02、Normal Bias 0.2,这个组合在展馆场景里基本通吃。

第二种是 peter panning(阴影脱离物体),表现是物体明明放在地上,阴影却飘在脚边。这通常是 Bias 设太大导致的。如果降低 Bias 之后 acne 又回来了,那就是阴影贴图分辨率不够,这时候才需要调高分辨率或者缩小 Shadow Distance。

第三种是漏光,表现是墙角和展柜缝隙里出现不应该有的亮边。这个八成不是阴影问题,而是烘焙的 Lightmap Padding 太小或者模型接缝处有缝隙。先检查模型是否完全贴合(用 ProBuilder 快速补一块密封面),再调 Padding。

Shadow Distance 我设的是 30 米,配合四级级联阴影(Cascade Count 4)。展馆纵深 18 米,30 米足够覆盖全部可见区域,同时保证近处阴影精度。设到 100 米的话,近处阴影会糊成一片。

现象最可能原因处理顺序
表面斜向条纹阴影 Bias 过小降 Normal Bias → 降 Bias → 提分辨率
阴影与物体分离Bias 过大降 Bias,必要时提分辨率
墙角亮边烘焙 Padding 或模型缝隙补密封面 → 调 Padding 到 4
阴影边缘锯齿级联数或分辨率不足Cascade 提到 4 → 提 Shadow Resolution

6. 性能预算与多端发布

6.1 先把预算定死,再动手优化

优化最大的敌人是"没有目标"。我在项目开始就定了三条线的预算:

指标PC 端WebGL一体机
目标帧率603072
Draw Call< 300< 150< 200
三角面总数< 120 万< 50 万< 80 万
实时阴影开启关闭,只用烘焙关闭,只用烘焙
后处理抗锯齿 + 轻微泛光
纹理内存< 1.5GB< 300MB< 500MB

定完预算之后,优化就有了方向。最有效的手段依次是:静态合批(把所有不动的展柜、地面、墙面标记 Static Batching)、遮挡剔除(Occlusion Culling,展厅是典型的隔断空间,效果极好)、LOD(远处展品用低模)、纹理压缩(PC 用 DXT/BC,移动端用 ASTC)。

静态合批有一个反直觉的点:它会增加内存占用(因为要存合并后的网格),所以在内存紧张的 WebGL 端要慎用大场景的静态合批,或者只对同一材质的小物件合并。

6.2 WebGL 部署到 IIS 的几个坑

WebGL 发布后传给客户,第一条常见反馈是"打开一片白"。九成是服务器 MIME 类型没配。

IIS 默认不认识 .wasm 和 .data 后缀,需要在站点根目录放一个 web.config:

<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <staticContent> <remove fileExtension=".data" /> <mimeMap fileExtension=".data" mimeType="application/octet-stream" /> <remove fileExtension=".wasm" /> <mimeMap fileExtension=".wasm" mimeType="application/wasm" /> <remove fileExtension=".symbols.json" /> <mimeMap fileExtension=".symbols.json" mimeType="application/octet-stream" /> </staticContent> </system.webServer> </configuration>

第二个坑是压缩。Unity 在 Build Settings 里可以选 Gzip 或 Brotli 压缩,好处是包体小很多,坏处是服务器必须配置对应的解压支持(Brotli 需要 IIS 装扩展)。如果你想省事,就勾上 Decompression Fallback,让 Unity 在运行时用 JS 解压,代价是首屏加载慢几秒。给不熟悉服务器配置的甲方交付时,我一般直接选这个兜底方案。

第三个坑是内存。WebGL 的默认堆内存是 256MB,展馆场景如果纹理吃得多会直接崩。可以在 Player Settings 的 Publishing Settings 里把 Memory 提到 512MB 以上,但别超过 1024MB,浏览器在移动端扛不住。

第四,首次加载。整个包体 60MB 的话,首次加载要等半分钟。我的做法是做一个 HTML 加载页,把 Unity 的进度回调接出来显示百分比,再配上满族刺绣的纹样做背景,等待焦虑感会低很多。

6.3 一体机和移动端的降级策略

一体机方向我做过一版,核心改动有四处。

第一,渲染路径改成 Single Pass Instanced,这是 VR 里最省性能的模式,能让双眼的 Draw Call 近乎减半。第二,必须锁帧并且稳定在 72 帧(PICO 4 的刷新率),掉帧带来的眩晕感比画面差更致命。第三,所有后处理关掉,抗锯齿改用 MSAA 4x。第四,交互从手柄射线改成"头部瞄准 + 单手确认",因为长时间抬手会很累。

还有一个细节是移动速度。VR 里 2.2 米/秒的速度太快了,会引起明显的晕动症。我最后把速度降到 1.2 米/秒,并且加了一个"瞬移"的备选模式——按下摇杆后出现一个落点指示器,松开直接跳过去,晕的人可以切到这个模式。

移动端(手机浏览器)我基本放弃了这个场景,因为展馆需要精细观看,手机屏幕太小,不如把精力放在 PC 和一体机上。如果确实要出手机版,建议只保留"定点导览"模式,去掉自由漫游。

7. 几个卡了我两天以上的问题

7.1 材质集体变粉红:管线切换的批量修复

这个坑几乎所有从 Built-in 转 URP 的人都会踩。原因是原材质的着色器在 URP 下找不到,Unity 就把它渲染成亮粉色当作错误提示。

批量修复的方法是:菜单 Window → Rendering → Render Pipeline Converter,选 Built-in to URP,勾选 Material Upgrade,全选后转换。但要注意,这个转换只对 Standard 着色器有效,如果你之前用过一些自定义着色器,它转不了,得手动重建。

我踩的更深的一层坑是:转换完之后有一部分材质球变成"过期"状态(Inspector 里显示 Missing Shader),原因是这些材质引用的着色器被删了。解决办法是把材质的 Shader 手动改成 URP/Lit,再把原来的贴图重新拖一遍。

7.2 烘焙后物体漂移:Static 标记的连带效应

有一次烘焙完之后,展厅里的一个旋转展台突然"不动了",代码明明还在转,但视觉上纹丝不动。查了两个小时才发现,这个展台被美术误勾了 Static,烘焙的时候 Unity 把它当成了静态物体合批进 Lightmap Static 网格,运行时的 Transform 变化对渲染无效。

这个问题很阴,因为 Console 里没有任何报错。排查思路是:先在 Scene 视图里选中物体,看 Inspector 右上角是否有 Static 勾选;再在 Rendering 调试视图(Shaded Wireframe 或 Albedo)里看它是否被画在了非预期的位置。

教训就是:所有需要运行时移动、旋转、缩放的物体,一律不勾 Static。我在项目后期加了一个编辑器脚本,扫描所有被标记 Static 但运行时位置会变的物体,直接在 Console 里报出来。

7.3 UI 点击穿透:一根射线打中两个目标

第三版上线前测试,发现点"展开"按钮的时候,背后的展柜也会被触发交互。原因是我用了两套射线:UI 用 EventSystem,场景用 Physics.Raycast,两者互不知情。

解决办法是在探针脚本里加一道判断:

if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject()) return; // 指针在 UI 上,跳过场景交互

如果用的是新输入系统,这个方法在某些设备上会有延迟,更稳的做法是继承IPointerEnterHandlerIPointerExitHandler,自己维护一个"指针是否在 UI 上"的布尔值,再给探针读。我用的是后者,因为一体机和触屏设备上都验证过,没有出现延迟丢帧。

还有一个相关的小坑:世界空间 Canvas 上的按钮,如果 Canvas 没挂 Graphic Raycaster,是点不动的。这个问题新手极容易卡住,因为按钮的 Interactable 明明是勾上的,就是没反应。

7.4 关于展品信息的几句实在话

满族刺绣的内容整理,比技术更难。我在写针法介绍的时候,一开始用了很多"精美绝伦""巧夺天工"这类形容词,后来全部删掉了。观众想看的是:这件绣片用的是平针还是打籽,纹样为什么是蝴蝶和石榴,什么年代的人会在什么场合用它。把这三件事说清楚,比任何形容词都有说服力。

数据字段里我专门留了一个eraAndRegion,因为这个信息对观众理解展品影响很大——同一件枕顶,清末民初和二十世纪中期的配色和题材差别很明显。这类细节写进去,展馆才有"信息量",而不只是"好看"。

跑完整个项目回看,虚拟展馆真正难的地方从来不是把模型放进去,而是让观众在里面的每一分钟都不别扭:走起来不晕、看得清细节、点得到想点的地方、听得懂讲解在说什么。技术在幕后,体验在台前,这句话我在这个项目里算是彻底体会了一遍。如果后面要做扩展,我打算加一个"观众自己配色"的互动模块,让手绣纹样在屏幕上实时换线色,这可能是让年轻人真的坐下来的唯一办法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 11:49:26

傲腾M10加速机械硬盘:Windows原生缓存实战指南

1. 项目概述&#xff1a;为什么今天还要折腾傲腾 M10 加速机械硬盘&#xff1f;你可能已经看到过太多“SSD取代HDD”的论调&#xff0c;甚至身边朋友的旧电脑早换上了NVMe固态&#xff0c;连系统盘都跑在PCIe 4.0通道上。但现实是——大量企业办公终端、NAS入门设备、老款游戏本…

作者头像 李华
网站建设 2026/9/18 11:45:47

Cherry Studio连接MCP报Connection closed?一文带你从零排查到底

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 11:44:10

Cherry Studio SVG 坐标精度优化:用 SVGO --precision 降低图标文件体积

Cherry Studio SVG 坐标精度优化&#xff1a;用 SVGO --precision 降低图标文件体积 【免费下载链接】cherry-studio &#x1f352; Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端 项目地址: https://gitcode.com/CherryHQ/cherry-studio 本文讲解 SVG 坐标精度…

作者头像 李华
网站建设 2026/9/18 11:43:59

2026主流AI论文工具客观排行榜|无恰饭实测,适配国内毕业审核标准

随着高校论文重复率AIGC双审机制全面落地&#xff0c;AI论文工具早已不是“可选辅助”&#xff0c;而是应届生刚需。但目前市面上工具参差不齐&#xff1a;海外工具学术性强但水土不服&#xff0c;通用大模型写作灵活却合规性差&#xff0c;小众工具功能单一且存在数据风险。本…

作者头像 李华