07 Three.js几何体顶点相关概念

625 阅读7分钟

1 物体Mesh和几何体Geometry

物体Mesh包含几何体Geometry和材质Material

image.png

网格模型Mesh其实就一个一个三角形(面)拼接构成。使用网格模型Mesh渲染几何体geometry,就是几何体所有顶点坐标三个为一组,构成一个三角形,多组顶点构成多个三角形,就可以用来模拟表示物体的表面。

三角形

1.1 缓冲类型几何体BufferGeometry

threejs的长方体BoxGeometry、球体SphereGeometry等几何体都是基于BufferGeometry类构建的,BufferGeometry是一个没有任何形状的空几何体,可以通过BufferGeometry自定义任何几何形状,具体一点说就是自定义顶点数据

//创建一个空的几何体对象
const geometry = new THREE.BufferGeometry(); 

2 几何体的attributes属性

image.png

image.png

attributes存储几何体相关的属性,如:

  • normal:法线(法向量)数据geometry.attributes.normal—— 根据光照方向,好确认折射角
  • position:顶点三维位置坐标 geometry.attributes.position
  • uv:顶点二维UV坐标 geometry.attributes.uv

2-1 法线 geometry.attributes.normal

image.png

数学上的法线概念,比如一个平面,法线的就是该平面的垂线,如果是光滑曲面,一点的法线就是该点切面的法线。

2-1-1 Three.js中法线

Three.js中法线是通过顶点定义,默认情况下,每个顶点都有一个法线数据,就像每一个顶点都有一个位置数据。

// 矩形平面,无索引,两个三角形,6个顶点
// 每个顶点的法线数据和顶点位置数据一一对应
const normals = new Float32Array([
    0, 0, 1, //顶点1法线( 法向量 )
    0, 0, 1, //顶点2法线
    0, 0, 1, //顶点3法线
    0, 0, 1, //顶点4法线
    0, 0, 1, //顶点5法线
    0, 0, 1, //顶点6法线
]);
// 设置几何体的顶点法线属性.attributes.normal
geometry.attributes.normal = new THREE.BufferAttribute(normals, 3); 

2-2 几何体顶点位置geometry.attributes.position

2-2-1 BufferAttribute定义几何体顶点数据

通过javascript类型化数组Float32Array创建一组xyz坐标数据用来表示几何体的顶点坐标。

//类型化数组创建顶点数据
const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    50, 0, 0, //顶点2坐标
    0, 100, 0, //顶点3坐标
    0, 0, 10, //顶点4坐标
    0, 0, 100, //顶点5坐标
    50, 0, 10, //顶点6坐标
]);

通过threejs的属性缓冲区对象BufferAttribute表示threejs几何体顶点数据。

// 创建属性缓冲区对象
//3个为一组,表示一个顶点的xyz坐标
const attribue = new THREE.BufferAttribute(vertices, 3); 

2-2-2 设置几何体顶点.attributes.position

通过geometry.attributes.position设置几何体顶点位置属性的值BufferAttribute

// 设置几何体attributes属性的位置属性
geometry.attributes.position = attribue;

2-3 顶点UV坐标geometry.attributes.uv

所有的图像文件都是二维的一个平面。水平方向是U,垂直方向是V,通过这个平面的,二维的UV坐标系。我们可以定位图像上的任意一个像素。

2-3-1 纹理贴图Texture

纹理贴图就是图片,将纹理贴图应用到几何体的面,或者作为几何体每个面的反射/折射贴图

// load a texture, set wrap mode to repeat 
const texture = new THREE.TextureLoader().load( "textures/water.jpg" ); 
texture.wrapS = THREE.RepeatWrapping; 
texture.wrapT = THREE.RepeatWrapping; 
texture.repeat.set( 4, 4 );

2-3-2 在多边形表面上定位纹理贴图

顶点UV坐标的作用是从纹理贴图上提取像素映射到网格模型Mesh的几何体表面上

多边形为了贴图额外引进了一个UV坐标,以便把多边形的顶点和图象文件上的像素对应起来,这样才能在多边形表面上定位纹理贴图。所以说多边形的顶点除了具有三维的空间坐标外。还具有二维的UV坐标。

UV 这里是指u,v纹理贴图坐标的简称(它和空间模型的X, Y, Z轴是类似的). 它定义了图片上每个点的位置的信息. 这些点与3D模型是相互联系的, 以决定表面纹理贴图的位置.

UV就是将纹理贴图上每一个点精确对应到模型物体的表面. 在点与点之间的间隙位置由软件进行图像光滑插值处理. 这就是所谓的UV贴图.

2-3-3 纹理贴图UV坐标范围

顶点UV坐标可以在0~1.0之间任意取值,纹理贴图左下角对应的UV坐标是(0,0)右上角对应的坐标(1,1)

2-3-4 自定义顶点UVgeometry.attributes.uv

