three.js 入门学习
Posted 房东家的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了three.js 入门学习相关的知识,希望对你有一定的参考价值。
上一个小案例
import * as THREE from \'three\';
===================================
const width = 960;
const height = 540;
// 渲染器
const renderer = new THREE.WebGLRenderer();
//设置像素
renderer.setPixelRatio(window.devicePixelRatio);
// 设置大小
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
// 视口
const scene = new THREE.Scene();
// 摄像头
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(0, 0, 1000);
// 一个网格物体
const geometry = new THREE.BoxGeometry(400, 400, 400);
// 材质 法线网格材质(种把法向量射到RGB颜色的材质)
const material = new THREE.MeshNormalMaterial();
const box = new THREE.Mesh(geometry, material);
scene.add(box);
function animate()
box.rotation.y += 0.01;
// 渲染到页面上, 视口,摄像头
renderer.render(scene, camera);
requestAnimationFrame(animate);
animate()
准备一个画布元素
<canvas id="myCanvas"></canvas>
WebGL 渲染的渲染器
const renderer = new THREE.WebGLRenderer(
canvas: document.querySelector(\'#myCanvas\')
);
在方法中setSize()
设置大小
renderer.setSize(960, 540);
创造一个场景
创建一个场景。场景是放置 3D 对象、光源等的 3D 空间。
const scene = new THREE.Scene();
相机
此功能称为“视点”或“相机”
在 Three.js 中,THREE.PerspectiveCamera
通过传递四个信息来创建相机:视角、纵横比、绘制开始距离和绘制结束距离,作为类构造函数中的参数。
const camera = new THREE.PerspectiveCamera(45, 960 / 540,1,1000);
## 立方体
网格的显示对象创建的。要创建网格,我们需要准备两种类型:几何体(shape)和材料(material)。
BoxGeometry
使用一种来生成像立方体和长方体这样的类似盒子的形状
const geometry = new THREE.BoxGeometry(500, 500, 500);
材质
法线网格材质(种把法向量射到RGB颜色的材质)MeshNormalMaterial
const material = new THREE.MeshNormalMaterial();
使用创建的几何体和材料创建网格。让我们将创建的网格添加到场景中。
const box = new THREE.Mesh(geometry, material);
scene.add(box);
为了用 JavaScript 制作动画,有必要随着时间的推移不断调用该函数。为此,requestAnimationFrame()
请使用名为 requestAnimationFrame()
将运行每帧作为参数传递的函数。
tick();
function tick()
requestAnimationFrame(tick);
Three.js不会自动将屏幕切换到最新,所以需要写一个指令来显式更新屏幕。renderer.render()
您可以使用命令指定更新
tick();
function tick()
requestAnimationFrame(tick);
box.rotation.y += 0.01;
renderer.render(scene, camera);
创建 Three.js 对象分为三个步骤:(1) 创建材质,(2) 创建几何体,(3) 创建网格
// 球体
const geometry = new THREE.SphereGeometry(300, 30, 30);
// 物理材质的制作
const material = new THREE.MeshStandardMaterial(color: 0xFF0000);
// 网格物体
const mesh = new THREE.Mesh(geometry, material);
// 添加到场景中
scene.add(mesh);
光源
THREE.DirectionalLight 平行光
const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
directionalLight.position.set(1, 1, 1)
scene.add(directionalLight);
环境光
它可以作为一盏灯来照亮整个空间。
THREE.AmbientLight
var pointLight = new THREE.PointLight("#ccffcc");
pointLight.position.set(0,10,10);
scene.add(pointLight);
材质中使用图像
THREE.TextureLoader
使用类指定文件路径
// 图像加载器
const loader = new THREE.TextureLoader();
const texture=loader.load("/assets/img/单人主体 (3).jpg")
// 物理材质
const material2=new THREE.MeshStandardMaterial(
map: texture,
)
球体集合
const geometry = new THREE.SphereGeometry( 5, 32, 32 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000 );
const sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
长方体几何
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000 );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
平面几何
const geometry = new THREE.PlaneGeometry( 5, 20, 32 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000, side: THREE.DoubleSide );
const plane = new THREE.Mesh( geometry, material );
scene.add( plane );
圆锥几何
// 半径, 高度, 管道横截面的分段数
const geometry = new THREE.ConeGeometry( 5, 20, 32 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000 );
const cone = new THREE.Mesh( geometry, material );
scene.add( cone );
圆柱几何
// 上圆半径,下圆半径,高度,管道横截面的分段数
const geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000 );
const cylinder = new THREE.Mesh( geometry, material );
scene.add( cylinder );
甜甜圈形几何(圆环缓冲几何体)
radius
- 环面的半径,从环面的中心到管道横截面的中心。默认值是1。
tube
— 管道的半径,默认值为0.4。
radialSegments
— 管道横截面的分段数,默认值为12。
tubularSegments
— 管道的分段数,默认值为48。
arc
— 圆环的圆心角(单位是弧度),默认值为Math.PI * 2
。
const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
const material = new THREE.MeshBasicMaterial( color: 0xFF0000 );
const torus = new THREE.Mesh( geometry, material );
scene.add( torus );
相机控制
- 自动旋转
- 定位鼠标
- 鼠标拖放
相机自动绕地球圆周移动
设置相机位置会为camera
对象的属性position
分配一个数值。
const radian = rot * Math.PI / 180;
camera.position.x = 1000 * Math.sin(radian);
camera.position.z = 1000 * Math.cos(radian);
使用camera
object方法来指定原点坐标。方法是可以强制从任意位置指向指定坐标的指令。lookAt()
(0, 0, 0)
camera.lookAt(new THREE.Vector3(0, 0, 0));
围绕地球旋转相机
let rot = 0;
function animate()
// 渲染到页面上, 视口,摄像头
renderer.render(scene, camera);
// 缓动公式
rot += 0.2;
const radian = rot * Math.PI / 180;
camera.position.x = 1000 * Math.sin(radian);
camera.position.z = 1000 * Math.cos(radian);
camera.lookAt(new THREE.Vector3(0, 0, 0));
requestAnimationFrame(animate);
animate()
自动控制相机移动的THREE.OrbitControls
- 安排相机绘制圆形轨道
- 使用指针操作更改相机位置和角度
const controls = new OrbitControls( camera, renderer.domElement );
function animate()
controls.update();
材质
THREE.MeshBasicMaterial类 (网格基础材质)是一种不考虑光照的材质。由于没有阴影
THREE.MeshNormalMaterial (网格法线材质)该类是一种可视化 RGB 中正常颜色的材质
THREE.MeshLambertMaterial (网状朗伯材质) 表现无光泽度的磨砂质感的材质。因为出现了阴影,所以可以表现出深度感。一种需要阴影的材料,所以它需要光
THREE.MeshPhongMaterial 表现光泽纹理的材质
THREE.MeshToonMaterial (网状卡通材质) 可以实现类似动漫的卡通着色
THREE.MeshStandardMaterial (网格标准材料)基于物理的渲染材质
光源
环境光源
AmbientLight
类是实现环境光源的类。均匀照亮整个 3D 空间。当你想要均匀地提亮时使用它是很好的。由于无法产生阴影和投射阴影,因此仅此光源无法表现出三维效果。通常与其他灯一起使用。
颜色, 光照强度
const light = new THREE.AmbientLight(0xFFFFFF, 1.0);
scene.add(light);
平行光源
DirectionalLight
类别是在特定方向发射的光。假设光源无限远,从它发出的所有光线都是平行的。一个简单的例子是阳光。由于太阳离地球如此之远,它的位置可以被认为是无限的。从太阳到地球表面的光线是平行的。
半球形光源
HemisphereLight
类AmbientLight
与类类似,但是你可以将来自上方的光的颜色和来自下方的光的颜色分开。来自下方的光是反射光,类似于室外的光
// 天空的颜色, 地面的颜色,光强度
const light = new THREE.HemisphereLight(0x888888, 0x0000FF, 1.0);
点光源
PointLight
类是从一个点向所有方向发射的光源。一个很好的例子是裸灯泡。裸露的灯泡照亮了周围的环境。
射灯光源
SpotLight
一个类是一种光源,它从一个点沿着一个圆锥体向一个方向发射。一个很好的例子是想象舞台上的手电筒或聚光灯。您可以指定衰减率和光的方向,因此可以指定的参数很多。如果放置很多,就会产生三维效果和存在感。
颜色、光强度、距离、照明角度、背景虚化、衰减率
const light = new THREE.SpotLight(0xFFFFFF, 4, 30, Math.PI / 4, 10, 0.5);
辅助类
// 射灯光源辅助类
const lightHelper = new THREE.SpotLightHelper(light);
矩形光源
// 颜色、光强、宽度、高度
const light = new THREE.RectAreaLight(0xFFFFFF, 5.0, 10, 10);
scene.add(light);
阴影
允许物体在其他物体上投射阴影对着光源。投射阴影将改善现实。
使用此功能有四种设置。
- 在渲染器中启用阴影
- 启用光源阴影
- 设置要投射阴影的网格对象
- 设置 Mesh 对象以接收阴影
需要注意:“投射阴影”和“接收阴影”。
分别设置castShadow
属性(投射阴影的属性)和属性(接收阴影的属性)。receiveShadow
在渲染器属性shadowMap
中启用它
renderer.shadowMap.enabled = true;
启用光源的castShadow
属性。光源使用定向SpotLight
发光。PointLight
// 创建照明
const light = new THREE.SpotLight(0xFFFFFF, 2, 100, Math.PI / 4, 1);
// 在灯光上启用阴影
light.castShadow = true;
scene.add(light);
投射阴影的网格receiveShadow
启用属性。
const meshKnot = new THREE.Mesh(
new THREE.TorusKnotGeometry(3, 1, 100, 16),
new THREE.MeshStandardMaterial());
// 设置上了一层阴影
meshKnot.castShadow = true;
scene.add(meshKnot);
设置阴影大小
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
相机
相机类型
THREE.PerspectiveCamera
: 应用透视的相机THREE.OrthographicCamera
:应用平行投影的相机
正交相机
// 左,右,上,下,近截面,远截面
// new THREE.OrthographicCamera(left, right, top, bottom, near, far)
const camera = new THREE.OrthographicCamera(-480, +480, 270, -270, 1, 1000);
雾效
一种使远处的物体看起来朦胧的效果
通过设置与相机的起始和结束距离,中间的对象将以指定的颜色变暗。
// 颜色、开始距离、结束距离
scene.fog = new THREE.Fog(0x000000, 50, 2000);
应用于雾效
material.fog = true;
组
创建嵌套结构,THREE.Object3D
或使用THREE.Group
类方法add()
添加它。相反,如果要从嵌套结构中删除,remove()
请使用 方法。
const wrap = new THREE.Object3D();
wrap.add(mesh);
scene.add(wrap);
const wrap = new THREE.Group();
wrap.add(mesh);
scene.add(wrap);
世界坐标
THREE.Object3D getWorldPosition()
您可以使用类方法获取世界坐标。由于我们需要计算世界坐标,所以要计算的3D对象必须添加到场景中
const world = object3D.getWorldPosition(new THREE.Vector3());
加载模型数据
GLTF 文件
const loader = new THREE.GLTFLoader();
const gltf = loader.loadAsync(\'./models/gltf/glTF/ToyCar.gltf\');
const model = gltf.scene;
scene.add(model);
3ds 文件
const loader = new THREE.TDSLoader();
// 指定纹理路径
loader.setResourcePath(\'models/3ds/portalgun/textures/\');
// 指定 3ds 文件的路径
const object = loader.loadAsync(\'models/3ds/portalgun/portalgun.3ds\');
scene.add(object);
Collada 文件
const loader = new THREE.ColladaLoader();
const collada = await loader.loadAsync(\'./models/collada/elf/elf.dae\');
const model = collada.scene;
scene.add(model);
调整大小
window.addEventListener(\'resize\', ()=>
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
);
检查与对象的交叉点
鼠标坐标
const mouse = new THREE.Vector2();
// 注册鼠标事件
canvas.addEventListener(\'mousemove\', handleMouseMove);
// 鼠标移动事件
function handleMouseMove(event)
const element = event.currentTarget;
//画布元素上的 XY 坐标
const x = event.clientX - element.offsetLeft;
const y = event.clientY - element.offsetTop;
// 画布元素的宽度/高度
const w = element.offsetWidth;
const h = element.offsetHeight;
//在 -1 到 +1 范围内注册当前鼠标坐标
mouse.x = ( x / w ) * 2 - 1;
mouse.y = -( y / h ) * 2 + 1;
射线
const raycaster = new THREE.Raycaster();
tick();
// 每帧运行的循环事件
function tick()
// 直接从鼠标位置生成光线矢量
raycaster.setFromCamera(mouse, camera);
// 获取被射线击中的物体
const intersects = raycaster.intersectObjects(scene.children);
if(intersects.length > 0)
// 对碰撞的物体做某事
renderer.render(scene, camera);
requestAnimationFrame(tick);
显示大量粒子
// 创建形状数据
const SIZE = 3000;
// 要放置的数字
const LENGTH = 1000;
const vertices = [];
for (let i = 0; i < LENGTH; i++)
const x = SIZE * (Math.random() - 0.5);
const y = SIZE * (Math.random() - 0.5);
const z = SIZE * (Math.random() - 0.5);
vertices.push(x, y, z);
// 创建形状数据
const geometry7 = new THREE.BufferGeometry();
geometry7.setAttribute(\'position\', new THREE.Float32BufferAttribute(vertices, 3));
const material7 = new THREE.PointsMaterial(
size: 10,
color: 0xffffff,
);
const mesh7 = new THREE.Points(geometry7, material7);
scene.add(mesh7);
移动几何物体
THREE.BufferAttribute
保存顶点坐标信息。
THREE.BufferAttribute
是一个不是数组的对象,所以它的用法有点特殊。count
由于您可以获得属性中的顶点数,因此for
使用与顶点数一样多的循环语句会很好。 您可以使用 getX()
方法获取每个顶点的位置信息getY()
。getZ()
const position = mesh.geometry.attributes.position;
for (let i = 0; i < position.count; i++)
// 各顶点的XYZ坐标
const x = position.getX(i);
const y = position.getY(i);
const z = position.getZ(i);
完整点的案例
// 平面几何
const geometry = new THREE.PlaneGeometry(400, 400, 20, 20);
// 骨架线性的
const material = new THREE.MeshBasicMaterial( wireframe: true );
// 物体制作
const mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = Math.PI / 2; // x轴的角度
scene.add(mesh);
tick();
function tick()
// 几何顶点坐标信息
const position:any = mesh.geometry.attributes["position"];
for (let i = 0; i < position.count; i++)
// // 各頂点的XYZ座標
const x = position.getX(i);
const y = position.getY(i);
const z = position.getZ(i);
// 计算高度(物体的Z 坐标)
const nextZ = Math.sin(x * 0.03 + y * 0.02 + Date.now() * 0.002) * 30;
// position.setX(i, x); // 您可以省略 x 和 y,因为它们不会改变
// position.setY(i, y);
position.setZ(i, nextZ);
// 需要更新顶点
position.needsUpdate = true;
renderer.render(scene, camera);
requestAnimationFrame(tick);
对平面的顶点应用波浪式运动
通过使用 SimplexNoise 生成噪声,您可以创建类似于地面的表达式。虽然 SimplexNoise 本身不包含在 Three.js 中,但它是作为附加组件提供的
使用时,THREE.SimplexNoise
从类中创建一个实例并noise()
使用该方法。noise()
当传递一个数字作为参数时,该方法返回一个介于 -1 和 1 之间的数字。
// 初始化噪声
// 使用实例
const simplexNoise = new THREE.SimplexNoise();
// x1和y1是任意数值
const value = simplexNoise.noise(x1, y1);
使用PlaneGeometry
创建类似地面的例子
// 平行光源
const light1 = new THREE.DirectionalLight(0x3399ff, 1);
light1.position.set(1, 1, 1);
scene.add(light1);
// 平面几何
// width, height,宽度横截面, 高度横截面
const geometry = new THREE.PlaneGeometry(1000, 1000, 80, 80);
const material = new THREE.MeshLambertMaterial(
// 顶点着色
flatShading: true,
// 定义将要渲染哪一面, 两面
side: THREE.DoubleSide,
);
const mesh = new THREE.Mesh(geometry, material);
// 地面旋转角度
mesh.rotation.x = Math.PI / 2;
scene.add(mesh);
// 初始化噪声
const simplexNoise = new SimplexNoise();
tick();
function tick()
// 几何顶点坐标信息
const position:any = mesh.geometry.attributes[\'position\'];
for (let i = 0; i < position.count; i++)
// 各頂点的XYZ座標
const x = position.getX(i);
const y = position.getY(i);
const time = Date.now() * 0.0001;
// 计算高度, 主要是z坐标
const nextZ = simplexNoise.noise(x * 0.002 + time, y * 0.001 + time) * 150;
position.setZ(i, nextZ);
// // 需要更新顶点
position.needsUpdate = true;
renderer.render(scene, camera);
requestAnimationFrame(tick);
setXYZ()
您可以使用单独的setX()
、setY()
和方法setZ()
来做同样的事情
烟花效果
const LENGTH = 1000;
const vertices = [];
/**
* 存储粒子速度的序列
* @type THREE.Vector3[]
*/
const speeds:any = [];
for (let i = 0; i < LENGTH; i++)
// 顶点 初期坐标
vertices.push(0, 0, 0);
// 定义粒子的速度
const x = 2 * (Math.random() - 0.5);
const y = 2 * (Math.random() - 0.5);
const z = 2 * (Math.random() - 0.5);
speeds.push(new THREE.Vector3(x, y, z));
// 缓冲存储器
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(\'position\', new THREE.Float32BufferAttribute(vertices, 3));
const material = new THREE.PointsMaterial(
size: 2,
color: 0xffffff,
);
// 点
const mesh = new THREE.Points(geometry, material);
scene.add(mesh)
tick();
function tick()
// 几何顶点坐标信息
const position:any = mesh.geometry.attributes[\'position\'];
for (let i = 0; i < position.count; i++)
// 各頂点のXYZ座標
const x = position.getX(i);
const y = position.getY(i);
const z = position.getZ(i);
// speeds序列是速度用序列。存储了各个顶点的速度
const nextX = x + speeds[i].x;
const nextY = y + speeds[i].y;
const nextZ = z + speeds[i].z;
// 新坐标
position.setXYZ(i, nextX, nextY, nextZ);
// 原点からの距離を計算
const length = new THREE.Vector3(x, y, z).length();
// 如果超过了一定的范围
if (length > 100)
// 回到原点
position.setXYZ(i, 0, 0, 0);
// 需要更新顶点
position.needsUpdate = true;
renderer.render(scene, camera);
requestAnimationFrame(tick);
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬
Three.JS程序化建模入门
在这个教程中,我们将学习如何在 three.js 中制作自己的自定义程序几何,以及如何利用程序几何来制作吸引人的低多边形地形。
学习本教程需要你具备以下基本技能:
- 基本熟悉three.js应用的结构
- 基础编程知识
- 3d几何的基本知识(顶点,uvs,法线等)
- 任何具有 webgl 兼容网络浏览器的机器
- 关于如何运行 javascript 代码的知识(本地或使用类似jsfiddle的东西)
如果你阅读了我之前的教程,会看到three.js 提供了许多不同的内置基本几何类型,再加上你可以轻松地将自己的3D 模型导入到three.js 中以及为什么需要制作你自己的几何?好吧,一个答案当然是为了好玩!如果你问我这就是你需要的唯一答案。
但是,对于更务实的读者来说,如果你想构建每次运行程序时都独一无二的几何图形,那么生成自己的几何图形也是有益的。或者,如果几何图形需要适应你事先不知道的某些约束,并且使其不适合存储。
1、基本几何体
首先,我们从一个基本Geometry实例开始。在上一个教程中,我们查看了许多不同类型的几何图形,例如SphereGeometry和CylinderGeometry,但出于我们的目的,我们需要一个普通的空Geometry对象。
var geometry = new THREE.Geometry();
var material = new THREE.MeshBasicMaterial();
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
现在,我们开始制作实际的几何图形。Three.js 至少需要两件东西才能使用Geometry。首先它需要vertices,如你所知,3D 图形是通过光栅化三角形绘制的。所以我们至少需要一个三角形来绘制一些东西(严格来说这不是真的,但就我们的目的而言,这是一个公平的假设)。我们需要 3D 空间中的 3 个点。其次,我们需要告诉 three.js 如何使用 three.js 将这些点连接在一起。Face3需要 3个点并将它们连接在一起以绘制一个三角形。现在让我们来看看。首先,让我们定义一个简单的三角形。
geometry.vertices.push(new THREE.Vector3( 1, -1, 0));
geometry.vertices.push(new THREE.Vector3( 0, 1, 0));
geometry.vertices.push(new THREE.Vector3(-1, -1, 0));
geometry.faces.push(new THREE.Face3(0, 1, 2));
请注意直接访问顶点数组和面数组是多么容易?这是three.js中Geometry类的好处。如果我们使用 BufferGeometry这将更加复杂。但是BufferGeometry速度稍快,因此你面临权衡取舍。
在我们继续之前还有几点。注意我们的面是如何取三个整数的。这些是我们三个顶点中每个顶点的数组索引。我们在事务中定义它们的顺序。在多个面中引用同一个顶点并没有错。事实上,这允许我们重用一些顶点并节省一些空间。让我们看看它现在是如何工作的。
geometry.vertices.push(new THREE.Vector3( 1, -1, 0));
geometry.vertices.push(new THREE.Vector3(-1, 1, 0));
geometry.vertices.push(new THREE.Vector3(-1, -1, 0));
geometry.vertices.push(new THREE.Vector3( 1, 1, 0));
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 3, 1));
正如在此处看到的,我们现在已经定义了两个三角形,但只添加了一个顶点。这是在不占用顶点数组中太多额外空间的情况下定义对象的好方法。
这是在 three.js 中创建自定义几何图形的基本方法,我们可以以任何我们想要的方式扩展它。可以使用可以设计的任何方法添加顶点和面。但是还有一些有用的考虑因素。为了充分利用 three.js 的特性,我们希望我们的几何体具有法线、顶点颜色和 uv。
2、法线
法线是我们最容易添加的属性。一旦我们定义了一个顶点数组,就可以通过调用computeVertexNormals或computeFlatNormals来来为我们计算法线。让我们看看两者:
geometry.computeVertexNormals();
geometry.normalsNeedUpdate = true;
顶点法线计算在三角形面上插值的每个顶点法线。顶点法线通常用于高保真外观模型。这是因为插值使法线看起来像是在三角形面上发生了变化,这使你可以计算更准确的三角形面上的光照。
geometry.computeFaceNormals();
geometry.normalsNeedUpdate = true;
对于面法线,整个三角形的法线保持不变。当计算照明时,我们将能够非常清楚地看到三角形本身。当做出特定的风格选择来展示三角形本身时,面法线很好。
两者都很容易调用。为了说明差异,让我们稍微弯曲一下我们的四边形,使三角形面向不同的方向,如下所示:
geometry.vertices.push(new THREE.Vector3( 1, -1, 0));
geometry.vertices.push(new THREE.Vector3(-1, 1, 0));
geometry.vertices.push(new THREE.Vector3(-1, -1, -1));
geometry.vertices.push(new THREE.Vector3( 1, 1, -1));
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 3, 1));
还有一件事。让我们将材质更改为 MeshNormalMaterial,以便我们可以说明法线的样子。
现在我们可以看到,当我们使用顶点法线时,它看起来像这样:
注意远角的颜色不同,而中间的两个顶点颜色相同?
现在让我们看一下带有面法线的弯曲四边形:
每个三角形都突出并具有不同的颜色。
3、UV
与法线密切相关的是 uv。将 2D 纹理映射到 3D 对象时使用 Uv。不幸的是,给定顶点数组,three.js 不会为我们计算 uv。因此,在构建几何体时,我们需要在顶点数组旁边构建一个 uv 数组。
three.js 中的 uv 数组是在Geometry的faceVertexUvs属性下访问的。faceVertexUvs是一个 uv 数组的数组。它们又是长度等于 faces 数组的数组,包含三个 uv,一个对应面中的每个顶点。现在这听起来很复杂,但实际上它非常简单。我们将顶点定义为法线,然后当我们定义我们的面时,我们会多做一步。
geometry.faces.push(new THREE.Face3(0, 1, 2));
var uvs1 = [new THREE.Vector2(1, 0), new THREE.Vector2(0, 1), new THREE.Vector2(0, 0)];
geometry.faceVertexUvs[0].push(uvs1); //remember faceVertexUvs is an array of arrays
geometry.faces.push(new THREE.Face3(0, 3, 1));
var uvs2 = [new THREE.Vector2(1, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1)];
geometry.faceVertexUvs[0].push(uvs2);
这样做可以让我们Geometry纹理化,我们可以轻松地在three.js 中应用纹理,我们只需将Texture添加到map材质中的属性。
var material = new THREE.MeshBasicMaterial(map: texture);
我们通过从磁盘加载一个纹理来获得一个纹理(虽然我们也可以创建自己的,但这对于本教程来说太高级了)。
var texture = new THREE.TextureLoader().load('uv_texture.jpg');
我们得到的是:
4、顶点颜色
几何的最后一个共同属性是顶点颜色。有时我们不想使用纹理或复杂的着色器。有时我们想提前存储一个顶点的颜色,然后简单地从中读取我们的材料。幸运的是,three.js 为我们提供了一种简单的方法来提前提供顶点颜色。
我们可以通过两种方式设置它们。第一种是将单一颜色传递给Color我们的Face3定义,从而为整个三角形提供单一颜色。或者我们可以传入一个颜色数组,以便每个顶点都有自己的颜色,该颜色被插在面上。
//Face3 takes three vertex indices, a normal and then a color
//We aren't worried about normals right now so we pass null in instead
geometry.faces.push(new THREE.Face3(0, 1, 2, null, new THREE.Color(0.9, 0.7, 0.75)));
geometry.faces.push(new THREE.Face3(0, 3, 1, null, new THREE.Color(0.6, 0.85, 0.7)));
如你所见,每个三角形都有自己的颜色。现在,如果我们希望每个顶点都有自己的颜色,我们只需创建一个包含三种颜色的数组并将其传入而不是单一的Color。
请注意,可以为每个面使用不同的数组,我只是选择不这样做。
你可以看到颜色在顶点面上混合。如果想在简单对象上平滑过渡颜色,这将变得特别有用。
5、Low-Poly地形
为了利用我们在上面学到的一切,我们将编写一个小脚本来生成一个不规则的平面,该平面被Perlin噪声抵消,从而产生一些漂亮的 Low-Poly 风格的地形。
上面没有提到的制作自己Geometry的额外好处之一是你只需要处理自己绝对需要的数据量。如果Geometry不需要法线,请不要浪费时间计算它们。如果不想添加纹理,请不要在 uvs 上浪费空间。
我们将绘制一小块 Low-Poly 风格的地形,因此我们不需要 uvs 或顶点颜色,我们将只使用每个面的法线并将单一颜色传递到材质中。
6、制作自己的平面
当 three.js 提供了如此简单的方法来制作我们自己的平面时,这似乎是一种浪费。但我们的平面将有一个显着的不同。我们不会统一传播我们的点。通常,平面由点网格定义,就像在 three.js 中一样。但是我们将把我们的点分散一点,这样当我们把平面变成地形时,它看起来就不会被锁定在网格中。
首先,让我们来一张平面图。我们将从一个常规的网格式平面开始,然后随着我们的进步移动到不同的东西。你已经了解了如何定义单个四边形。现在我们将把它扩展到更大的东西。毕竟,平面只是一堆排列在一起的四边形。让我们从一段代码开始,然后我们将它分开。
makeTile = function(size, res)
geometry = new THREE.Geometry();
for (var i = 0; i <= res; i++)
for (var j = 0; j <= res; j++)
var z = j * size;
var x = i * size;
var position = new THREE.Vector3(x, 0, z);
var addFace = (i > 0) && (j > 0);
this.makeQuad(geometry, position, addFace, res + 1);
geometry.computeFaceNormals();
geometry.normalsNeedUpdate = true;
return geometry;
;
该函数makeTile有两个参数size和res。size定义构建平面的每个四边形的大小,res定义平面的宽度。很容易。然后我们有一个嵌套循环,我们在二维中计数 0 到res。很简单,你现在可以看到网格形成的开始。
魔法发生在循环内。我们将变量x和z设置为我们的顶点位置,我们通过将四边形位置乘以四边形i, j的大小来计算。我们将其保存到Vector3.
但是addFace呢。这实际上是一个聪明的小技巧。你看,我们的第一行顶点不足以形成四边形。所以在我们平面的边缘,我们告诉我们的脚本不要形成三角形,但是一旦我们进入第二行,我们就可以开始组合顶点来形成四边形和三角形。
然后我们调用makeQuad并将它传递给我们在这个循环中计算的所有信息。简单的!还有一件事,一旦我们完成循环,我们告诉three.js 为我们计算法线。然后我们返回新的几何对象以在程序的主要部分中使用。太棒了!现在让我们看看makeQuad function.
makeQuad = function(geometry, position, addFace, verts)
geometry.vertices.push(position);
if (addFace)
var index1 = geometry.vertices.length - 1;
var index2 = index1 - 1;
var index3 = index1 - verts;
var index4 = index1 - verts - 1;
geometry.faces.push(new THREE.Face3(index2, index3, index1));
geometry.faces.push(new THREE.Face3(index2, index4, index3));
;
好的,首先我们将顶点添加到顶点数组中。这很容易理解。如果addFace是 False,那么我们停止。否则,我们计算周围顶点的位置并形成两个面,就像我们上面所做的那样。只是现在我们必须计算索引位置。这就是它的全部!使用这两个函数,你最终应该得到如下结果:
7、为平面添加一些噪声
为了让事情变得有趣,我们将向顶点添加一些噪声以稍微改变高度。如果你需要复习在 javascript 中使用噪声,请查看此教程。
var position = new THREE.Vector3(x, noise.perlin2(x, z)*size, z);
我将噪声乘以大小,以便它与你的平面大小很好地缩放。如果你使用 MeshNormalMaterial你的平面现在看起来像这样:
其实有点无聊。你可以看到它只是一个顶点被上下推的网格。让我们通过更多地分解顶点位置使其更有趣。我们将通过调用 Math.random 随机偏移x和位置来做到这一点。
var z = j * size + (Math.random() - 0.5) * size;
var x = i * size + (Math.random() - 0.5) * size;
现在,如果将高度重置为 0,你可以看到三角形的变化程度。
如果你再次添加高度。
这就是创建自己的小地形所需要做的一切!尝试使用不同的设置,特别是尝试使四边形非常小并且分辨率大。x然后尝试通过乘以不同大小的数字来稍微混合噪声z以缩放噪声。你应该能够创建所有不同类型的 Low-Poly 地形。对于示例,这里是使用MeshPhongMaterial.
8、结束语
我知道上面有很多东西。程序几何是一个非常深奥的话题。但是现在你有了开始在three.js 中进一步探索它的工具!希望你已经学会:
- 如何使用 three.js 的Geometry类来制作自己的自定义几何图形,包括:
- 如何定义自己的uvs,
- 如何定义自己的顶点颜色,
- 以及如何使用 three.js 计算法线
- 如何自动创建抖动平面
原文链接:用 Three.js 学习程序几何 — BimAnt
以上是关于three.js 入门学习的主要内容,如果未能解决你的问题,请参考以下文章