三维空间中绘制点线面UV贴图,万能的BufferGeometry(three.js实战4)
Posted 点燃火柴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三维空间中绘制点线面UV贴图,万能的BufferGeometry(three.js实战4)相关的知识,希望对你有一定的参考价值。
使用BufferGeometry
1. 序言
three.js 中提供了一系列绘制几何体的类,如BoxGeometry、SphereGeometry,PlaneGeometry、CircleGeometry、CylinderGeometry 等,使用这些类,可以快速创建对应的几何体,three.js同时提供了对应的 BoxBufferGeometry、SphereBufferGeometry,PlaneBufferGeometry、CircleBufferGeometry、CylinderBufferGeometry等直接使用缓存的构建几何体的 xxxBufferGeometry 类簇,这些类簇相比于 xxxGeometry 类簇,可以有效减少向 GPU 传输数据所需的开销,极大提高效率,但如果提供的这些几何都不能满足需求怎么办,这时就需要用到万能的 BufferGeometry ,通过它可以向缓存中传输几何体的顶点坐标、面索引、顶点颜色、顶点法向量、顶点UV甚至是自定义属性, 使用自定义属性和着色器材质配合使用强大到无所不能
这里顺便说说ShaderMaterial-着色器材质和RawShaderMaterial-原始着色器材质,它们都支持GLSL语言编写的shader,不同之处是ShaderMaterial会把three.js内置attributes和uniforms一起传给shader,而RawShaderMaterial 不会 向shader中传递任何内置属性
2. 如何使用BufferGeometry
2.1 创建BufferGeometry对象
创建BufferGeometry对象与创建其他THREE对象一样,非常简单,如下
const bufferGeom = new THREE.BufferGeometry();
2.2 向BufferGeometry对象添加属性
这一步其实就是初始化绘制几何体所需的顶点坐标、面索引、顶点颜色、顶点法向量、顶点UV等信息,这些属性three.js都是内置属性,属性名已定死,例如:position,color,normal,index等
这一步可向BufferGeometry对象添加任意属性,我们以position属性和index为例说明一下
- 添加position属性
首先通过Float32Array类创建序列化数组,示例中是每3个数构成一个点,然后使用BufferAttribute类创建于BufferGeometry对象关联的存储缓存,最后使用setAttribute方法关联缓存,示例代码如下
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0 //point3
]);
//设置顶点信息,第二个参数3表示三个数确定一个顶点
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
其他一些相关的属性color,normal等以及自定义属性的添加都可以参数以上方式
- 添加index属性
index 属性与其他属性添加有一些不一样,序列化数组的类型是Uint16Array,不是通过setAttribute设置属性,而是直接设置到BufferGeometry实例的index属性下,使用BufferAttribute创建缓存数据是第二个参数为1,代表一个数就是一个索引信息
const indexs = new Uint16Array([
0, 1, 2,
3, 0
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
2.3 创建Mesh
创建mesh需要两个参数一个几何体一个材质,几何体通过上述两步创建,创建材质时,如果设置的顶点颜色属性,且需要使用自定义的也是着色,要将材质的vertexColors属性设置为 THREE.VertexColors。表示使用缓存中的颜色着色
//创建材质
const material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors, //使用缓存中的颜色
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(bufferGeom, material);
3. BufferGeometry使用示例
3.1 绘制点
首先创建BufferGeometry实例,然后创建存放顶信息的序列化数组,接着设置position属性,最后创建PointsMaterial材质并使用该材质创建Points对象,就可以完成点的绘制
function drawPointByBufferGeometry() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0 //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//创建点材质
const material = new THREE.PointsMaterial({
color: 'red',
size: 2
});
const mesh = new THREE.Points(bufferGeom, material);
scene.add(mesh);
}
绘制效果如下:
3.2 绘制线
- 创建BufferGeometry实例
使用new THREE.BufferGeometry()
创建bufferGeom实例 - 设置position,color,index属性
position,color属性设置与之前的一样,请注意设置index属性时,index序列化数组的值为0, 1, 2, 3, 0 最终还要回到原点否则最后一条线无法绘制 - 创建Mesh
这里使用LineBasicMaterial创建材质,同样将材质的vertexColors属性设置为 THREE.VertexColors。表示使用缓存中的颜色着色,并使用Line创建线
function drawLineByBufferGeometry() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0 //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//初始化存放颜色信息的序列化数组
const colors = new Float32Array([
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
0.0, 0.5, 0.5
]);
//设置颜色信息
bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const indexs = new Uint16Array([
0, 1, 2,
3, 0
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
//创建材质
const material = new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors, //使用缓存中的颜色
side: THREE.DoubleSide
});
const mesh = new THREE.Line(bufferGeom, material);
scene.add(mesh);
}
绘制的结果如上如,会发现绘制的线和使用WebGL绘制线一样。会根据顶点颜色作插值计算
3.3 绘制面
- 创建BufferGeometry实例
使用new THREE.BufferGeometry()
创建bufferGeom实例 - 设置position,color,index属性
position,color属性设置与之前的一样,需要注意的是设置index属性时,index序列化数组的值为0, 1, 2, 0, 2, 3 表示使用索引为0,1,2的点绘制一个三角面和使用索引为0,2,3的点绘制另一个三角面 - 创建Mesh
使用MeshBasicMaterial创建材质,也需要将材质的vertexColors属性设置为 THREE.VertexColors。表示使用缓存中的颜色着色,然后创建Mesh对象
function drawPlaneByBufferGeometry() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0 //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//初始化存放颜色信息的序列化数组
const colors = new Float32Array([
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
0.0, 0.5, 0.5
]);
//设置颜色信息
bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const indexs = new Uint16Array([
0, 1, 2,
0, 2, 3
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
//创建材质
const material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors, //使用缓存中的颜色
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(bufferGeom, material);
scene.add(mesh);
}
绘制结果如上图,绘制了一个梯形,颜色同样根据顶点颜色进行了线性插值
3.4 绘制自定义UV贴图
其他的步骤与绘制面一样,不同的是需要添加uv属性,添加uv属性前先来捋一下uv坐标与顶点坐标的关系
上图是uv坐标与梯形顶点坐标的关系图,只需要按这个顺添加uv属性即可,需要注意的数uv坐标是一个Vector2类型,设置的时候需要注意,创建BufferAttribute时第二个参数为2,具体实现代码如下:
function drawPlaneByBufferGeometryUV() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0, //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//初始化存放颜色信息的序列化数组
const colors = new Float32Array([
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
]);
//设置颜色信息
bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const indexs = new Uint16Array([
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
const uvs = new Uint16Array([
0, 1,
1, 1,
1, 0,
0, 0,
]);
//设置UV
bufferGeom.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
const planetTexture = new THREE.TextureLoader().load("../assets/textures/test.png");
//创建材质
const material = new THREE.MeshBasicMaterial({
map: planetTexture,
vertexColors: THREE.VertexColors, //使用缓存中的颜色
side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(bufferGeom, material);
scene.add(mesh);
}
上图为代码执行结果,可以看到狗狗贴图被正确加载到梯形上了
4. 示例代码
<!DOCTYPE html>
<html>
<head>
<title>使用BufferGeometry</title>
<script type="text/javascript" src="../three/build/three.js"></script>
<script type="text/javascript" src="../three/examples/js/controls/OrbitControls.js"></script>
<script type="text/javascript" src="../three/examples/js/libs/stats.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output"></div>
<div id="WebGL-output"></div>
<script type="text/javascript">
var scene, camera, renderer, arrowLineTexture, flowingLineTexture, stats, controls, clock;
function initScene() {
scene = new THREE.Scene();
//用一张图加载为纹理作为场景背景
scene.background = new THREE.TextureLoader().load("../assets/textures/starry-deep-outer-space-galaxy.jpg");
}
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(20, 30, 50);
camera.lookAt(new THREE.Vector3(0, 0, 0));
}
function initLight() {
//添加环境光
const ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight('#fff')
directionalLight.position.set(30, 30, 30).normalize()
scene.add(directionalLight)
//添加聚光灯
const spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
scene.add(spotLight);
}
function initModel() {
//drawPointByBufferGeometry();
//drawLineByBufferGeometry();
//drawPlaneByBufferGeometry();
drawPlaneByBufferGeometryUV();
initPlane();
}
function drawPlaneByBufferGeometryUV() {
//创建BufferGeometry实例
const bufferGeom = new THREE.BufferGeometry();
//初始化存放顶点信息的序列化数组
const positions = new Float32Array([
-5.0, 3.0, 0.0, //point0
5.0, 3.0, 0.0, //point1
6.0, -3.0, 0.0, //point2
-6.0, -3.0, 0.0, //point3
]);
//设置顶点信息
bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
//初始化存放颜色信息的序列化数组
const colors = new Float32Array([
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
0.5, 0.3, 0.6,
]);
//设置颜色信息
bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const indexs = new Uint16Array([
0, 1, 2,
0, 2, 3,
4, 5, 6,
4, 6, 7
]);
//设置画面的索引
bufferGeom.index = new THREE.BufferAttribute(indexs, 1);
const uvs = new Uint16Array([
0, 1,
1, 1,
1, 0,
0, 0,
]);
//设置UV
bufferGeom.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
const planetTexture = 以上是关于三维空间中绘制点线面UV贴图,万能的BufferGeometry(three.js实战4)的主要内容,如果未能解决你的问题,请参考以下文章