什么是three.js
Three.js是一款运行在浏览器中的 3D 引擎(基于WebGL的API的封装)
什么是WebGL?
WebGL(英语:Web Graphics Library)是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。显然,WebGL技术标准免去了开发网页专用渲染插件的麻烦,可被用于创建具有复杂3D结构的网站页面,甚至可以用来设计3D网页游戏等等。
three.js
本地环境搭建,因为spring的官方文档是访问起来比较慢,所以我们在本地搭建起来three.js的官方文档和案例
首先访问github.com/mrdoob/thre... 通过 git或者压缩包把代码下载到本地
使用parcel搭建three.js开发环境
为什么需要使用parcel 因为parcel不需要配置。webpack需要去进行配置。
第一个Three.js小demo
JavaScript
import * as THREE from "three";
//轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
//创建场景
const scence=new THREE.Scene();
//创建相机
const camera=new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000
)
//设置相机位置
camera.position.set(0,0,10);
//添加相机到场景
scence.add(camera);
//创建物体
//创建几何体
const geometry=new THREE.BoxGeometry(1,1,1);
//创建材质
const meterial=new THREE.MeshBasicMaterial({color:0x00ff00});
//根据几何体和材质,创建物体
const cube=new THREE.Mesh(geometry,meterial);
//控制物体移动
let x=0,y=1,z=0;
console.log(cube);
//缩放
cube.scale.set(.5,1,1.5);
//旋转
// cube.rotation.set(Math.PI/2,Math.PI*2,Math.PI/3,"YXZ");
cube.rotation.set(Math.PI/4,0,0,"YZX");
//将物体添加到场景中
scence.add(cube);
//初始化渲染器
const renderer=new THREE.WebGLRenderer();
//设置渲染器的尺寸大小
renderer.setSize(window.innerWidth,window.innerHeight);
//将webgl渲染的fcanvas内容添加到body
document.body.append(renderer.domElement);
//添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper( 5 );
scence.add( axesHelper );
//添加控制器,只有添加了控制器才能拖动查看
const controller=new OrbitControls(camera,renderer.domElement);
//每一帧渲染一次
const render=()=>{
x+=0.01;y+=0.01;z+=0.01;
if(x >5|| y >5 || z>5){x=0;y=0;z=0;}
cube.position.set(x,y,z);
cube.rotation.x+=0.01;
//使用渲染器,通过相机将场景渲染出来
renderer.render(scence,camera);
// requestAnimationFrame函数是浏览器提供了,浏览器每次请求一帧则执行一次回调
requestAnimationFrame(render);
}
render();
应用requestAnimationFrame
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
const scence=new THREE.Scene();
const camera=new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000
)
camera.position.set(0,0,10);
scence.add(camera);
const geometry=new THREE.BoxGeometry(1,1,1);
const meterial=new THREE.MeshBasicMaterial({color:0x00ff00});
const cube=new THREE.Mesh(geometry,meterial);
let x=0,y=1,z=0;
console.log(cube);
cube.scale.set(.5,1,1.5);
cube.rotation.set(Math.PI/4,0,0,"YZX");
scence.add(cube);
const renderer=new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper( 5 );
scence.add( axesHelper );
const controller=new OrbitControls(camera,renderer.domElement);
const render=(time)=>{
/**
* time参数表示每一帧的时间
*
*
* x+=0.01;y+=0.01;z+=0.01;
*
* 像这样每次加0.1是不对的,因为电脑的性能问题,会导致物体运动的不一样。
* 所以我们需要让物体有规律的运行
*
*
*/
//比如设置1秒运动多少
const t=time/1000 %5;
cube.rotation.x=t*1;
x=t*1,y=t*1,z=t*1;
cube.position.set(x,y,z);
renderer.render(scence,camera);
requestAnimationFrame(render);
}
render();
Gsap动画库(简单使用)
是一个功能十分强大的动画平台,能够帮助我们实现大部分的动画需求,构建高性能的适用于所有主流浏览器的高性能动画。
首先进行安装
- yarn add gsap
js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
//创建场景
const scence=new THREE.Scene();
//创建相机
const camera=new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000
)
//设置相机位置
camera.position.set(0,0,10);
//添加相机到场景
scence.add(camera);
//创建物体
//创建集合体
const geometry=new THREE.BoxGeometry(1,1,1);
//创建材质
const meterial=new THREE.MeshBasicMaterial({color:0x00ff00});
//根据几何体和材质,创建物体
const cube=new THREE.Mesh(geometry,meterial);
//控制物体移动
let x=0,y=1,z=0;
console.log(cube);
//缩放
cube.scale.set(.5,1,1.5);
//旋转
// cube.rotation.set(Math.PI/2,Math.PI*2,Math.PI/3,"YXZ");
cube.rotation.set(Math.PI/4,0,0,"YZX");
//将物体添加到场景中
scence.add(cube);
//初始化渲染器
const renderer=new THREE.WebGLRenderer();
//设置渲染器的尺寸大小
renderer.setSize(window.innerWidth,window.innerHeight);
//将webgl渲染的fcanvas内容添加到body
document.body.append(renderer.domElement);
//添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper( 5 );
scence.add( axesHelper );
//添加控制器,只有添加了控制器才能拖动查看
const controller=new OrbitControls(camera,renderer.domElement);
controller.enableDamping=true
//使用gsap的动画,来帮助我们设置物体的运动
gsap.to(cube.position, {x:5, duration: 2.5, ease: "power2.in" });
//每一帧渲染一次
const render=(time)=>{
//使用渲染器,通过相机将场景渲染出来
renderer.render(scence,camera);
// requestAnimationFrame函数是浏览器提供了,浏览器每次请求一帧则执行一次回调
requestAnimationFrame(render);
controller.update();
}
render();
监听画面变化,更新渲染画面
现在可以看到,当我们的浏览器的尺寸变化时,我们的渲染画面是没有跟着变的。所以我们需要监听画面的变化更新渲染页面。
js
//监听页面变化,更新摄像头
window.addEventListener("resize",()=>{
//更新摄像头
camera.aspect=window.innerWidth/window.innerHeight;
//更新摄影机的摄像矩阵
camera.updateProjectionMatrix();
//更新渲染器
renderer.setSize(window.innerWidth,window.innerHeight);
//设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
})
render();
js控制画面全屏与退出全屏
一般都是双击屏幕进入全屏,所以我们写双击进入&退出全屏
JavaScript
//给window绑定双击事件
window.addEventListener("dblclick",()=>{
//获取浏览器当前的全屏对象
/**
* 这里因为不同浏览器的兼容,所以我们使用两个
*/
const fullScreenElement =document.fullScreenElement|| document.webkitFullscreenElement
//当为null的时候,我们调用canvas的dom,请求进入全屏
if(!fullScreenElement){
renderer.domElement.requestFullscreen();
}else{
//直接使用document退出全屏
document.exitFullscreen();
}
});
dat.gui
dat.gui是一个轻量级的控制器,它可以在界面上来进行操作变量。 在webgl里就是一个控制器,通过控制器来进行物体的控制。
js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
//创建场景
const scence = new THREE.Scene();
//创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
const geometry = new THREE.BoxGeometry(1, 1, 1);
const meterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, meterial);
let x = 0, y = 1, z = 0;
cube.scale.set(.5, 1, 1.5);
cube.rotation.set(Math.PI / 4, 0, 0, "YZX");
scence.add(cube);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const gui = new datGui.GUI();
//添加控制物体移动
gui.add(cube.position, 'x')
.min(0).max(5).step(0.01)
.name('移动x轴').onChange(value => {
console.log("值被改变", value);
}).onFinishChange(value => {
{
console.log("停止修改", value);
}
});
const params = {
color: '#ffff00',
fn: () => { //这里的fn与下边的添加事件是对应的
gsap.to(cube.position, { x: 5, duration: 2, yoyo: true, repeat: -1 });
}
}
//颜色控制
gui.addColor(params, 'color').onChange(value => {
cube.material.color.set(value);
});
//选项框
gui.add(cube, 'visible').name('是否显示');
//添加文件夹
const folder = gui.addFolder('设置立方体');
//添加文件夹内容
folder.add(cube.material, 'wireframe');
//添加点击事件
gui.add(params, 'fn').name('点击运动')
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();
认识ThreeJs物体
几何体
在three.js中所有的几何体都是基于BufferGeomatry的基础上来进行构建的。
BufferGeomatry里有三个基础属性postion(位置),normar(法向量),uv(顶点);比如说我们一个矩形的立方体,有6个面,每个面又4个顶点,就是24个顶点,然后每个顶点又x,y,z三个方向上的位置,那么这里的数据就是72个。我们可以根据顶点的位置去进行添加材质和进行贴图。我们可以通过uv来进行我们的一个材质贴图。normal的作用呢就是顶点的法向量它的作用呢就是比如有一束光进行,我们需要知道这个光的一个折射位置和面的一个朝向。
通过BufferGeomatry来创建矩形
三个顶点就可以创建一个面
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
const scence = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
//创建基础的几何体
const geometry = new THREE.BufferGeometry();
const meterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });//设置材质
//顶点位置 (由两个三角形片组成一个矩形,一个三角形片是三个顶点,两个就是六个顶点,一个顶点有三个位置所以是长度是18的一维数组)
const vertices =new Float32Array([
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0,1.0,1.0,
1.0,1.0,1.0,
-1.0,1.0,1.0,
-1.0,-1.0,1.0
]);
//设置顶点 (每三个数据为一个顶点)
geometry.setAttribute('position',new THREE.BufferAttribute(vertices,3));
const mesh=new THREE.Mesh(geometry,meterial);
scence.add(mesh);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();
根据顶点创建酷炫三角形
JavaScript
//创建50个三角形,形成凌乱穿插效果
for (let i = 0; i < 50; i++) {
const geometry = new THREE.BufferGeometry();
//顶点坐标
const positionArray = new Float32Array(9);
//三个顶点组成一个三角形,每个顶点需要x,y,z三个坐标
for (let j = 0; j < 9; j++) {
positionArray[j] = Math.random() * 5;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3));
//颜色进行随机
const color = new THREE.Color(Math.random(), Math.random(), Math.random());
//创建材质
const meterial = new THREE.MeshBasicMaterial({
color,
transparent: true,//保持透明,
opacity: .5 //透明度
});
const mesh = new THREE.Mesh(geometry, meterial);
scence.add(mesh);
}
常用的网格几何体
three.js中给我们提供了很多的几何体我们可以去进行使用,不用自己再去进行设置一个很多顶点。
我们都可以通过在文档中去查找几何体,然后通过设置他们的构造参数来得到想要的物体。
认识Three的材质
上一个我们说到了物体,也就是几何体,我们通过几何体与材质进行一个贴合,才构建成了我们的一个物体。
基本颜色材质搭建
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
const scence = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
const renderer = new THREE.WebGLRenderer();
// 创建一个正方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
//导入纹理
const textureLoader=new THREE.TextureLoader().load('./public/door.jpg');
//创建材质
/**
* map是颜色材质纹理,具体的我们可以看文档
*/
const material = new THREE.MeshBasicMaterial({ map:textureLoader});
const cube = new THREE.Mesh(geometry, material);
scence.add(cube);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();
纹理偏移 旋转 重复
three.js给我们提供了,纹理在物体上如何进行设置(旋转,偏移,重复)
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
const scence = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
const renderer = new THREE.WebGLRenderer();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const texture = new THREE.TextureLoader().load('./public/door.jpg');
//纹理偏移
// texture.offset.set(0.5,0.5);
//纹理旋转
//texture.rotation=Math.PI/4;
//texture.center.set(0.5,0.5); //设置旋转原点
//纹理重复
texture.repeat.set(2, 3);
texture.wrapS=THREE.MirroredRepeatWrapping;
texture.wrapT=THREE.RepeatWrapping;
/**
* wrapS 指代的是水平方向上纹理重复如何包裹
* wrapT 指代的是垂直方向上纹理重复如何包裹
* MirroredRepeatWrapping和RepeatWrapping是three.js提供的常量值
*/
const material = new THREE.MeshBasicMaterial({ map: texture });
const cube = new THREE.Mesh(geometry, material);
scence.add(cube);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();
纹理的显示算法
magFilter,就是在渲染时一个纹素覆盖多个屏幕项目的情况下如何对这些纹素采样确定最终的颜色, 当使用THREE.LinerFilter时,它表示贴图将使用四个最接近的纹素,并在它们之间进行双线性插值。 当设置为THREE.NearestFilter时,它表示贴图将使用最接近的单个纹素的值,这意味着只会考虑最接近的一个纹素,而不考虑周围其他的纹素。
minFilter 当一个纹素小于一个屏幕像素时的设置
透明纹理
透明纹理的含义的就是,比如我们的门的图片,它展示的不仅仅是一个门的形状,它还包括边缘的一些形状,那么怎么办呢?three.js的材质里提供了属性alphaMap,它可以通过导入进来的材质图片,导入的材质图片吧必须只有黑色和白色,黑色就是完全透明,白色就是完全不透明,贴入之后,来展示形状,比如我们第二个导入进来的黑白图片,白色的就只是一个门的形状,在贴入之后我们就只能看到门了。
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
const scence = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
const renderer = new THREE.WebGLRenderer();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const texture = new THREE.TextureLoader().load('./public/door.jpg');
//导入透明材质纹理图片
const alphaMap=new THREE.TextureLoader().load('./public/door_opticy.jpeg');
const material = new THREE.MeshBasicMaterial({
map: texture,
alphaMap, // 透明材质
transparent:true, //透明
side:THREE.DoubleSide //单面 || 双面显示
});
const cube = new THREE.Mesh(geometry, material);
scence.add(cube);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();
环境遮挡贴图
环境贴图,就是比如我们的门,门上的一些纹理,我们需要显示的更加真实。
JavaScript
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as datGui from 'dat.gui';
const scence = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10);
scence.add(camera);
const renderer = new THREE.WebGLRenderer();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const texture = new THREE.TextureLoader().load('./public/door.jpg');
const alphaMap=new THREE.TextureLoader().load('./public/door_opticy.jpeg');
//引入环境贴图的图片
const aoMap=new THREE.TextureLoader().load('./public/aoDoor.jpg');
const material = new THREE.MeshBasicMaterial({
map: texture,
alphaMap,
transparent:true,
aoMap,
side:THREE.DoubleSide });
//规定,使用aoMap环境贴图的话需要给几何体添加第二组uv
geometry.setAttribute('uv2',new THREE.BufferAttribute(geometry.attributes.uv.array,2));
const cube = new THREE.Mesh(geometry, material);
scence.add(cube);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.append(renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scence.add(axesHelper);
const controller = new OrbitControls(camera, renderer.domElement);
const render = () => {
renderer.render(scence, camera);
requestAnimationFrame(render);
}
render();