顶点UV坐标geometry.attributes.uv和顶点位置坐标geometry.attributes.position是一一对应的,

UV顶点坐标你可以根据需要在0~1之间任意设置,具体怎么设置,要看你想把图片的哪部分映射到Mesh的几何体表面上。

/**纹理坐标0~1之间随意定义*/
const uvs = new Float32Array([
    0, 0, //图片左下角
    1, 0, //图片右下角
    1, 1, //图片右上角
    0, 1, //图片左上角
]);
// 设置几何体attributes属性的uv属性
geometry.attributes.uv = new THREE.BufferAttribute(uvs, 2); //2个为一组,表示一个顶点的纹理坐标

2-3-5 获取纹理贴图四分之一

获取纹理贴图左下角四分之一部分的像素值

const uvs = new Float32Array([
    0, 0, 
    0.5, 0, 
    0.5, 0.5, 
    0, 0.5, 
]);

3. BufferGeometry创建矩形实例

一个矩形平面,可通过两个三角形拼接而成。而且两个三角形有两个顶点的坐标是重合的。

image.png

const vertices = new Float32Array([
    0, 0, 0, //顶点1坐标
    80, 0, 0, //顶点2坐标
    80, 80, 0, //顶点3坐标

    0, 0, 0, //顶点4坐标   和顶点1位置相同
    80, 80, 0, //顶点5坐标  和顶点3位置相同
    0, 80, 0, //顶点6坐标
]);
// 创建几何体
// const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
const cubeGeometry = new THREE.BufferGeometry()
const vertices = new Float32Array([
  1, 1, 1, 1, 1, -1, -1, 1, 1, // 每三个代表一个顶点,三个顶点组成一个面

  -1, 1, 1, 1, 1, -1, -1, 1, -1
])
cubeGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)

3-1 自定义矩形效果

image.png

import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 1、创建场景
const scene = new THREE.Scene()
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
// 设置相机位置
camera.position.set(0, 0, 10)
scene.add(camera)

// 添加物体
// 创建几何体
// const cubeGeometry = new THREE.BoxGeometry(1, 1, 1)
const cubeGeometry = new THREE.BufferGeometry()
const vertices = new Float32Array([
  1, 1, 1, 1, 1, -1, -1, 1, 1,

  -1, 1, 1, 1, 1, -1, -1, 1, -1
])
cubeGeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3))
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
// 将几何体添加到场景中
scene.add(cube)
console.log(cube)
console.log(cubeGeometry)

// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement)

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)

function render() {
  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

render()

4、场景中循环添加多个随机三角形实例

4-1 Three.js颜色对象Color

颜色对象有三个属性,分别为.r.g.b,表示颜色RGB的三个分量。

image.png

//empty constructor - will default white 
const color1 = new THREE.Color(); 
//Hexadecimal color (recommended) 
const color2 = new THREE.Color( 0xff0000 ); 
//RGB string 
const color3 = new THREE.Color("rgb(255, 0, 0)"); 
const color4 = new THREE.Color("rgb(100%, 0%, 0%)"); 
//X11 color name - all 140 color names are supported. 
//Note the lack of CamelCase in the name 
const color5 = new THREE.Color( 'skyblue' ); 
//HSL string 
const color6 = new THREE.Color("hsl(0, 100%, 50%)"); 
//Separate RGB values between 0 and 1 
const color7 = new THREE.Color( 1, 0, 0 );

4-2 Material材质设置透明度

image.png

image.png

const cubeMaterial = new THREE.MeshBasicMaterial({
    // color: color,
    transparent: true,
    opacity: 0.5
  })

4-3 半透明三角形效果

1.gif

import * as THREE from 'three'
import { Color } from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 1、创建场景
const scene = new THREE.Scene()
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
)
// 设置相机位置
camera.position.set(0, 0, 10)
scene.add(camera)

// 添加物体
// 创建几何体
for (let i = 0; i < 50; i++) {
  const cubeGeometry = new THREE.BufferGeometry()
  const verticesArray = new Float32Array(9) // 必须告诉Float32Array缓冲区设置9个值
  // 一个面三个顶点,一个顶点需要3个值,因此一个面共需要9个值
  for (let j = 0; j < 9; j++) {
    // 顶点坐标的三个坐标值在-5 到 5之间
    verticesArray[j] = Math.random() * 10 - 5
  }
  cubeGeometry.setAttribute(
    'position',
    new THREE.BufferAttribute(verticesArray, 3)
  )
  // 每一个三角形颜色随机
  const color = new THREE.Color(Math.random(), Math.random(), Math.random())
  const cubeMaterial = new THREE.MeshBasicMaterial({
    color: color,
    transparent: true,
    opacity: 0.5
  })
  // 根据几何体和材质创建物体
  const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
  // 将几何体添加到场景中
  scene.add(cube)
}

// 初始化渲染器
const renderer = new THREE.WebGLRenderer()
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement)

// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)

// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)

function render() {
  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

render()