Skip to content

连栋温室的一轮昼夜:光照、通风窗与 55 天长势 ​

初始化中…

这篇的场景不大,但它同时跑着两条时间轴:一条是 36 秒走完的 24 小时日照,一条是同一轮里从定植长到采收的 55 天。两条轴叠在一起,读数、光照、开窗、生长各自的口径就特别容易串味——HUD 写着 45° 开窗、画面里窗板却压进棚顶,就是这么来的。

场景里有什么 ​

  • 棚体:3 跨 × 8 开间,覆盖 28.8 × 32 m,肩高 3.2 m、脊高 4.6 m,单跨屋面坡度 16.3°。
  • 骨架:立柱 36 根、天沟 4 条通长、人字斜梁 54 根、脊檩 9 条,全部实例化或通长盒。
  • 覆盖:四周 4 块侧墙加每跨两坡共 6 块屋面板,透明度 0.2、depthWrite: false。
  • 通风窗:6 扇铰接在脊上,随太阳高度角开合,正午开度最大 24°。
  • 栽培:9 条栽培床、滴灌带 18 条、作物 234 株,茎与冠两次实例化,逐日长大并同时把颜色从嫩绿混到熟绿。
  • 补光与喷灌:3 条灯带(夜间亮紫光谱)、2 组点光源;900 个雾滴做下落循环,夜间停灌。
  • 配套:场外机耕道、相邻田块 5 片、水肥罐 3 只。

交给 AI 的提示词(我固定分四组写) ​

总体与尺度

  1. 纯程序化生成,不引入外部模型与贴图;几何加材质参数,作物用茎干加冠层两级实例化表示。
  2. 尺寸按真实量纲给死:单跨 9.6 m、开间 4.0 m、肩高 3.2 m、脊高 4.6 m、屋面坡度由 atan2(脊高−肩高, 单跨一半) 推出来,不许手填一个"看着像"的角度。
  3. 明确区分两条时间轴:一轮 = 一次 24 小时日照循环,同一轮内株高从定植长到采收;HUD 里两者都要写清楚,不能混着报。

骨架与覆盖

  1. 立柱、天沟、人字梁、脊檩按网格批量生成;人字梁必须落在屋面斜线上,倾角用屋面坡角,两坡符号相反。
  2. 屋面板与侧墙用同一种半透明膜材质,depthWrite: false 并留出双面试样,否则从棚内看屋面会消失。
  3. 通风窗铰接在脊上:窗板几何先沿开向偏移半个宽度(让铰链落在脊上而不是窗中心),再绕铰链转;开 = 从屋面向上掀起,所以角度是 −坡角 + 开度。

栽培与生长

  1. 栽培床按跨内等距排布,滴灌带贴在床面两侧;作物槽位用确定性的行内错位,刷新多次位置不变。
  2. 株高与冠幅随生长进度缩放,允许每株有一个固定的抖动系数让田面不齐平;颜色用实例色,从嫩绿插值到熟绿,和尺寸同源。
  3. 生长矩阵只在进度跨越阈值时重算(把进度量化到 60 档比较),别每帧重刷 234 × 2 个实例。
  4. 补光灯夜间要是紫光谱,不能一句"变亮"就把白炽色拉满;喷灌粒子夜间归零不模拟。

镜头与读数

  1. 机位在场外俯视与棚内低角之间切换,必须用渐变过渡,不许 0/1 阶跃。
  2. 太阳按高度角与方位角走一整圈,方向光强度随高度角截到 0,半球光同步压暗,天空色与雾色跟同一份颜色走。
  3. 拖拽随时接管,5 秒缓动回自动机位;卸载时几何、材质、控制器全部释放。
  4. 零控件,读数写在左下角:模拟时刻、太阳高度角、生长天数、株数、通风窗开度、补光与喷灌状态、棚体尺寸、三角面、帧耗时。

实现里几个值得记的点 ​

"掀开"和"压进去"只差一个符号。 通风窗我写的是 hinge.rotation.z = dir * (-roofAngle - open),直觉上"多减一点就是开大一点"。错。窗板几何沿开向偏移了半宽,铰链在脊上,闭合姿态本来就等于屋面坡角 −roofAngle;再往负方向转,窗板是朝屋面里压,正午最"开"的时候恰好完全嵌进棚顶板里。正确的做法是往正方向回:−roofAngle + open。dir = -1 那一侧同理,符号跟着 dir 走,不是跟着"开度"走。

改之前先确认哪一侧错了。 屋面坡板和人字梁用的是同一套 rotation.z = dir * -roofAngle,我看到通风窗出错时,第一反应是把这三处一起改成 +。停下来推了一遍:屋面板和梁是从脊往檐口下坡,−坡角 对 dir=+1 就是往下斜,本来是对的;通风窗是从脊往掀起方向,同一个式子语义正好相反。结论是只有窗要改,屋面和梁一行都不许动。这一步要是省了,整个棚顶会散架。

