当前位置: 首页 > 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.dtcms.com/a/308099.html

相关文章:

  • Linux网络-------3.应⽤层协议HTTP
  • 【运维基础】Linux 进程调度管理
  • 异步I/O和同步I/O
  • USRP捕获手机/路由器数据传输信号波形(下)
  • 金融专题|某跨境支付机构:以榫卯企业云平台 VPC 功能保障业务主体安全
  • 文档识别算法-文字识别接口-表格还原-图表文字识别API
  • HCIA-Datacom认证笔记:IP路由基础——核心概念与路由分类
  • Amazon Aurora MySQL 8.0 完整指南
  • 一些利用AIOps工具进行云原生技术持续创新的成功案例
  • Python 元编程实战:动态属性与数据结构转换技巧
  • Pycaita二次开发基础代码解析:曲面法线生成、零件加载与材料应用
  • 基于LSTM-GRU混合网络的动态解析:美联储维稳政策与黄金单日跌1.5%的非线性关联
  • AI陪伴的发展现状
  • STM32——HAL 库MDK工程创建
  • 2000-2024年中国1KM分辨率年度植被指数(NDVI、EVI)数据集
  • 万物都有属于自己的律动
  • 公路坑槽检测分析原理和思路
  • 嵌入式开发学习———Linux环境下IO进程线程学习(一)
  • 【0基础PS】Photoshop (PS) 理论知识
  • linux线程互斥和同步
  • 操作系统系统面试常问(内存、快表、相关知识)
  • 中欧建交50周年,中硼医疗领衔中意BNCT合作月,中国尖端技术出海欧洲
  • main函数,常量指针与指针常量,野指针等,void与void的区别
  • Kubernetes 应用部署实战:为什么需要 Kubernetes?
  • Apache Tomcat样例目录session操纵漏洞解读
  • Import Maps 实战指南:无需打包器,浏览器原生模块路径重映射!
  • python 检查带有标题行,以逗号为分隔符的文本文件
  • Vue 的双向数据绑定原理
  • 自我学习----绘制Mark点
  • 解决Pycharm内存一直升高卡死、反应慢、CPU占用高