基于three.js的三維空間曲線軌跡運動

引言

我們在做項目的時候唧垦,有時候會遇到物體或者相機需要做復(fù)雜軌跡運動的情況头滔,往往沒法簡單的通過修改位置來達成我們想要的運動效果。
這時候可以通過引入多段曲線去擬合我們想要的運動軌跡己沛,再獲取曲線的參數(shù)去控制物體做相應(yīng)軌跡的運動臂拓。

目錄

  • 1、創(chuàng)建關(guān)鍵空間點數(shù)組
  • 2押逼、根據(jù)點數(shù)組繪制曲線
  • 3步藕、獲取曲線上特定位置的點,修改物體位置
  • 4挑格、獲取曲線上特定位置的切線咙冗,修改物體朝向
  • 5、隨時間實時改變物體位置和朝向
  • 6漂彤、添加修改曲線功能
  • 7雾消、引入模型模擬應(yīng)用場景

1、創(chuàng)建關(guān)鍵空間點數(shù)組

首先我們可以先找出運動軌跡上幾個特定的點挫望。
假設(shè)給定的點是(1,1,-1),(1,0,1),(-1,0,1),(-1,0,-1)
這里在每個點放了一個實體方塊用于示意點的位置立润,同時為后面的調(diào)整功能做準(zhǔn)備

        const initialPoints = [
            { x: 1, y: 1, z: -1 },
            { x: 1, y: 0, z: 1 },
            { x: -1, y: 0, z: 1 },
            { x: -1, y: 0, z: -1 }
        ];

        const addCube = (pos) => {
            const geometry = new THREE.BoxBufferGeometry(0.1, 0.1, 0.1);
            const material = new THREE.MeshBasicMaterial(0xffffff);
            const cube = new THREE.Mesh(geometry, material);
            cube.position.copy(pos);
            scene.add(cube);
        }

        const cubeList = initialPoints.map(pos => {
            return this.addCube(pos);
        });
繪制曲線的關(guān)鍵空間點

2、根據(jù)點數(shù)組繪制曲線

three.js 提供了好幾種方法繪制曲線媳板,這里采用的是 CatmullRom 插值的方法繪制曲線桑腮。

CatmullRom 插值的曲線一定會經(jīng)過所有給定的點,所以這種方法會更適合用作軌跡曲線的繪制蛉幸。

        const curve = new THREE.CatmullRomCurve3(
            cubeList.map((cube) => cube.position) // 直接綁定方塊的position以便后續(xù)用方塊調(diào)整曲線
        );
        curve.curveType = 'chordal'; // 曲線類型
        curve.closed = true; // 曲線是否閉合

        const points = curve.getPoints(50); // 50等分獲取曲線點數(shù)組
        const line = new THREE.LineLoop(
            new THREE.BufferGeometry().setFromPoints(points),
            new THREE.LineBasicMaterial({ color: 0x00ff00 })
        ); // 繪制實體線條破讨,僅用于示意曲線,后面的向量線條同理奕纫,相關(guān)代碼就省略了

        scene.add(line);
繪制曲線

3提陶、獲取曲線上特定位置的點,修改物體位置

有了曲線之后若锁,可以通過 getPointAt 函數(shù)獲取曲線上特定位置的點向量搁骑,然后復(fù)制給物體的 position

        function changePosition (t) {
            const position = curve.getPointAt(t); // t: 當(dāng)前點在線條上的位置百分比,后面計算
            mesh.position.copy(position);
        }

為了直觀表現(xiàn)下圖采用 30 等分取點把位置向量繪制出來了,后面的圖片也采用一樣的方式展現(xiàn)向量


獲取曲線上的點向量

4仲器、獲取曲線上特定位置的切線煤率,修改物體朝向

現(xiàn)在物體的位置對上了,但是朝向卻是固定的乏冀,不符合生活經(jīng)驗蝶糯。一般來說物體在運動的時候,正面總是朝向軌跡的切線方向的辆沦。

