title: How can I create a 3D cubic-bezier curved triangle from 3D points in Three.js?
body: <p>Following <a href="https://stackoverflow.com/questions/54573790/how-can-i-create-a-3d-surface-from-3d-points-in-three-js/54574556">this topic</a>, 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.</p>
  
  <p>Here is the current implementation :</p>
  
  <pre><code>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],  // &lt;-
      cpts,
      nurbs ;
  
  nc = ctrlPoints.length ;
  while (nc-- &gt; 0) knots1.push(0) ;
  nc = ctrlPoints.length ;
  while (nc-- &gt; 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) ;
  </code></pre>
  
  <p>And here is the result:</p>
  
  <p><a href="https://i.stack.imgur.com/LhCul.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/LhCul.png" alt="enter image description here"></a></p>
  
  <p>I tried many different settings but can't find any working well.</p>
  
  <p><em>Note: The white points are the edges ends ; The red points are the bezier curve middle points.</em><br/>
  <em>Note 2: <code>dots[0]</code> refers to the point <code>0</code> in the sample picture, and so on.</em></p>
  
  <p>Here is working snippet (and fiddle version <a href="https://jsfiddle.net/Totjoss/v0dkztyw/" rel="nofollow noreferrer">here</a>) </p>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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],  // &lt;-
      cpts,
      nurbs ;
  
  nc = ctrlPoints.length ;
  while (nc-- &gt; 0) knots1.push(0) ;
  nc = ctrlPoints.length ;
  while (nc-- &gt; 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 &lt; 0) azimut = 360 - azimut ;
      
    pitch -= (me.clientY - prevEv.clientY) * 0.5 ;
      if (pitch &lt; 1) pitch = 1 ;
      if (pitch &gt; 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) ;</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background: #1c2228;
    overflow: hidden;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/curves/NURBSUtils.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/curves/NURBSCurve.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/curves/NURBSSurface.js"&gt;&lt;/script&gt;
  
  &lt;canvas&gt;&lt;/canvas&gt;</code></pre>
  </div>
  </div>
  </p>