常见3d动画框架three.js
canvas可以获取哪些上下文?也就是canvas.getContext()的参数有哪些?(多选)
A. 2d
B. webgl
C. webgl2
D. bitmaprenderer
E. 3d
正确答案:A、B、C、D
bitmaprenderer简绍文档:
https://developer.mozilla.org/zh-CN/docs/Web/API/ImageBitmapRenderingContext
WebGL从入门到放弃
如何使用WebGL?
1.创建 WebGL上下文;
2.创建 WebGL 程序(WebGL Program);
3.将数据存入缓冲区;
4.将缓冲区数据读取到 GPU;
5.GPU 执行 WebGL 程序,输出结果,呈现画面。
WebGL例子:
// 创建 WebGL上下文
// 思考起:如果我们下面这一行代码注释掉会有什么问题吗?
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
// 1. 使用GLSL语言
const vertex = `
attribute vec2 position;
varying vec3 color;
void main() {
gl_PointSize = 1.0;
color = vec3(0.5 + position * 0.5, 0.0);
gl_Position = vec4(position * 0.5, 1.0, 1.0);
}
`;
// 渐变:gl_FragColor = vec4(color, 1.0)
// 红色:gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
// 黄色:gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0)
const fragment = `
precision mediump float;
varying vec3 color;
void main()
{
gl_FragColor = vec4(color, 1.0);
}
`;
// 顶点着色器(Vertex Shader)
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
// 片元着色器(Fragment Shader)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(fragmentShader);
// 2. 创建 WebGL 程序(WebGL Program)
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const points = new Float32Array([
-1, -1,
0, 1,
1, -1,
]);
// 将数据存入缓冲区
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
// 将缓冲区数据读取到GPU
const vPosition = gl.getAttribLocation(program, 'position');
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
// GPU 执行 WebGL 程序,输出结果,呈现画面
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
顶点着色器(Vertex Shader): 处理顶点的 GPU 程序代码。它可以改变顶点的信息(如顶点的坐标、法线方向、材质等等)
片元着色器(Fragment Shader): 处理光栅化后的像素信息。
原生的WebGL使用起来还是比较困难的,这个时候就是使用框架的时候了,比如集团的Oasis,或者社区的Three.js。
Oasis:https://oasisengine.cn/
Three.js:https://threejs.org/
mrdoob:https://github.com/mrdoob
Three.js初体验
如何使用three.js?
- 创建相机;
- 创建场景;
- 创建几何体,并且把几何体添加到场景中;
- 创建渲染器,关联场景和相机,并且添加DOM元素到html中。
入门的例子,我们直接使用Three.js官网提供的第一个例子:
import * as THREE from 'three';
// 创建相机
const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
// 创建场景
const scene = new THREE.Scene();
// 创建正方体
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// 渲染器
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );
// 动画
function animation( time ) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
renderer.render( scene, camera );
}
上面有几个比较重要的概念:相机、场景、几何体、网格、材质、渲染器。
番外:浏览器原生es-module
Talk is cheap, show me the code.
export default {
a: 1,
sayHello: () => {
console.log('hello');
}
};
<script type="module"> import test from './03_es_module.js'; console.log(test.a); test.sayHello(); </script>
思考:官网的例子如果不使用importmap,那么怎么去使用es-module来引入three.js呢?
import * as THREE from '//unpkg.com/three@0.142.0/build/three.module.js';
场景
场景(Scene):相当于是一个容器,可以在它上面添加光线,物体等,最后Three.js把它和相机一起渲染到DOM中。
文档:https://threejs.org/docs/index.html?q=Scene#api/en/scenes/Scene
修改场景颜色:
scene.background = new THREE.Color('orange');
Three.js颜色表示:
// 颜色的关键字
var color = new THREE.Color('orange');
// 默认背景,白色的 注意Three.js渲染的默认背景是黑色的
var color = new THREE.Color();
// 十六进制数字
var color = new THREE.Color( 0xff0000 );
// RGB字符串
var color = new THREE.Color("rgb(255, 0, 0)");
var color = new THREE.Color("rgb(100%, 0%, 0%)");
// HSL字符串
var color = new THREE.Color("hsl(0, 100%, 50%)");
// RGB的值 取值范围0~1 如红色:
var color = new THREE.Color( 1, 0, 0 );
相机
文档:https://threejs.org/docs/index.html?q=Camera#api/en/cameras/Camera
Three.js支持两种摄像机透视投影摄像机和正交投影摄像机。
透视投影摄像机(PerspectiveCamera)是最常用的摄像机,他跟我们的眼睛类似,越近的物体看到的越大,越远的物体看到的越小。
PerspectiveCamera的构造方法有4个参数,分别是视场、长宽比、近处距离、远处距离,其中视场表示眼睛看到的度数。
正交投影摄像机(OrthographicCamera)看到相同大小的物体,都是一样大的。其实相当于平行光照射到一个平面上的映射。
OrthographicCamera的构造方法有6个参数,分别是left、right、top、bottom、near、far,即左边、右边、上边、下边、近处和远处的位置,6个值刚好确定了一个长方体,正是投射的长方体。
官方给的这个例子可以帮助我们去理解:https://threejs.org/examples/#webgl_camera
几何体
几何体(Geometry)描述了网格的形状。Three.js内置了好多的几何体,比如我们上面用到的BoxGeometry,此外还有PlaneGeometry、CircleGeometry、RingGeometry、SphereGeometry等。
重要的公式:网格(Mesh) = 几何体(Geometry) + 材质(Material)
import * as THREE from 'three';
const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 100 );
camera.position.x = 0;
camera.position.y = 2;
camera.position.z = 50;
const scene = new THREE.Scene();
// 添加正方体
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterial = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 正方体位置
cube.position.x = -6;
cube.position.y = -6;
cube.position.z = 0;
// 把正方体添加到场景中
scene.add(cube);
// 添加小球
const sphereGeometry = new THREE.SphereGeometry(2, 20, 20);
const sphereMaterial = new THREE.MeshNormalMaterial();
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
// 小球位置
sphere.position.x = 6;
sphere.position.y = -6;
sphere.position.z = 0;
// 把小球添加到场景中
scene.add(sphere);
// 添加一片平地
const planeGeometry = new THREE.PlaneGeometry(30, 30, 100, 100);
const planeMaterial = new THREE.MeshNormalMaterial();
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 由于平地添加后默认是在正前方 所以需要旋转一下
plane.rotation.x = -0.5 * Math.PI;
plane.position.y = -10;
scene.add(plane);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );
// 看向场景
camera.lookAt(scene.position);
function animation(time) {
cube.rotation.x = time / 2000;
cube.rotation.y = time / 1000;
sphere.rotation.x = time / 2000;
sphere.rotation.y = time / 1000;
renderer.render( scene, camera );
}
材质
在WebGL中只能绘制3种东西,分别是点、线和三角形。
材质描述了几何体的颜色、感光等信息。我们上面用到了MeshNormalMaterial,同样的Three.js也提供了很多材质,如MeshBasicMaterial、MeshDepthMaterial、MeshPhongMaterial等。
const cubeMaterial = new THREE.MeshBasicMaterial({
color: '#ff0000',
wireframe: true,
opacity: 1,
});
const sphereMaterial = new THREE.MeshBasicMaterial({
color: new THREE.Color('yellow'),
wireframe: true,
opacity: 0.5,
});
相如正方体这种也可以给一个数组:
const cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
const cubeMaterials = [];
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 0xff0000}));
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 0x00ff00}));
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 0x0000ff}));
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 'orange'}));
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 'yellow'}));
cubeMaterials.push(new THREE.MeshBasicMaterial({color: 'grey'}));
const cube = new THREE.Mesh(cubeGeometry, cubeMaterials);
使用图片:
const texture = new THREE.TextureLoader().load('./asserts/lufei.jpeg');
const material = new THREE.MeshBasicMaterial({ map: texture });
// 添加正方体
const cubeGeometry = new THREE.BoxGeometry(8, 8, 8);
const cube = new THREE.Mesh(cubeGeometry, material);
// 正方体位置
cube.position.x = -6;
cube.position.y = -2;
cube.position.z = 0;
// 把正方体添加到场景中
scene.add(cube);
// 添加小球
const sphereGeometry = new THREE.SphereGeometry(5, 20, 20);
const sphere = new THREE.Mesh(sphereGeometry, material);
// 小球位置
sphere.position.x = 6;
sphere.position.y = -2;
sphere.position.z = 0;
// 把小球添加到场景中
scene.add(sphere);
更多Texture相关文档请看这里:https://threejs.org/docs/index.html?q=Texture#api/en/constants/Textures
光源
为了让3D效果更立体那么必须加入光源,加入光源后不同的物体还会形成阴影。Three.js也支持好几种光源,本节就简绍一下。
Three.js默认是不支持生成阴影的,主要是为了保证性能。这里我们详细以SpotLight(聚光灯光源)来简绍如何使用光源并生成阴影。
SpotLight(聚光灯光源)
使用光源并添加阴影主要分为4个步骤:
1.添加光源并设置可以传播阴影:
// 添加光源 const spotLight = new THREE.SpotLight(0xffffff); spotLight.position.set(0, 10, 0); spotLight.castShadow = true; scene.add(spotLight);
2.使用可以感光的材质。
const cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
const sphereMaterial = new THREE.MeshLambertMaterial({color: 0x00ff00});
const planeMaterial = new THREE.MeshLambertMaterial({color: 0xdddddd});
3.设置物体传播(产生)阴影或接收阴影:
cube.castShadow = true; sphere.castShadow = true; plane.receiveShadow = true;
4.渲染器开启阴影映射。
renderer.shadowMap.enabled = true;
PointLight(点光源)
PointLight(点光源)是从一个点散发出的光源,点光源不会产生阴影。
const pointLight = new THREE.PointLight("#ffd200");
scene.add(pointLight);
DirectionalLight(直线光)
DirectionalLight(直线光)顾名思义是一种平行的直线光源(平行光光源)。平行光光源的光线是平行的,可以产生阴影,所有光的强度都一样。它有一个target属性表示照射到哪个位置上,另外可以使用directionalLight.shadow.camera.left来设置阴影的左边距,同样的也可以设置右边、上边、下边等边距,这样就可以确定一个阴影的范围(为了优化性能)。
// 添加光源
const directionalLight = new THREE.DirectionalLight('#ffffff');
directionalLight.position.set(0, 100, 0);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 512; // default
directionalLight.shadow.mapSize.height = 512; // default
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 1000;
directionalLight.shadow.camera.left = -15;
directionalLight.shadow.camera.right = 15;
directionalLight.shadow.camera.top = 15;
directionalLight.shadow.camera.bottom = -15;
scene.add(directionalLight);
// 光照指向平地
directionalLight.target = plane;
其他光源
AmbientLight的作用是给场景添加一种全局的颜色。该光源没有方向,也不产生阴影。如果你需要给场景中添加一种额外的统一的颜色,那么可以考虑使用AmbientLight,比如在上一个例子中添加一种紫色来烘托氛围,那么就可以使用该光源。
AmbientLight主要的作用就是给环境中添加一种颜色,还有一种给环境中添加颜色的光源,就是HemisphereLight。HemisphereLight是一种更加贴近自然的光源,往往用于模拟天空的光源,它的第一个参数表示天空的颜色,第二个参数表示地面(或者环境)的颜色,第三个参数是intensity表示强度。使用方法都差不多,如下:
const ambientLight = new THREE.AmbientLight('#9370DB');
scene.add(ambientLight);
const hemisphereLight = new THREE.HemisphereLight('#87ceeb', '#f5deb3', 0.4);
scene.add(hemisphereLight);
至此Three.js的基本概念我们都已经讲明白了,我们再回顾一下Three.js的基本物体:相机、场景、几何体、网格、材质、渲染器。

