Following this topic, I am trying to generate a 3D curved triangle as a NURBS surface, but I don't understand how to set up my 3D points to do that.
Here is the current implementation :
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) ;
And here is the result:
I tried many different settings but can't find any working well.
Note: The white points are the edges ends ; The red points are the bezier curve middle points.
Note 2: dots[0]
refers to the point 0
in the sample picture, and so on.
Here is working snippet (and fiddle version here)
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>