three初始化创建项目

88 阅读7分钟

下载threejs

地址:github.com/mrdoob/thre…

版本号更改信息:github.com/mrdoob/thre…

学习链接:www.webgl3d.cn/

版本问题如何对待

查文档的时候就以自己的项目为准

three源码

最常用到的就是docs文档和examples案例

three.js-文件包
└───build——three.js相关库,可以引入你的.html文件中。
    │
└───docs——Three.js API文档文件
    │───index.html——打开该文件,本地离线方式预览threejs文档
└───examples——大量的3D案例,是你平时开发参考学习的最佳资源
    │───jsm——threejs各种功能扩展库
└───src——Three.js引擎的源码,有兴趣可以阅读。
    │
└───editor——Three.js的可视化编辑器,可以编辑3D场景
    │───index.html——打开应用程序  

查看threejs实例

Npm install live-server -g

在根目录运行live-server即可打开

引入threejs

开发环境:使用vue,react引入

学习环境直接使用html引入

开发环境

Npm install three@0.148.0 --save
//引入
import * as THREE from "three"
//引入扩展库
import {OrbitControls} from "three/addons/controls/OribitControls.js"
// 引入扩展库
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js"

//旧版本引入 例如122 新版本addons替换了examples/jsm
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js"

学习环境

<script src="./build/three.js">

//es6引入方式
<script type="module">
  import * as THREE from "./build/three.module.js"
</script>

type="importmap" 引入路径

<script type="importmap">
{
   "imports":{
       "three":"./build/three.module.js" 
   }
}
</script>
<script type="module">
  import * as THREE from "three"
</script>

学习threejs的第一步

场景scene,相机camera,渲染器renderer很重要

scene

//创建scene
const scene=new THREE.Scene();

camera

  • 透视投影相机:PerspectiveCamera本质上是四棱台
  • 立体相机(StereoCamera)
  • 正交相机(OrthographicCamera)
  • 立方相机(CubeCamera)

还有一个相机辅助和相机数组

  • CameraHelper:用于模拟相机视锥体的辅助对象.
  • 摄像机阵列(ArrayCamera):多相机
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
//相机位置.position
//相机在Three.js三维坐标系中的位置// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200); 
//相机观察目标.lookAt()
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
camera.lookAt(mesh.position);//指向mesh对应的位置

透视投影相机PerspectiveCamera:视锥体

透视投影相机的四个参数fov, aspect, near, far构成一个四棱台3D空间,被称为 锥体

  • near:近裁截面
  • far:远裁截面
  • aspect:宽高比
  • fov:视场角度

参数含义默认值
fov相机视锥体竖直方向视野角度50
aspect相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height1
near相机视锥体近裁截面相对相机距离0.1
far相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向2000

renderer

// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
//执行渲染操作
renderer.render(scene, camera); 
//显示
document.body.appendChild(renderer.domElement);

抗锯齿

const renderer = new THREE.WebGLRenderer({antialias:true,});
//或
renderer.antialias = true,

设置设备像素比

 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);

设置背景颜色.setClearColor()

renderer.setClearColor(0x444444, 1); //设置背景颜色

最后的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="importmap">
        {
            "imports":{
                "three":"../build/three.module.js"
            }
        }

    </script>
    <script type="module"> 
    import * as THREE from 'three';

    //创建场景
    const scene=new THREE.Scene();

     //创建一个立方体,添加到场景中
     const geometry=new THREE.BoxGeometry(1,1,1);
     const material=new THREE.MeshBasicMaterial({color:0xff0000});
     const cube=new THREE.Mesh(geometry,material);
     scene.add(cube);

     //设置相机,透视相机
     const camera=new THREE.PerspectiveCamera();
     //设置相机位置
     camera.position.set(0,10,0);
     //设置相机看的位置
     camera.lookAt(cube.position);
     
     //设置渲染器
     const renderer=new THREE.WebGLRenderer();
     //执行渲染器
     renderer.render(scene,camera);
     //设置大小
    //  renderer.setSize(300,300)
     //显示到页面上
     document.body.appendChild(renderer.domElement)

    </script>
</body>
</html>

如果你只想写一个demo看到这就可以了

api学习

材质

  • 不受光照影响

    • MeshBaiscMaterial
  • 受光照影响材质

    • 漫反射

      • MeshLambertMaterial
    • 高光

      • MeshPhongMaterial
    • 物理

      • MeshStandardMaterial
      • MeshPysicalMaterial

MeshPhonematerial

// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
  color: 0xff0000,
  shininess: 20, //高光部分的亮度,默认30
  specular: 0x444444, //高光部分的颜色
});

MeshPhonematerial和MeshLambertMaterial 区别

  • MeshPhonematerial可以提供一个镜面反射效果

  • MeshLambertMaterial没有镜面反射效果,向四周反射

光源

  • 环境光AmbientLight
  • 点光源PointLight
  • 平行光:DirectionalLight
  • 聚光灯光源 SpotLight

动画

threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame实现动画渲染,非常的简单,如下

    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5 });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    function render() {
        cube.rotateY(0.01);//每次绕y轴旋转0.01弧度
           requestAnimationFrame(render);
           renderer.render(scene, camera);
    }
    render();

计算两帧时间间隔

 // 渲染循环
 const clock = new THREE.Clock();
 function render() {
    cube.rotateY(0.01);//每次绕y轴旋转0.01弧度
    const spt = clock.getDelta() * 1000;//毫秒
     // console.log('两帧渲染时间间隔(毫秒)', spt);
     // console.log('帧率FPS', 1000 / spt);
     requestAnimationFrame(render);
     renderer.render(scene, camera);//周期性执行相机的渲染功能,更新canvas画布的内容
}

