three.js绘制变径管道的再次改进
·
根据three.js中TubeGeometry几何体对源码做了些改动用于绘制变径管道,暂且命名为VarTubeGeometry
/**
* @author icecream
* @version 2.0.0
* @description 绘制变径管道 curve, radiusArr, 20, valuesArr, mapValuesArr, LUT
* @param curve <THREE.CatmullRomCurve3> 轨迹
* @param radiusArr <Array> [['井深'<Number>,'半径'<Number>]...] 半径数组,
* @param radialSegments <Number> 管道截面分段数
* @param valuesArr <Array> ['值域最小值'<Number>,'值域最大值'<Number>] 半径值域,
* @param mapValuesArr <Array> ['映射最小值'<Number>,'映射最大值'<Number>] 半径值域映射
* @param LUT <THREE.Lut> 根据半径给顶点上色
*/
THREE.VarTubeGeometry = class VarTubeGeometry extends THREE.BufferGeometry {
constructor(path, radiusArr, radialSegments = 8, valuesArr, mapValuesArr, lut) {
super();
const tubularSegments = radiusArr.length - 1
const wellLength = path.getLength()
this.type = 'VarTubeGeometry';
this.parameters = {
lut: lut,
path: path,
tubularSegments: tubularSegments,
radialSegments: radialSegments,
closed: closed
};
if (lut) {
lut.setMax(valuesArr[1])
lut.setMin(valuesArr[0])
}
const frames = computeFrenetFrames(path, radiusArr); // expose internals
this.tangents = frames.tangents;
this.normals = frames.normals;
this.binormals = frames.binormals; // helper variables
const vertex = new THREE.Vector3();
const normal = new THREE.Vector3();
const uv = new THREE.Vector2();
let P = new THREE.Vector3(); // buffer
const vertices = [];
const normals = [];
const uvs = [];
const colors = [];
const indices = []; // create buffer data
generateBufferData(); // build geometry
this.setIndex(indices);
this.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
this.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
this.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)); // functions
this.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
function generateBufferData() {
for (let i = 0; i <= tubularSegments; i++) {
generateSegment(i, radiusArr[i]);
} // if the geometry is not closed, generate the last row of vertices and normals
generateUVs(); // finally create faces
generateIndices();
}
function generateSegment(i, arr) {
// we use getPointAt to sample evenly distributed points from the given path
P = path.getPointAt(arr[0] / wellLength, P); // retrieve corresponding normal and binormal
const radius = THREE.Math.mapLinear(arr[1], valuesArr[0], valuesArr[1], mapValuesArr[0], mapValuesArr[1]);
const N = frames.normals[i];
const B = frames.binormals[i]; // generate normals and vertices for the current segment
var color = null;
if (lut) {
color = lut.getColor(arr[1])
}
for (let j = 0; j <= radialSegments; j++) {
const v = j / radialSegments * Math.PI * 2;
const sin = Math.sin(v);
const cos = -Math.cos(v); // normal
normal.x = cos * N.x + sin * B.x;
normal.y = cos * N.y + sin * B.y;
normal.z = cos * N.z + sin * B.z;
normal.normalize();
normals.push(normal.x, normal.y, normal.z); // vertex
if (color) {
colors.push(color.r, color.g, color.b)
}
vertex.x = P.x + radius * normal.x;
vertex.y = P.y + radius * normal.y;
vertex.z = P.z + radius * normal.z;
vertices.push(vertex.x, vertex.y, vertex.z);
}
}
function generateIndices() {
for (let j = 1; j <= tubularSegments; j++) {
for (let i = 1; i <= radialSegments; i++) {
const a = (radialSegments + 1) * (j - 1) + (i - 1);
const b = (radialSegments + 1) * j + (i - 1);
const c = (radialSegments + 1) * j + i;
const d = (radialSegments + 1) * (j - 1) + i; // faces
indices.push(a, b, d);
indices.push(b, c, d);
}
}
}
function computeFrenetFrames(curve, arrRadius) {
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
const normal = new THREE.Vector3();
const tangents = [];
const seg = radiusArr.length - 1
const normals = [];
const binormals = [];
const vec = new THREE.Vector3();
const mat = new THREE.Matrix4(); // compute the tangent vectors for each segment on the curve
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
for (let i = 0; i < arrRadius.length; i++) {
const u = arrRadius[i][0] / wellLength;
tangents[i] = curve.getTangentAt(u, new THREE.Vector3());
tangents[i].normalize();
} // select an initial normal vector perpendicular to the first tangent vector,
// and in the direction of the minimum tangent xyz component
normals[0] = new THREE.Vector3();
binormals[0] = new THREE.Vector3();
let min = Number.MAX_VALUE;
const tx = Math.abs(tangents[0].x);
const ty = Math.abs(tangents[0].y);
const tz = Math.abs(tangents[0].z);
if (tx <= min) {
min = tx;
normal.set(1, 0, 0);
}
if (ty <= min) {
min = ty;
normal.set(0, 1, 0);
}
if (tz <= min) {
normal.set(0, 0, 1);
}
vec.crossVectors(tangents[0], normal).normalize();
normals[0].crossVectors(tangents[0], vec);
binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve
for (let i = 1; i <= seg; i++) {
normals[i] = normals[i - 1].clone();
binormals[i] = binormals[i - 1].clone();
vec.crossVectors(tangents[i - 1], tangents[i]);
if (vec.length() > Number.EPSILON) {
vec.normalize();
const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
}
binormals[i].crossVectors(tangents[i], normals[i]);
} // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
return {
tangents: tangents,
normals: normals,
binormals: binormals
};
}
function generateUVs() {
for (let i = 0; i <= tubularSegments; i++) {
for (let j = 0; j <= radialSegments; j++) {
uv.x = i / tubularSegments;
uv.y = j / radialSegments;
uvs.push(uv.x, uv.y);
}
}
}
}
}
demo
var curve = new THREE.CatmullRomCurve3([
[10, 20, 15],
[16, 10, 20],
[10, 0, 35],
[45, 30, 20],
[86, 60, 63],
[93, 20, 67],
[10, 60, 55]
].map(el => new THREE.Vector3(...el)));
var radiusArr = []
valuesArr = [0, 10];
mapValuesArr = [1, 1.5];
for (var a = 0; a < curve.getLength(); a++) {
radiusArr.push([a, Math.random() * 20])
}
const LUT = new THREE.Lut()
var _geo = new THREE.VarTubeGeometry(curve, radiusArr, 20, valuesArr, mapValuesArr, LUT);
_materialData = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
vertexColors: true,
});
var _mesh = new THREE.Mesh(_geo, _materialData)
scene.add(_mesh)
效果:
一下感觉比之前的上档次了。
更多推荐
所有评论(0)