07-THREE.JS 各种形状的几何图形

<!DOCTYPE html>

<html>

<head>
    <title>Example 02.04 - Geometries</title>
    <script src="https://cdn.bootcss.com/three.js/r67/three.js"></script>
    <script type="text/javascript" src="../libs/ParametricGeometries.js"></script>
    <script type="text/javascript" src="../libs/ConvexGeometry.js"></script>
    <script src="https://cdn.bootcss.com/stats.js/r10/Stats.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/dat-gui/0.7.3/dat.gui.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">

    // 初始化
    function init() {

        var stats = initStats();

        // 创建一个场景
        var scene = new THREE.Scene();

        // 创建一个相机
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // 创建渲染器
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // 创建地面
        var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // 旋转地面
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // 把地面添加到场景中去
        scene.add(plane);

        // 相机对准场景
        camera.position.x = -50;
        camera.position.y = 30;
        camera.position.z = 20;
        camera.lookAt(new THREE.Vector3(-10, 0, 0));

        // 添加自然光
        var ambientLight = new THREE.AmbientLight(0x090909);
        scene.add(ambientLight);

        // 添加聚光灯
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 40, 50);
        spotLight.castShadow = true;
        scene.add(spotLight);

        // 添加形状
        addGeometries(scene);

        // 生成的渲染到DOM元素中去
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        
        var step = 0;


        render();


        function addGeometries(scene) {
            var geoms = [];

            geoms.push(new THREE.CylinderGeometry(1, 4, 4));

            // 基本立体型
            geoms.push(new THREE.BoxGeometry(2, 2, 2));

            // 圆形
            geoms.push(new THREE.SphereGeometry(2));

            //用于生成二十面体几何的类
            geoms.push(new THREE.IcosahedronGeometry(4));

            //几何/凸面
            var points = [
                new THREE.Vector3(2, 2, 2),
                new THREE.Vector3(2, 2, -2),
                new THREE.Vector3(-2, 2, -2),
                new THREE.Vector3(-2, 2, 2),
                new THREE.Vector3(2, -2, 2),
                new THREE.Vector3(2, -2, -2),
                new THREE.Vector3(-2, -2, -2),
                new THREE.Vector3(-2, -2, 2)
            ];
            geoms.push(new THREE.ConvexGeometry(points));

            // create a lathgeometry
            //http://en.wikipedia.org/wiki/Lathe_(graphics)
            var pts = [];//points array - the path profile points will be stored here
            var detail = .1;//half-circle detail - how many angle increments will be used to generate points
            var radius = 3;//radius for half_sphere
            for (var angle = 0.0; angle < Math.PI; angle += detail)//loop from 0.0 radians to PI (0 - 180 degrees)
                pts.push(new THREE.Vector3(Math.cos(angle) * radius, 0, Math.sin(angle) * radius));//angle/radius to x,z
            geoms.push(new THREE.LatheGeometry(pts, 12));

            //面包圈
            geoms.push(new THREE.OctahedronGeometry(3));

            // create a geometry based on a function
            geoms.push(new THREE.ParametricGeometry(THREE.ParametricGeometries.mobius3d, 20, 10));

            //
            geoms.push(new THREE.TetrahedronGeometry(3));

            geoms.push(new THREE.TorusGeometry(3, 1, 10, 10));

            geoms.push(new THREE.TorusKnotGeometry(3, 0.5, 50, 20));

            var j = 0;
            for (var i = 0; i < geoms.length; i++) {
                var cubeMaterial = new THREE.MeshLambertMaterial({wireframe: true, color: Math.random() * 0xffffff});

                var materials = [

                    new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff, shading: THREE.FlatShading}),
                    new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})

                ];

                var mesh = THREE.SceneUtils.createMultiMaterialObject(geoms[i], materials);
                mesh.traverse(function (e) {
                    e.castShadow = true
                });

                mesh.position.x = -24 + ((i % 4) * 12);
                mesh.position.y = 4;
                mesh.position.z = -8 + (j * 12);

                if ((i + 1) % 4 == 0) j++;
                scene.add(mesh);
            }

        }

        function render() {
            stats.update();
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init


</script>
</body>
</html>
ParametricGeometries.js  
/*
 * @author zz85
 *
 * Experimenting of primitive geometry creation using Surface Parametric equations
 *
 */


THREE.ParametricGeometries = {

    klein: function (v, u) {
        u *= Math.PI;
        v *= 2 * Math.PI;

        u = u * 2;
        var x, y, z;
        if (u < Math.PI) {
            x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(u) * Math.cos(v);
            z = -8 * Math.sin(u) - 2 * (1 - Math.cos(u) / 2) * Math.sin(u) * Math.cos(v);
        } else {
            x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(v + Math.PI);
            z = -8 * Math.sin(u);
        }

        y = -2 * (1 - Math.cos(u) / 2) * Math.sin(v);

        return new THREE.Vector3(x, y, z);
    },

    plane: function (width, height) {

        return function(u, v) {
            var x = u * width;
            var y = 0;
            var z = v * height;

            return new THREE.Vector3(x, y, z);
        };
    },

    mobius: function(u, t) {

        // flat mobius strip
        // http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
        u = u - 0.5;
        var v = 2 * Math.PI * t;

        var x, y, z;

        var a = 2;
        x = Math.cos(v) * (a + u * Math.cos(v/2));
        y = Math.sin(v) * (a + u * Math.cos(v/2));
        z = u * Math.sin(v/2);
        return new THREE.Vector3(x, y, z);

    },

    mobius3d: function(u, t) {

        // volumetric mobius strip
        u *= Math.PI;
        t *= 2 * Math.PI;

        u = u * 2;
        var phi = u / 2;
        var major = 2.25, a = 0.125, b = 0.65;
        var x, y, z;
        x = a * Math.cos(t) * Math.cos(phi) - b * Math.sin(t) * Math.sin(phi);
        z = a * Math.cos(t) * Math.sin(phi) + b * Math.sin(t) * Math.cos(phi);
        y = (major + x) * Math.sin(u);
        x = (major + x) * Math.cos(u);
        return new THREE.Vector3(x, y, z);
    }

};


/*********************************************
 *
 * Parametric Replacement for TubeGeometry
 *
 *********************************************/

THREE.ParametricGeometries.TubeGeometry = function(path, segments, radius, segmentsRadius, closed, debug) {

    this.path = path;
    this.segments = segments || 64;
    this.radius = radius || 1;
    this.segmentsRadius = segmentsRadius || 8;
    this.closed = closed || false;
    if (debug) this.debug = new THREE.Object3D();


    var scope = this,

        tangent, normal, binormal,

        numpoints = this.segments + 1,

        x, y, z, tx, ty, tz, u, v,

        cx, cy, pos, pos2 = new THREE.Vector3(),
        i, j, ip, jp, a, b, c, d, uva, uvb, uvc, uvd;

    var frames = new THREE.TubeGeometry.FrenetFrames(path, segments, closed),
        tangents = frames.tangents,
        normals = frames.normals,
        binormals = frames.binormals;

    // proxy internals
    this.tangents = tangents;
    this.normals = normals;
    this.binormals = binormals;



    var ParametricTube = function(u, v) {
        v *= 2 * Math.PI;

        i = u * (numpoints - 1);
        i = Math.floor(i);

        pos = path.getPointAt(u);

        tangent = tangents[i];
        normal = normals[i];
        binormal = binormals[i];

        if (scope.debug) {

            scope.debug.add(new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff));
            scope.debug.add(new THREE.ArrowHelper(normal, pos, radius, 0xff0000));
            scope.debug.add(new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00));

        }

        cx = -scope.radius * Math.cos(v); // TODO: Hack: Negating it so it faces outside.
        cy = scope.radius * Math.sin(v);

        pos2.copy(pos);
        pos2.x += cx * normal.x + cy * binormal.x;
        pos2.y += cx * normal.y + cy * binormal.y;
        pos2.z += cx * normal.z + cy * binormal.z;

        return pos2.clone();
    };

    THREE.ParametricGeometry.call(this, ParametricTube, segments, segmentsRadius);

};