读数不能报一个画面里不存在的角度。 HUD 原本写 open / 0.42 * 45 度,把最大开度硬翻译成 45°。但 open 是弧度,0.42 rad 实测 24°。现在直接 open * 57.3,读数就是几何真值。这类"换算系数写死在文案里"的坑,只要物理量纲改过一次就会立刻说谎。

补光灯亮起来应该是紫的。 M.lamp.color.setScalar(night ? 1 : 0.06) 把 RGB 三个通道一起拉满——那就是白光,跟“植物工厂紫光谱补光”完全两回事。现在直接 setHex 一个紫(红蓝高、绿低),配合本来就有的一组紫点光源,夜里棚内才像补光而不是像照明。

机位阶跃等于每轮甩一次镜。 原来的写法是 p < 0.4 ? 0 : p < 0.7 ? 1 : ...,kk 在两个值之间硬跳。因为相机用了指数平滑,跳变会被摊成一两秒的急速横移,看上去像剪辑事故。现在把进出棚写成两个 smoothstep 相减:clamp01((p-0.32)/0.12) - clamp01((p-0.72)/0.12),滑进来、停一会儿、滑出去。

两条时间轴要在读数里分开。 HUD 原来最后一行写"一轮 36 秒 = 一昼夜",可同一句上面还有"生长第 40 / 55 天"。看的人会算:一昼夜怎么长 55 天?现在写成"一轮 36 秒 = 一昼夜 + 55 天长势",明确告诉读者这一轮里压缩了两件事。

生长与颜色必须同源。 实例色用 young.lerp(ripe, g),g 就是缩放用的那个生长量,所以"变深"和"变高"是同一帧同一系数驱动,不会出现长了但没熟、或者绿了但还矮的情况。顺带一个细节:setColorAt 第一次调用才会建 instanceColor,所以刷完必须判空再 needsUpdate,直接取会踩 undefined。

夜间要停的东西不止灯。 雾滴的可见度原来跟"是否在棚内"绑着,夜里照样飘 900 颗白点,在深蓝天光下像下虫子。现在 mistMat.opacity = night ? 0 : 0.75,且跳过整个更新循环——反正看不见,省下这部分逐帧写 buffer 的开销。

按交付要求核对 ​

要求实际做到判定
零外部素材全几何 + 材质参数达成
真实量纲9.6 / 4.0 / 3.2 / 4.6 m,坡角由公式推达成
通风窗掀向正确−坡角 + 开度,屋面板与梁不动达成
开度读数真实按弧度换算,最大值 24°达成
双时间轴口径昼夜与长势在读数里分开写明达成
紫光谱补光夜间灯带为紫色,配 2 组点光源达成
机位无跳变smoothstep 渐变进出棚达成
生长与颜色同源同一个 g 驱动缩放与插值达成
布局确定性作物槽位用固定行内错位,刷新一致达成
文字读数无控件五项读数,页面无按钮达成
卸载清理几何、材质、控制器全部释放达成

已知不足 ​

  • 灯带是一根 0.5 m 宽的板条。 它不是错,是偷懒。相机进棚仰视时,这条 29 m 长、悬在作物上方 2.95 m 的深灰板会被透视拉成一根从画面顶部插到远处的巨大楔形,一点都不像灯具。真该做的是把它拆成一节节的吊管加吊线。
  • 作物是抽象的。 茎加一颗多面体当冠层,没有叶片、没有果实、没有倒伏,采收前看不出"这是什么作物"。
  • 开窗机构是假的。 真实文洛温室是齿条推杆带动一批窄窗扇沿脊两侧交错开合,这里每侧一整条 6 m 板,只是为了表达开合状态。
  • 补光只有 3 条灯带 2 个光源。 点光源没按跨数铺开,中间那跨其实照不到;也没有按日照积分自动调节补光时长。
  • 没有环流与湿度闭环。 通风机、湿帘、内遮阳、外遮阳一个都没做;喷雾只是粒子下落,不参与任何读数。
  • 生长模型单调。 只按进度线性长高,没有昼夜温差影响株型、没有强光抑制徒长,55 天里除了绿和高的变化看不出农艺逻辑。
  • 覆盖膜不老化。 结露、积尘、透光率衰减都不随时间变化,夜间从外面看棚体永远是同一个透明度的壳。
  • 没有出入口。 侧墙四面封死,没有人员门、物流门和缓冲间,所以这台孪生不能表达"人怎么进去"。
  • 后台标签页里时间会走得极慢。 帧间时间截在 0.05 秒,浏览器把标签页降频后一轮 36 秒要拖很久,切回前台才恢复。

本站所有 3D 孪生场景、可视化系统均为个人技术实验 Demo,仅用于技术学习与效果展示,不属于对外运营平台,不提供任何商业服务。

本站为个人技术展示网站,仅用于个人研发作品、3D 数字孪生技术学习展示,无任何经营性服务、无社区互动、不对外提供商业服务。

本站为个人技术展示网站,仅用于个人研发作品、3D 数字孪生技术学习展示,无任何经营性服务、无社区互动、不对外提供商业服务。

蜀ICP备2026068005号-1|个人 3D 数字孪生技术研发展示站