加载模型
Three.js提供了好多的Geometry,也可以使用自定义Geometry。当然还有一种更简单的添加几何体的办法就是使用已有的模型。Three.js支持很多的模型加载器,使用方式大致相同,以obj模型为例。
let goku = null;
new MTLLoader().setPath('./asserts/goku/').load('Goku.mtl', function ( materials ) {
materials.preload();
new OBJLoader().setMaterials( materials ).setPath( './asserts/goku/' ).load( 'Goku.obj', function ( object ) {
goku = object;
goku.position.y = -6;
goku.children.forEach(mesh => {
mesh.castShadow = true;
});
scene.add( goku );
});
});
控制器
Three.js提供了好多控制器,使用方式都是一样的,这里主要简绍一下TrackballControls(轨迹器控制器)。
const controls = new TrackballControls( camera, renderer.domElement );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
function animation(time) {
controls.update();
renderer.render( scene, camera );
}
document.addEventListener('dblclick',() => {
controls.reset();
});
function onWindowResize() {
const aspect = window.innerWidth / window.innerHeight;
camera.aspect = aspect;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
}
window.addEventListener( 'resize', onWindowResize );
学习资源
分享中的代码:📎share-three-js.zip
Three.js官网:https://threejs.org/
Three.js官方例子:https://threejs.org/examples/
Three.js Github:https://threejs.org/
Three.js中文教程:https://techbrood.com/threejs/docs/
《WebGL编程指南》:http://product.dangdang.com/23493086.html
《Three.js开发指南(第三版)》:http://product.dangdang.com/27893992.html
《HTML5 Canvas开发详解》:http://product.dangdang.com/672420753.html
《HTML5Canvas核心技术图形动画与游戏开发》:http://product.dangdang.com/671239642.html
相关demo可在文件中下载
author:凯哥

浙公网安备 33010602011771号