Three.js使用THREE.ParametricGeometry生成平面圖形、波浪圖形、和克萊因瓶

簡介

使用THREE.ParametricGeometry可以根據傳入的公式,計算出相應的幾何體。這是一個很具有擴展性的幾何體。

簡單實現

var geometry = new THREE.ParametricGeometry(function, slices, stacks);

通過上面的方法,就可以創建出一個THREE.ParametricGeometry的幾何體。相關的參數為:

相關參數

屬性 是否必需 描述
function 該屬性為一個函數,該函數以u、v值作為參數定義每個頂點的位置。需要返回THREE.Vector3的值
slices 該屬性定義u值應該分成多少份
stacks 該屬性定義v值應該分成多少份

方法講解

在我們設置好相關的屬性值之後,THREE.ParametricGeometry會根據我們定義的slices和stacks將u和v生成的值傳給function循環調用。u和v的范圍是0-1。

舉個列子:如果我們要生成一個width為10,depth為10的平面圖形,將slices設置為5,stacks設置為4。function的表達式為

    //創建平面模型的方法
    var plane = function (u, v) {
        var x = u*10;
        var y = 0;
        var z = v*10;
        return new THREE.Vector3(x, y, z);
    };

那麼,在運算的時候,會生成的點就是:

u:0/5; v:0/4; return: THREE.Vector3(0, 0, 0);
u:1/5; v:0/4; return: THREE.Vector3(2, 0, 0);
u:2/5; v:0/4; return: THREE.Vector3(4, 0, 0);
u:3/5; v:0/4; return: THREE.Vector3(6, 0, 0);
u:4/5; v:0/4; return: THREE.Vector3(8, 0, 0);
u:5/5; v:0/4; return: THREE.Vector3(10, 0, 0);
u:0/5; v:1/4; return: THREE.Vector3(0, 0, 2.5);
u:1/5; v:1/4; return: THREE.Vector3(2, 0, 2.5);
u:2/5; v:1/4; return: THREE.Vector3(4, 0, 2.5);
...
u:3/5; v:4/4; return: THREE.Vector3(6, 0, 10);
u:4/5; v:4/4; return: THREE.Vector3(8, 0, 10);
u:5/5; v:4/4; return: THREE.Vector3(10, 0, 10);

然後,程序通過生成的這些一組一組的點,生成相應的面,再繪制出來。

案例代碼

設置slices和stacks為120時,分的層次很多,所以生成的圖形很平滑。

平面圖形

波浪圖形

克萊因瓶

如果設置slices和stacks為12,分的層次少,則能夠看出來點的關系。

平面圖形

波浪圖形

克萊因瓶

通過上面我們可以看出來程序通過我們生成的點,繪制出來瞭,相應的圖形。

以下是相關案例代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        html, body {
            margin: 0;
            height: 100%;
        }

        canvas {
            display: block;
        }

    </style>
</head>
<body onload="draw();">
</body>
<script src="https://johnson2heng.github.io/three.js-demo/lib/three.js"></script>
<script src="https://johnson2heng.github.io/three.js-demo/lib/js/controls/OrbitControls.js"></script>
<script src="https://johnson2heng.github.io/three.js-demo/lib/js/libs/stats.min.js"></script>
<script src="https://johnson2heng.github.io/three.js-demo/lib/js/libs/dat.gui.min.js"></script>
<script>
    var renderer;
    function initRender() {
        renderer = new THREE.WebGLRenderer({antialias:true});
        //renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0)); //設置背景顏色
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
    }

    var camera;
    function initCamera() {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 10000);
        camera.position.set(0, 0, 150);
    }

    var scene;
    function initScene() {
        scene = new THREE.Scene();
    }

    var light;
    function initLight() {
        scene.add(new THREE.AmbientLight(0x404040));

        light = new THREE.DirectionalLight(0xffffff);
        light.position.set(1,1,1);
        scene.add(light);
    }

    //創建平面模型的方法
    var plane = function (u, v) {
        var x = u*50;
        var y = 0;
        var z = v*50;
        return new THREE.Vector3(x, y, z);
    };

    //創建波浪圖形的方法
    var radialWave = function (u, v) {
        var r = 50;

        var x = Math.sin(u) * r;
        var z = Math.sin(v / 2) * 2 * r;
        var y = (Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI)) * 2.8;

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

    var klein = function (u, v) {
        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);
    };

    function initModel() {

        //軸輔助 (每一個軸的長度)
        object = new THREE.AxisHelper( 50 );
        scene.add( object );

        var mesh = createMesh(new THREE.ParametricGeometry(klein, 12, 12));
        scene.add(mesh);
    }

    //生成模型
    function createMesh(geom) {

        //設置當前的模型矩陣沿xy軸偏移,讓圖片處於顯示中心
        geom.applyMatrix(new THREE.Matrix4().makeTranslation(0, 0, 0));

        // 創建法向量紋理
        var meshMaterial = new THREE.MeshNormalMaterial({
            flatShading: THREE.FlatShading,
            transparent: true,
            opacity: 0.9
        });

        //  創建一個線框紋理
        var wireFrameMat = new THREE.MeshBasicMaterial();
        wireFrameMat.wireframe = true;

        // 創建模型
        var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial,wireFrameMat]);

        return mesh;
    }

    //初始化性能插件
    var stats;
    function initStats() {
        stats = new Stats();
        document.body.appendChild(stats.dom);
    }

    //用戶交互插件 鼠標左鍵按住旋轉,右鍵按住平移,滾輪縮放
    var controls;
    function initControls() {

        controls = new THREE.OrbitControls( camera, renderer.domElement );

        // 如果使用animate方法時,將此函數刪除
        //controls.addEventListener( 'change', render );
        // 使動畫循環使用時阻尼或自轉 意思是否有慣性
        controls.enableDamping = true;
        //動態阻尼系數 就是鼠標拖拽旋轉靈敏度
        //controls.dampingFactor = 0.25;
        //是否可以縮放
        controls.enableZoom = true;
        //是否自動旋轉
        controls.autoRotate = false;
        //設置相機距離原點的最遠距離
        controls.minDistance  = 20;
        //設置相機距離原點的最遠距離
        controls.maxDistance  = 10000;
        //是否開啟右鍵拖拽
        controls.enablePan = true;
    }

    //生成gui設置配置項
    var gui;
    function initGui() {
        //聲明一個保存需求修改的相關數據的對象
        gui = {};
        var datGui = new dat.GUI();
        //將設置屬性添加到gui當中,gui.add(對象,屬性,最小值,最大值)

        //調用生成一次圖形
        gui.asGeom();
    }

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

    //窗口變動觸發的函數
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        render();
        renderer.setSize( window.innerWidth, window.innerHeight );

    }

    function animate() {
        //更新控制器
        controls.update();
        render();

        //更新性能插件
        stats.update();
        requestAnimationFrame(animate);
    }

    function draw() {
        initRender();
        initScene();
        initCamera();
        initLight();
        initModel();
        initControls();
        initStats();
        //initGui();

        animate();
        window.onresize = onWindowResize;
    }
</script>
</html>

You May Also Like