繁体   English   中英

如何从 Three.js 中的 3D 点创建 3D 三次贝塞尔曲线三角形?

[英]How can I create a 3D cubic-bezier curved triangle from 3D points in Three.js?

按照这个主题,我试图生成一个 3D 弯曲三角形作为 NURBS 曲面,但我不明白如何设置我的 3D 点来做到这一点。

这是当前的实现:

var edges = this.getEdges(), // An edge is a line following 4 dots as a bezier curve.
    dots = self.getDotsFromEdges(edges), // Get all dots in order for building the surface.

    ctrlPoints = [ // Is generated only once before, but copy-pasted here for this sample code.
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ],
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ],
        [
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1),
            new THREE.Vector4(0, 0, 0, 1)
        ]
    ],

    nc,
    deg1 = ctrlPoints.length - 1,
    knots1 = [],
    deg2 = 3,                           // Cubic bezier
    knots2 = [0, 0, 0, 0, 1, 1, 1, 1],  // <-
    cpts,
    nurbs ;

nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(0) ;
nc = ctrlPoints.length ;
while (nc-- > 0) knots1.push(1) ;


// The following seems to be the problem... :

cpts = ctrlPoints[0] ;
cpts[0].set(dots[0].x, dots[0].y, dots[0].z, 1) ;
cpts[1].set(dots[1].x, dots[1].y, dots[1].z, 1) ;
cpts[2].set(dots[2].x, dots[2].y, dots[2].z, 1) ;
cpts[3].set(dots[3].x, dots[3].y, dots[3].z, 1) ;

cpts = ctrlPoints[1] ;
cpts[0].set(dots[6].x, dots[6].y, dots[6].z, 1) ;
cpts[1].set(dots[5].x, dots[5].y, dots[5].z, 1) ;
cpts[2].set(dots[4].x, dots[4].y, dots[4].z, 1) ;
cpts[3].set(dots[3].x, dots[3].y, dots[3].z, 1) ;

cpts = ctrlPoints[2] ;
cpts[0].set(dots[6].x, dots[6].y, dots[6].z, 1) ;
cpts[1].set(dots[7].x, dots[7].y, dots[7].z, 1) ;
cpts[2].set(dots[8].x, dots[8].y, dots[8].z, 1) ;
cpts[3].set(dots[0].x, dots[0].y, dots[0].z, 1) ;



nurbs = new THREE.NURBSSurface(deg1, deg2, knots1, knots2, ctrlPoints) ;

this.mesh.geometry.dispose() ;
this.mesh.geometry = new THREE.ParametricBufferGeometry(function(u, v, target) {
    return nurbs.getPoint(u, v, target) ;
}, 10, 10) ;

结果如下:

在此处输入图片说明

我尝试了许多不同的设置,但找不到任何工作正常。

注:白点为边端; 红点是贝塞尔曲线的中点。
注2: dots[0]指的是样张图片中的点0 ,以此类推。

