当前位置: 首页 > news >正文

第二篇:Three.js核心三要素:场景、相机、渲染器

第二篇:Three.js核心三要素:场景、相机、渲染器

引言

在Three.js的世界里,场景(Scene)、相机(Camera)和渲染器(Renderer)构成了最基础的"铁三角"。它们如同导演、摄像机和放映机,共同决定了3D内容的呈现方式。本篇将深入解析这三个核心组件,并通过Vue3实战案例展示它们的协同工作。


在这里插入图片描述

1. 场景(Scene):3D世界的容器
1.1 场景的本质

场景是Three.js的顶级容器,所有3D对象(网格、灯光、相机)都需要加入场景才能被渲染。可以将其理解为:

  • 3D对象的舞台
  • 空间坐标系的管理者
  • 场景图(Scene Graph)的根节点
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB); // 设置天空蓝背景
1.2 场景层级管理

Three.js使用树状结构管理对象:

Scene
Camera
Mesh1
Group
Mesh2
Mesh3

实战:使用Group组织对象

<script setup>
import { ref, onMounted } from 'vue';
import * as THREE from 'three';const scene = new THREE.Scene();// 创建汽车组
const carGroup = new THREE.Group();
scene.add(carGroup);// 车身
const bodyGeo = new THREE.BoxGeometry(2, 0.5, 1);
const bodyMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const body = new THREE.Mesh(bodyGeo, bodyMat);
carGroup.add(body);// 车轮
const wheelGeo = new THREE.CylinderGeometry(0.3, 0.3, 0.2, 16);
wheelGeo.rotateZ(Math.PI/2); // 旋转90度使其立起
const wheelMat = new THREE.MeshBasicMaterial({ color: 0x333333 });const wheel1 = new THREE.Mesh(wheelGeo, wheelMat);
wheel1.position.set(0.7, -0.3, 0.5);
carGroup.add(wheel1);const wheel2 = wheel1.clone();
wheel2.position.z = -0.5;
carGroup.add(wheel2);// 移动整个汽车组
carGroup.position.x = -3;
</script>

关键点Group允许将多个对象作为单一实体操作,大幅简化复杂对象的变换控制。


2. 相机(Camera):观察世界的眼睛
2.1 透视相机(PerspectiveCamera)

模拟人眼视角,近大远小效果:

const camera = new THREE.PerspectiveCamera(75, // 视野角度(FOV)window.innerWidth / window.innerHeight, // 宽高比0.1, // 近裁剪面(near)1000 // 远裁剪面(far)
);
camera.position.set(0, 2, 5); // 设置相机位置
2.2 正交相机(OrthographicCamera)

平行投影,无透视变形:

const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.OrthographicCamera(-5 * aspect, // left5 * aspect,  // right5,           // top-5,          // bottom0.1,         // near100          // far
);
2.3 相机类型对比
特性透视相机正交相机
投影方式锥形投影平行投影
适用场景真实感场景技术图纸/2.5D游戏
尺寸感知近大远小保持物体原尺寸
参数复杂度简单(4参数)复杂(6参数)
典型应用第一人称游戏CAD查看器

3. 渲染器(Renderer):将3D转为2D
3.1 渲染器核心配置
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';const canvasRef = ref(null);onMounted(() => {const renderer = new THREE.WebGLRenderer({canvas: canvasRef.value,antialias: true, // 开启抗锯齿alpha: true,     // 允许透明背景powerPreference: "high-performance" // 高性能模式});// 设置像素比和尺寸renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));renderer.setSize(window.innerWidth, window.innerHeight);// 开启阴影renderer.shadowMap.enabled = true;renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影
});
</script>
3.2 响应式窗口处理
// Vue3中使用@vueuse/core监听窗口变化
import { useWindowSize } from '@vueuse/core';const { width, height } = useWindowSize();watch([width, height], () => {camera.aspect = width.value / height.value;camera.updateProjectionMatrix(); // 必须更新相机renderer.setSize(width.value, height.value);
});

4. 综合实战:多相机切换系统
4.1 项目结构
src/├── components/│    ├── CameraSystem.vue   // 相机系统组件│    └── SceneObjects.vue   // 场景对象组件└── App.vue
4.2 相机切换核心代码
<!-- CameraSystem.vue -->
<script setup>
import { ref } from 'vue';// 相机枚举类型
const CameraType = {PERSPECTIVE: 0,ORTHOGRAPHIC: 1
};const currentCamera = ref(CameraType.PERSPECTIVE);
const cameras = {[CameraType.PERSPECTIVE]: createPerspectiveCamera(),[CameraType.ORTHOGRAPHIC]: createOrthographicCamera()
};function createPerspectiveCamera() {const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);camera.position.set(0, 2, 5);return camera;
}function createOrthographicCamera() {const aspect = window.innerWidth / window.innerHeight;const camera = new THREE.OrthographicCamera(-5*aspect, 5*aspect, 5, -5, 0.1, 100);camera.position.set(0, 2, 5);camera.lookAt(0, 0, 0);return camera;
}function toggleCamera() {currentCamera.value = currentCamera.value === CameraType.PERSPECTIVE ? CameraType.ORTHOGRAPHIC : CameraType.PERSPECTIVE;
}
</script><template><button @click="toggleCamera" class="camera-toggle">{{ currentCamera === CameraType.PERSPECTIVE ? '正交视图' : '透视视图' }}</button>
</template>
4.3 渲染循环适配多相机
// 在渲染循环中使用当前相机
function animate() {requestAnimationFrame(animate);// 获取当前激活的相机const activeCamera = cameras[currentCamera.value];// 旋转场景物体scene.traverse(obj => {if (obj.isMesh) obj.rotation.y += 0.01;});renderer.render(scene, activeCamera);
}
4.4 相机切换效果对比

5. 高级技巧:相机控制器
5.1 引入OrbitControls
npm install three-orbitcontrols
<script setup>
import { OrbitControls } from 'three-orbitcontrols';onMounted(() => {const controls = new OrbitControls(camera, renderer.domElement);// 配置控制器参数controls.enableDamping = true; // 启用阻尼效果controls.dampingFactor = 0.05; // 阻尼系数controls.autoRotate = true;    // 自动旋转controls.autoRotateSpeed = 1.0;// 在渲染循环中更新控制器function animate() {requestAnimationFrame(animate);controls.update(); // 必须每帧更新renderer.render(scene, camera);}
});
</script>
5.2 控制器限制设置
// 限制垂直旋转角度
controls.minPolarAngle = Math.PI / 6; // 30度
controls.maxPolarAngle = Math.PI / 2; // 90度// 禁用平移
controls.enablePan = false;// 缩放限制
controls.minDistance = 3;
controls.maxDistance = 15;

6. 常见问题解答

Q1:物体在场景中不可见怎么办?

  1. 检查物体是否添加到场景 scene.add(mesh)
  2. 确认相机位置是否在物体前方
  3. 验证物体是否在相机裁剪范围内

Q2:如何实现画布透明背景?

const renderer = new THREE.WebGLRenderer({alpha: true, // 开启透明度premultipliedAlpha: false // 避免颜色预乘
});
scene.background = null; // 清除场景背景

Q3:为什么正交相机看到的物体是扁平的?

  • 调整正交相机参数范围:
// 正确设置左右上下参数的比例关系
const aspect = window.innerWidth / window.innerHeight;
const height = 10;
const width = height * aspect;const camera = new THREE.OrthographicCamera(-width/2, width/2, // left, rightheight/2, -height/2, // top, bottom1, 1000
);

7. 总结

通过本篇学习,你已掌握:

  1. 场景的层级管理技巧(使用Group组织对象)
  2. 透视相机与正交相机的核心区别及适用场景
  3. 渲染器的关键配置项(抗锯齿/阴影/响应式)
  4. Vue3中实现多相机切换系统
  5. 使用OrbitControls实现交互控制

核心原理:Three.js的渲染流程本质上是将场景中的3D对象,通过相机的视角转换,最终由渲染器投影到2D画布上的过程。


下一篇预告

第三篇:几何体入门:内置几何体全解析
你将学习:

  • 12种基础几何体的创建与参数调整
  • 几何体顶点(Vertex)与面(Face)的底层原理
  • 动态生成参数化几何体(如可调节分段数的球体)
  • 几何体性能优化技巧(BufferGeometry详解)
  • Vue3实现几何体参数实时调节面板

准备好探索Three.js的几何世界了吗?让我们从最简单的立方体开始,逐步揭开3D建模的神秘面纱!

http://www.lryc.cn/news/606571.html

相关文章:

  • AscendantPath | 实现视觉小说的人物对话系统(二)
  • 嵌入式开发学习———Linux环境下IO进程线程学习(一)
  • 分布内侧内嗅皮层的层Ⅱ或层Ⅲ的网格细胞(grid cells)对NLP中的深层语义分析的积极影响和启示
  • 什么是数据集成?和数据融合有什么区别?
  • AI陪伴的发展现状
  • Linux应用开发基础知识——Makefile初级教程(九)
  • 20250731解决RK3588的AIOT参考设计刷机之后可以启动但是断电进MASKROM模式
  • Ⅹ—6.计算机二级综合题19---22套
  • flowable对已经部署的流程进行更新,不产生新版本
  • anaconda searchanaconda show | conda 检索包资源安装指定版本包指定源安装命令package
  • Java学习------Executor框架
  • 数据结构:多项式表示(polynomial representation)
  • 力扣 Pandas 挑战(6)---数据合并
  • 从零开始搞定类和对象(上)
  • Pycaita二次开发基础代码解析:曲面法线生成、零件加载与材料应用
  • stm32F407 实现有感BLDC 六步换相 cubemx配置及源代码(一)
  • MySQL 中的聚簇索引和非聚簇索引的区别
  • 【STM32】HAL库中的实现(一)GPIO/SysTick/EXTI
  • Cesium 快速入门(五)坐标系
  • 【JavaEE】(7) 网络原理 TCP/IP 协议
  • Python 环境配置
  • React的介绍和特点
  • 学习曲线之TS
  • 检索召回率优化探究二:基于 LangChain 0.3集成 Milvus 2.5向量数据库构建的智能问答系统
  • 结构体、共用体,位运算
  • MPU6050模块
  • 谷歌正在美国测试一项基于机器学习的年龄识别技术
  • 7月31日作业
  • Qt之CJSON:从基础到进阶的 JSON 数据处理指南
  • langchain--2--invoke、batch、stream、ainvoke、abatch、astream