阅读视图

发现新文章,点击刷新页面。

Three.js × Blender:从建模到 Web 3D 的完整工作流深度解析

一名同时精通 Blender 建模与 Three.js 渲染的工程师,带你打通从艺术创作到 Web 实时渲染的全链路。


前言

很多开发者在学习 Three.js 时,习惯用代码"画"出几何体——一个 BoxGeometry,一个 SphereGeometry,就此打住。而建模师则沉浸在 Blender 的雕刻与材质世界里,对 Web 端渲染一无所知。

真正有价值的 3D Web 项目,往往需要这两者的深度结合:Blender 负责内容生产,Three.js 负责实时呈现。本文将从工作流、格式选择、材质映射、性能优化到动画同步,系统拆解这套协作体系的每一个关键节点。

图片


一、为什么选择 Blender + Three.js?

Blender 的优势

Blender 是目前最强大的开源 3D 创作套件,集建模、雕刻、UV 展开、材质编辑、骨骼绑定、动画、渲染于一体。它的 PBR(基于物理的渲染)材质系统与 Three.js 的 MeshStandardMaterial 有高度的理论对应关系,这使得材质迁移成为可能,而非只能靠"近似"。

Three.js 的优势

Three.js 是 WebGL 的高级封装,让开发者无需手写 GLSL Shader 就能实现实时 3D 渲染。它支持 glTF 2.0 标准、骨骼动画、变形动画、PBR 材质、HDR 环境贴图,在浏览器端提供接近原生的视觉质量。

两者的天然契合点

图片图片

特性 Blender Three.js
材质模型 Principled BSDF MeshStandardMaterial
动画系统 Action / NLA AnimationMixer
导出格式 glTF 2.0 原生支持 GLTFLoader 原生支持
坐标系 Y-up(可配置) Y-up
PBR 贴图 BaseColor / Roughness / Metallic / Normal map / roughnessMap / metalnessMap / normalMap

glTF 2.0 是连接两者的"通用语言",它由 Khronos Group 制定,Blender 对其有完整的原生导出支持,Three.js 也将其作为首推格式。


二、Blender 建模的 Web 友好实践

在 Blender 中建模时,如果目标是 Web 端实时渲染,需要从一开始就考虑"面向 Web 的建模规范",而不是按离线渲染的标准来做。

2.1 多边形控制:少即是多

离线渲染可以承受千万面模型,但 Web 端 GPU 对三角面数极为敏感。经验值如下:

图片

  • 单个主体模型:5,000 ~ 50,000 三角面(取决于镜头距离)
  • 背景/远景物体:500 ~ 2,000 三角面
  • 整个场景:尽量控制在 300,000 三角面以内(移动端减半)

实用技巧:

在 Blender 中使用 Decimate 修改器可以智能减面,
Ratio 参数从 1.0 逐渐降低,观察模型形变程度,
通常 0.5 ~ 0.7 可在不明显损失细节的前提下大幅减面。

高模细节应通过 法线贴图(Normal Map)  烘焙到低模上,而不是保留在几何体中。这是 Web 3D 最核心的优化手段之一。

2.2 UV 展开的关键性

Three.js 中所有贴图都依赖 UV 坐标。Blender 中的 UV 展开质量直接决定贴图利用率和最终画质。

UV 展开建议:

  1. 使用 Smart UV Project 快速处理机械类硬表面模型
  2. 有机体(角色、生物)使用 Mark Seam + Unwrap 手动控制接缝位置
  3. UV 岛之间保留 至少 4 像素的边距(Margin)  ,防止贴图渗色
  4. 避免 UV 重叠(除非是对称模型且确定共用贴图)

2.3 坐标系与朝向

Blender 默认坐标系:Z 轴朝上,Y 轴朝前。 Three.js 坐标系:Y 轴朝上,Z 轴朝向观察者。

图片图片

在 glTF 导出时,Blender 会自动进行坐标系转换,因此通常不需要手动旋转。但如果你在 Blender 中对模型进行了非标准旋转,导出后可能在 Three.js 中出现方向错误。

最佳实践:  在 Blender 中完成所有变换后,按 Ctrl+A → All Transforms 应用变换,确保对象的 Location/Rotation/Scale 归零。


三、PBR 材质:从 Blender 到 Three.js 的精确映射

这是整个工作流中最需要深入理解的环节。

3.1 Principled BSDF 与 MeshStandardMaterial 的对应关系

Blender 的 Principled BSDF 节点是工业级 PBR 着色器,其核心参数与 Three.js 的 MeshStandardMaterial 有如下对应:

图片图片

Principled BSDF 参数 Three.js 对应属性 说明
Base Color color / map 基础颜色/漫反射贴图
Metallic metalness / metalnessMap 金属度(0=非金属,1=纯金属)
Roughness roughness / roughnessMap 粗糙度(0=镜面,1=完全漫反射)
Normal Map normalMap 法线贴图(切线空间)
Emission emissive / emissiveMap 自发光
Alpha opacity / alphaMap 透明度
Ambient Occlusion aoMap 环境遮蔽(需要第二套 UV)

注意:  glTF 2.0 格式将 Roughness 存储在贴图的 G 通道,Metallic 存储在 B 通道,合并为一张 metallicRoughnessMap。Three.js 的 GLTFLoader 会自动处理这个细节,开发者无需干预。

3.2 在 Blender 中烘焙 PBR 贴图

当模型使用了复杂的程序化材质(Noise、Voronoi、Wave 节点等)时,需要将其"烘焙"成位图才能在 Web 端使用。

烘焙流程:

图片

  1. 选中模型,进入 Cycles 渲染引擎(烘焙必须使用 Cycles)
  2. 新建一张空白图像节点(Image Texture),不连接任何节点,只需选中它
  3. 在 Render Properties → Bake 中选择烘焙类型:
    • Diffuse(取消 Direct/Indirect,仅勾选 Color)→ Base Color 贴图
    • Roughness → Roughness 贴图
    • Normal(Space: Tangent)→ 法线贴图
    • Combined → 全局光照效果烘焙(含 AO、阴影,适合静态场景)
  4. 点击 Bake,等待计算完成
  5. 导出图像为 PNG(法线图)或 JPEG(颜色图,注意法线图不能用 JPEG 有损压缩!)

这里我推荐一个B站的烘焙教材,很不错的。

链接如下:

(www.bilibili.com/video/BV185…)

3.3 法线贴图的空间问题

Blender 默认导出**切线空间(Tangent Space)**法线贴图,Three.js 也默认使用切线空间法线,两者一致,无需额外处理。

但如果你在 Blender 中烘焙了**对象空间(Object Space)**法线贴图,则需要在 Three.js 中将 normalMapType 设置为 THREE.ObjectSpaceNormalMap

material.normalMapType = THREE.ObjectSpaceNormalMap;

四、glTF 导出配置详解

glTF 是连接 Blender 和 Three.js 的核心桥梁,正确的导出配置至关重要。

4.1 .gltf vs .glb

格式 说明 适用场景
.gltf JSON 文本 + 外部 .bin + 外部贴图 调试、需要单独管理资产
.glb 全部打包为单一二进制文件 生产环境,减少 HTTP 请求

图片

推荐:生产环境使用 .glb,加载更快,管理更简单。

4.2 Blender 导出设置

图片

File → Export → glTF 2.0,关键设置:

图片图片

Format: glTF Binary (.glb)

 Include:
  - Selected Objects(只导出选中对象,避免导出无关物体)
  - Custom Properties(可传递自定义属性到 Three.js)

 Transform:
  - Y Up(确保坐标系正确)

 Geometry:
  - Apply Modifiers(应用所有修改器,但注意会破坏骨骼绑定)
  - UVs / Normals / Tangents / Vertex Colors
  - Loose Edges / Points(通常取消勾选)

 Animation:
  - Animations(导出动画数据)
  - Skinning(导出骨骼绑定)
  - Shape Keys(导出变形目标/Morph Targets)
  - NLA Strips(从非线性动画编辑器导出所有 Action)

 Draco Mesh Compression:
  开启后可大幅压缩几何体数据,但 Three.js 需要额外加载 DRACOLoader

4.3 在 Three.js 中加载 glTF

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';

// 配置 Draco 解码器(如果导出时开启了 Draco 压缩)
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/'); // 需要将 draco 解码器文件放在此路径

const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);

loader.load(
  '/models/scene.glb',
  (gltf) => {
    const model = gltf.scene;

    // 遍历所有网格,开启阴影
    model.traverse((node) => {
      if (node.isMesh) {
        node.castShadow = true;
        node.receiveShadow = true;

        // 如果使用 HDR 环境贴图,开启环境光反射
        node.material.envMapIntensity = 1.0;
      }
    });

    scene.add(model);
  },
  (progress) => {
    console.log(`加载进度: ${(progress.loaded / progress.total * 100).toFixed(1)}%`);
  },
  (error) => {
    console.error('加载失败:', error);
  }
);

五、动画系统深度对接

Blender 的动画系统与 Three.js 的 AnimationMixer 是整个工作流中最复杂的对接点。

5.1 Blender 动画类型与 Three.js 对应

骨骼动画(Armature Animation)

最常见的角色动画类型。Blender 中为骨骼创建 Action,绑定到 Mesh。导出后 Three.js 通过 SkinnedMesh 和 AnimationClip 驱动。

// 加载后获取所有动画
const animations = gltf.animations; // AnimationClip[]
const mixer = new THREE.AnimationMixer(gltf.scene);

// 播放特定动画(如 "Walk" Action)
const walkClip = THREE.AnimationClip.findByName(animations, 'Walk');
const walkAction = mixer.clipAction(walkClip);
walkAction.play();

// 在渲染循环中更新
const clock = new THREE.Clock();
function animate() {
  requestAnimationFrame(animate);
  const delta = clock.getDelta();
  mixer.update(delta); // 关键:每帧更新动画混合器
  renderer.render(scene, camera);
}
animate();

变形目标动画(Shape Keys / Morph Targets)

适用于面部表情、布料变形等。Blender 中的 Shape Key 导出为 glTF 的 Morph Target。

// 访问变形目标
const mesh = gltf.scene.getObjectByName('Face');
console.log(mesh.morphTargetDictionary); // { "Smile": 0, "Blink": 1, ... }

// 直接设置变形权重(0~1)
mesh.morphTargetInfluences[0] = 0.8; // 80% 的 Smile 表情

// 或通过 AnimationMixer 驱动
const smileClip = THREE.AnimationClip.findByName(animations, 'Smile');
mixer.clipAction(smileClip).play();

5.2 动画过渡:crossFadeTo

游戏和交互应用中,动画之间的平滑过渡至关重要:

图片

let currentAction = idleAction;

function transitionTo(newAction, duration = 0.3) {
  if (currentAction === newAction) return;

  newAction.reset();
  newAction.play();
  currentAction.crossFadeTo(newAction, duration, true);
  currentAction = newAction;
}

// 按键触发动画切换
document.addEventListener('keydown', (e) => {
  if (e.code === 'Space') transitionTo(runAction);
});

document.addEventListener('keyup', (e) => {
  if (e.code === 'Space') transitionTo(idleAction);
});

5.3 NLA 编辑器与多 Action 管理

在 Blender 中,推荐使用 NLA(Non-Linear Animation)编辑器将不同 Action(Idle、Walk、Run、Attack)管理在同一骨骼上。导出时勾选 NLA Strips,Three.js 端会收到所有 Action 作为独立的 AnimationClip