这是工作片段(和小提琴版本在这里

 const PI = Math.PI, sin = Math.sin, cos = Math.cos, W = 480, H = 400, log = console.log, DISTANCE = 100 ; let renderer = new THREE.WebGLRenderer({ canvas : document.querySelector('canvas'), antialias : true, alpha : true }), camera = new THREE.PerspectiveCamera(25, W/H), scene = new THREE.Scene(), center = new THREE.Vector3(0, 0, 0), pts = [] ; renderer.setClearColor(0x000000, 0) ; renderer.setSize(W, H) ; // camera.position.set(-48, 32, 80) ; camera.position.set(0, 0, DISTANCE) ; camera.lookAt(center) ; function createPoint(x, y, z, color) { let pt = new THREE.Mesh( new THREE.SphereGeometry(1, 10, 10), new THREE.MeshBasicMaterial({ color }) ) ; pt.position.set(x, y, z) ; pt.x = x ; pt.y = y ; pt.z = z ; pts.push(pt) ; scene.add(pt) ; } function createEdge(pt1, pt2, pt3, pt4) { let curve = new THREE.CubicBezierCurve3( pt1.position, pt2.position, pt3.position, pt4.position ), mesh = new THREE.Mesh( new THREE.TubeGeometry(curve, 8, 0.5, 8, false), new THREE.MeshBasicMaterial({ color : 0x203040 }) ) ; scene.add(mesh) ; } /////////////////////////////////////////////// // POINTS // createPoint(-16, -8, 0, 0xcc0000) ; // RED createPoint(-8, -12, 0, 0x999999) ; createPoint(8, -12, 0, 0x888888) ; createPoint(16, -8, 0, 0x00cc00) ; // GREEN createPoint(12, -6, -8, 0x777777) ; createPoint(8, 6, -8, 0x666666) ; createPoint(0, 12, 0, 0x0000cc) ; // BLUE createPoint(-8, 6, -8, 0x555555) ; createPoint(-12, -6, -8, 0x444444) ; // EDGES // createEdge(pts[0], pts[1], pts[2], pts[3]) ; createEdge(pts[3], pts[4], pts[5], pts[6]) ; createEdge(pts[6], pts[7], pts[8], pts[0]) ; // SURFACE // let ctrlPoints = [ [ new THREE.Vector4(pts[0].x, pts[0].y, pts[0].z, 1), new THREE.Vector4(pts[1].x, pts[1].y, pts[1].z, 1), new THREE.Vector4(pts[2].x, pts[2].y, pts[2].z, 1), new THREE.Vector4(pts[3].x, pts[3].y, pts[3].z, 1) ], [ new THREE.Vector4(pts[6].x, pts[6].y, pts[6].z, 1), new THREE.Vector4(pts[5].x, pts[5].y, pts[5].z, 1), new THREE.Vector4(pts[4].x, pts[4].y, pts[4].z, 1), new THREE.Vector4(pts[3].x, pts[3].y, pts[3].z, 1) ], [ new THREE.Vector4(pts[6].x, pts[6].y, pts[6].z, 1), new THREE.Vector4(pts[7].x, pts[7].y, pts[7].z, 1), new THREE.Vector4(pts[8].x, pts[8].y, pts[8].z, 1), new THREE.Vector4(pts[0].x, pts[0].y, pts[0].z, 1) ] ], nc, deg1 = ctrlPoints.length - 1, knots1 = [], deg2 = 3, // Cubic bezier knots2 = [0, 0, 0, 0, 1, 1, 1, 1], // <- cpts, nurbs ; nc = ctrlPoints.length ; while (nc-- > 0) knots1.push(0) ; nc = ctrlPoints.length ; while (nc-- > 0) knots1.push(1) ; nurbs = new THREE.NURBSSurface(deg1, deg2, knots1, knots2, ctrlPoints) ; let surfaceMesh = new THREE.Mesh( new THREE.ParametricBufferGeometry(function(u, v, target) { return nurbs.getPoint(u, v, target) ; }, 10, 10), new THREE.MeshBasicMaterial({ side : THREE.DoubleSide, opacity : 0.9, transparent : true, color : 0x405060 }) ) ; scene.add(surfaceMesh) ; /////////////////////////////////////////////// let azimut = 0, pitch = 90, isDown = false, prevEv ; function down(de) { prevEv = de ; isDown = true ; } function move(me) { if (!isDown) return ; azimut -= (me.clientX - prevEv.clientX) * 0.5 ; azimut %= 360 ; if (azimut < 0) azimut = 360 - azimut ; pitch -= (me.clientY - prevEv.clientY) * 0.5 ; if (pitch < 1) pitch = 1 ; if (pitch > 180) pitch = 180 ; prevEv = me ; let theta = pitch / 180 * PI, phi = azimut / 180 * PI, radius = DISTANCE ; camera.position.set( radius * sin(theta) * sin(phi), radius * cos(theta), radius * sin(theta) * cos(phi), ) ; camera.lookAt(center) ; renderer.render(scene, camera) ; } function up(ue) { isDown = false ; } renderer.domElement.onmousedown = down ; window.onmousemove = move ; window.onmouseup = up ; renderer.render(scene, camera) ;
 body { display: flex; flex-direction: row; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1c2228; overflow: hidden; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script> <script src="https://threejs.org/examples/js/curves/NURBSUtils.js"></script> <script src="https://threejs.org/examples/js/curves/NURBSCurve.js"></script> <script src="https://threejs.org/examples/js/curves/NURBSSurface.js"></script> <canvas></canvas>

这是绘制贝塞尔三角形的方法(下面的代码段) - 算法在Geometry类中。 您在constructor设置的三角形一侧的三角形数。 在代码中,我在算法/计算( Geometry类)和绘图代码( Draw类)之间进行了硬分离。

对于贝塞尔三角形,我们需要使用 10 个控制点(9 个用于边缘,1 个用于“平面”),如下图所示(此处为src ):

在此处输入图片说明

在这段代码中,我们不使用法线和B点名称更改为P(如: b003p003 )。 我们使用以下公式(对于三次贝塞尔三角形n=3

在此处输入图片说明

其中p_ijk是控制点(对于上面的 n=3,sum 有 10 个元素,所以我们有 10 个控制点),其中 B^n_ijk(r,s,t) 是为 i,j,k>=0 和 i 定义的 Bernstein 多项式+j+k=n

在此处输入图片说明

或在其他情况下为 0。 r,s,t 的重心坐标域(其中 r,s,t 是来自 [0, 1] 和 r+s+t=1 的实数),其中r =(r=1, s=t=0 ), s =(s=1, r=t=0), t =(t=1, r=s=0) 看起来如下(黑点 - 我们将每个三角形的边分成 5 部分 - 但我们可以改变它到任何数字)

在此处输入图片说明

我们在方法barycentricCoords(n)计算黑域点的规则位置,并在Geometry类的方法genTrianglesIndexes(n)中定义哪个点创建哪些三角形。 但是,您可以将此点的位置和密度更改为任何(三角形内)以获得不同的表面三角形分割。 下面是在 2D 中显示域的片段

 let pp= ((s='.myCanvas',c=document.querySelector(s),ctx=c.getContext('2d'),id=ctx.createImageData(1,1)) => (x,y,r=0,g=0,b=0,a=255)=>(id.data.set([r,g,b,a]),ctx.putImageData(id, x, y),c))() cr=[255,0,0,255]; cg=[0,255,0,255]; cb=[0,0,255,255]; w=400; h=400; const p1=[0,h-1]; const p2=[w-1,h-1]; const p3=[w/2,0]; mainTriangle=[p1,p2,p3]; //mainTriangle.map(p => pp(...p,...cr)); let n=5; let points=[]; function calcPoint(p1,p2,p3,r,s,t) { const px=p1[0]*r + p2[0]*s + p3[0]*t; const py=p1[1]*r + p2[1]*s + p3[1]*t; return [px,py]; } // barycentric coordinates r,s,t of point in triangle // the points given from triangle bottom to top line by line // first line has n+1 pojnts, second has n, third n-1 // coordinates has property r+s+t=1 function barycentricCoords(n) { let rst=[]; for(let i=0; i<=n; i++) for(let j=0; j<=ni; j++) { s=(j/n); t=(i/n); r=1-st; rst.push([r,s,t]); } return rst; } // Procedure calc indexes for each triangle from // points list (in format returned by barycentricCoords(n) ) function genTrianglesIndexes(n) { let st=0; let m=n; let triangles=[]; for(let j=n; j>0; j--) { for(let i=0; i<m; i++) { triangles.push([st+i, st+i+1, st+m+i+1]); if(i<m-1) triangles.push([st+i+1, st+m+i+2, st+m+i+1 ]); } m--; st+=j+1; } return triangles; } function drawLine(p1,p2,c) { let n=Math.max(Math.abs(p1[0]-p2[0]),Math.abs(p1[1]-p2[1]))/2; for(let i=0; i<=n; i++) { let s=i/n; let x=p1[0]*s + p2[0]*(1-s); let y=p1[1]*s + p2[1]*(1-s); pp(x,y,...c); } } function drawTriangle(p1,p2,p3,c) { drawLine(p1,p2,c); drawLine(p2,p3,c); drawLine(p3,p1,c); } // Bernstein Polynomial, i+j+k=n function bp(n,i,j,k, r,s,t) { const f=x=>x?f(x-1)*x:1 // number fractional f(4)=1*2*3*4=24 return r**i * s**j * t**k * f(n) / (f(i)*f(j)*f(k)); } //drawTriangle(...mainTriangle,cr); // draw main triangle let bar=barycentricCoords(n); // each domain point barycentric coordinates let ti=genTrianglesIndexes(n); // indexes in bar for each triangle // triangles calculated to cartesian coordinate system let triangles = ti.map(tr=> tr.map(x=>calcPoint(...mainTriangle,...bar[x]) ) ); triangles.map(t => drawTriangle(...t, cg)); // domain points calculated to cartesian coordinate system (for draw) let dp = bar.map(x=> calcPoint(...mainTriangle,...x) ); // draw black dots (4 pixels for each dot) dp.map(x=> pp(x[0],x[1]) ) dp.map(x=> pp(x[0],x[1]-1) ) dp.map(x=> pp(x[0]-1,x[1]) ) dp.map(x=> pp(x[0]-1,x[1]-1) )
 <canvas class="myCanvas" width=400 height=400 style="background: white"></canvas>

下面是带有 3D 贝塞尔三次三角形的最终片段(算法从Geometry类中的genTrianglesForCubicBezierTriangle(n, controlPoints)方法开始)- (注意:这很奇怪,但在首次运行后的 SO 片段中,您将看不到线条,您需要重新加载页面并再次运行以查看三角形线)

 /////////////////////////////////////////////////////// // THIS PART/CLASS IS FOR ALGORITHMS AND CALCULATIONS /////////////////////////////////////////////////////// class Geometry { constructor() { this.init(); } init(n) { this.pts = [ { x:-16, y: -8, z:0, color:0xcc0000 }, // p003 RED { x:8, y:-12, z:0, color:0x888888 }, // p201 { x:-8, y:-12, z:0, color:0x999999 }, // p102 { x:16, y:-8, z:0, color:0x00cc00 }, // p300 GREEN { x:12, y:-6, z:-8, color:0x777777 }, // p210 { x:8, y:6, z:-8, color:0x666666 }, // p120 { x:0, y:12, z:0, color:0x0000cc }, // p030 BLUE { x:-8, y:6, z:-8, color:0x555555 }, // p021 { x:-12, y:-6, z:-8, color:0x444444 }, // p012 { x:0, y:0, z:8, color:0xffff00 }, // p111 YELLOW (plane control point) ]; this.mainTriangle = [this.pts[0],this.pts[3],this.pts[6]]; this.bezierCurvesPoints = [ [ this.pts[0], this.pts[2], this.pts[1], this.pts[3] ], [ this.pts[3], this.pts[4], this.pts[5], this.pts[6] ], [ this.pts[6], this.pts[7], this.pts[8], this.pts[0] ] ]; //this.triangles = [ // { points: [this.pts[0], this.pts[1], this.pts[2]], color: null }, // wireframe // { points: [this.pts[1], this.pts[2], this.pts[3]], color: 0xffff00 } // yellow //] this.triangles = this.genTrianglesForCubicBezierTriangle(25, this.pts); } // n = number of triangles per triangle side genTrianglesForCubicBezierTriangle(n, controlPoints) { let bar= this.barycentricCoords(n); // domain in barycentric coordinats let ti = this.genTrianglesIndexes(n); // indexes of triangles (in bar array) let val= bar.map(x => this.calcCubicBezierTriangleValue(controlPoints,...x)); // Calc Bezier triangle vertex for each domain (bar) point let tv= ti.map(tr=> tr.map(x=>val[x]) ); // generate triangles using their indexes (ti) and val return tv.map(t=> ({ points: t, color: null}) ); // map triangles to proper format (color=null gives wireframe) // Generate domain triangles //let td= ti.map(tr=> tr.map(x=>this.calcPointFromBar(...this.mainTriangle,...bar[x]) ) ); //this.trianglesDomain = td.map(t=> ({ points: t, color: null}) ); } // more: https://www.mdpi.com/2073-8994/8/3/13/pdf // Bézier Triangles with G2 Continuity across Boundaries // Chang-Ki Lee, Hae-Do Hwang and Seung-Hyun Yoon calcCubicBezierTriangleValue(controlPoints, r,s,t ) { let p = controlPoints, b=[]; b[0]= this.bp(0,0,3,r,s,t); // p[0]=p003 b[1]= this.bp(2,0,1,r,s,t); // p[1]=p201 b[2]= this.bp(1,0,2,r,s,t); // p[2]=p102 b[3]= this.bp(3,0,0,r,s,t); // p[3]=p300 b[4]= this.bp(2,1,0,r,s,t); // p[4]=p210 b[5]= this.bp(1,2,0,r,s,t); // p[5]=p120 b[6]= this.bp(0,3,0,r,s,t); // p[6]=p030 b[7]= this.bp(0,2,1,r,s,t); // p[7]=p021 b[8]= this.bp(0,1,2,r,s,t); // p[8]=p012 b[9]= this.bp(1,1,1,r,s,t); // p[9]=p111 let x=0, y=0, z=0; for(let i=0; i<=9; i++) { x+=p[i].x*b[i]; y+=p[i].y*b[i]; z+=p[i].z*b[i]; } return { x:x, y:y, z:z }; } // Bernstein Polynomial degree n, i+j+k=n bp(i,j,k, r,s,t, n=3) { const f=x=>x?f(x-1)*x:1 // number fractional f(4)=1*2*3*4=24 return r**i * s**j * t**k * f(n) / (f(i)*f(j)*f(k)); } coordArrToObj(p) { return { x:p[0], y:p[1], z:p[2] } } // Calc cartesian point from barycentric coords system calcPointFromBar(p1,p2,p3,r,s,t) { const px=p1.x*r + p2.x*s + p3.x*t; const py=p1.y*r + p2.y*s + p3.y*t; const pz=p1.z*r + p2.z*s + p3.z*t; return { x:px, y:py, z:pz}; } // barycentric coordinates r,s,t of point in triangle // the points given from triangle bottom to top line by line // first line has n+1 pojnts, second has n, third n-1 // coordinates has property r+s+t=1 barycentricCoords(n) { let rst=[]; for(let i=0; i<=n; i++) for(let j=0; j<=ni; j++) { let s=(j/n); let t=(i/n); let r=1-st; rst.push([r,s,t]); } return rst; } // Procedure calc indexes for each triangle from // points list (in format returned by barycentricCoords(n) ) genTrianglesIndexes(n) { let st=0; let m=n; let triangles=[]; for(let j=n; j>0; j--) { for(let i=0; i<m; i++) { triangles.push([st+i, st+i+1, st+m+i+1]); if(i<m-1) triangles.push([st+i+1, st+m+i+2, st+m+i+1 ]); } m--; st+=j+1; } return triangles; } // This procedures are interface for Draw class getPoints() { return this.pts } getTriangles() { return this.triangles } getBezierCurves() { return this.bezierCurvesPoints; } } /////////////////////////////////////////////// // THIS PART IS FOR DRAWING /////////////////////////////////////////////// // init tree js and draw geometry objects class Draw { constructor(geometry) { this.init(geometry); } initGeom() { this.geometry.getPoints().forEach(p=> this.createPoint(p)); this.geometry.getTriangles().forEach(t=> this.createTriangle(t)); this.geometry.getBezierCurves().forEach(c=> this.createEdge(...c)); } init(geometry) { this.geometry = geometry; this.W = 480, this.H = 400, this.DISTANCE = 100 ; this.PI = Math.PI, this.renderer = new THREE.WebGLRenderer({ canvas : document.querySelector('canvas'), antialias : true, alpha : true }), this.camera = new THREE.PerspectiveCamera(25, this.W/this.H), this.scene = new THREE.Scene(), this.center = new THREE.Vector3(0, 0, 0), this.pts = [] ; this.renderer.setClearColor(0x000000, 0) ; this.renderer.setSize(this.W, this.H) ; // camera.position.set(-48, 32, 80) ; this.camera.position.set(0, 0, this.DISTANCE) ; this.camera.lookAt(this.center) ; this.initGeom(); this.azimut = 0; this.pitch = 90; this.isDown = false; this.prevEv = null; this.renderer.domElement.onmousedown = e => this.down(e) ; window.onmousemove = e => this.move(e) ; window.onmouseup = e => this.up(e) ; this.renderer.render(this.scene, this.camera) ; } createPoint(p) { let {x, y, z, color} = p; let pt = new THREE.Mesh( new THREE.SphereGeometry(1, 10, 10), new THREE.MeshBasicMaterial({ color }) ) ; pt.position.set(x, y, z) ; pt.x = x ; pt.y = y ; pt.z = z ; this.pts.push(pt) ; this.scene.add(pt) ; } createTriangle(t) { var geom = new THREE.Geometry(); var v1 = new THREE.Vector3(t.points[0].x, t.points[0].y, t.points[0].z); var v2 = new THREE.Vector3(t.points[1].x, t.points[1].y, t.points[1].z); var v3 = new THREE.Vector3(t.points[2].x, t.points[2].y, t.points[2].z); geom.vertices.push(v1); geom.vertices.push(v2); geom.vertices.push(v3); let material = new THREE.MeshNormalMaterial({wireframe: true,}) if(t.color != null) material = new THREE.MeshBasicMaterial( { color: t.color, side: THREE.DoubleSide, } ); geom.faces.push( new THREE.Face3( 0, 1, 2 ) ); geom.computeFaceNormals(); var mesh= new THREE.Mesh( geom, material); this.scene.add(mesh) ; } createEdge(pt1, pt2, pt3, pt4) { let curve = new THREE.CubicBezierCurve3( new THREE.Vector3(pt1.x, pt1.y, pt1.z), new THREE.Vector3(pt2.x, pt2.y, pt2.z), new THREE.Vector3(pt3.x, pt3.y, pt3.z), new THREE.Vector3(pt4.x, pt4.y, pt4.z), ), mesh = new THREE.Mesh( new THREE.TubeGeometry(curve, 8, 0.5, 8, false), new THREE.MeshBasicMaterial({ color : 0x203040 }) ) ; this.scene.add(mesh) ; } down(de) { this.prevEv = de ; this.isDown = true ; } move(me) { if (!this.isDown) return ; this.azimut -= (me.clientX - this.prevEv.clientX) * 0.5 ; this.azimut %= 360 ; if (this.azimut < 0) this.azimut = 360 - this.azimut ; this.pitch -= (me.clientY - this.prevEv.clientY) * 0.5 ; if (this.pitch < 1) this.pitch = 1 ; if (this.pitch > 180) this.pitch = 180 ; this.prevEv = me ; let theta = this.pitch / 180 * this.PI, phi = this.azimut / 180 * this.PI, radius = this.DISTANCE ; this.camera.position.set( radius * Math.sin(theta) * Math.sin(phi), radius * Math.cos(theta), radius * Math.sin(theta) * Math.cos(phi), ) ; this.camera.lookAt(this.center) ; this.renderer.render(this.scene, this.camera) ; } up(ue) { this.isDown = false ; } } // SYSTEM SET UP let geom= new Geometry(); let draw = new Draw(geom);
 body { display: flex; flex-direction: row; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1c2228; overflow: hidden; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script> <canvas></canvas>

小提琴版本在这里 我把信息放在评论中,但算法很复杂,如果你有问题 - 作为评论提出 - 我会回答。

我修改了Kamil Kiełczewski的代码并将其分为 2 个类:

  1. 基于ParametricBufferGeometry BarycentricBufferGeometry
  2. BezierTriangle基于NURBSSurface

现在它的功能类似于NURBSSurface.js,并且效率更高。

BarycentricBufferGeometry.js

import { BufferGeometry, Float32BufferAttribute, Vector3 } from './three.module.js';

class BarycentricBufferGeometry extends BufferGeometry {

    constructor(func, slices) {

        super();

        this.type = 'BezierTriangleGeometry';

        this.parameters = {
            func: func,
            slices: slices
        };

        // buffers
        const indices = [];
        const vertices = [];
        const normals = [];
        const uvs = [];

        const EPS = 0.00001;

        const normal = new Vector3();

        const p0 = new Vector3(), p1 = new Vector3();
        const pu = new Vector3(), pv = new Vector3();

        if (func.length < 3) {

            console.error('THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.');

        }

        // generate vertices, normals and uvs
        for (let i = 0; i <= slices; i++) {

            for (let j = 0; j <= slices - i; j++) {

                const u = j / slices;
                const v = i / slices;

                // vertex
                func(u, v, p0);
                vertices.push(p0.x, p0.y, p0.z);

                // normal
                // approximate tangent vectors via finite differences
                if (u - EPS >= 0) {

                    func(u - EPS, v, p1);
                    pu.subVectors(p0, p1);

                } else {

                    func(u + EPS, v, p1);
                    pu.subVectors(p1, p0);

                }

                if (v - EPS >= 0) {

                    func(u, v - EPS, p1);
                    pv.subVectors(p0, p1);

                } else {

                    func(u, v + EPS, p1);
                    pv.subVectors(p1, p0);

                }

                // cross product of tangent vectors returns surface normal
                normal.crossVectors(pu, pv).normalize();
                normals.push(normal.x, normal.y, normal.z);

                // uv
                uvs.push(u, v);

            }

        }

        // generate indices
        let st = 0;
        let m = slices;

        for (let j = slices; j > 0; j--) {

            for (let i = 0; i < m; i++) {

                const a = st + i;
                const b = st + i + 1;
                const c = st + i + 1 + m;

                indices.push(a, b, c);

                if (i < m - 1)
                    indices.push(st + i + 1, st + m + i + 2, st + m + i + 1);
            }

            m = m - 1;
            st += j + 1;
        }

        // build geometry
        this.setIndex(indices);
        this.setAttribute('position', new Float32BufferAttribute(vertices, 3));
        this.setAttribute('normal', new Float32BufferAttribute(normals, 3));
        this.setAttribute('uv', new Float32BufferAttribute(uvs, 2));

    }
}

// BarycentricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
;


export { BarycentricBufferGeometry };

BezierTriangle.js

class BezierTriangle {

    constructor(controlPoints) {

        this.controlPoints = controlPoints;
    }

    static bp(i, j, k, r, s, t, n = 3) {
        const f = x => x ? f(x - 1) * x : 1;
        return r ** i * s ** j * t ** k * f(n) / (f(i) * f(j) * f(k));
    }

    static calcSurfacePoint(p, u, v, target) {

        const t = 1 - u - v;
        let b = [];

        b[0] = BezierTriangle.bp(0, 0, 3, u, v, t);
        b[1] = BezierTriangle.bp(1, 0, 2, u, v, t);
        b[2] = BezierTriangle.bp(2, 0, 1, u, v, t);
        b[3] = BezierTriangle.bp(3, 0, 0, u, v, t);
        b[4] = BezierTriangle.bp(2, 1, 0, u, v, t);
        b[5] = BezierTriangle.bp(1, 2, 0, u, v, t);
        b[6] = BezierTriangle.bp(0, 3, 0, u, v, t);
        b[7] = BezierTriangle.bp(0, 2, 1, u, v, t);
        b[8] = BezierTriangle.bp(0, 1, 2, u, v, t);
        b[9] = BezierTriangle.bp(1, 1, 1, u, v, t);

        let x = 0,
            y = 0,
            z = 0;

        for (let i = 0; i < 10; i++) {
            x += p[i].x * b[i];
            y += p[i].y * b[i];
            z += p[i].z * b[i];
        }

        target.set(x, y, z);
    }

    getPoint(u, v, target) {

        BezierTriangle.calcSurfacePoint(this.controlPoints, u, v, target);
    }
}


export { BezierTriangle };

例子:

import * as THREE from './three.module.js';

import { BarycentricBufferGeometry } from './BarycentricBufferGeometry.js';
import { BezierTriangle } from './BezierTriangle.js';


//setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, .01, 10000);
camera.position.set(2, 2, 6)

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);


// bezier triangle points
const points = [
    { x: 0, y: 0, z: 0, c: 'red' },

    { x: 0, y: 1, z: 0, c: 'grey' },
    { x: 0, y: 2, z: 0, c: 'grey' },
    { x: 0, y: 3, z: 1, c: 'green' },

    { x: 1, y: 3, z: 1, c: 'grey' },
    { x: 2, y: 3, z: 1, c: 'grey' },
    { x: 3, y: 3, z: 2, c: 'blue' },

    { x: 2, y: 2, z: 0, c: 'grey' },
    { x: 1, y: 1, z: 0, c: 'grey' },

    { x: 1, y: 2, z: 0, c: 'yellow' },
];

// add some colored spheres to help identify points
points.forEach(p => {
    const sphere = new THREE.Mesh(
        new THREE.SphereBufferGeometry(.1, 32, 32),
        new THREE.MeshBasicMaterial({ color: p.c ? p.c : 'white' })
    );
    sphere.position.set(p.x, p.y, p.z);
    scene.add(sphere);
});

// draw bezier triangle
const triangle = new BezierTriangle(points);

function getSurfacePoint(u, v, target) {
    return triangle.getPoint(u, v, target);
}

const geometry = new BarycentricBufferGeometry(getSurfacePoint, 3);
const material = new THREE.MeshBasicMaterial({ color: 'gold', wireframe: true });
const mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

renderer.render(scene, camera);

在你的代码使用NURBSSurface功能从NURBSSurface.js文件,该功能使用NURBSUtils.calcSurfacePoint从功能NURBSUtils.js文件。 但是calcSurfacePoint计算标准 NUBRB 表面的点,其中参数来自矩形 (u,v) wiki

您不会以这种方式生成“3D 三次贝塞尔三角形” - 为此,您需要编写自己的代码,该代码将使用贝塞尔三角形公式(其中输入参数是Barycentric_coordinate_system中的三角形点)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM