简介
本系列教程需要具备threejs的基础入门知识,了场景、几何体、相机等基础概念。
学习本教程之前,建议学习【纹理贴图与环境贴图】的基础知识。
几何体简介
通过前置知识,我们已经对几何体有了初步的认识。
比如最基本的立方体BoxGeometry
// 2、创建几何体Geometry模型
const geometry = new THREE.BoxGeometry(20, 20, 20);
还有球形几何体SphereGeometry
// 2、创建球体模型
const sphere = new THREE.SphereGeometry(20);
从官方示例图中我们可以看出,几何体都是由面组成的,所以想要深入了解几何体,我们就需要从最基本的面开始。
平面几何体PlaneGeometry
Api:https://threejs.org/docs/#api/zh/geometries/PlaneGeometry
PlaneGeometry一个用于生成平面几何体的类。其语法示例
PlaneGeometry(width , height, widthSegments, heightSegments)
- width — 平面沿着 X 轴的宽度。默认值是 1。
- height — 平面沿着 Y 轴的高度。默认值是 1。
- widthSegments — (可选)平面的宽度分段数,默认值是 1。
- heightSegments — (可选)平面的高度分段数,默认值是 1。
代码示例
const geometry = new THREE.PlaneGeometry(40, 40); const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide }); const mesh = new THREE.Mesh(geometry, material); // 设置模型mesh的xyz坐标 mesh.position.set(0, 40, 0); scene.add(mesh);
side: THREE.DoubleSide 是用来设定材质的哪一面应该被渲染。THREE.DoubleSide 意味着两面都会被渲染。
几何体的模型数据
我们在数据层面看看这个最基本的几何体的结构长什么样
可以看到,一个几何体的对象包含了非常多的数据
在Three.js中,geometry 对象的 attributes 属性包含了许多用于定义几何体形状和外观的信息。其中,normal、position 和 uv 是最常用的几种。
- normal: 法线(Normal)是三维图形中用来表示表面向外方向的一个矢量。在渲染过程中,光线会根据表面的法线方向来确定如何照亮表面。通过改变法线的方向,可以改变表面受光的方向和形状的阴影效果。
- position: 位置(Position)属性定义了每个顶点在三维空间中的坐标。这些坐标被用来确定顶点如何连接以及它们如何形成形状。每个顶点的位置都是相对于原点的,原点(0,0,0)通常在形状的中心。
- uv: UV坐标(也称为U、V、V1、V2等)定义了纹理映射的位置和方向。纹理映射是一种将二维图像(纹理)映射到三维形状表面的技术。UV坐标是用来确定纹理如何映射到形状的顶点上的。它们通常被用来实现复杂的表面细节和外观,例如通过将纹理映射到物体的表面上。
这些属性一起定义了物体的形状、外观和光照效果。
上图中,我们可以看到geometry的原型链指向BufferGeometry构造函数,这意味着它具备BufferGeometry的所有属性和方法。
BufferGeometry
我先看看官网是如何定义它的:
简单来说,它是所有不同几何体的基类,使用它可以创造出所有的几何体,比如立方体BoxGeometry、球体SphereGeometry等等。
使用BufferGeometry创建平面几何体
我们参考官方代码示例,创建一个平面几何体
const geometry = new THREE.BufferGeometry();
// 创建一个简单的矩形. 在这里我们左上和右下顶点被复制了两次。
// 因为在两个三角面片里,这两个顶点都需要被用到。
const vertices = new Float32Array( [
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0
] );
// itemSize = 3 因为每个顶点都是一个三元组。
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
const material = new THREE.MeshBasicMaterial( {
color: 0xff0000 } );
const mesh = new THREE.Mesh( geometry, material );
我们对核心代码也做出一点点调整,方便更好的演示效果
<template>
<div class="wrap" ref="threeContainer"></div>
</template>
<script setup>
import * as THREE from "three";
import {
onMounted, ref } from "vue";
import {
OrbitControls } from "three/addons/controls/OrbitControls.js";
const threeContainer = ref(null);
// 1、创建3D场景对象Scene
const scene = new THREE.Scene();
// 添加背景颜色
const cubeTexture = new THREE.CubeTextureLoader().setPath("/sky/").load(["posx.jpg", "negx.jpg", "posy.jpg", "negy.jpg", "posz.jpg", "negz.jpg"]);
scene.background = cubeTexture;
// 添加网格地面
const gridHelper = new THREE.GridHelper(200, 10);
scene.add(gridHelper);
// 添加三维坐标轴
const axesHelper = new THREE.AxesHelper(8);
scene.add(axesHelper);
// 2.使用BufferGeometry创建模型
const geometry = new THREE.BufferGeometry();
// 创建一个简单的矩形. 在这里我们左上和右下顶点被复制了两次。
// 因为在两个三角面片里,这两个顶点都需要被用到。
const vertices = new Float32Array([
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0,
]);
// itemSize = 3 因为每个顶点都是一个三元组。
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
const material = new THREE.MeshBasicMaterial({
color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 3、使用虚拟相机观察模型
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 15);
camera.lookAt(0, 0, 0); //坐标原点
// 4、渲染3D场景到DOM上
const width = 800; //宽度
const height = 500; //高度
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.setAnimationLoop(animation);
// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 添加阻尼
controls.enableDamping = true;
controls.dampingFactor = 0.01;
function animation() {
controls.update();
renderer.render(scene, camera);
}
onMounted(() => {
threeContainer.value.appendChild(renderer.domElement);
});
</script>
<style scoped></style>
我们也许会很好奇,为什么当移动到图形北面时,看不到物体了?其实很简单,和side: THREE.DoubleSide配置有关。
渲染面side设置
API:https://threejs.org/docs/#api/zh/materials/Material.side
语法结构:
.side : Integer
定义将要渲染哪一面 - 正面,背面或两者。 默认为THREE.FrontSide。其他选项有THREE.BackSide 和 THREE.DoubleSide。
// 双面渲染
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, side: THREE.DoubleSide });
渲染线框wireframe
之前的章节中,我们做过线框设置
我们在代码中体现一下
const material = new THREE.MeshBasicMaterial(
{
color: 0xff0000,
side: THREE.DoubleSide,
+ wireframe: true
}
);
小节
本节课,我们对几何体进行了一些较为深入的了解,知道了所有几何体继承自BufferGeometry基类,通过BufferGeometry我们也可以创建各种各样形状的几何体。因为正常开发中,一个复杂的模型几乎不会使用这种方式创建,而是直接导出其他软件开发好的模型,所以我们不做深入了解。