Blender 操作:

  1. 在 Dope Sheet 中切换到 NLA Editor
  2. 将 Action 下压为 NLA Strip
  3. 每个 Strip 对应一个独立动画
  4. 确保每个 Action 有清晰的命名(会直接成为 Three.js 中 clip.name

六、灯光与环境:让 Web 端复现 Blender 的视觉效果

图片

6.1 HDR 环境贴图

Blender 中使用 HDR 环境光照(World → Environment Texture),Three.js 中同样支持,且是让模型在 Web 端看起来"专业"的关键。

import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';

const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();

new RGBELoader().load('/hdr/studio_small.hdr', (texture) => {
  const envMap = pmremGenerator.fromEquirectangular(texture).texture;

  scene.environment = envMap;  // 影响所有 MeshStandardMaterial 的反射
  scene.background = envMap;   // 可选:将 HDR 作为背景

  texture.dispose();
  pmremGenerator.dispose();
});

推荐资源:Poly Haven(polyhaven.com/hdris) 提供大量免费高质量 HDR 文件。

6.2 灯光类型对应

图片

Blender 灯光 Three.js 等价
Sun DirectionalLight
Point PointLight
Spot SpotLight
Area RectAreaLight
World (HDR) scene.environment

注意:  glTF 导出支持 Point、Spot、Directional 灯光(需开启 KHR_lights_punctual 扩展,Blender 默认勾选),Area Light 不支持直接导出,需在 Three.js 端手动添加。

6.3 阴影质量配置

// 渲染器阴影配置
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影

// 方向光阴影配置
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;  // 阴影贴图分辨率
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 50;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
dirLight.shadow.bias = -0.001; // 防止阴影痤疮(Shadow Acne)

七、性能优化:从 Blender 到浏览器的极致压缩

图片

7.1 纹理压缩:KTX2 + Basis Universal

传统 JPEG/PNG 贴图在 GPU 中需要解码为原始像素,占用大量显存。KTX2/Basis Universal 是可以直接在 GPU 上保持压缩状态的格式,显存占用可降低 4~8 倍。

工具链:

# 安装 KTX-Software
# 将 PNG 转换为 KTX2(UASTC 模式,高质量)
toktx --uastc --uastc_rdo_l 4 output.ktx2 input.png

# Three.js 中加载 KTX2
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';

const ktx2Loader = new KTX2Loader()
  .setTranscoderPath('/basis/')
  .detectSupport(renderer);

loader.setKTX2Loader(ktx2Loader);

7.2 实例化渲染:InstancedMesh

场景中有大量相同模型(树木、石头、草地)时,使用 InstancedMesh 可以将数千次 Draw Call 合并为一次:

// 在 Blender 中建好单个树木模型,导出后:
const treeGeometry = treeModel.geometry;
const treeMaterial = treeModel.material;

const COUNT = 1000;
const instancedMesh = new THREE.InstancedMesh(treeGeometry, treeMaterial, COUNT);

const dummy = new THREE.Object3D();
for (let i = 0; i < COUNT; i++) {
  dummy.position.set(
    (Math.random() - 0.5) * 200,
    0,
    (Math.random() - 0.5) * 200
  );
  dummy.rotation.y = Math.random() * Math.PI * 2;
  dummy.scale.setScalar(0.8 + Math.random() * 0.4);
  dummy.updateMatrix();
  instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);

7.3 LOD(多细节层次)

对于远近距离视觉差异大的模型,在 Blender 中准备 3 个精度版本(高/中/低),Three.js 中根据距离自动切换:

const lod = new THREE.LOD();

// 高精度:0~10 米
lod.addLevel(highDetailMesh, 0);
// 中精度:10~50 米  
lod.addLevel(medDetailMesh, 10);
// 低精度:50 米以上
lod.addLevel(lowDetailMesh, 50);

scene.add(lod);
// LOD 会在每帧自动根据相机距离切换

八、进阶:自定义 Shader 扩展 glTF 材质

Three.js 的 onBeforeCompile 钩子允许在不放弃 PBR 管线的前提下,向材质注入自定义 GLSL 代码。这是高阶扩展的核心技巧。

// 在 Blender 中建好基础 PBR 材质,导出后:
model.traverse((node) => {
  if (node.isMesh && node.material.name === 'WindyGrass') {
    node.material.onBeforeCompile = (shader) => {
      // 注入 uniform
      shader.uniforms.uTime = { value: 0 };
      shader.uniforms.uWindStrength = { value: 0.3 };

      // 在顶点着色器头部注入声明
      shader.vertexShader = shader.vertexShader.replace(
        '#include <common>',
        `
        #include <common>
        uniform float uTime;
        uniform float uWindStrength;
        `
      );

      // 在顶点变换前注入风力位移
      shader.vertexShader = shader.vertexShader.replace(
        '#include <begin_vertex>',
        `
        #include <begin_vertex>
        // 根据 Y 轴高度决定摆动幅度(根部固定)
        float windFactor = position.y * uWindStrength;
        transformed.x += sin(uTime * 2.0 + position.z * 0.5) * windFactor;
        transformed.z += cos(uTime * 1.5 + position.x * 0.5) * windFactor * 0.5;
        `
      );

      // 保存 shader 引用以便每帧更新
      node.material.userData.shader = shader;
    };
  }
});

// 在渲染循环中更新 uniform
function animate() {
  requestAnimationFrame(animate);
  const t = clock.getElapsedTime();

  scene.traverse((node) => {
    if (node.isMesh && node.material.userData.shader) {
      node.material.userData.shader.uniforms.uTime.value = t;
    }
  });

  renderer.render(scene, camera);
}

九、完整工作流总结

图片

Blender 创作阶段
│
├── 建模(控制面数,UV 展开)
├── 材质(Principled BSDF,程序化贴图)
├── 烘焙(BaseColor / Roughness / Normal / AO)
├── 动画(骨骼 / Shape Key / 物理模拟烘焙)
└── 导出(glTF 2.0 / .glb / Draco 压缩)
         │
         ▼
    glb / gltf 文件
         │
         ▼
Three.js 运行时阶段
│
├── 加载(GLTFLoader + DRACOLoader + KTX2Loader)
├── 材质增强(envMap / onBeforeCompile / 自定义 Shader)
├── 动画驱动(AnimationMixer / crossFadeTo)
├── 灯光配置(HDR 环境 + 实时灯光)
├── 性能优化(InstancedMesh / LOD / 纹理压缩)
└── 交互与后处理(Controls / EffectComposer)

十、推荐工具与资源

工具/资源 用途
gltf.report(gltf.report/) 分析 glTF 文件结构与优化建议
glTF Viewer(gltf-viewer.donmccurdy.com/) 快速预览 glTF/glb 文件
KTX-Software(github.com/KhronosGrou…) 纹理压缩工具
Poly Haven(polyhaven.com/) 免费 HDR / 贴图 / 3D 模型
Three.js Editor(threejs.org/editor/) 在线 Three.js 场景编辑器
glTF Transform(gltf-transform.dev/) 命令行 glTF 优化工具
Blender glTF 文档(developer.blender.org/docs) 官方导出插件文档

推荐大佬

最后

Three.js 与 Blender 的结合,不是简单的"导出然后加载",而是一套需要深度理解两个系统各自机制,并在接缝处做精细处理的工程实践。从 PBR 材质的精确映射,到动画系统的无缝对接,再到面向 Web 的性能优化,每个环节都有大量细节值得深挖。

掌握这套工作流,你将拥有从零到一打造高质量 Web 3D 体验的完整能力——既能胜任艺术侧的内容生产,也能驾驭工程侧的实时渲染。这正是当下 Web 3D 领域最稀缺的复合型能力。

现在AI还无法胜任3D可视化相关的工作,学起来为自己增加点筹码。需要相关blender、可视化学习资料的可以关注我私信获取


本文覆盖 Blender 4.x + Three.js r160+ 版本,部分 API 在旧版本中可能有差异

从动漫水面到赛博飞船:这位开发者的Three.js作品太惊艳了

不是游戏引擎做不起,而是 React Three Fiber更有性价比。

今天给大家安利一个宝藏开发者 Christian Ortiz,以及他的两个开源项目——看完你会明白,用Web技术做3D视觉效果,已经卷到什么程度了。


项目一:Anime Water Scene —— 动漫风格水面场景

GitHub: github.com/cortiz2894/…

先看效果:

  • 类似《海贼王》《鬼灭之刃》那种手绘风格的动漫水面
  • 物体入水时有经典的动漫式涟漪圆环
  • 水下有随水流波动的Voronoi纹理海底
  • 水面与物体交界处有发光轮廓线

技术亮点拆解:

1. 多层渲染管线(6层叠加)

SeabedFloor(海底纹理)
  ↓
WaterFloor(水面着色)
  ↓
WaterDepthIntersection(深度发光)
  ↓
WaterWaveSimulation(波浪模拟)
  ↓
WaterSparkles(水面闪光粒子)
  ↓
Ripple System(涟漪系统)

这不是简单的"贴图+水面",而是真正的分层合成渲染

2. 自定义GLSL着色器 —— 核心黑科技

Voronoi Cel-Shading(赛璐珞着色)

// 简化版核心逻辑
float voronoi = voronoiF1(pos) - smoothVoronoiF1(pos);
vec3 waterColor = mix(deepColor, highlightColor, voronoi);

Voronoi F1 − SmoothF1 算法,复刻了Blender的动态绘画效果,实现了那种"一块一块"的动漫水面质感。

3. GPU物理波浪模拟(PDE方程)

不是简单的正弦波,而是真正的偏微分方程 模拟

h_next = 2·h_cur − h_prev + c²·∇²h

每帧三次渲染通道:

  1. Injection —— 检测物体入水形状
  2. Wave Update —— 求解波浪方程(ping-pong双缓冲)
  3. Display —— 根据高度梯度渲染波纹

4. 屏幕空间深度检测

物体与水面交界处的效果,用深度图比较实现:

  • 渲染一遍场景深度到纹理
  • 水面像素对比自身深度和场景深度
  • 差值越小 → 发光越强

这技术在各种3A游戏里都在用,现在Web端也能跑了。


项目二:Ship Selection Page —— 赛博飞船选择界面

GitHub: github.com/cortiz2894/…

这是游戏《Laser Drift: Neon Blast》的飞船选择界面,有完整的YouTube教程系列。

核心效果:

截屏2026-03-20 19.44.22.png

截屏2026-03-20 19.45.36.png

  • 蒸汽波(Vaporwave)美学风格
  • 飞船线框揭示动画(Wireframe Reveal)
  • 3D飞船展示 + 属性面板
  • 粒子背景系统
  • 手势控制支持

技术亮点:

1. 线框揭示动画(Wireframe Reveal)

不是简单的淡入淡出,而是从线框到实体的渐变:

  • 先用GLSL把模型渲染成线框
  • 通过shader的discard逻辑,控制像素显示/隐藏
  • 配合GSAP动画,实现"绘制出来"的效果

2. GLB模型烘焙纹理

  • 从Blender导出GLB格式
  • 烘焙光照贴图(Lightmap)
  • 在Web端还原高质量的静态光照

3. 完整的UI+3D融合

ShipSelection/
├── BaseModel/      # 3D展示平台
├── Ships/          # 飞船模型数据
├── ShipGrid/       # 选择网格UI
├── ShipStats/      # 属性面板
└── ShipDescription/# 描述面板

3D场景和React UI组件完美融合,不是"3D画布上面盖一层HTML"的简单做法。


两个项目的共同技术栈

技术 用途 学习价值
Next.js 15 框架 App Router + 服务端渲染
React Three Fiber 3D渲染 React式声明化3D开发
Drei R3F辅助库 常用3D组件开箱即用
GSAP 动画 时间轴控制、缓动函数
Leva GUI调试 实时参数调节
Tailwind CSS 样式 快速UI开发
TypeScript 类型 大型3D项目必备

你可以从中学到什么?

1. 动漫风格渲染的秘密

  • Cel-Shading(赛璐珞着色)不是"卡通材质"那么简单
  • Voronoi噪声可以实现手绘质感的纹理
  • 多层合成比单一大shader更可控

2. 物理模拟不用全靠库

  • 自己写PDE求解器,理解GPU计算的本质
  • Ping-pong双缓冲是实现反馈效果的关键
  • WebGL的FrameBuffer对象可以玩出很多花样

3. 3D项目工程化

  • 用React组件化思维组织3D代码
  • Store模式管理跨组件的3D状态
  • 自定义Hook封装可复用的3D逻辑

4. 性能优化技巧

  • DPR-aware渲染(适配高分辨率屏)
  • GPU粒子系统(gl_PointCoord)
  • 深度图复用(避免重复渲染)

如何运行这两个项目

# 项目一:动漫水面
git clone https://github.com/cortiz2894/water-anime-shader.git
cd water-anime-shader
pnpm install
pnpm dev

# 项目二:飞船选择
git clone https://github.com/cortiz2894/ship-selection-page.git
cd ship-selection-page
npm install
npm run dev

注意:都需要Node 18+,推荐用pnpm(项目一作者用的pnpm)。


适合谁学?

人群 建议重点看
前端开发者 React Three Fiber的组件化思维
Three.js初学者 两个项目的shader入门
创意开发者 视觉效果实现思路
游戏开发者 UI与3D场景融合方案
设计师 技术可行性参考

写在最后

Christian Ortiz 的作品最打动我的地方:他把Blender的动态绘画、3A游戏的深度检测、物理模拟的 PDE 方程,全部搬进了Web端

而且代码组织得非常干净——不是那种"shader写2000行"的硬核风格,而是组件化、模块化、React化的现代前端工程实践。

如果你想:

  • 做创意视觉网站
  • 做游戏风格的3D交互
  • 深入理解WebGL shader
  • 看如何用React做3D工程

这两个项目都值得clone下来,一行行啃。


项目链接:


如果对你有帮助,点个关注呗!

❌