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

后端 未结 3 1275
Happy的楠姐
Happy的楠姐 2021-01-07 01:28

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 cur

3条回答
  •  伪装坚强ぢ
    2021-01-07 01:53

    I modified Kamil Kiełczewski's code and separated it into 2 classes:

    1. BarycentricBufferGeometry based on ParametricBufferGeometry
    2. BezierTriangle based on NURBSSurface

    Now it functions similar to NURBSSurface.js and is more efficient.

    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 };
    

    Example:

    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);
    

提交回复
热议问题