之前做游戏 demo 的时候,最常见的反馈是“玩法太普通”“一眼就能猜到下一关”。后来办公室桌上正好有一副红蓝 3D 眼镜,我突发奇想:如果做一款只有戴上红蓝 3D 眼镜才能正常看清画面层次的游戏,会不会更有意思?于是就有了这篇教程的主角——一个基于 Unity 的 2D 躲避小游戏 demo。
这篇文章不只讲玩法创意,更重要的是完整拆解红蓝 3D 游戏背后的实现原理:左右眼视差怎么产生、红色/青色通道如何分离、RenderTexture 双相机渲染怎么做、Canvas 叠加显示如何配置。所有代码都是可直接复制运行的 Unity 脚本和 Shader,适合 Unity 初学者、游戏开发爱好者,以及想在个人项目中加入立体视觉效果的开发者。
读者跟完整个流程后,可以掌握以下内容:
- 红蓝 3D 成像的基本原理,以及和偏振 3D、快门 3D 的区别。
- 在 Unity 中搭建“双相机 + RenderTexture + 自定义 Shader”的立体渲染框架。
- 用几个简单脚本实现玩家控制、障碍生成、计分与游戏结束。
- 处理红蓝重影、亮度异常、手机性能等常见问题。
1. 背景与核心概念
1.1 红蓝 3D 眼镜是怎么工作的
先理解一个最基本的生理常识:人的两只眼睛之间大约有 6 厘米距离,所以左眼和右眼观察同一个场景时,看到的画面并不是完全相同的。两只眼睛分别捕捉到两幅带有轻微角度差异的画面,大脑再把它们融合成一个具有深度感的立体图像,这就是“双目视差”。
普通显示器和普通游戏画面只提供“一个视角”,无论你左眼还是右眼看到的都是同一张图片,大脑无法从单张图片中提取可靠的深度信息,所以看起来就是平的。
红蓝 3D 眼镜采用一种非常古老的“分色法”。
它的工作方式可以概括为两步:
- 屏幕同时显示两张画面:左眼画面被着色成红色,右眼画面被着色成青色。
- 眼镜的左镜片是一块红色滤光片,它会过滤掉青色内容,只让红色画面进入左眼;右镜片是青色滤光片,只让青色画面进入右眼。
这样,两只眼睛虽然同时在看同一个屏幕,但每只眼睛实际收到的图像却是不同的。大脑再进行视差融合,立体感就出现了。
这套方案不需要专门的 3D 显示器,也不需要红外同步,只需要一副十几块钱的红蓝纸框眼镜,因此在低成本 demo、线下展台互动、老式 3D 电影还原等场景中非常实用。
1.2 为什么“只有戴上 3D 眼镜才能玩”
这里有一个很容易被忽略的点:红蓝 3D 游戏在不戴眼镜时的画面,是“红色左眼视图”和“青色右眼视图”直接叠加在一起的效果。
不戴眼镜时,你会看到:
- 画面上同时存在红、青两种颜色的重影。
- 物体边缘出现明显的红边和青边。
- 场景层次看起来是错位的,甚至有点眩晕。
- 玩家和障碍物的碰撞位置很难目测准确。
换言之,不戴眼镜时游戏是“不可玩”的。一旦戴上红蓝眼镜,左眼和右眼各看各的画面,大脑会把两层图像合成,重影消失,场景的远近关系立刻变得清晰。这种“戴上才正常”的体验,就构成了游戏的核心乐趣和话题点。
1.3 红蓝 3D 和其他 3D 方案的差异
很多读者会联想到电影院里的偏振 3D、电视上的快门式 3D、还有 VR 头盔。简单对比一下:
| 方案 | 原理 | 硬件成本 | 游戏适配难度 | 长时间使用体验 |
|---|---|---|---|---|
| 红蓝分色 3D | 用颜色通道分离左右眼画面 | 很低,一副纸眼镜即可 | 低,普通屏幕即可运行 | 画面偏色明显,容易疲劳 |
| 偏振 3D | 两束偏振光分别进入左右眼 | 需要偏振屏幕和眼镜 | 较高,普通显示器不支持 | 色彩还原好,但亮度会降低 |
| 快门式 3D | 左右眼交替显示,液晶眼镜交替遮挡 | 需要 120Hz 显示器和同步眼镜 | 较高,需要精确同步 | 刷新率不足时闪烁明显 |
| VR 头显 | 两块独立屏幕分别显示左右眼画面 | 很高 | 需要完整的 VR 渲染管线 | 沉浸感强,但硬件门槛高 |
红蓝 3D 的缺点很明显:色彩失真、长期佩戴不适。但它的优点也很直接:开发门槛低、运行环境要求低、分享传播方便。对于一个小型创意 demo 来说,红蓝 3D 是最容易实现的立体视觉方案。
2. 环境准备与版本说明
2.1 运行环境
这次 demo 我以 Unity 2021.3 LTS 为例,使用内置渲染管线(Built-in Render Pipeline)。如果你使用 URP 或 HDRP,Shader 的语法和坐标空间会有差异,需要额外适配。为了减少弯路,建议新建工程时选择3D 模板,而不是“通用渲染管线模板”。
系统环境方面,Windows、macOS 都可以,只要能运行 Unity 编辑器就行。最终打包发布到 Windows 可执行程序或 Android 手机都可以。下面的代码不依赖平台特性。
2.2 准备红蓝 3D 眼镜
你不需要买很贵的设备,某宝或者拼多多上搜索“红蓝 3D 眼镜”,选择红色左镜片、青色右镜片的普通纸框眼镜即可。
这里有一个容易踩坑的地方:部分便宜眼镜的红色滤片颜色很深,青色滤片偏蓝,不同批次之间的透光率差别很大。后面调试颜色通道时,如果发现左右眼画面亮度差异明显,不要怀疑代码,先换一副眼镜试一下。
2.3 示例项目结构
为了便于理解,这个 demo 的项目结构非常简单:
Assets/ |-- Scenes/ | |-- Main.unity |-- Scripts/ | |-- GameController.cs | |-- GameView.cs | |-- PlayerController.cs | |-- EnemySpawner.cs | |-- Enemy.cs |-- Shaders/ | |-- RedEye.shader | |-- CyanEye.shader |-- Materials/ | |-- RedEyeMat.mat | |-- CyanEyeMat.mat |-- Prefabs/ | |-- Enemy.prefab实际开发时可以继续拆分目录,这里保持最小结构,方便复制脚本和对照路径。
3. 核心原理与方案选型
3.1 从“双眼视差”到“红蓝分离”
在 Unity 中实现红蓝 3D 有两个问题需要解决:
- 如何生成左右眼两幅不同视角的画面。
- 如何把两幅画面分别染成红色和青色,并叠加到同一个屏幕上。
第一个问题最简单的方式是使用两个 Camera:左眼 Camera 放在主相机位置左侧一点,右眼 Camera 放在主相机位置右侧一点,它们分别渲染到两张 RenderTexture。位置偏移量通常在 0.1 到 0.3 之间,模拟人眼的瞳距。
第二个问题可以通过自定义 Shader 解决。左眼画面只保留红色通道,右眼画面只保留绿色和蓝色通道。
在 Shader 中,核心代码只有一行:
// 左眼:只保留红色通道 fixed4 c = tex2D(_MainTex, i.uv); return fixed4(c.r, 0, 0, 1);// 右眼:只保留绿色和蓝色通道,也就是青色 fixed4 c = tex2D(_MainTex, i.uv); return fixed4(0, c.g, c.b, 1);当这两层画面叠加在屏幕上时,红蓝眼镜就会自动完成左右眼的画面分离。
3.2 两条实现路线对比
在开始写代码之前,我先对比两条常用路线:
| 路线 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 单相机后处理 | 只用一个相机,渲染完成后在屏幕后处理 Shader 里做红蓝染色 | 性能开销小,实现代码少 | 只有一个视角,没有真正的双眼视差 |
| 双相机 RenderTexture | 用两个相机分别代表左右眼,各自渲染到纹理,再叠加到 Canvas 上 | 符合人眼视差原理,立体感真实 | 需要两倍场景渲染开销,代码量稍多 |
这个 demo 必须选择双相机 RenderTexture路线,否则戴不戴 3D 眼镜只是画面颜色不同,没有真实的纵深层次,也就没有“戴上才能玩”的效果。
3.3 整体架构
整个游戏运行逻辑可以用一句话概括:
主相机只作为“参考相机”提供位置和参数,左右眼相机负责把场景渲染到两张纹理上,Canvas 上的两个 RawImage 分别显示这两张纹理,并通过自定义材质分离红、青通道。
层次关系如下:
GameController ├── Player (玩家,可交互对象,z=0) ├── EnemySpawner (障碍生成器) ├── Main Camera (参考相机,运行时禁用渲染) ├── Canvas │ ├── LeftEyeImage (红色画面) │ ├── RightEyeImage (青色画面) │ ├── ScoreText (得分文本) │ └── StateText (状态提示文本) └── Background (背景装饰,多层 z)4. 完整实战:制作一个红蓝 3D 躲避小游戏
4.1 创建项目与场景
打开 Unity 编辑器,新建一个 3D 项目。场景中默认会有一个 Main Camera,把它的位置设置为(0, 0, -10),Rotation 保持(0, 0, 0)。
这个相机在运行时会被禁用渲染,只负责提供“参考视角”的位置和参数。左右眼 Camera 会自动创建,不需要手动摆放 Camera 到场景中。
然后创建核心物体:
| 物体 | 类型 | 位置 | 说明 |
|---|---|---|---|
| GameController | 空物体 | (0, 0, 0) | 挂载游戏控制脚本 |
| Player | Sprite | (0, -3, 0) | 玩家角色,挂载碰撞体 |
| EnemySpawner | 空物体 | (0, 0, 0) | 挂载障碍生成脚本 |
| Canvas | UI | (0, 0, 0) | 用于叠加左右眼画面和 UI 文字 |
4.2 创建玩家与障碍预制体
玩家可以用一个简单的正方形 Sprite 表示。切换到 2D 项目不需要,这里我们直接在 3D 场景中使用 SpriteRenderer。
选择菜单GameObject -> 2D Object -> Sprite,创建两个 Sprite:一个作为 Player,一个作为 Enemy 预制体。
Player 需要挂载以下组件:
Rigidbody2D,Gravity Scale 设为 0。CircleCollider2D,勾选 Is Trigger。PlayerController脚本。- Tag 设置为
Player。
Enemy 预制体需要挂载:
Rigidbody2D,Gravity Scale 设为 0,Body Type 设置为 Dynamic。BoxCollider2D,勾选 Is Trigger。Enemy脚本。- Tag 设置为
Enemy。
4.3 编写两个红蓝分离 Shader
在Assets下新建Shaders文件夹,然后创建RedEye.shader。
Shader "Custom/RedEye" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "Queue"="Transparent" "RenderType"="Transparent" } Pass { Blend One One ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv); return fixed4(c.r, 0, 0, 1); } ENDCG } } }再创建CyanEye.shader,片元着色器部分改为:
fixed4 frag (v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv); return fixed4(0, c.g, c.b, 1); }这里有一个细节需要注意:两个 RawImage 是上下重叠显示的,所以 Shader 的混合模式必须使用Blend One One,也就是加法混合。如果使用默认的Blend One Zero,后绘制的青色图会完全覆盖红色图,最终只能看到一种颜色。
4.4 创建红蓝显示材质
在Assets下新建Materials文件夹,创建两个 Material:
RedEyeMat,Shader 选择Custom/RedEye。CyanEyeMat,Shader 选择Custom/CyanEye。
这两个材质分别赋值给 Canvas 下的两个 RawImage。
Canvas 结构如下:
Canvas (Screen Space - Overlay) ├── LeftEyeImage (RawImage) ├── RightEyeImage (RawImage) ├── ScoreText (Text) └── StateText (Text)两个 RawImage 的属性设置一致:
- Anchor 设置为拉伸模式,四个角都设置为
(0, 0)和(1, 1),让图片铺满整个 Canvas。 - LeftEyeImage 的 Material 设置为
RedEyeMat。 - RightEyeImage 的 Material 设置为
CyanEyeMat。 - ScoreText 和 StateText 放在最后,保证显示在最上层。
- 所有 UI 组件的 Raycast Target 可以取消勾选,避免遮挡后续交互。
4.5 编写相机与 RenderTexture 管理脚本
这是整个红蓝 3D 效果最核心的部分。新建GameView.cs,并挂载到 GameController 上。
using UnityEngine; using UnityEngine.UI; public class GameView : MonoBehaviour { [Header("参考相机")] public Camera sceneCamera; [Header("左右眼画面")] public RawImage leftEyeImage; public RawImage rightEyeImage; [Header("参数")] public float eyeDistance = 0.2f; private Camera leftCamera; private Camera rightCamera; private RenderTexture leftRT; private RenderTexture rightRT; void Start() { if (sceneCamera == null) { sceneCamera = Camera.main; } CreateEyeCamera(ref leftCamera, ref leftRT, "LeftEyeCamera"); CreateEyeCamera(ref rightCamera, ref rightRT, "RightEyeCamera"); // 主相机只保留位置参数,不再直接渲染到屏幕 sceneCamera.enabled = false; } void LateUpdate() { // 左右眼相机沿场景相机右侧方向偏移 Vector3 leftPos = sceneCamera.transform.position - sceneCamera.transform.right * (eyeDistance * 0.5f); Vector3 rightPos = sceneCamera.transform.position + sceneCamera.transform.right * (eyeDistance * 0.5f); leftCamera.transform.position = leftPos; rightCamera.transform.position = rightPos; leftCamera.transform.rotation = sceneCamera.transform.rotation; rightCamera.transform.rotation = sceneCamera.transform.rotation; // 把渲染纹理绑定到 RawImage if (leftEyeImage != null && leftEyeImage.texture != leftRT) { leftEyeImage.texture = leftRT; } if (rightEyeImage != null && rightEyeImage.texture != rightRT) { rightEyeImage.texture = rightRT; } } void CreateEyeCamera(ref Camera cam, ref RenderTexture rt, string goName) { GameObject go = new GameObject(goName); cam = go.AddComponent<Camera>(); cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = Color.black; cam.depth = -10; cam.cullingMask = sceneCamera.cullingMask; // 复制参考相机的投影参数 if (sceneCamera.orthographic) { cam.orthographic = true; cam.orthographicSize = sceneCamera.orthographicSize; } else { cam.orthographic = false; cam.fieldOfView = sceneCamera.fieldOfView; } int w = Mathf.Max(2, Screen.width / 2); int h = Mathf.Max(2, Screen.height / 2); rt = new RenderTexture(w, h, 24); rt.name = goName + "_RT"; cam.targetTexture = rt; } void OnDestroy() { if (leftRT != null) { leftRT.Release(); } if (rightRT != null) { rightRT.Release(); } } }这里的重点有四个:
- 左右眼相机是运行时动态创建的,不需要手动在场景里摆放。
eyeDistance模拟瞳距,数值越大立体感越强,但太大容易导致重影。- RenderTexture 分辨率取屏幕的一半,用于平衡清晰度和性能。
- 主相机被禁用,但它的
transform仍然可以作为左右眼相机的基准位置。
如果你希望立体感更明显,可以把eyeDistance调到0.3到0.5。如果画面看起来太晕,就调回0.1。
4.6 编写玩家控制脚本
新建PlayerController.cs,挂载到 Player 物体上。
using UnityEngine; public class PlayerController : MonoBehaviour { public float moveSpeed = 6f; public float xLimit = 2.5f; private bool isGameOver = false; void Update() { if (isGameOver) { return; } float horizontal = Input.GetAxis("Horizontal"); Vector3 pos = transform.position; pos.x += horizontal * moveSpeed * Time.deltaTime; pos.x = Mathf.Clamp(pos.x, -xLimit, xLimit); transform.position = pos; } public void SetGameOver(bool value) { isGameOver = value; } }这个脚本控制玩家左右移动。这里没有使用Rigidbody2D.velocity,是因为 2D 躲避小游戏的移动逻辑比较简单,直接修改transform.position更直观。
需要注意的是,这里只限制 X 轴方向,Y 轴和 Z 轴位置保持不变,这样玩家的碰撞判定始终固定在z = 0平面,避免左右眼视差导致“视觉位置”和“碰撞位置”不一致。
4.7 编写障碍生成脚本
新建EnemySpawner.cs,挂载到 EnemySpawner 空物体上。
using System.Collections; using UnityEngine; public class EnemySpawner : MonoBehaviour { public GameObject enemyPrefab; public float spawnInterval = 1f; public float fallSpeed = 3f; private bool isSpawning = true; void Start() { StartCoroutine(SpawnLoop()); } IEnumerator SpawnLoop() { while (isSpawning) { SpawnOne(); yield return new WaitForSeconds(spawnInterval); } } void SpawnOne() { GameObject go = Instantiate(enemyPrefab, transform); float randomX = Random.Range(-2.2f, 2.2f); go.transform.position = new Vector3(randomX, 5f, 0f); go.GetComponent<Rigidbody2D>().velocity = new Vector2(0, -fallSpeed); } public void StopSpawn() { isSpawning = false; } }这里的障碍物体 z 轴固定为0,和玩家一致。所有可碰撞对象都在同一个平面上,2D 碰撞体才能正常工作。
如果想增加视觉层次,可以把背景装饰物放在不同的 z 轴上,比如远处背景放在z = 8,近处贴图放在z = -3。这样戴上眼镜后,背景和游戏层会有明显的远近分离。
4.8 编写障碍与计分逻辑
新建Enemy.cs,挂载到 Enemy 预制体上。
using UnityEngine; public class Enemy : MonoBehaviour { private bool scored = false; void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { GameController.Instance.EndGame(); } } void Update() { if (transform.position.y < -6f && !scored) { scored = true; GameController.Instance.AddScore(); Destroy(gameObject); } } }新建GameController.cs,挂载到 GameController 空物体上。
using UnityEngine; using UnityEngine.UI; public class GameController : MonoBehaviour { public static GameController Instance { get; private set; } public PlayerController player; public EnemySpawner spawner; public Text scoreText; public Text stateText; private int score; void Awake() { Instance = this; score = 0; UpdateScore(); } public void AddScore() { score++; UpdateScore(); } public void EndGame() { spawner.StopSpawn(); player.SetGameOver(true); stateText.text = "Game Over,重新运行场景再来一局"; } void UpdateScore() { if (scoreText != null) { scoreText.text = "得分: " + score; } } }运行场景后,玩家可以按 A/D 或方向键左右移动,躲避从上方掉落的方块。掉落物到底部会自动加分,碰到玩家则游戏结束。
4.9 运行与验证
运行场景后,从 Game 视图观察画面:
- 不戴眼镜时,画面是红、青叠加的,重影明显。
- 戴上红蓝眼镜后,场景会变成比较正常的分层彩色画面。
- 背景层和游戏层之间有明显纵深,得分文字显示在顶层。
如果画面看起来全红或全青,只需要检查两个 RawImage 的材质是否分别使用了RedEyeMat和CyanEyeMat。
5. 常见问题与排查思路
5.1 常见现象表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 画面只有红色或只有青色 | 两个 RawImage 的材质被设置成了同一个 | 检查 LeftEyeImage 和 RightEyeImage 的 Material 是否分别对应红、青 |
| 画面黑色,没有任何游戏内容 | 左右眼 Camera 没有渲染,或主相机被禁用后场景无输出 | 检查左右眼 Camera 的 enable 状态和 RenderTexture 是否创建成功 |
| 戴上眼镜后重影很大 | eyeDistance数值太大 | 把eyeDistance降到 0.1~0.3 |
| 游戏层之间没有立体感 | 所有物体都在同一个 z 平面上 | 把背景、装饰、可碰撞层分层放置 |
| UI 文字模糊或拉伸 | RawImage 的 RenderTexture 分辨率过低 | 提高 RenderTexture 分辨率,或按屏幕宽高比创建 |
| 手机帧率低 | 双相机渲染两张全屏 RT,开销较大 | 降低 RT 分辨率,限制物体数量,使用对象池 |
| 颜色偏差严重,青画面很暗 | 某些红蓝眼镜滤片浓度不一致 | 换一副标准红蓝眼镜,或在 Shader 里微调颜色权重 |
5.2 黑屏问题排查顺序
如果运行后看不到任何内容,按照下面的顺序排查:
- 确认
sceneCamera在 GameView 组件中被正确引用。 - 确认两个 RawImage 的 Texture 在运行时被赋值。
- 确认两个 RawImage 的材质不是默认材质。
- 确认左右眼 Camera 的
cullingMask包含了游戏物体所在的 Layer。 - 把
sceneCamera.enabled = false;这一行注释掉,临时观察主相机是否能看到场景。
5.3 颜色校准调整
不同厂家生产的红蓝眼镜透光率差异很大。如果戴上去后左眼和右眼看到的亮度不一致,可以在 Shader 中增加权重参数。
例如红色通道保留时,额外保留一点绿色:
return fixed4(c.r * 1.0, c.g * 0.05, c.b * 0.05, 1.0);青色通道保留时,也可以根据实际情况增加一点红色补偿。这类微调没有标准答案,需要结合你手头眼镜的实际透光效果来测试。
6. 最佳实践与工程建议
6.1 视差参数管理
不要把eyeDistance写死在代码里,最好做成 GameView 上的可序列化字段,方便在 Inspector 中调试。设计上还要区分“近距离物体”和“远距离物体”:
- 可交互物体固定在同一个 z 平面,避免 2D 碰撞位置与视觉重影冲突。
- 背景、装饰、粒子效果放在不同 z 平面,产生景深。
- 不要给 UI 文字添加视差,UI 始终作为最顶层平面。
立体感的强弱不是只看相机偏移量,还和场景中物体的 z 值分布有关。在透视相机下,距离相机越近的物体,左右眼视差越大;距离越远的物体,视差越小。合理拉开 z 值,能让场景层次更明显。
6.2 UI 和文字处理
红蓝画面叠加之后,如果文字直接放在 RawImage 之下,会被红青色调干扰。建议:
- 所有 UI 元素放在 Canvas 下、两个 RawImage 之后,保证绘制顺序在最上层。
- 文字使用白色或纯色,不使用半透明材质。
- 避免给 Text 添加阴影或描边效果,红蓝眼镜下这些效果容易产生伪影。
- 用
GraphicRaycaster时,取消 RawImage 的 Raycast Target,防止画面层拦截点击事件。
6.3 性能优化方向
双相机意味着场景会被渲染两次,虽然 demo 中物体数量少,看不出来性能问题,但正式项目必须注意:
- RenderTexture 分辨率不必等于屏幕分辨率,使用一半分辨率往往就够了。
- 左右眼 Camera 只渲染必要的 Layer,UI、透明特效不要进入 cullingMask。
- 障碍物和生产对象使用对象池,避免频繁 Instantiate 和 Destroy 造成 GC 压力。
- 如果目标平台是手机,可以限制同屏物体数量,关闭抗锯齿或降低阴影质量。
- 不要盲目使用
Camera.Render()手动渲染,让它自动渲染即可,手动调用会导致一帧内重复渲染。
6.4 玩法和安全体验
红蓝 3D 游戏不适合长时间游玩,长时间佩戴容易出现眼睛疲劳。作为游戏设计者,应该在游戏开始前给出提示:
- 建议每玩 15 分钟休息一次。
- 遇到头晕、眼睛干涩时立即停止。
- 儿童需要在成年人陪同下使用。
- 红绿色弱用户可能无法正常体验,可以考虑增加“普通模式”开关。
这些不只是一句文案,更是游戏内容里的基本体验保障。
7. 总结与学习路线
这篇教程实现了一个完整的红蓝 3D 小游戏 demo,代码和配置都可以直接运行。核心收获有三点:
- 理解红蓝 3D 眼镜的颜色分离原理,知道“左右眼看到不同图像”才是立体感的来源。
- 掌握 Unity 中双相机渲染、RenderTexture、RawImage 叠加显示这套组合用法。
- 能自己通过调整相机偏移和场景 z 轴深度,控制立体感的强弱。
如果你想继续深入,可以尝试以下几个方向:
- 把 2D 玩家和障碍层改成真正的 3D 模型,让障碍物本身拥有立体厚度。
- 在 Shader 中增加颜色校准参数,适配不同品牌的 3D 眼镜。
- 加入对偏振 3D 或者 VR 设备的适配,把同一套视差渲染逻辑迁移到其他平台。
- 做一个简单的立体拼图或找茬游戏,利用左右眼视觉差设计核心玩法。
红蓝 3D 虽然不是什么新技术,但在个人项目和创意游戏 demo 里依然是一个很有性价比的亮点。戴上眼镜的那一刻,看到场景真实地“分开”再“合上”,那种直观的沉浸感,是普通 2D 游戏很难带来的。
如果你也按照今天的步骤做出来了自己的红蓝 3D 小游戏,可以试试把眼距调到 0.3,再对比一下和 0.1 时的立体感差异。参数调校的过程,本身就是对双目视差最好的理解方式。