現(xiàn)在我們通過 getTangentAt 函數(shù)獲取曲線上特定位置的切線向量昼捍,根據(jù)該切線向量和點的位置向量計算物體朝向的點向量,傳入物體的 lookAt 函數(shù)

        function changeLookAt (t) {
            const tangent = curve.getTangentAt(t);
            const lookAtVec = tangent.add(position); // 位置向量和切線向量相加即為所需朝向的點向量
            mesh.lookAt(lookAtVec);
        }
獲取切線向量(黃色線條)

注意上圖示的切線(黃線)實際起點為原點(0,0,0)肢扯,這里為了示意切線在曲線上的位置妒茬,平移到了點所在位置上

向量相加得到朝向的點向量(藍色線條)

因為 lookAt 實際上是指向某個點向量,如果直接傳切線向量會導(dǎo)致物體朝向下圖 A 點蔚晨,需要和位置向量相加后才能得到所需的點向量(藍線)即 C 點

image.png

5乍钻、隨時間實時改變物體位置和朝向

現(xiàn)在軌跡上單一點的位置和朝向都可以獲取到了,剩下的就是在渲染函數(shù)中實時修改了铭腕。

根據(jù)時間計算當(dāng)前點在曲線上的位置百分比银择,傳入第 3、4 步中

        const loopTime = 10 * 1000; // loopTime: 循環(huán)一圈的時間

        // 在渲染函數(shù)中獲取當(dāng)前時間
        const render = () => {
            let time = Date.now();
            let t = (time % loopTime) / loopTime; // 計算當(dāng)前時間進度百分比

            changePosition(t);
            changeLookAt(t);

            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        requestAnimationFrame(render);
物體曲線軌跡運動

相機曲線軌跡運動

6累舷、添加修改曲線功能

到這里曲線運動的效果是做出來了浩考,但是如果我們想調(diào)整曲線,就得修改最初的點數(shù)組被盈,既不直觀也很繁瑣析孽。

參考 three.js 官網(wǎng)的 demo 發(fā)現(xiàn)可以通過 TransformControls 控制方塊位置,實時修改曲線害捕。同時因為前面的 curve 是通過方塊的 position 生成的绿淋,所以方塊位置的修改可以直接反映到 curve 上

        import { TransformControls } from 'TransformControls.js'; // 引入模塊

        const control = new TransformControls(camera, renderer.domElement);

        // 獲取點擊位置
        const mouse = new THREE.Vector2();
        renderer.domElement.addEventListener(
            'click',
            (event) => {
                mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            },
            false
        );

        // 方塊點擊檢測
        const rayCaster = new THREE.Raycaster();
        rayCaster.setFromCamera(mouse, camera);
        const intersects = rayCaster.intersectObjects(cubeList);
        if (intersects.length) {
            const target = intersects[0].object;
            control.attach(target); // 綁定controls和方塊
            scene.add(control);
        }

        // 修改曲線后同步修改實體線條
        control.addEventListener('dragging-changed', (event) => {
            if (!event.value) {
                const points = curve.getPoints(50);
                line.geometry.setFromPoints(points);
            }
        });
實時修改曲線

7、引入模型模擬應(yīng)用場景

經(jīng)過前面的步驟現(xiàn)在有了一個比較抽象的場景尝盼,現(xiàn)在可以考慮通過模型讓應(yīng)用場景更具象化。這里采用和場景契合度較高的過山車模型佑菩。

車模型的處理方式和方塊基本沒區(qū)別這里就不放相關(guān)代碼了盾沫,軌道是通過一小段的軌道模型不斷重復(fù)的方式去模擬。

        // 軌道分段數(shù)
        let railNum = 50;

        // 導(dǎo)入模型
        const loader = new GLTFLoader().setPath('model/');
        loader.load('scene.gltf', (gltf) => {
            // 軌道容器
            const railway = new THREE.Object3D();
            let position = new THREE.Vector3();
            let tangent = new THREE.Vector3();

            for (let i = 0; i < railNum; i++) {
                // 復(fù)制多段軌道模型
                let model = gltf.scene.clone();
                railway.add(model);
                
                // 這里和前面一樣通過獲取位置和切線向量去計算每段軌道的朝向
                position = curve.getPointAt(i / railNum);
                tangent = curve.getTangentAt(i / railNum);
                model.position.copy(position);
                model.lookAt(tangent.add(position));
            }
            scene.add(railway);
        });
導(dǎo)入模型

不過這樣的方式相當(dāng)于用多段直線拼出來的曲線殿漠,整體會比較生硬赴精。如果把曲線調(diào)整的過長也會出現(xiàn)軌道接不上的問題。

three.js 官網(wǎng)的 examples 里有一個過山車 demo绞幌,軌道不是使用模型而是通過代碼建模去模擬軌道蕾哟,效果會自然很多。詳見:https://threejs.org/examples/?q=roller#webxr_vr_rollercoaster

Demo 地址

http://demo.treedom.cn/threejs.curve_animation.wyl/

參考

https://threejs.org/examples/?q=curve#webgl_modifier_curve

https://threejs.org/examples/?q=spli#webgl_geometry_extrude_splines

了解更多

原文來源: 基于three.js的三維空間曲線軌跡運動

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谭确,隨后出現(xiàn)的幾起案子帘营,更是在濱河造成了極大的恐慌,老刑警劉巖逐哈,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芬迄,死亡現(xiàn)場離奇詭異,居然都是意外死亡昂秃,警方通過查閱死者的電腦和手機禀梳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肠骆,“玉大人算途,你說我怎么就攤上這事∈赐龋” “怎么了嘴瓤?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唯咬。 經(jīng)常有香客問我纱注,道長,這世上最難降的妖魔是什么胆胰? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任狞贱,我火速辦了婚禮,結(jié)果婚禮上蜀涨,老公的妹妹穿的比我還像新娘瞎嬉。我一直安慰自己,他們只是感情好厚柳,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布氧枣。 她就那樣靜靜地躺著,像睡著了一般别垮。 火紅的嫁衣襯著肌膚如雪便监。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天碳想,我揣著相機與錄音烧董,去河邊找鬼。 笑死胧奔,一個胖子當(dāng)著我的面吹牛逊移,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播龙填,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼胳泉,長吁一口氣:“原來是場噩夢啊……” “哼拐叉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扇商,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤凤瘦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钳吟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廷粒,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年红且,在試婚紗的時候發(fā)現(xiàn)自己被綠了坝茎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡暇番,死狀恐怖嗤放,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壁酬,我是刑警寧澤次酌,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站舆乔,受9級特大地震影響岳服,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜希俩,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一吊宋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颜武,春花似錦璃搜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至篙议,卻和暖如春唾糯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鬼贱。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工趾断, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吩愧。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像增显,于是被迫代替她去往敵國和親雁佳。 傳聞我的和親對象是個殘疾皇子脐帝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Threejs 為什么? webGL太難用糖权,太復(fù)雜堵腹! 但是現(xiàn)代瀏覽器都支持 WebGL 這樣我們就不必使用 Fla...
    強某某閱讀 6,012評論 1 21
  • 照相機 此處所說照相機與現(xiàn)實有所差別, 由于threejs創(chuàng)建的場景是三維的,人眼要看出三維效果就需要有透視點星澳。 ...
    風(fēng)銘閱讀 1,933評論 0 1
  • Threejs中文文檔 郭隆邦技術(shù)博客 2018-09-21 20:40:17 關(guān)注 Three.js中文文檔 今...
    情人波閱讀 13,993評論 0 7
  • Three.js是構(gòu)建web3d場景非常流行的框架疚顷,利用three.js我們可以更優(yōu)雅地創(chuàng)建出三維場景和三維動畫,...
    YoneChen閱讀 9,801評論 3 13
  • 1.重用Material和Geometry 2.不在render()中實例化或是賦值操作 3.粒子系統(tǒng)代替粒子 4...
    bbh123閱讀 4,608評論 1 3