<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Three.js on 诺亚的小站</title><link>https://www.noahland.cn/tags/three.js/</link><description>Recent content in Three.js on 诺亚的小站</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 13 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.noahland.cn/tags/three.js/index.xml" rel="self" type="application/rss+xml"/><item><title>雨夜便利店：一个 Three.js 单文件场景的写实化渲染重构</title><link>https://www.noahland.cn/p/rainy-store-render/</link><pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate><guid>https://www.noahland.cn/p/rainy-store-render/</guid><description>&lt;img src="https://www.noahland.cn/p/rainy-store-render/images/overview.jpg" alt="Featured image of post 雨夜便利店：一个 Three.js 单文件场景的写实化渲染重构" /&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;在线试玩 → &lt;a class="link" href="https://www.noahland.cn/rainy-store/" &gt;www.noahland.cn/rainy-store/&lt;/a&gt;&lt;/strong&gt;&#10;浏览器直接打开即可（推荐 Chrome/Edge + 独显），鼠标拖动环绕视角。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;&lt;img alt="雨夜便利店 全景" class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="720" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://www.noahland.cn/p/rainy-store-render/images/overview.jpg" srcset="https://www.noahland.cn/p/rainy-store-render/images/overview_hu_e39c9b76ebcce27c.jpg 800w, https://www.noahland.cn/p/rainy-store-render/images/overview.jpg 1280w" width="1280"&gt;&lt;/p&gt;&#10;&lt;p&gt;雨天的深夜，街角亮着灯的便利店——这是我基于店长测试Agent产生的渲染项目修改而来：把一个 Three.js r184 的卡通风格单文件场景，重构成 PBR 写实管线，同时尝试保持：&lt;strong&gt;成品必须仍是单个 HTML 文件，零外部依赖，双击就能开&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;img alt="近景" class="gallery-image" data-flex-basis="402px" data-flex-grow="167" height="560" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://www.noahland.cn/p/rainy-store-render/images/closeup.jpg" srcset="https://www.noahland.cn/p/rainy-store-render/images/closeup_hu_ef3632f72c59fc32.jpg 800w, https://www.noahland.cn/p/rainy-store-render/images/closeup.jpg 940w" width="940"&gt;&lt;/p&gt;&#10;&lt;h2 id="从卡通到写实差距在哪"&gt;从卡通到写实，差距在哪&#10;&lt;/h2&gt;&lt;p&gt;店长测试产生的原场景（baseline）是 &lt;code&gt;MeshToonMaterial&lt;/code&gt; 四阶卡通渐变 + 描边后处理，可爱：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;项&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;baseline&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;目标&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;材质&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MeshToonMaterial + 顶点色&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全 PBR（albedo/roughness/normal/ORM）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;阴影&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;完全没有&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;烘焙阴影贴图&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;后处理&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Outline + Bloom&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;SSAO + SSGI + SSR + SSS + Bloom&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;纹理&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3 张 2048 Canvas 图集&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全部程序化生成，禁止手绘位图&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;img alt="改造前的卡通风格 baseline" class="gallery-image" data-flex-basis="426px" data-flex-grow="177" height="720" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://www.noahland.cn/p/rainy-store-render/images/baseline.jpg" srcset="https://www.noahland.cn/p/rainy-store-render/images/baseline_hu_a1637873836e7010.jpg 800w, https://www.noahland.cn/p/rainy-store-render/images/baseline.jpg 1280w" width="1280"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;改造前的卡通风格 baseline。图集资源全部复用，我仅修改了整条渲染管线。&lt;/em&gt;&lt;/p&gt;&#10;&lt;h2 id="几条关键技术决策"&gt;几条关键技术决策&#10;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;吃「静态场景」的红利。&lt;/strong&gt; 光源位置 100% 静态，全场只有路灯闪烁强度和信号灯颜色两处动态 → 阴影贴图只烘焙一次，&lt;code&gt;renderer.shadowMap.autoUpdate = false&lt;/code&gt;，之后每帧零成本。几何也基本静态 → SSGI 走低频更新 + 时域累积 + 重投影，不在每帧全量 raymarch。这两条是在 4060 上跑得动 SSGI 的前提。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;所有画质 Pass 一律半分辨率。&lt;/strong&gt; SSAO / SSGI / SSR / SSS 全部 ≤ 0.5× 渲染，配套空间降噪；SSGI 另配时域累积。每帧全场景渲染次数硬约束 ≤ 2 次（GBuffer prepass + 主渲染）。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;纹理零手绘。&lt;/strong&gt; 没有额外的纯手绘材质贴图，优先级是「复用现有图集 → 公式程序化生成 → 参考公开技术实现」。Roughness/Metal/AO 由公式生成 ORM 图烘焙内联，法线贴图由高度函数 Sobel 求导生成，一切能离线烘焙的全离线。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;自适应降级而不是砍画质。&lt;/strong&gt; 沿用帧时长统计，超预算时按「关 SSGI 时域累积 → 降 renderScale → 砍雨粒子数」的顺序降；首帧黑屏时逐级降级（关 MSAA → 关 Bloom → 关 SSGI）再展示画面。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;反射的分面处理。&lt;/strong&gt; 路面和人行道分开各渲一张反射 RT，反射查询点按反射面高度修正，再配逐面几何剔除——根治了抬高面倒影错位和矮墙底面镜面错位这类经典 bug。&lt;/p&gt;&#10;&lt;h2 id="性能红线"&gt;性能红线&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;项&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;值&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;显卡&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;RTX 4060 Laptop&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;帧率&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;30–60 FPS（低于 30 不达标）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;主渲染分辨率&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;renderScale 0.75（可调）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;网络请求&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;场景本身不大——13 个 Mesh 承载整条街，Draw Call 个位数——压力全在屏幕空间管线上。真正难的不是把效果做出来，难的是在 30 FPS 的预算里把效果做出来。&lt;/p&gt;&#10;&lt;h2 id="为什么是单文件"&gt;为什么是单文件&#10;&lt;/h2&gt;&lt;p&gt;这是项目最有性格的约束：禁止 CDN、禁止 fetch、所有纹理运行时 Canvas 生成或构建期内联为 data URI，最终产物 &lt;code&gt;file://&lt;/code&gt; 双击即开。这样就构造出了一个可以随手发给朋友双击打开的雨夜。&lt;/p&gt;&#10;&lt;h2 id="后续"&gt;后续&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;代码整理后若店长同意，我会开源到 GitHub，届时补上链接&lt;/li&gt;&#10;&lt;li&gt;待办清单：SSS 改进、雨滴打在玻璃上的折射、低配模式更多档位&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;如果你打开 demo 觉得卡，&lt;code&gt;?q=low&lt;/code&gt; 可以切低配。我自己手机反正是没抗住。&lt;/p&gt;&#10;</description></item></channel></rss>