THREE.ParametricGeometries.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );


/*********************************************
 *
 * Parametric Replacement for TorusKnotGeometry
 *
 *********************************************/
THREE.ParametricGeometries.TorusKnotGeometry = function ( radius, tube, segmentsR, segmentsT, p, q, heightScale ) {

    var scope = this;

    this.radius = radius || 200;
    this.tube = tube || 40;
    this.segmentsR = segmentsR || 64;
    this.segmentsT = segmentsT || 8;
    this.p = p || 2;
    this.q = q || 3;
    this.heightScale = heightScale || 1;


    var TorusKnotCurve = THREE.Curve.create(

        function() {
        },

        function(t) {

            t *= Math.PI * 2;

            var r = 0.5;

            var tx = (1 + r * Math.cos(q * t)) * Math.cos(p * t),
                ty = (1 + r * Math.cos(q * t)) * Math.sin(p * t),
                tz = r * Math.sin(q * t);

            return new THREE.Vector3(tx, ty * heightScale, tz).multiplyScalar(radius);

        }

    );
    var segments = segmentsR;
    var radiusSegments = segmentsT;
    var extrudePath = new TorusKnotCurve();

    THREE.ParametricGeometries.TubeGeometry.call( this, extrudePath, segments, tube, radiusSegments, true, false );


};