物体

几何体

//BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);

双面可见

new THREE.MeshBasicMaterial({side: THREE.FrontSide, //默认只有正面可见});
new THREE.MeshBasicMaterial({side: THREE.DoubleSide, //两面可见});

手势操作

相机空间轨道控制器OrbitControl

  • 旋转:拖动鼠标左键
  • 缩放:滚动鼠标中键
  • 平移:拖动鼠标右键
  <script type="importmap">
        {
            "imports":{
                "three":"../build/three.module.js",
                "MyThreeCommon":"./js/three_common.js",
                "three/addons/":"../jsm/"
            }
        }

 </script>
 <script type="module">

 import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
    renderer.render(scene, camera); //执行渲染操作
 });//监听鼠标、键盘事件
 }

工具

stats

性能监控

//引入性能监视器stats.jsimport Stats from 'three/addons/libs/stats.module.js';
//创建stats对象
const stats = new Stats();
//stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);
// 渲染函数
function render() {
//requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
        stats.update();
        renderer.render(scene, camera); 
        //执行渲染操作
        requestAnimationFrame(render); 
        //请求再次执行渲染函数render,渲染下一帧
}
render();



// stats.domElement显示:渲染帧率  刷新频率,一秒渲染次数 
stats.setMode(0);//默认模式//stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);

Gui

常用支持

  • 颜色 .addColor
  • 名称.name
  • 步长.step
  • 事件:onChange
  • 下拉框,第三个参数为对象或者数组
  • 单选框,参数的值为布尔
  • 分组.addFolder
  let obj = {
            color: 0xff0000,
            specular: 0x111111,// 材质高光颜色
            scale: 0,
            isRotate: false,

        }
//gui设置
                // 通过GUI改变mesh.position对象的xyz属性,.name为名称
                //平行光强度
                const directFolder=gui.addFolder('平行光');
                directFolder.add(DirectionalLight, 'intensity', 0, 2.0).step(0.1).name('平行光强度');
                //改变物体位置
                const cubeFolder=gui.addFolder('正方体')
                const positionFolder=cubeFolder.addFolder('位置');
                positionFolder.add(cube.position, 'x', 0, 10).name('球x轴移动').step(0.01);
                positionFolder.add(cube.position, 'y', 0, 10).name('球y轴移动').step(0.01);
                positionFolder.add(cube.position, 'z', 0, 10).name('球z轴移动').step(0.01);
                const materialFolder=cubeFolder.addFolder('材质');
                //设置颜色
                materialFolder.addColor(obj, 'color').name('颜色').onChange((value) => {
                    cube.material.color.set(value);
                })
                //下拉菜单设置
                // gui.add(obj,'scale',[-4,0,4]).name('y坐标').onChange((value)=>{
                //     cube.position.y=value;
                // })
                cubeFolder.add(obj, 'scale', { '下': -4, '中': 0, '上': 4 }).name('y坐标').onChange((value) => {
                    cube.position.y = value;
                })
                //单选框
                cubeFolder.add(obj, 'isRotate').name('是否旋转').onChange((value) => {
                    console.log(value)

                })

遇到的问题

如何让threejs适应画布不伸缩

three默认返回的尺寸是300,150;我们设置的正方体的盒子,从下图看就是一个被压扁的盒子,为什么

//设置相机,透视相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);

需要注意的是:camera的的aspect和renderer的setSize是有关系的,还有就是renderer.setSize要在render之前,不然是白屏

解决了一开始的适配问题,那么当窗口大小改变时,box也会拉伸压缩,使用如下方法:

按照网络上找到的方法设置

window.addEventListener('resize', onWindowResize);
function onWindowResize() {
    //设置观察范围长宽比aspect为窗口宽高比
    camera.aspect = window.innerWidth / window.innerHeight;
    // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
    camera.updateProjectionMatrix();
    // // 重置渲染器输出画布canvas尺寸
    renderer.setSize(window.innerWidth, window.innerHeight);
 }

设置后依然不管用,百思不得其解

最后思考应该没用重新 renderer.render(scene,camera);

看了下官网的方法

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

每一帧都会去渲染scene,camera

其他相机的设置

正交相机适配

// onresize 事件会在窗口被调整大小时发生
window.onresize=function(){
  // 重置渲染器输出画布canvas尺寸
  renderer.setSize(window.innerWidth,window.innerHeight);
  // 重置相机投影的相关参数
  k = window.innerWidth/window.innerHeight;//窗口宽高比
  camera.left = -s*k;
  camera.right = s*k;
  camera.top = s;
  camera.bottom = -s;
  // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
  // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
  // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
  camera.updateProjectionMatrix ();
}

改变相机lookAt位置没有成功

注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数

// const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);const camera = new THREE.PerspectiveCamera(30, width / height, 1, 8000);
camera.position.set(2000, 2000, 2000);// camera.lookAt(0, 0, 0);// 改变相机观察目标点
camera.lookAt(1000, 0, 1000);
// 设置相机控件轨道控制器OrbitControlsconst controls = new OrbitControls(camera, renderer.domElement);// 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0// console.log('controls.target', controls.target);
controls.target.set(1000, 0, 1000);
controls.update();//update()函数内会执行camera.lookAt(controls.targe)

思考

代码一定要按自己的思路写,不要直接copy官方实例,因为不是你自己写的,有些问题你根本发现不了。因为官网已经给你写好了