guofeng-threejs · diff

git:20260813.4dbb4e2 to git:20260820.cdd2f19

54 added, 3 removed. Audit A to A.

---
name: guofeng-threejs
description: 国风 3D 网页渲染。当用户要"水墨风 3D""中国风网页特效""Three.js 水墨""国风 H5""青绿山水 3D""非真实感渲染 NPR""古风 WebGL"时使用。提供可运行的中式渲染 shader 与实现方法,不是通用 Three.js 教程。
metadata:
author: sanhuang520-ship-it
category: development
tags: development, design, animation, chinese
---
# 国风 Three.js 渲染
**定位说明**:通用 Three.js 教程已经很多(英文社区有大量高质量资源)。
这个技能只做一件他们不做的事——**中式美学的实时渲染**。
## 什么时候用
中国风官网 / 文旅 H5 / 品牌活动页 / 水墨风交互 / 国风游戏原型 / 非真实感渲染(NPR)。
**不适用**:通用 3D 建模、物理仿真、写实渲染——那些去看 Three.js 官方文档更好。
## 先问清楚
1. **载体**(PC 官网 / 手机 H5 / 微信内嵌)—— 决定性能预算
2. **要哪种中式质感**(水墨 / 青绿山水 / 敦煌壁画 / 剪纸皮影)
3. **有没有模型**(有 glTF?还是用几何体?)
4. **动效需求**(自动旋转 / 跟随鼠标 / 滚动驱动)
> ⚠️ **移动端警告**:Three.js + 自定义 shader 在中低端安卓上会明显发热掉帧。
> H5 项目务必先确认目标机型,必要时降级为静态图。
## 水墨渲染的三个核心
水墨效果不是加个滤镜,是**三件事的组合**:
### 1️⃣ 墨分五色 —— 色阶量化
真实光照是连续的,水墨是**分层**的。把明暗量化成 5 阶:
```glsl
float lam = max(dot(N, normalize(uLight)), 0.0);
float steps = 5.0;
float q = floor(clamp(lam, 0.0, 1.0) * steps) / (steps - 1.0);
vec3 col = mix(uInk, uPaper, q); // 浓墨 → 纸白
```
> 阶数太多(>8)失去水墨感,太少(<3)像色块。**5 阶是甜点**。
### 2️⃣ 边缘积墨 —— rim 压深
毛笔在轮廓处停留更久,墨更浓。用视线与法线夹角模拟:
```glsl
float rim = 1.0 - max(dot(N, normalize(vV)), 0.0);
float edge = smoothstep(0.45, 0.95, rim);
col = mix(col, uInk, edge * 0.85);
```
### 3️⃣ 笔触扰动 —— 噪声打破机械感
纯数学量化的边界太整齐,不像手绘。**在量化前扰动光照值**:
```glsl
float n = noise(vP * 5.5 + uTime * 0.06);
lam += (n - 0.5) * 0.16; // 扰动幅度别超过 0.2
```
再叠一层细噪声当宣纸颗粒:
```glsl
col *= 0.97 + 0.03 * noise(vP * 40.0);
```
+ ## 剪纸皮影:把水墨那套反过来
+
+ 水墨方案的三步是「量化明暗 → 边缘压深 → 噪声扰动」。剪纸皮影**每一步都要反着做**,
+ 这也是为什么它值得单独实现一遍——能验证这套思路不是只会「色调分离」一招。
+
+ ### 1️⃣ 完全平面化 —— 不写 `dot(N, L)`
+
+ 皮影是一张平的皮子,正面背面一个色,没有受光面和背光面。所以片元着色器里
+ **刻意不引入光照项**:
+
+ ```glsl
+ // 水墨: float lam = max(dot(N, uLight), 0.0); → 再量化
+ // 皮影: 直接就是纸的本色,颜色不随朝向变化
+ vec3 col = uPaper;
+ ```
+
+ ### 2️⃣ 背光透射 —— rim 往亮里走,不往暗里走
+
+ 同样是 rim,方向完全相反:水墨用它压出浓墨,皮影用它表现灯光从薄边透出来。
+
+ ```glsl
+ float rim = 1.0 - max(dot(N, normalize(vV)), 0.0);
+ float bleed = smoothstep(0.05, 0.75, rim);
+ col = mix(col, uGlow, bleed * 0.95); // 水墨这里是 mix(col, uInk, ...)
+ ```
+
+ 阈值要放低、范围要拉宽,否则只有镂空边缘发光、外轮廓是死的。
+
+ ### 3️⃣ 信息量在「镂空」不在「体积」
+
+ 剪纸没有明暗层次可用,全靠轮廓和镂空说话,所以几何体要用 `Shape` + `holes`
+ 而不是现成的球/环:
+
+ - **瓣尖必须尖**:`pow(abs(cos(n*t/2)), 1.9)` 把峰压尖、谷压宽。用圆润的
+ 正弦花瓣会像齿轮,不像刀剪的。
+ - **放射长条槽比圆孔像得多**:`Path.absellipse(..., rotation)` 让长轴对准圆心。
+ - **多层要错开**:三层同心同大小时,前面那张会把后面全挡住,「层」根本看不出来。
+ 尺寸拉开 + 初始角度错开 + 轻微偏移才有叠透效果。
+
+ ### 灯光与幕布
+
+ 皮影的光源在幕布后面,画面是「暗幕布上一盏灯」,**不是满屏发光**。
+ 灯晕范围和强度都要收着用,否则背景过曝会把剪影吞掉。
+
## 配色方案
复用中国传统色,三套起步:
| 风格 | 纸色 | 浓墨 | 中间调 |
|------|------|------|--------|
| **水墨** | `#F7F5F0` | `#2E2A26` | `#5A5550` |
| **青绿** | `#F2F5F2` | `#14322B` | `#2F6B5E` |
| **朱砂** | `#FBF7F4` | `#3A1512` | `#B23A2E` |
> 关键:**背景色要和纸色一致**,否则物体像贴在画上而不是画在纸上。
## 完整可运行示例
- `demo.html` 是一个完整的单文件实现(Three.js r170 + importmap,无需构建):
+ `demo.html` —— 水墨(Three.js r170 + importmap,单文件无需构建):
- 实时水墨 shader(上面三个技法全在里面)
- - 三套配色可切换
+ - 三套配色可切换(水墨 / 青绿 / 朱砂)
- 实测:2 draw call / 14496 三角面 / WebGL 无错误
+ `papercut-demo.html` —— 剪纸皮影(2026-08-20 新增):
+
+ - **和水墨正好相反的一套做法**,见下面「剪纸皮影」一节
+ - 三层镂空叠透 + 幕布灯晕,三套配色可切换(皮影 / 窗花 / 敦煌)
+ - 实测:headless Chrome 真 GPU 渲染无 WebGL 错误;1400×950 与 390×844
+ 两种尺寸都完整成像(相机距离按宽高比自适应,早期版本在竖屏会裁掉一圈)
+
在线预览:https://sanhuang520-ship-it.github.io/awesome-chinese-ai-tools/themes/ink3d.html
### 只做方案审查时
用户明确说“不修改、不运行,只做技术方案或检查现成 Demo”时,**先给结论,限制读取范围**:
1. 先用本文件已有的三项技法、性能要点和 Demo 路径回答。
2. 如需核对实现,只搜索 `demo.html` 中与问题直接相关的行;不要整文件输出,也不要读取 `intro-demo.html`,除非用户问开场动画。
3. 最多核对 3 类证据:Three.js 版本、shader 关键字、性能保护。每类只摘必要行号与结论。
4. 不启动浏览器、不运行 Demo,就明确写“静态源码审查,未做运行时验证”。
静态审查应控制在:**技术结论 → 移动端风险 → Demo 路径 → 未验证项**,不要因为仓库里已有源码就展开成逐行代码审计。
#### 用户给出字数上限时
字数上限是硬门槛,不是大致篇幅。先在内部压缩并计数字符,再只输出成品;不要把过程说明、长绝对路径或额外寒暄计入最终答案。中文短审查优先使用下面的紧凑结构:
```text
结论:静态思路可行,未运行。
技法:①…;②…;③…。
风险:①…;②…;③…。
路径:`skills/guofeng-threejs/demo.html`;`skills/guofeng-threejs/intro-demo.html`。
未实测:FPS/功耗/内存、目标机型与 WebView、降级和上下文恢复。
```
- 用户要求 300 字内时,最终响应按 Unicode 字符计数必须 `<= 300`;拿不准就压到约 220–260 字留余量。
- 路径用仓库相对路径,不泄露临时目录或用户本机路径。
- 用户禁止运行时,只做 `rg` / 定点读取等静态核对;不要启动浏览器、服务器、构建或 Demo。
## 其他中式风格的思路
| 风格 | 关键技法 |
|------|---------|
| **青绿山水** | 色阶量化 + 石青石绿双色映射 + 金色描边(edge 用暖金而非墨黑) |
| **敦煌壁画** | 基础色阶 + 强噪声做斑驳 + 土红/石青三色限定 |
- | **剪纸皮影** | 完全平面化(关掉光照)+ 纯剪影 + 背光透射(rim 用暖光而非压深) |
+ | **剪纸皮影** | ✅ **已实现**,见 `papercut-demo.html` 与下节 |
| **工笔** | 提高色阶数(8-10)+ 细线描边(Sobel 后处理)+ 高饱和矿物色 |
## 性能要点
1. **shader 里的 noise 很贵**。手机端把 `noise(vP * 40.0)` 的纸纹改成贴图采样。
2. **draw call 越少越好**。同材质的物体用 `InstancedMesh`。
3. **像素比要限制**:`renderer.setPixelRatio(Math.min(devicePixelRatio, 2))`,否则高分屏直接卡死。
4. **不用后处理就别引 EffectComposer**,能在材质里做的就在材质里做。
5. 移动端建议 **60fps 掉到 30fps 就该降级**——加个 FPS 监测自动降质量。
## 边界
1. **不做通用 Three.js 教学**。基础用法请看 [官方文档](https://threejs.org/docs/),这里只讲中式渲染。
2. **不保证跨设备一致**。GLSL 在不同 GPU/驱动上有精度差异,重要项目要真机测。
3. **不模仿具体画家风格**。可以做"水墨感",不做"齐白石风格"——风格模仿有争议。
4. **不确定的图形学细节要说明**。涉及特定 GPU 兼容性、WebGPU 迁移等,建议查最新文档。
## 相关
- `guochao-visual-cn` —— 国潮视觉(AI 出图用)
- `chinese-web-themes` —— 中式网页主题(2D 排版用)
- 三者关系:**出图** → **排版** → **3D 呈现**