THREE.ParametricGeometries.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );


/*********************************************
 *
 * Parametric Replacement for SphereGeometry
 *
 *********************************************/
THREE.ParametricGeometries.SphereGeometry = function(size, u, v) {

    function sphere(u, v) {
        u *= Math.PI;
        v *= 2 * Math.PI;

        var x = size * Math.sin(u) * Math.cos(v);
        var y = size * Math.sin(u) * Math.sin(v);
        var z = size * Math.cos(u);


        return new THREE.Vector3(x, y, z);
    }

    THREE.ParametricGeometry.call(this, sphere, u, v, !true);

};

THREE.ParametricGeometries.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );


/*********************************************
 *
 * Parametric Replacement for PlaneGeometry
 *
 *********************************************/

THREE.ParametricGeometries.PlaneGeometry = function(width, depth, segmentsWidth, segmentsDepth) {

    function plane(u, v) {

        var x = u * width;
        var y = 0;
        var z = v * depth;

        return new THREE.Vector3(x, y, z);
    }

    THREE.ParametricGeometry.call(this, plane, segmentsWidth, segmentsDepth);

};

THREE.ParametricGeometries.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );

 

   
ConvexGeometry.js
/**
 * @author qiao / https://github.com/qiao
 * @fileoverview This is a convex hull generator using the incremental method. 
 * The complexity is O(n^2) where n is the number of vertices.
 * O(nlogn) algorithms do exist, but they are much more complicated.
 *
 * Benchmark: 
 *
 *  Platform: CPU: P7350 @2.00GHz Engine: V8
 *
 *  Num Vertices    Time(ms)
 *
 *     10           1
 *     20           3
 *     30           19
 *     40           48
 *     50           107
 */

