常见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?

  1. 创建相机;
  2. 创建场景;
  3. 创建几何体,并且把几何体添加到场景中;
  4. 创建渲染器,关联场景和相机,并且添加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,此外还有PlaneGeometryCircleGeometryRingGeometrySphereGeometry等。

重要的公式:网格(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也提供了很多材质,如MeshBasicMaterialMeshDepthMaterialMeshPhongMaterial等。

 

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的基本物体:相机、场景、几何体、网格、材质、渲染器。

常见3d动画框架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 );

 

学习资源

分享中的代码:

原创文章,作者:sunnyman218,如若转载,请注明出处:https://blog.ytso.com/274590.html

(0)
上一篇 2022年7月15日
下一篇 2022年7月15日

相关推荐

发表回复

登录后才能评论