ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Coding Challenge 8 详解:用 Processing 将 2D 太阳系生成器升级为 3D 交互模拟

Coding Challenge 8 详解:用 Processing 将 2D 太阳系生成器升级为 3D 交互模拟 教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载本篇文章围绕 The Coding Train编码挑战系列第 8 个挑战展开讲解 Daniel Shiffman 如何把 Part 1 的 2D 太阳系生成器基于ellipse()的平面轨道升级为带自由视角相机的 3D 模拟核心是P3D渲染模式、PeasyCam 相机库、PVector.random3D()随机轨道平面以及cross()叉积求旋转轴后调用任意轴旋转。读者将掌握 Processing 中从平面公转到三维公转的完整改造思路并对照仓库中的 p5.js 移植版使用 p5.EasyCam理解两套生态的差异为后续 Part 3 的 3D 纹理版 打下基础。一、挑战背景从 Part 12D到 Part 23D本次挑战video_number: 8发布日期 2016-05-02是Solar System in Processing三部曲的第二部官方文档在 008-solarsystemgenerator3d.md 中明确说明在本挑战的第二部分中我使用 Processing把 2D 太阳系Part 1 的代码变成三维。文档的parts字段给出了完整的三部曲脉络部分主题仓库目录Part 1Solar System in Processing - Part 1 (2D)CodingChallenges/CC_007_SolarSystemGeneratorPart 2Solar System in Processing - Part 2 (3D)CodingChallenges/CC_008_SolarSystemGenerator3DPart 3Solar System in Processing - Part 3 (3D textures)CodingChallenges/CC_009_SolarSystemGenerator3D_texture1.1 回顾 Part 1 的 2D 实现2D 版本的 Planet 类CodingChallenges/CC_007_SolarSystemGenerator/Processing/CC_007_SolarSystemGenerator/Planet.pde使用经典的rotate()translate()ellipse()组合来绘制轨道上的行星void show() { pushMatrix(); fill(255, 100); rotate(angle); // 绕 Z 轴屏幕平面旋转 translate(distance, 0); // 沿 X 方向平移 ellipse(0, 0, radius*2, radius*2); // ... 递归绘制子行星 popMatrix(); }在 2D 场景中所有行星都被约束在同一个平面屏幕平面内公转因此只需绕 Z 轴旋转角度angle即可。这是本次 3D 改造需要突破的核心限制如何让每颗行星在三维空间中拥有各自不同的轨道平面。二、主程序改造P3D 渲染模式与 PeasyCam 相机3D 版主程序位于 CodingChallenges/CC_008_SolarSystemGenerator3D/Processing/CC_008_SolarSystemGenerator3D/CC_008_SolarSystemGenerator3D.pde与 2D 版相比主要有三处变化import peasy.*; // 引入 PeasyCam 库Jonathan Feinberg 开发 Planet sun; PeasyCam cam; void setup() { size(600, 600, P3D); // ① 渲染模式改为 P3D cam new PeasyCam(this, 500); // ② 创建 3D 相机视距 500 sun new Planet(50, 0, 0); // 太阳半径 50无轨道 sun.spawnMoons(4, 1); // 为太阳生成 4 颗行星 } void draw() { background(0); lights(); // ③ 开启光照让 sphere() 呈现立体感 sun.show(); sun.orbit(); }2.1size(600, 600, P3D)开启 3D 渲染Processing 默认使用 2D 渲染器JAVA2D只有传入第三个参数P3D或OPENGL才会启用 OpenGL 加速的 3D 渲染管线。这是整个改造的第一步——没有P3D后面的sphere()、任意轴旋转和 PeasyCam 都无法工作。2.2new PeasyCam(this, 500)获得自由视角PeasyCam 是 Jonathan Feinberg 开发的 Processing 库文档links字段即指向其官方页面它把鼠标交互封装为轨道相机左键拖拽旋转视角右键拖拽 / 滚轮缩放500为初始视距即相机距离场景中心的距离中键拖拽平移。只需一行cam new PeasyCam(this, 500);即可在draw()中通过鼠标自由观察整个太阳系无需手动编写任何相机矩阵或键盘控制逻辑。2.3lights()让球体立体起来sphere()在无光照时只是一团均匀的白色剪影看不出球面的明暗起伏。调用lights()后Processing 会设置默认的平行光、环境光等光源组合球体表面随之呈现高光与阴影视觉上才真正三维。三、核心改造Planet 类的 3D 化轨道平面、叉积与任意轴旋转3D 版的核心逻辑全部集中在 CodingChallenges/CC_008_SolarSystemGenerator3D/Processing/CC_008_SolarSystemGenerator3D/Planet.pde。与 2D 版相比Planet类新增了一个关键成员PVector v它同时承担轨道方向与当前位置两个职责。3.1 构造函数用random3D()定义随机轨道平面Planet(float r, float d, float o) { v PVector.random3D(); // 生成立方体表面均匀分布的随机三维单位向量 radius r; distance d; v.mult(distance); // 缩放为单位向量 × 轨道半径 angle random(TWO_PI); // 起始公转角度 orbitspeed o; // 公转速度 }PVector.random3D()返回一个长度为 1 的随机三维向量其方向在球面上均匀分布。将它乘以轨道半径distance后v就指向轨道平面上的一个位置。关键在于每颗行星的v都是独立随机的因此每颗行星的轨道平面由v与其旋转轴张成都各不相同呈现出 3D 太阳系那种轨道朝向四面八方的视觉效果——这正是与 2D 版全部共面的本质区别。3.2show()叉积求旋转轴 任意轴旋转 sphere()void show() { pushMatrix(); noStroke(); PVector v2 new PVector(1, 0, 1); PVector p v.cross(v2); // 用叉积计算轨道平面的法向量旋转轴 rotate(angle, p.x, p.y, p.z); // 绕旋转轴 p 旋转 angle translate(v.x, v.y, v.z); // 平移到轨道上的当前位置 noStroke(); fill(255); sphere(radius); // 用球体替代 2D 的 ellipse // ... 递归绘制子行星 popMatrix(); }这段代码是整个 3D 改造中最精妙的部分值得逐行拆解v.cross(v2)求旋转轴v是行星当前所在位置向量v2 (1, 0, 1)是一个固定的参考向量。二者叉积p v × v2得到同时垂直于v与v2的向量即轨道平面的法向量它正是公转旋转轴。rotate(angle, p.x, p.y, p.z)任意轴旋转2D 版的rotate(angle)隐含绕 Z 轴旋转3D 版必须改为rotate(angle, axisX, axisY, axisZ)绕任意轴旋转。由于每颗行星的v不同其旋转轴p也不同于是公转平面在空间中自然散开。translate(v.x, v.y, v.z)旋转之后再平移到当前位置注意v已经在构造函数里乘过了distance所以这里不需要像 2D 版那样单独translate(distance, 0)。sphere(radius)用三维球体直接替换 2D 的ellipse()。Processing 的sphere()默认生成细分网格球体配合lights()呈现立体感。源码中注释掉的//ellipse(0, 0, radius*2, radius*2)和两行//line(0,0,0, v.x,v.y,v.z)表明作者在调试阶段曾用线段可视化v与旋转轴p读者可取消注释观察轨道朝向。p5.js 版本的一个边界细节在 CodingChallenges/CC_008_SolarSystemGenerator3D/P5/Planet.js 中作者额外加了判断——当叉积结果为零向量时跳过旋转源码注释Rotation around a 0-length axis doesnt work in p5.js。这是 p5.js 渲染实现与 Processing 的细微差异移植时不可忽略。四、递归行星系统spawnMoons()与orbit()的树状结构3D 版沿用并调整了 2D 版的递归设计Planet内部持有Planet[] planets数组形成太阳 → 行星 → 卫星的树状层级。4.1spawnMoons(total, level)按层级递归生成void spawnMoons(int total, int level) { planets new Planet[total]; for (int i 0; i planets.length; i) { float r radius/(level*2); // 子行星半径为父行星半径按层级递减 float d random((radius r), (radiusr)*2); // 轨道距离与父子半径相关 float o random(-0.1, 0.1); // 公转速度可正可负方向随机 planets[i] new Planet(r, d, o); if (level 2) { // 递归深度限制 int num int(random(0, 3)); planets[i].spawnMoons(num, level1); } } }r radius/(level*2)层级越深子天体越小。太阳半径为 50level 1 的行星半径约为 25level 2 的卫星半径约为 6.25。d random((radius r), (radiusr)*2)轨道距离取父子半径之和到其二倍之间的随机值保证子天体不会与父天体表面重叠。o random(-0.1, 0.1)公转速度在 ±0.1 之间随机正负号决定顺/逆时针方向。递归终止level 2时继续生成下一层即最多两层行星带卫星。与 2D 版对比见 CodingChallenges/CC_007_SolarSystemGenerator/Processing/CC_007_SolarSystemGenerator/Planet.pde2D 版轨道距离为固定范围random(50, 150)、递归深度为 3 层、太阳生成 5 颗行星3D 版改为与半径相关的距离、2 层递归、4 颗行星。可见作者在三维化时同步调整了层级参数以避免 3D 空间中天体距离过近造成视觉混乱。4.2orbit()逐帧累积角度并递归更新void orbit() { angle angle orbitspeed; // 每帧增加公转角度 if (planets ! null) { for (int i 0; i planets.length; i) { planets[i].orbit(); // 递归更新所有子行星 } } }orbit()只更新角度增量不直接移动位置——真正的位置计算发生在show()的rotate()translate()中。这种数据更新orbit与渲染show分离的结构为后续扩展物理模拟如万有引力预留了接口。五、p5.js 移植版用 p5.EasyCam 替代 PeasyCam仓库中的 CodingChallenges/CC_008_SolarSystemGenerator3D/P5 目录提供了浏览器端移植版与 Processing 版一一对应。由于 PeasyCam 是 Java 库p5.js 版改用基于 PeasyCam 思想开发的 p5.EasyCamfreshfork 维护代码注释对此有明确说明。5.1 HTML 依赖引入CodingChallenges/CC_008_SolarSystemGenerator3D/P5/index.html 需要按顺序引入 p5 本体与 EasyCam 库script srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/p5.min.js/script script srchttps://cdn.jsdelivr.net/gh/freshfork/p5.EasyCam1.0.10/p5.easycam.min.js/script5.2 主程序WEBGL渲染 createEasyCam()CodingChallenges/CC_008_SolarSystemGenerator3D/P5/sketch.js 对应 Processing 版setup()/draw()function setup() { let canvas createCanvas(600, 600, WEBGL); // p5.js 的 3D 渲染模式 // 禁用画布右键菜单以便相机使用右键缩放 canvas.elt.oncontextmenu () false; cam createEasyCam({ distance: 500 }); // 等价于 new PeasyCam(this, 500) sun new Planet(50, 0, 0); sun.spawnMoons(4, 1); } function draw() { background(0); lights(); sun.show(); sun.orbit(); }移植要点createCanvas(600, 600, WEBGL)对应 Processing 的P3D是 p5.js 开启 3D 渲染的唯一方式canvas.elt.oncontextmenu () false;禁用浏览器默认右键菜单否则 EasyCam 的右键缩放会被菜单遮挡createEasyCam({ distance: 500 })以对象字面量配置相机初始视距语义与new PeasyCam(this, 500)等价初始化顺序与调用链sun.spawnMoons(4, 1)→sun.show()/sun.orbit()与 Processing 版完全一致。5.3 结构差异小结功能Processing 版p5.js 版3D 渲染size(600,600,P3D)createCanvas(600,600,WEBGL)相机库PeasyCamJavap5.EasyCamJavaScript相机创建new PeasyCam(this, 500)createEasyCam({distance:500})随机方向PVector.random3D()p5.Vector.random3D()叉积v.cross(v2)this.v.cross(v2)球体sphere(radius)sphere(this.radius)两份源码中Planet类的字段与方法v/radius/distance/angle/orbitspeed、orbit()、spawnMoons()、show()保持一一对应非常适合对照阅读以理解两种语言的 API 映射。六、与 Part 3 的承接PShape 与纹理贴图本次挑战在文档中明确指向三部曲的终章 009-solarsystemgenerator3d-texture.md在第三部分中我会为第二部分创建的 3D 物体添加纹理涉及PShape类与createShape()函数。也就是说Part 2 建立的三维结构与相机控制是 Part 3 的工作基础Part 3 将用PShape结合createShape()生成带纹理贴图的球体仓库 CodingChallenges/CC_009_SolarSystemGenerator3D_texture 中的*.jpg即行星纹理图替换 Part 2 中纯白色的sphere()使模拟从抽象几何迈向写实太阳系。七、社区贡献与扩展方向文档contributions字段收录了社区基于本次挑战的衍生作品可作为进一步实践的方向参考Simple 3D Solar SystemDaniel Kaye在 p5.js 编辑器中复刻的简化版 3D 太阳系3D Gravity Simulator with free camCássio Fernando Brito de Souza引入真实引力模拟并保留自由相机把运动学轨道升级为动力学轨道。这些作品源码与演示链接见文档元数据展示了从本挑战出发的两条典型扩展路径一是把固定轨道替换为Newtonian万有引力积分可参考本仓库 144 号挑战黑洞可视化 中的引力实现思路二是结合 Part 3 的纹理方案 提升视觉真实度。八、运行与上手建议Processing 版用 Processing IDE需支持 OpenGL打开 CodingChallenges/CC_008_SolarSystemGenerator3D/Processing/CC_008_SolarSystemGenerator3D 目录并安装 PeasyCam 库Sketch → Import Library → Add Library中搜索 PeasyCam后即可运行p5.js 版将 CodingChallenges/CC_008_SolarSystemGenerator3D/P5 目录部署到任意静态服务器或在 p5.js 在线编辑器中导入sketch.js、Planet.js与index.html注意需联网加载 CDN 上的 p5 与 p5.EasyCam 脚本观察建议运行后拖动鼠标旋转视角会发现每颗行星的轨道平面方向各不相同可尝试把sun.spawnMoons(4, 1)的 4 改为更大的数或调整spawnMoons()中的level 2与random(-0.1, 0.1)观察层级密度与公转速度对整体效果的影响。总结Coding Challenge #8 是一次教科书级的2D → 3D改造示范主程序层面引入P3D渲染模式与 PeasyCam 相机Planet类层面用PVector.random3D()定义随机轨道平面、用cross()叉积计算旋转轴、用任意轴rotate()取代平面旋转、用sphere()取代ellipse()。仓库同时保留了 Processing 与 p5.js 两套等价实现并衔接 Part 3 的纹理增强是学习 Processing 3D 编程、递归几何结构与相机交互控制的理想范例。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐2D 太阳系生成器递归行星轨道系统的 Processing 与 p5.js 实现Coding Challenge 72D 太阳系生成器递归行星轨道系统的 Processing 与 p5.js 实现Coding Challenge 7 本篇文章以 Coding Train教程文档示例工程Obsidian AI工具链Claudian插件与其他AI服务集成Obsidian AI工具链Claudian插件与其他AI服务集成 Obsidian作为一款强大的知识管理工具其插件生态系统不断扩展。Claudian插件作AI 应用代码智能体交互助手人工智能AI Agent终极Windows风扇控制指南FanControl让你的电脑散热更智能、更安静终极Windows风扇控制指南FanControl让你的电脑散热更智能、更安静 你是否厌倦了电脑风扇的噪音是否想让你的Intel Arc显卡、AMD或NVI桌面应用智能硬件上一篇英特尔 OpenVINO 如何在 Day0 完成文心大模型 4.5 系列端侧部署技术拆解与开发者实操路径下一篇使用 aws accessanalyzer cancel-policy-generation 取消 IAM Access Analyzer 策略生成任务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进