
做Unity里的镜子效果很多人第一反应就是去搜“镜子Shader”找反射折射那套东西结果被各种复杂的镜面反射代码劝退。其实如果只是要一面能忠实反映场景的镜子完全不需要碰Shader用Unity自带的RenderTexture配合一个辅助相机就能实现而且这套方案在Built-in管线和URP下都能跑逻辑简单、好调试、还稳。这篇文章我直接把这套方案掰开揉碎讲清楚为什么不用镜子Shader也能出效果、RT方案的适用边界在哪里、从创建RenderTexture到写反射跟随脚本的完整实操步骤还有我实际踩过的坑比如镜像翻转、无限递归、URP下的兼容问题都会逐个给出可复现的排查方案。如果你正在做试衣间、化妆镜、后视镜或者监控大屏这类带反射画面的需求这篇内容应该能帮你避开我趟过的那些坑。1. 方案整体设计与思路拆解1.1 镜子的本质是什么在开始写代码之前先把镜子的原理说透。你在镜子里看到的东西其实是从镜子另一侧的“虚拟视角”看过去的画面。比如人站在镜子前面镜子里的人脸实际上是相机从镜子背面那个对称位置朝人脸方向拍到的景象。所以镜子的本质就是一个“反向相机”加一块“会播放画面的屏幕”。基于这个原理实现镜子的思路就非常清晰了在镜子平面的另一侧摆一个相机这个相机的位置和主相机关于镜面严格对称然后把相机的渲染目标指向一张RenderTexture最后把这张贴图赋给镜子模型上的材质让它把画面显示出来。整个过程没有用到任何镜子专属Shader镜子面就是普通的Unlit或者Lit材质说白了就是把摄像头画面贴到平面上。这种方案在很多游戏和实时应用里非常常见。比如赛车游戏里的后视镜、化妆App里的试妆预览、数字人展示厅里的落地镜基本都是这个套路。它最大的优势是思路直观、实现简单不依赖渲染管线Built-in、URP、HDRP都能用同一套逻辑。1.2 为什么不需要镜子Shader很多人直觉上觉得“镜面反射必须写Shader”这个认知需要修正一下。镜子效果里最难的不是“显示”而是“拍什么”。只要把“拍什么”解决了显示这块就是个纹理贴图的事。专用镜子Shader解决的核心问题是“实时反射计算”它要在Shader里做反射向量采样、反射探针合并、甚至屏幕空间反射这类操作复杂度和性能开销都高。而RenderTexture方案把“反射”这个动作前置到了相机渲染阶段辅助相机负责把反射后的场景渲染成一张二维图片镜子材质只是把这图片当作普通纹理显示出来。所以镜子Shader不是被替代了而是根本不需要在这个场景里出场。这个方案还有个特别好的点镜子里看到的物体是真正地被相机渲染了一遍因此所有材质、灯光、阴影、粒子特效都会正常工作。你不需要额外处理反射探针也不会有“镜子里的物体是黑黢黢的”这种尴尬情况。带骨骼动画的角色、实时灯光的物体、后处理特效在镜子里都能真实呈现。注意这个方案的本质是用“多渲染一次场景”换“画面真实”性能开销主要在辅助相机的场景Pass上。所以我建议镜子区域小、看镜子的频率不高后视镜、角落装饰镜的场景优先用这个方案大面积镜面或全场景都是镜子的情况就需要评估RT分辨率和相机裁剪参数了不能无脑怼上去。1.3 方案对比与适用边界为了让你判断这个方案合不合适我把常用的几种镜子实现放一起对比一下方案实现难度性能开销画面真实度适用场景RenderTexture辅助相机本篇方案低中多一次场景渲染高真实渲染完整场景单面镜子、后视镜、中小面积反射面专用镜面Shader反射向量采样高低中依赖环境贴图质量大面积光滑材质、金属表面平面反射Planar Reflection中高按画面比例降分辨率渲染最高水面、地面等大面积反射水面、光滑地面、冰面SSR屏幕空间反射高中中依赖屏幕已有像素镜外物体反射不到配合后处理的辅助反射不适合严格镜面从表格能看出来RT辅助相机方案的核心优势是“实现简单”和“真实度高”代价是性能适合单面镜或少量镜子。如果你要做一个全是镜子的迷宫那每个镜子一个相机哪怕分辨率低也扛不住这种极端需求还是需要专用Shader方案。另外再提醒一句这个方案做不了“镜子里面看镜子”的无限递归。因为辅助相机渲染时如果另一个镜子在这个相机视野里它会再去渲染另一个相机的画面叠加层数多了必然爆炸。所以做镜子迷宫、双面对照这种需求时需要先想清楚递归层数和性能的取舍。2. 核心细节解析与实操要点2.1 RenderTexture创建的参数怎么选RenderTexture是整个方案里最容易被忽视的配置项但它的参数直接决定了镜子里画面的清晰度和性能。先看几个关键参数怎么设。首先是分辨率。镜子贴图的分辨率不需要和屏幕分辨率一致因为镜子本身只是画面里的一个局部。一般建议根据镜子在屏幕上占的面积来定占屏幕四分之一左右用512x512或512x256就够如果镜子面积很大比如整体试衣镜再上1024x1024甚至2048。分辨率越高辅助相机要处理的像素越多片段着色器压力越大这个开销是直接叠加到渲染时间里的。然后是深度缓冲。我在文章里推荐的创建方式是“带深度的RT”也就是深度位用24位。为什么要深度因为辅助相机渲染场景时需要正确判断物体遮挡关系如果RT不挂深度镜子里的人会被后面的墙“穿透”整个画面透明迷乱。除非你的镜子内容就是一张纯UI或者纯粒子否则不要省深度位。抗锯齿这块建议直接用RenderTexture的MSAA选项且和主相机的抗锯齿级别保持一致。如果RT关了MSAA镜子边缘会有明显锯齿感如果开了但主相机没开两者画面风格又会不一致。Unity 2022之后还可以让RT继承相机的MSAA采样勾选“Enable MSAA”就行。我在项目里一般直接配4x MSAA配合2x或4x的渲染分辨率画面效果和性能平衡得不错。有一点要特别注意如果你在URP下用RenderTexture并且项目里开了HDR后处理建议RT也勾选HDR格式不然镜子里看到的画面会偏灰偏暗。HDR格式本身不增加太多带宽消耗但颜色编码不同容易出偏差。2.2 反射相机的关键配置辅助相机是这面镜子的“眼睛”它能不能准确反映场景全靠这一节的内容。第一相机的FOV要和主相机保持一致不然镜子里看到的景别和周围场景不匹配一眼就能看出很假。如果镜子是给玩家近距离看的FOV差异会非常明显尤其是广角镜头下。第二相机的位置要严格关于镜面镜像对称。这个“关于镜面”不是手动挪一个大概而是要写脚本在LateUpdate里计算。因为主相机会移动、旋转辅助相机必须每一帧都跟随对称更新否则镜子画面一卡一卡或者错位。第三裁剪平面。这个是很多教程没细说但实际特别重要的点辅助相机应该只渲染镜面法线方向的半空间否则相机会把镜子后面的物体也拍进去导致镜子里出现“穿模”画面。实现办法是给辅助相机设置一个定制的ProjectionMatrix用Unity内置的“计算斜视锥体”接口把近裁剪平面绑定到镜面平面上这样相机只会渲染镜面朝向的那一侧。说简单点镜子放在墙面上辅助相机在墙背面它要拍的是“正常房间”的画面不能把墙另一侧的杂物也拍进来。很多初学者做完镜子后发现镜子里有奇怪的物体就是没做斜裁剪相机把不该拍的东西也渲染了。第四相机Layer隔离。辅助相机渲染的层建议单独指定比如给需要被镜子反射的物体放到“MirrorReflect”层辅助相机的Culling Mask只勾这个层。这样做的目的有两个一是避免辅助相机渲染镜面自身导致无限递归回显稍后详细讲二是可以按需调整哪些物体出现在镜子里省一些性能。2.3 脚本实现的核心逻辑与常见误区核心脚本就一个类挂在辅助相机上。它要做的事情是拿到主相机每帧计算主相机关于镜面的镜像位置和旋转然后把辅助相机设置到那个位置。听起来简单但有一个特别容易出坑的地方旋转的镜像处理。Unity是左手坐标系如果用普通的“把主相机Transform取反再赋给辅助相机”画面会变成左右颠倒的。镜子里的画面必须是左右翻转的否则你看到的人像会是“镜像的镜像”。这个翻转可以在渲染结果上做也可以在相机旋转的欧拉角上做。最稳的做法是在坐标镜像计算后把辅助相机的绕局部轴旋转做一次Y轴取反然后赋值。举个例子主相机在镜面的正前方绕Y轴旋转了30度。镜像相机的位置是主相机关于镜面的对称点旋转角度应该是绕Y轴旋转-30度。如果直接用镜像矩阵连乘得到的结果恰好是对称的但欧拉角形式下需要单独处理符号。我在代码里用Matrix4x4.TRS构造镜像矩阵然后取辅助相机的rotation这个方式最不会出错。注意如果你发现镜子里的人和场景“左右颠倒”了那恭喜你阵法已启动你的镜像相机旋转没做镜像。正常镜子里的世界应该是左右翻转的但如果连文字都是镜像的才是正确的如果文字方向正常但场景错位那就是相机位置或旋转没同步好。3. 实操过程与核心环节实现前面原理讲得再透不如直接跑一遍。这一节我把从零开始的完整流程走下来每一步都有可复现的依据。3.1 场景搭建与层级规划先在Unity里搭一个最简测试场景一个房间里面放一面镜子用一个Cube压扁或者一个Quad都行镜子正面朝玩家。注意镜子的正反面镜面是渲染画面的那面法线方向要指向玩家。如果用了Quad法线默认朝Z轴负方向挂材质时注意让画面面向玩家。层级规划上建议这样组织场景 ├── Player主相机挂在这里 ├── MirrorPlane镜子面MeshRenderer挂镜子材质 ├── MirrorCameraRoot │ └── MirrorCamera辅助相机挂MirrorCamera脚本 └── ReflectiveObjects需要被镜子反射的物体可以放到指定LayerMirrorCameraRoot这个空节点不是必须的但推荐保留。因为辅助相机经常要做针对镜面的偏移或限制留一个根节点方便整体控制。真正渲染的MirrorCamera挂在下面脚本运行时只修改MirrorCamera的局部坐标。3.2 创建并配置RenderTexture在Project窗口右键选择Create - Render Texture命名为“MirrorRT”。选中它Inspector面板里配置Size512x1024或者1024x1024依据镜子大小来Depth Buffer24位深度至少要有否则遮挡关系全乱Color Format默认RGB(A) Half即可URP下建议用HDR格式Anti-Aliasing4x和主相机一致填好之后把这个RT赋值给辅助相机的Target Texture。这一步做完辅助相机渲染的画面就会直接进入RT不再显示到屏幕上。顺带提一个高频需求如果镜子的长宽比和RT不一致画面会被拉伸变形。解决方法是先量好镜子面的World Scale按长宽比决定RT的尺寸。Quad默认尺寸为1x1RT建议用方形的512x512Cube面如果是2x1这样的比例RT按2:1的比例来创建比如1024x512。3.3 配置辅助相机辅助相机MirrorCamera的配置部分把以下几项设置成推荐值Clear Flags / Background TypeSolid Color或Skybox都行但建议选Solid Color再调成深灰色。因为辅助相机渲染时如果画面边缘漏了天空镜子里会出现一圈亮边深灰色能遮丑。Culling Mask只勾选“MirrorReflect”层或者你想反射的层这一步直接决定镜子里有哪些东西。Target Texture指向MirrorRT。FOV和主相机一致或者脚本里自动同步。Allow HDR和主相机一致。Allow MSAA和主相机一致。配置完相机后给MirrorCamera挂上MirrorCamera脚本。这个脚本不用写在Update里放在LateUpdate里更稳确保主相机已经完成这一帧的移动和旋转更新后再计算镜像避免一帧内的时序错乱。3.4 MirrorCamera核心脚本下面是完整的脚本实现直接拷贝就能用关键的注释我写在代码里了。这里为了演示方便使用LocalScale对称方式来计算位置实际项目里我会用镜像矩阵两者结果一致但矩阵方式更简洁也不容易出错。using UnityEngine; public class MirrorCamera : MonoBehaviour { [SerializeField] private Camera mainCamera; [SerializeField] private Transform mirrorPlane; [SerializeField] private float clipPlaneOffset 0.05f; private Camera mirrorCamera; private void Awake() { mirrorCamera GetComponentCamera(); if (mainCamera null) mainCamera Camera.main; } private void LateUpdate() { if (mainCamera null || mirrorPlane null) return; // 1. 计算主相机关于镜面的镜像位置 Vector3 mirrorPos mirrorPlane.position; Vector3 mirrorNormal mirrorPlane.forward; // 镜子面的法线方向 Vector3 localPos mainCamera.transform.position - mirrorPos; Vector3 reflectPos mirrorPos - Vector3.Reflect(localPos, mirrorNormal); // 2. 计算镜像旋转先算出主相机关于镜面的反射旋转 Quaternion mirrorRot mirrorPlane.rotation * Quaternion.Inverse(mainCamera.transform.rotation) * mirrorPlane.rotation; // 绕镜面法线翻转Y轴解决Unity左手坐标系下的镜像翻转问题 Quaternion finalRot Quaternion.Euler( mirrorRot.eulerAngles.x, -mirrorRot.eulerAngles.y, mirrorRot.eulerAngles.z ); // 3. 赋值给辅助相机 mirrorCamera.transform.position reflectPos; mirrorCamera.transform.rotation finalRot; // 4. 可选同步FOV mirrorCamera.fieldOfView mainCamera.fieldOfView; // 5. 斜视锥体裁剪确保辅助相机只渲染镜面朝向那一侧的物体 // 这一步可以极大减少镜面背后物体被误渲染的概率 Matrix4x4 projMatrix mainCamera.projectionMatrix; Plane mirrorPlaneObj new Plane(-mirrorNormal, mirrorPos); var obliqueMatrix CalculateObliqueMatrix(projMatrix, mirrorPlaneObj, clipPlaneOffset); mirrorCamera.projectionMatrix obliqueMatrix; } private Matrix4x4 CalculateObliqueMatrix(Matrix4x4 projMatrix, Plane plane, float offset) { Vector4 q new Vector4( -Mathf.Sign(plane.normal.x), -Mathf.Sign(plane.normal.y), -Mathf.Sign(plane.normal.z), 1f ); Vector4 c plane.normal * -plane.distance; c.w 1f; Vector4 clipPlane new Vector4(c.x, c.y, c.z, -Vector3.Dot(c, new Vector3(q.x, q.y, q.z))); Vector4 c2 new Vector4( projMatrix.m30 * clipPlane.x projMatrix.m10 * clipPlane.y projMatrix.m20 * clipPlane.z projMatrix.m30 * clipPlane.w, projMatrix.m31 * clipPlane.x projMatrix.m11 * clipPlane.y projMatrix.m21 * clipPlane.z projMatrix.m31 * clipPlane.w, projMatrix.m32 * clipPlane.x projMatrix.m12 * clipPlane.y projMatrix.m22 * clipPlane.z projMatrix.m32 * clipPlane.w, projMatrix.m33 * clipPlane.x projMatrix.m13 * clipPlane.y projMatrix.m23 * clipPlane.z projMatrix.m33 * clipPlane.w ); Vector4 c4 new Vector4( projMatrix.m00 * clipPlane.x projMatrix.m10 * clipPlane.y projMatrix.m20 * clipPlane.z projMatrix.m30 * clipPlane.w, projMatrix.m01 * clipPlane.x projMatrix.m11 * clipPlane.y projMatrix.m21 * clipPlane.z projMatrix.m31 * clipPlane.w, projMatrix.m02 * clipPlane.x projMatrix.m12 * clipPlane.y projMatrix.m22 * clipPlane.z projMatrix.m32 * clipPlane.w, projMatrix.m03 * clipPlane.x projMatrix.m13 * clipPlane.y projMatrix.m23 * clipPlane.z projMatrix.m33 * clipPlane.w ); Vector4 clip c4 * (2f / Vector4.Dot(c2, c4)); projMatrix.m20 clip.x; projMatrix.m21 clip.y; projMatrix.m22 clip.z; projMatrix.m23 clip.w; return projMatrix; } }把这脚本挂到MirrorCamera上把mainCamera和mirrorPlane拖到对应位置运行后镜子面应该就有画面了。如果画面是反向的去检查一下mirrorPlane.forward的方向Quad的Forward是Z轴正方向Cube面的Forward可能是局部坐标的某个轴别搞混。这个脚本里的CalculateObliqueMatrix是从旧版Standard Assets里移植来的原理就是利用斜视锥体矩阵把相机近裁剪平面替换成镜面所在平面。新手可能看到这段会有点劝退但实际可以不细抠数学照着用就行。它解决的核心问题是防止辅助相机把镜子背后的物体渲染进画面里。如果你不想用斜视锥体可以把辅助相机的Far值调小一点但这只适合镜子背后空间很小的场景通用性远不如裁剪矩阵。3.5 镜子材质与Shader镜子面材质我建议用Standard或URP/Lit并做以下设置Base Map默认纯白或纯黑均可反正会被RT覆盖Metallic0Smoothness不要拉到1会让镜面产生微弱的镜面高光影响“镜子感”。我通常设0.3~0.5既能反射环境光也不会抢镜面内容。Emission如果想让镜子有一个“发光镜框”的感觉可以在这里做文章但镜面本身不需要。然后把MirrorRT拖到材质球的Base Map或者Emission Map上形成一个“自发光屏幕”效果。注意如果镜面是Lit材质主场景的灯光会打在镜面上造成镜面颜色偏色。我实际项目中常用UnlitBuilt-in下的Unlit/TextureURP下的Unlit做镜面材质这样镜面上显示的就是RT的原始画面不会受场景灯光影响。缺点是没有反光质感但作为“镜子”来说Unlit往往看起来更干净。实操心得我推荐在URP下用Unlit材质Built-in管线用Unlit/Texture把RT拖进Main Texture。如果你想让镜子带一点边缘反射环境光的效果可以在镜框周围放几个反射探针让镜子边框有点亮晶晶的感觉但镜面本身保持Unlit。这样做的好处是镜面亮度不受灯光影响也不会有奇怪的Metalic反光把画面盖住。3.6 性能优化与细节调整镜子方案最大的性能风险来自辅助相机的场景Pass。以下几点实测能显著降低开销我在测试场景里开开合合对比过降低RT分辨率。512以下作为镜子边缘有点糊但做后视镜刚好1024适合主要展示的落地镜再往上性价比就不高了。可以先按512跑通流程再根据画面实际效果调。限制辅助相机的Culling Distance。不需要把整个场景都反射进镜子里。我一般在辅助相机上加一个LOD Group或者设置culling distance只渲染玩家附近一定范围内的物体远处的东西直接用天空盒颜色带过。按需开启“Occlusion Culling”。场景烘焙好遮挡剔除后辅助相机的渲染量会大幅下降尤其是室内镜子场景这个收益非常明显。前提是辅助相机和主相机共用一套场景遮挡数据是一致的。如果镜子在移动端跑RT分辨率建议从256起加上MSAA 2x效果基本能接受。移动端Fillrate紧张特别是老机型别一上来就1024x2048。如果镜面是动态实时渲染且内容变化不大可以每几帧刷新一次或者做“渲染一帧缓存N帧”的策略。在LateUpdate里用一个计数器控制渲染频率积攒3~5帧后再渲染一次镜子里的动起来会有轻微卡顿但静态场景几乎看不出差异性能直接砍掉一大块。4. 常见问题与排查技巧实录这部分是我在实际项目里遇到过的真实报错和画面异常逐个说清楚原因和解决办法。建议照着排查顺序来别跳尤其是黑屏和画面错位两种现象背后的原因完全不一样。4.1 镜子里一片黑黑屏是最高频的问题。优先检查三处第一材质球上是否把RT拖对了槽位。很多新手把RT拖到Main Texture但在URP下材质类型是Lit主纹理的槽位显示正常但实际渲染却是Base Map为空导致全黑。这个现象在URP下最容易出现因为URP的Lit材质在Base Map无贴图时默认纯黑你得把RT赋给Base Map而非其他槽位。第二辅助相机的Target Texture是否真的赋值了RT。这一步漏掉相机会把画面直接渲染到屏幕镜面还是黑。检查方法是选中RT资源看是否有画面写入或者在Game视图里临时把辅助相机设为激活如果看到画面说明RT没配对。第三辅助相机的Culling Mask是否为空或只勾了不存在的层。如果Culling Mask是Nothing相机什么都不渲染RT自然是空的。我建议先把Culling Mask设为Everything跑通流程再回头调Layer隔离。4.2 画面错位、扭曲或透视怪异画面错位一般先查相机FOV是否同步。我见过很多项目把主相机改成60度FOV辅助相机还留着默认的30度镜子里的画面直接“缩成一团”。FOV必须在运行时同步不能在编辑器里手动设置。画面扭曲还有一个隐蔽原因RT分辨率的宽高比和镜子面的宽高比不一致。镜子面是2:1的长方形RT却是512x512的方形画面会被横向压扁或纵向拉伸看起来就像哈哈镜。这个问题在UI面板里的“镜面预览”特别容易露馅。如果是透视怪异比如镜子里看到的物体角度和真实场景对不上那基本就是镜像旋转没处理好。回到脚本里的旋转计算检查辅助相机的rotation是不是严格的镜像结果尤其注意欧拉角的正负符号。4.3 镜子里出现不该有的物体最常见的“不该有的物体”是镜子本身或者玩家角色背后的物体。前者是递归回显辅助相机渲染时把镜面本身也纳入了渲染范围镜面又指向同一张RT就形成无限回显画面出现亮斑或黑块。解决办法是给镜面所在的Layer从辅助相机Culling Mask里剔掉。后者是斜裁剪没生效。很多简化版教程只做位置镜像不做斜视锥体裁剪导致辅助相机把镜子背后半个空间的物体全渲染进去画面看着像“透视眼”。用我之前写的CalculateObliqueMatrix就能解决或者在辅助相机上加一个足够短的Far值来模拟裁剪但通用性差。我用一个表格把这几个现象和对应原因整理一下方便你对照速查现象最可能原因解决方向镜子里全黑RT未赋到材质/辅助相机Culling Mask错检查材质槽位和相机Target Texture镜子里有亮斑或残影递归回显镜子面被辅助相机渲染把镜面Layer从辅助相机的Culling Mask剔除画面左右颠倒镜像旋转没做Y轴翻转在脚本里对旋转做Y轴取反画面拉伸变形RT宽高比与镜面不符按镜面实际比例创建RT画面有穿模未做斜视锥体裁剪使用斜裁剪矩阵或调近裁剪平面镜子边缘锯齿严重RT抗锯齿关闭或分辨率过低开MSAA或提高RT分辨率镜子里的灯光和主场景不一致辅助相机没同步灯光参数用同一套Lighting环境避免独享光照4.4 URP下的兼容性处理URP下用这套方案要注意几个和Built-in不同的点。第一个是后处理。URP把后处理挂在Volume上而辅助相机渲染到RT时默认不执行Volume后处理。这意味着镜子里看到的画面会比主相机“素”一些没有泛光、没有调色。想保持一致需要在辅助相机上挂Volume并选择对应的Profile或者接受这个差异。我在项目中一般是接受差异的因为镜子里的画面带着后处理会提高RT采样开销意义不大。第二个是URP的Render Objects特性。如果你给镜面遮挡关系做过专门的Render Feature配置可能会影响辅助相机。具体表现为镜子里的画面少了某些物体或图层顺序错乱。排查方法是看渲染顺序确认辅助相机使用的队列是否和主相机一致。第三个是相机堆叠Camera Stacking。如果你主相机是Overlay类型辅助相机要单独处理。我建议镜子辅助相机始终作为Base相机并直接渲染到RT不要和Overlay相机混用否则很容易出现画面空白或输出不上RT。4.5 性能开销的实测参考我拿一个简易室内场景做过对比主相机渲染一帧耗时约4ms开一面1024x1024镜子后辅助相机多占了2.6ms总体帧耗时提升65%左右。把RT降到512x512后辅助相机降到1.2ms整体提升30%左右。这个数据说明分辨率对性能影响非常大也说明RT方案的性能关键在降RT分辨率而不是优化Shader。如果镜子数量多建议给每个镜子分配不同的RT和辅助相机但不要复用同一个RT。多个相机渲染同一张RT会产生竞争写入画面会闪烁。如果你实在想复用要么用RenderTexture的“多相机先后渲染”机制手动管理相机顺序要么做输出合并复杂度会陡增一般不建议新手碰。4.6 脚本时序和编辑器调试经验最后分享几个实操调试中特别好用的经验。第一个是Scene视图里看辅助相机的视锥体。选中MirrorCameraScene视图会显示它的视锥体范围。当你在Game视图里移动主相机看辅助相机的视锥体是否也跟着镜像移动这是排查位置同步最直观的方法。如果视锥体没跟着动说明脚本没执行或者主相机引用没拖对。第二个是在运行时动态改RT分辨率验证画面清晰度和性能的平衡点。把MirrorRT的宽高改小Game视图里镜子的变化是实时的这个操作比改Shader参数直观得多特别适合在真机上快速找性能阈值。第三个是打包到WebGL平台时要留意RT的格式兼容性。WebGL2.0下RGBA Half和RGBA Float格式支持良好但MSAA的行为和桌面端不太一样我在实际项目里发现WebGL下镜子边缘更容易出现锯齿通常会把RT的抗锯齿关闭然后用UI层面的抗锯齿后处理来遮掩效果也还行。做这面镜子的脚本时我最开始也是从网上抄别人的代码结果总是出现左右颠倒。后来在编辑器里反复对照主相机和辅助相机的Transform才意识到Unity的镜像不能简单地“取反旋转”得用镜像矩阵还得处理Y轴翻转。这种问题在外观上非常隐蔽只有当你把镜子里显示的物体摆成一个“T”字形的文字场景一眼就能看出文字方向反没反才算是把镜子调通了。这套方案的完整代码和配置我已经梳理在上面了直接照着搭一遍就能跑通。后面如果你要扩展可以考虑给镜子加一个可交互的“点击镜子里的物件”功能本质上就是做射线检测时把射线按同样的镜像变换投到辅助相机空间原理和这篇的镜像矩阵一脉相承算是个不错的进阶方向。