---
# System prepended metadata

title: Three.js 學習筆記
tags: [教育訓練]

---

---
title: 'Three.js 學習筆記'
tags: 教育訓練
disqus: hackmd
---

Three.js 學習筆記 
===

## Table of Contents

[TOC]
## 大綱

* 場景（Scene）：供其他元素設置的空間。
* 相機（Camera）：在場景中建立觀察點，並確定觀察方向、角度。
* 物體（Objects）：在場景中添加被觀察的物體。
* 光源（Light）：在場景中用來照亮物體的光。
* 渲染器（Renderer）：將所要呈現的場景渲染到畫面上。

## Scene 場景

> 所有東西都從裡開始 用.add() 將物件加進去
> 
```javascript=
scene.add(obj)
```

Camera 視角
---
> 可以想像成我們的視角，可以控制位置，面向
```javascript=
camera.position.set(x,y,z)  // 設定位置
```
```javascript=
camera.lookAt(x,y,z)  // 設定面向
```
> 不同種類的 camera 可以做不同的事
> 
**透視投影相機（PerspectiveCamera）**

在透視投影相機中，越遠的物體會有比較小的尺寸，與人類眼睛所見相同。

![](https://i.imgur.com/XtxUrfr.png)

**正交投影相機（OrthographicCamera）**

正交投影相機中的物體不論遠近，看起來的尺寸都一樣，這樣的相機一般被用在二維場景中。

![](https://i.imgur.com/lz1AGZu.png)

```js=
const camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
```
**Constructor**
```javascript=
PerspectiveCamera ( 
    fov : Number, 
    aspect : Number, 
    near : Number, 
    far : Number 
)
```
    fov — 視角寬度
    aspect — 畫面比例
    near — 可視最近距離
    far — 可視最遠距離
![](https://i.imgur.com/w8OxyEn.png)


> Read more about PerspectiveCamera: https://threejs.org/docs/?q=camera#api/en/cameras/PerspectiveCamera

object 物體
---
![](https://i.imgur.com/UbW2MDm.png)

**內建物件**
:::info
Three.js 中內建許多，幾何形可以使用，搭配材質(material)，即可創建物體。
:::
```javascript=
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 );
```
![](https://i.imgur.com/AWU4GBN.png)

> Read more about BoxGeometry here: https://threejs.org/docs/#api/en/geometries/BoxGeometry

**引入 gLTF file**

![](https://i.imgur.com/zT0bS3b.jpg)
```javascript=
// Instantiate a loader
const loader = new GLTFLoader();
var model2 = new THREE.Object3D(); // 用來存放模型

// Load a glTF resource
loader.load(
	// resource URL
	'models/gltf/duck/duck.gltf',
	// called when the resource is loaded
	function ( gltf ) {

	model2 = gltf.scene; //載入完成後 存在全域變數中
        
        model2.traverse(function (node) {
        if (node.isMesh) {
            
          // node.material.needsUpdate = true;
          // node.material.envMapIntensity = 1;
          // node.material.envMap = textureEquirec;
          // node.material.metalness = 0.5;
          // node.material.roughness = 0.4;
        
        }
      });
          model2.scale.set(0.05, 0.05, 0.05); // 縮放大小
          scene.add(model2); // 加入場景

	},
	// called while loading is progressing
	function ( xhr ) {

		console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

	},
	// called when loading has errors
	function ( error ) {

		console.log( 'An error happened' );

	}
);
```

:::success
這邊有個 traverse 方法，這是 THREE.Scene 中提供一個用來遍歷目標物件及其所有後代的方法，透過傳入的 function，可以對目標底下的所有子元件都設定效果。
:::

Light 燈光
---
**環境光 AmbientLight**
> 環境光。散佈在環境中的光，會將光源的顏色疊加到場景中所有物體上，不能創造陰影，通常拿來增加一些柔性的光線補強色彩。
> 
**點光源 PointLight**
> 點光源。從特定一點向所有方向發射光線，可以投射陰影。類似燈泡、螢火蟲的概念。
> 
**聚光燈 SpotLight**
> 聚光燈。從特定一點對某個方向發射錐形的光線，可以投射陰影。類似手電筒、舞台聚光燈的概念。
> 
**方向光 DirectionLight**
> 平行光、無限光。從一個二維平面發射光線，光線彼此平行，可以投射陰影，類似太陽光的概念。

[前四項燈光範例](https://dezchuang.github.io/ironman-three.js/day09_light/index.html)

**半球光 HemisphereLight**
> 用來模擬真實世界的環境光，一部份是從上方照射模擬太陽光，一部分是從下方反射地面後照射到物體

[半球光範例](https://threejs.org/examples/?q=Hemisphere#webgl_lights_hemisphere)

**專案中實際應用**
```javascript=
let spotLight = new THREE.SpotLight(0xeeff00) // 燈光顏色
spotLight.position.set(-10, 20, 20) // 擺放位置
spotLight.castShadow = true // 照射到物體 是否產生陰影
scene.add(spotLight)
let spotLightHelper = new THREE.SpotLightHelper(spotLight) // 加上輔助線 方便做調整
scene.add(spotLightHelper)
```

Renderer 渲染器
---
```javascript=
function animate() {
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
        if (resizeRendererToDisplaySize(renderer)) {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix(); 
        }
    };

animate();

function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    var width = window.innerWidth;
    var height = window.innerHeight;
    var canvasPixelWidth = canvas.width / window.devicePixelRatio;
    var canvasPixelHeight = canvas.height / window.devicePixelRatio;
    const needResize =
      canvasPixelWidth !== width || canvasPixelHeight !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }
```

:::success
**requestAnimationFrame** has a number of advantages. Perhaps the most important one is that it pauses when the user navigates to another browser tab, hence not wasting their precious processing power and battery life.
:::

## CatmullRomCurve3 曲線

概念: 先用關鍵點，用演算法算出路徑，再用此路徑繪製ExtrudeGeometry，也就是沿著路徑用多邊形連接起來的水管狀幾何形。

```javascript=
// 劃出目標點
  const curve = new THREE.CatmullRomCurve3(
    [...vectorArray],
    props.isClosed,
    "catmullrom",
    0.5
  );
// 繪製軌道
  // Set up settings for later extrusion
  var extrudeSettings = {
    steps: 200,
    // 幾個切面
    bevelEnabled: false,
    extrudePath: curve,  //套用我們算好的路徑
  };

  // // Define a triangle
  var pts = [],
    // 橫切面 是幾邊型
    count = 5; //五邊形
  for (var i = 0; i < count; i++) {
    var l = 0.5;
    // 邊長
    var a = ((2 * i) / count) * Math.PI;
    pts.push(new THREE.Vector2(Math.cos(a) * l, Math.sin(a) * l));
  }
  var shape = new THREE.Shape(pts);

  // Extrude the triangle along the CatmullRom curve
  var geometryLine = new THREE.ExtrudeGeometry(shape, extrudeSettings);
  var materialLine = new THREE.MeshLambertMaterial({
    color: 0x262626,
    wireframe: false,
  });

  // Create mesh with the resulting geometry
  var mesh = new THREE.Mesh(geometryLine, materialLine);
  scene.add(mesh);
```

## Appendix and FAQ

:::info
**Find this document incomplete?** Leave a comment!
:::

###### tags: `Templates` `Documentation`
