three.js 入門學習(一)

webGl和three.js

http://webgl3d.cn/pages/aac9ab/

圖形學算法

Web3D

WebGPU

下載

yarn add three @types/three

使用

import * as THREE from 'three';

onst scene = new THREE.Scene();
僅導入你所需要的部分

import { Scene } from 'three';

一個初始化的demo

場景、相機和渲染器, 設置大小, 添加到頁面上

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

添加一個立方體, 設置樣式, 網格對象放入到我們的場景中, 物體將會添加到座標中, 設置層級

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 5;

循環場景, 添加

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

讓立方體動起來(添加到animate()函數中renderer.render調用的上方)

cube.rotation.x += 0.01;
cube.rotation.y += 0.01;

https://github.com/pmndrs/postprocessing

後期

WebGLRenderer

渲染器

場景Scene相機Camera渲染器Renderer



        var renderer: THREE.WebGLRenderer, scene: THREE.Scene | THREE.Object3D<THREE.Event>, camera: any, composer,
            circle: THREE.Object3D<THREE.Event>, skelet: any, particle: THREE.Object3D<THREE.Event>;

        window.onload = function () {
            init();
            animate();
            // 設置相機控件軌道控制器OrbitControls
            const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改變了相機參數,重新調用渲染器渲染三維場景
            controls.addEventListener('change', function () {
                renderer.render(scene, camera); //執行渲染操作
            });//監聽鼠標、鍵盤事件
        }

        function init() {
            // antialias - 是否執行抗鋸齒。默認爲false.
            // alpha 默認爲false 顏色透明度
            renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
            // 設置設備像素比
            // window.devicePixelRatio是設備上物理像素和設備獨立像素,
            // 公式表示就是:window.devicePixelRatio = 物理像素 / dips
            // dip或dp 與屏幕密度有關。dip可以用來輔助區分視網膜設備還是非視網膜設備。
            renderer.setPixelRatio((window.devicePixelRatio) ? window.devicePixelRatio : 1);
            // 設置大小
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.autoClear = false;
            // 設置顏色及其透明度
            renderer.setClearColor(0x000000, 0.0);
            // 把渲染器的dom添加到頁面上
            (document.getElementById('canvas') as any).appendChild(renderer.domElement);
            // 場景
            scene = new THREE.Scene();
            // 相機
            /*
            * fov — 攝像機視錐體垂直視野角度
              aspect — 攝像機視錐體長寬比
              near — 攝像機視錐體近端面
              far — 攝像機視錐體遠端面
            * */
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
            // 設置相機的位置
            camera.position.z = 400;
            // 把相機添加到場景中
            scene.add(camera);
            // 圓
            circle = new THREE.Object3D();
            skelet = new THREE.Object3D();
            // 基類, 提供一些屬性和方法來對三維空間中的物體進行操縱
            particle = new THREE.Object3D();

            scene.add(circle);
            scene.add(skelet);
            scene.add(particle);
            // 四面緩衝幾何體
            var geometry = new THREE.TetrahedronGeometry(2, 0);
            // 二十面緩衝幾何體
            // 二十面體的半徑,默認爲1。
            //  默認值爲0。將這個值設爲一個大於0的數將會爲它增加一些頂點,使其不再是一個二十面體。當這個值大於1的時候,實際上它將變成一個球體。
            var geom = new THREE.IcosahedronGeometry(7, 1);
            // 半徑大一些
            var geom2 = new THREE.IcosahedronGeometry(15, 1);
            // 材質, 受高光影響的材質
            var material = new THREE.MeshPhongMaterial({
                color: 0xffffff,
                flatShading: true // 定義材質是否使用平面着色進行渲染
            });

            for (var i = 0; i < 1000; i++) {
                // 網格模型, 一個幾何體, 一個材質
                var mesh = new THREE.Mesh(geometry, material);
                // 隨機設置位置
                mesh.position.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize();
                // 將該向量與所傳入的標量s進行相乘
                mesh.position.multiplyScalar(70 + (Math.random() * 700));
                //設置旋轉角度
                mesh.rotation.set(Math.random() * 2, Math.random() * 2, Math.random() * 2);
                // 把物體添加到基類中, 也可以直接添加到scene 中
                particle.add(mesh);
            }

            var mat = new THREE.MeshPhongMaterial({
                color: 0xffffff,
                flatShading: true
            });

            var mat2 = new THREE.MeshPhongMaterial({
                color: 0xffffff,
                wireframe: true,// 這個是設置骨架
                side: THREE.DoubleSide

            });
            // 這個 是小的
            var planet = new THREE.Mesh(geom, mat);
            // 小多面體放大的倍數
            planet.scale.x = planet.scale.y = planet.scale.z = 16;
            circle.add(planet);
            // 這個是大的骨架
            var planet2 = new THREE.Mesh(geom2, mat2);
            // 大多面體放大的倍數
            planet2.scale.x = planet2.scale.y = planet2.scale.z = 10;
            skelet.add(planet2);
            // 環境光
            var ambientLight = new THREE.AmbientLight(0x999999);
            scene.add(ambientLight);

            var lights = [];
            // 加入三個不同平行光, 展示不同的顏色
            // 平行光
            lights[0] = new THREE.DirectionalLight(0xffffff, 1);
            // 位置
            lights[0].position.set(1, 0, 0);
            lights[1] = new THREE.DirectionalLight(0x11E8BB, 1);
            lights[1].position.set(0.75, 1, 0.5);
            lights[2] = new THREE.DirectionalLight(0x8200C9, 1);
            lights[2].position.set(-0.75, -1, 0.5);
            scene.add(lights[0]);
            scene.add(lights[1]);
            scene.add(lights[2]);


            window.addEventListener('resize', onWindowResize, false);

        };

        function onWindowResize() {
            // 設置相機的長寬比
            camera.aspect = window.innerWidth / window.innerHeight;
            // 更新攝像機投影矩陣。在任何參數被改變以後必須被調用。
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth-100, window.innerHeight-100);
        }

        function animate() {
            requestAnimationFrame(animate);
            // 設置三個網格體的旋轉數據
            particle.rotation.x += 0.0000;
            particle.rotation.y -= 0.0040;
            circle.rotation.x -= 0.0020;
            circle.rotation.y -= 0.0030;
            skelet.rotation.x -= 0.0010;
            skelet.rotation.y += 0.0020;
            // 渲染器清除顏色、深度或模板緩存
            renderer.clear();
            renderer.render(scene, camera)
        }