THREE.ConvexGeometry = function( vertices ) {

    THREE.Geometry.call( this );

    var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ]; 

    for ( var i = 3; i < vertices.length; i++ ) {

        addPoint( i );

    }


    function addPoint( vertexId ) {

        var vertex = vertices[ vertexId ].clone();

        var mag = vertex.length();
        vertex.x += mag * randomOffset();
        vertex.y += mag * randomOffset();
        vertex.z += mag * randomOffset();

        var hole = [];

        for ( var f = 0; f < faces.length; ) {

            var face = faces[ f ];

            // for each face, if the vertex can see it,
            // then we try to add the face's edges into the hole.
            if ( visible( face, vertex ) ) {

                for ( var e = 0; e < 3; e++ ) {

                    var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ];
                    var boundary = true;

                    // remove duplicated edges.
                    for ( var h = 0; h < hole.length; h++ ) {

                        if ( equalEdge( hole[ h ], edge ) ) {

                            hole[ h ] = hole[ hole.length - 1 ];
                            hole.pop();
                            boundary = false;
                            break;

                        }

                    }

                    if ( boundary ) {

                        hole.push( edge );

                    }

                }

                // remove faces[ f ]
                faces[ f ] = faces[ faces.length - 1 ];
                faces.pop();

            } else { // not visible

                f++;

            }
        }

        // construct the new faces formed by the edges of the hole and the vertex
        for ( var h = 0; h < hole.length; h++ ) {

            faces.push( [ 
                hole[ h ][ 0 ],
                hole[ h ][ 1 ],
                vertexId
            ] );

        }
    }

    /**
     * Whether the face is visible from the vertex
     */
    function visible( face, vertex ) {

        var va = vertices[ face[ 0 ] ];
        var vb = vertices[ face[ 1 ] ];
        var vc = vertices[ face[ 2 ] ];

        var n = normal( va, vb, vc );

        // distance from face to origin
        var dist = n.dot( va );

        return n.dot( vertex ) >= dist; 

    }

    /**
     * Face normal
     */
    function normal( va, vb, vc ) {

        var cb = new THREE.Vector3();
        var ab = new THREE.Vector3();

        cb.subVectors( vc, vb );
        ab.subVectors( va, vb );
        cb.cross( ab );

        cb.normalize();

        return cb;

    }

    /**
     * Detect whether two edges are equal.
     * Note that when constructing the convex hull, two same edges can only
     * be of the negative direction.
     */
    function equalEdge( ea, eb ) {

        return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ]; 

    }

    /**
     * Create a random offset between -1e-6 and 1e-6.
     */
    function randomOffset() {

        return ( Math.random() - 0.5 ) * 2 * 1e-6;

    }


    /**
     * XXX: Not sure if this is the correct approach. Need someone to review.
     */
    function vertexUv( vertex ) {

        var mag = vertex.length();
        return new THREE.Vector2( vertex.x / mag, vertex.y / mag );

    }

    // Push vertices into `this.vertices`, skipping those inside the hull
    var id = 0;
    var newId = new Array( vertices.length ); // map from old vertex id to new id

    for ( var i = 0; i < faces.length; i++ ) {

         var face = faces[ i ];

         for ( var j = 0; j < 3; j++ ) {

                if ( newId[ face[ j ] ] === undefined ) {

                        newId[ face[ j ] ] = id++;
                        this.vertices.push( vertices[ face[ j ] ] );

                }

                face[ j ] = newId[ face[ j ] ];

         }

    }

    // Convert faces into instances of THREE.Face3
    for ( var i = 0; i < faces.length; i++ ) {

        this.faces.push( new THREE.Face3( 
                faces[ i ][ 0 ],
                faces[ i ][ 1 ],
                faces[ i ][ 2 ]
        ) );

    }

    // Compute UVs
    for ( var i = 0; i < this.faces.length; i++ ) {

        var face = this.faces[ i ];

        this.faceVertexUvs[ 0 ].push( [
            vertexUv( this.vertices[ face.a ] ),
            vertexUv( this.vertices[ face.b ] ),
            vertexUv( this.vertices[ face.c ])
        ] );

    }

    this.computeFaceNormals();
    this.computeVertexNormals();

};

THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );

 



posted on 2018-10-30 09:40  -韩帅  阅读(1506)  评论(0编辑  收藏  举报

导航