outputEncoding

outputEncoding屬性控制輸出渲染編碼。默認情況下,outputEncoding的值爲THREE.LinearEncoding,看起來還行但是不真實,建議將值改爲`THREE.sRGBEncoding

圓環緩衝扭結幾何體

        const canvas: any = document.getElementById("canvas");
        let camera: any, scene: any, renderer: any, controls: any;
        let geometry, material, mesh: any;

        let ww, hh;
        const size = 3;

        ww = document.body.clientWidth / 2 / window.devicePixelRatio;
        hh = ww;
        // 場景
        scene = new THREE.Scene();
        // 渲染器
        renderer = new THREE.WebGLRenderer({
            antialias: true,//抗鋸齒
            alpha: true // 是否透明
        });
        // 把渲染器的dom添加到頁面上
        (document.getElementById('canvas') as any).appendChild(renderer.domElement);
        // // 設置設備像素比
        renderer.setPixelRatio(window.devicePixelRatio);
        // 設置大小
        renderer.setSize(ww, hh, false);
        // outputEncoding默認是LinearEncoding看起來還行但是不真實,可以設置爲sRGBEncoding 會更自然
        renderer.outputEncoding = THREE.sRGBEncoding;
        // 攝像機
        camera = new THREE.PerspectiveCamera(40, ww / hh, 0.01, size * 30);
        // 設置攝像機的位置
        camera.position.set(0, size, size * 6);
        // 設置網格模型對象的座標原點
        camera.lookAt(0, 0, 0);

        // 環境光
        const light = new THREE.AmbientLight(0x2980B9, 0.5);
        scene.add(light);

        // 平行光, 第二個參數光的強度
        const directionalLight = new THREE.DirectionalLight(0xF8C471, 0.8);
        directionalLight.position.set(0, size * 6, size * 5);
        // 模擬場景中平行光 DirectionalLight 的輔助對象
        const helper = new THREE.DirectionalLightHelper(directionalLight, 2, 0x239B56);
        scene.add(helper);
        scene.add(directionalLight);

        // 幾何體---- 圓環緩衝扭結幾何體
        /*
    * radius - 圓環的半徑,默認值爲1。
        tube — 管道的半徑,默認值爲0.4。
        tubularSegments — 管道的分段數量,默認值爲64。
        radialSegments — 橫截面分段數量,默認值爲8。
        p — 這個值決定了幾何體將繞着其旋轉對稱軸旋轉多少次,默認值是2。
        q — 這個值決定了幾何體將繞着其內部圓環旋轉多少次,默認值是3。
    * */
        geometry = new THREE.TorusKnotGeometry(size, size / 3, 100, 16);
        // 一種基於物理的標準材質
        material = new THREE.MeshStandardMaterial({
            color: new THREE.Color("#FF7F50"),
            roughness: 0 // 材質的粗糙程度。0.0表示平滑的鏡面反射,1.0表示完全漫反射。默認值爲1.0。
        });
        // 幾何體和材質變成網格模型
        mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        //軌道控制器--- 可以使得相機圍繞目標進行軌道運動
        controls = new OrbitControls(camera, renderer.domElement);
        // 將其設爲true,以自動圍繞目標旋轉
        controls.autoRotate = false;
        //阻尼慣性有多大。 Default is 0.05
        controls.enableDamping = true;
        // 啓用或禁用攝像機平移,默認爲true。
        controls.enablePan = false;
        // 相機向內移動多少
        controls.minDistance = size * 5.5;
        // 將相機向外移動多少
        controls.maxDistance = size * 10;
        controls.target.set(0, 0, 0);
        controls.update();

        window.addEventListener("resize", () => {
            ww = document.body.clientWidth / 2 / window.devicePixelRatio;
            hh = ww;
            camera.aspect = ww / hh;
            camera.updateProjectionMatrix();
            renderer.setSize(ww, hh, false);
        }, false);

        animate();
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            mesh.rotation.x += 0.01;
            mesh.rotation.z -= 0.01;
            renderer.render(scene, camera);
        }

指導幾個概練

場景Scene相機Camera渲染器Renderer

寫一個帶箭頭的輔助線

        const dir = new THREE.Vector3(-2.49, 4.74, -3.01).normalize();
        const origin = new THREE.Vector3(0, 0, 0);
        const length = 10;
        const hex = 0xffff00;
        const arrowHelper = new THREE.ArrowHelper(dir, origin, length, hex);
        scene.add(arrowHelper)

讓天球以北極星爲中心旋轉

  // 向量
        const dir = new THREE.Vector3(-2.49, 4.74, -3.01).normalize();
        // 建立四元數
        const quaternion = new THREE.Quaternion();
        let rotation1 = 0;
        // 由dir 爲軸心, rotation 爲旋轉弧度
        quaternion.setFromAxisAngle(dir, rotation1);

        function animate(){
            // 不斷增加弧度
            rotation1 += 0.01;
            // // 更新四元數
            quaternion.setFromAxisAngle(dir, rotation1);
            // // 增加的弧度更新到我們的天球上
            sphere.rotation.setFromQuaternion(quaternion);
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        }

Control

圍繞中心點的控制鏡頭方式

控制鏡頭的方式有:

  • OrbitControls

    軌道控制, 最常用, 你的鏡頭在一個隱形的圓形的軌道中移動,它永遠面向場景中的一個點。預設原點。

  • ArcballControls

    弧球控制,比軌道控制難用一點的控制,差在可以360度旋轉鏡頭,使得你的鏡頭水平不平衡。

  • DragControls

    用來拖拽場景中的物體,鏡頭不會移動

  • FirstPersonControls & FlyControls & PointerLockControls

    第一人稱視角,沒有軌道概念

  • TrackballControls

    OrbitControls 很像, 可是當用戶把鏡頭繞過最頂端之後, 並不會繞過頭,而TrackballControls 則會,

  • TransformControls

    主要是作爲控制物體, 而非控制鏡頭的

OrbitControls

它會操控你的鏡頭

你會修改自己的鏡頭位置,它也會。控制鏡頭爲至的方式就是修改Camera.position

OrbitControl.target:鏡頭所看向的目標物件,是一個位置資訊Vecro3

OrbitControl不會在每幀渲染時自動控制,得用OrbitControl.update()更新。

圓形軌道的鏡頭軌道

      const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
       // 設置鏡頭位置
        camera.position.set(0, 10, 15);
        // 渲染器
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        const geometry = new THREE.SphereGeometry(100, 50, 50);
        // 加載材質
        const texture = new THREE.TextureLoader().load('https://storage.googleapis.com/umas_public_assets/michaelBay/free_star_sky_hdri_spherical_map_by_kirriaa_dbw8p0w%20(1).jpg');
        // 物理的標準材質
        const material=new THREE.MeshStandardMaterial({map:texture, side: THREE.DoubleSide})
        // 環境光
        const light = new THREE.AmbientLight(0xffffff, 1);
        scene.add(light);
        // 星空
        const sphere = new THREE.Mesh(geometry, material);
        scene.add(sphere);

        const earthGeometry = new THREE.SphereGeometry(5, 50, 50);
        // 載入材質
        const earthTexture = new THREE.TextureLoader().load('https://storage.googleapis.com/umas_public_assets/michaelBay/1280px-Solarsystemscope_texture_8k_earth_daymap.jpeg')
        const earthMaterial = new THREE.MeshStandardMaterial( { map: earthTexture, side: THREE.DoubleSide})
        // 地球
        const earth = new THREE.Mesh(earthGeometry, earthMaterial);
        scene.add(earth);
        // 控制鏡頭
        const control = new OrbitControls( camera, renderer.domElement );

        const axesHelper = new THREE.AxesHelper( 5 );
        scene.add( axesHelper );

        // 修改鏡頭的方式, 修改位置,讓他鏡頭軌道更真實
        control.target.set(10, 0, 0);
        control.update();
        function animate() {
            requestAnimationFrame( animate );
            renderer.render( scene, camera );

        }
        animate();

總結: target 跟 lookAt 的差異

使用orbitControl.target = car.position.clone() 就能移動中心點

向量

.lerp()

改成百分比的向量使用

  	    const v1 = new THREE.Vector3(0, 0, 0);
        const v2 = new THREE.Vector3(10, 10, 10);
        const a = v1.lerp(v2, 0.25);
        console.log(a);
        // Vector3 {x: 2.5, y: 2.5, z: 2.5}

.add ( v : Vector3 )

兩個向量想加

    	const v1 = new THREE.Vector3(20, 20, 20);
        const v2 = new THREE.Vector3(10, 10, 10);
        console.log(v1.add(v2));
        // Vector3 {x: 30, y: 30, z: 30}

.addScalar ( s : Float )

將傳入的標量s和這個向量的x值、y值以及z值相加。

        const v1 = new THREE.Vector3(20, 20, 20);
        console.log(v1.addScalar(20));
        // Vector3 {x: 40, y: 40, z: 40}

.addScaledVector ( v : Vector3, s : Float )

將所傳入的v與s相乘所得的乘積和這個向量相加。

const v2 = new THREE.Vector3(10, 10, 10);
const v1 = new THREE.Vector3(20, 20, 20);
console.log(v1.addScaledVector(v2, 2));
// v2 * 2 +v1
Vector3 {x: 40, y: 40, z: 40}

.addVectors ( a : Vector3, b : Vector3 ) : this

將該向量設置爲a + b

 		const v2 = new THREE.Vector3(10, 10, 10);
        const v1 = new THREE.Vector3(20, 20, 20);
        const v3 = new THREE.Vector3();
        v3.addVectors(v1, v2);
		v3
        //  Vector3 {x: 30, y: 30, z: 30}

向量歸一化

向量歸一化常用於進行移動和方向計算,通過歸一化向量可以得到一個方向向量,並用它來對物體進行移動,而不需要關心物體的速度。此外,向量歸一化還可以用於實現光線投射、碰撞檢測等功能。

.applyAxisAngle ( axis : Vector3, angle : Float ) : this

axis - 一個被歸一化的Vector3
angle - 以弧度表示的角度。

將軸和角度所指定的旋轉應用到該向量上。

        const v2 = new THREE.Vector3(10, 10, 10).normalize();
        const v1 = new THREE.Vector3(20, 20, 20);
		v1.applyAxisAngle(v2, 1/4*Math.PI)
		以v1的起點, v2 爲軸, 然後旋轉的角度, 找到新設置的旋轉點

.applyEuler ( euler : Euler ) : this

通過將Euler(歐拉)對象轉換爲Quaternion(四元數)並應用, 將歐拉變換應用到這個向量

歐拉對象Euler

構造函數:Euler(x,y,z,order) 參數xyz分別表示繞xyz軸旋轉的角度值,角度單位是弧度。參數order表示旋轉順序,默認值XYZ,也可以設置爲YXZYZX等值

// 創建一個歐拉對象,表示繞着xyz軸分別旋轉45度,0度,90度
var Euler = new THREE.Euler( Math.PI/4,0, Math.PI/2);

四元數Quaternion

四元數對象Quaternion使用x、y、z和w四個分量表示, 用四元數來處理模型旋轉

Euler 類(歐拉角)與角度有關, 而Vector3 與位置有關,在實際使用中,Vector3 可用於設置位置,而 Euler 是設置對象方向的一種方式

       const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(60, 320 / 240, 0.1, 1000);
        camera.position.set(2, 2, 2);
        camera.lookAt(0, 0, 0);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(640, 480, false);
        (document.body).appendChild(renderer.domElement);
        // 歐拉角
        const euler = new THREE.Euler(Math.PI / 180 * 45, 0, 0)
        // 法線網格材質(種把法向量射到RGB顏色的材質)
        const meshA = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1),
            new THREE.MeshNormalMaterial());
        // 複製三個物體
        const box1 = meshA.clone(),
            box2 = meshA.clone(),
            box3 = meshA.clone();
        // 使用 EULER 的實例來設置狀態
        box2.rotation.copy(euler);
        box3.rotation.copy(euler);
        // 設置位置
        box2.position.set(-1, 0, 0);
        box3.position.set(1, 0, 0);
        // 添加到視圖上
        scene.add(box1);
        scene.add(box2);
        scene.add(box3);
        // 添加到渲染器上
        renderer.render(scene, camera);

設置環形旋轉的效果

 let lt:any = new Date();
        const loop = function () {
            const now: any = new Date(),
                secs: any = (now - lt) / 1000;
            // 計算每秒消耗時間
            requestAnimationFrame(loop);
            if (secs >= 0.075) {
                lt = now;
                // USING EULER XYZ PROPS
                box2.rotation.x += 1 * secs;
                box2.rotation.x %= Math.PI * 2;
                box3.rotation.y += 1 * secs;
                box3.rotation.y %= Math.PI * 2;
                renderer.render(scene, camera);
            }
        };
        loop();

具體使用旋轉的方法

  let lt:any = new Date();
        const state = {
            x: 0,
            y: 0,
            z: 0
        };
        const loop = function () {
            const now:any = new Date(),
                secs:any = (now - lt) / 1000;
            requestAnimationFrame(loop);
            if (secs >= 0.075) {
                lt = now;
                state.x += 0.5 * secs;
                state.y += 1.0 * secs;
                state.z += 1.5 * secs;
                state.x %= Math.PI * 2;
                // 設置角度
                box2.rotation.set(state.x, state.y, state.z);
                renderer.render(scene, camera);
            }
        };
        loop();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章