threejs--02threejs手册起步+进阶

快速过一下基本用法

入门

01 场景

//场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );//大小
document.body.appendChild( renderer.domElement );

//加物体
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 );//加到场景中

camera.position.z = 5;//设置位置

function animate() {
	//使渲染器能够在每次屏幕刷新时对场景进行绘制的循环,当用户切换到其它的标签页时,它会暂停(跟setInterval差不多)。不然以上代码画面是空的
	requestAnimationFrame( animate );

	cube.rotation.x += 0.01;//动画
	cube.rotation.y += 0.01;//动画
	
	renderer.render( scene, camera );//渲染
}
animate();

02 兼容性检查

引入WebGL.js

if (WebGL.isWebGLAvailable()) {
    // Initiate function or other initializations here
    animate();
} else {
    const error = WebGL.getWebGLErrorMessage();
    console.error(error)
}

03 画线

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

const renderer = new THREE.WebGLRenderer();//渲染器
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
camera.position.set( 0, 0, 100 );//相机位置
camera.lookAt( 0, 0, 0 );//相机朝向

const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );//材质
//构建三维几何体
const points = [];
points.push( new THREE.Vector3( - 10, 0, 0 ) );//Vector3--三维空间中的向量
points.push( new THREE.Vector3( 0, 10, 0 ) );//Vector3--三维空间中的向量
points.push( new THREE.Vector3( 10, 0, 0 ) );//Vector3--三维空间中的向量
//线是画在每一对连续的顶点之间的,不闭合
const geometry = new THREE.BufferGeometry().setFromPoints( points );//BufferGeometry--定义3D几何体
const line = new THREE.Line( geometry, material );//物体=几何体+材质

scene.add( line );//加到场景中
renderer.render( scene, camera );//渲染

04 字体

  1. CSS2DRenderer 或CSS3DRenderer

2.TextureLoader

const texture = new THREE.TextureLoader().load( "textures/water.jpg" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 4, 4 );

3.TextGeometry

new THREE.TextGeometry( text, parameters );
import * as THREE from 'three';
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

THREE.Cache.enabled = true;

let container;
let camera, cameraTarget, scene, renderer;
let group, textMesh1, textMesh2, textGeo, materials;
let firstLetter = true;
let text = 'three.js',
  bevelEnabled = true,
  font = undefined,
  fontName = 'optimer', // helvetiker, optimer, gentilis, droid sans, droid serif
  fontWeight = 'bold'; // normal bold

const depth = 20,
  size = 70,
  hover = 30,
  curveSegments = 4,
  bevelThickness = 2,
  bevelSize = 1.5;

const mirror = true;

const fontMap = {
  helvetiker: 0,
  optimer: 1,
  gentilis: 2,
  'droid/droid_sans': 3,
  'droid/droid_serif': 4,
};

const weightMap = {
  regular: 0,
  bold: 1,
};

const reverseFontMap = [];
const reverseWeightMap = [];

for (const i in fontMap) reverseFontMap[fontMap[i]] = i;
for (const i in weightMap) reverseWeightMap[weightMap[i]] = i;

let targetRotation = 0;
let targetRotationOnPointerDown = 0;

let pointerX = 0;
let pointerXOnPointerDown = 0;

let windowHalfX = window.innerWidth / 2;

let fontIndex = 1;

init();
animate();

function init() {
  container = document.createElement('div');
  document.body.appendChild(container);

  //相机
  camera = new THREE.PerspectiveCamera(30,window.innerWidth / window.innerHeight,1,1500,);
  camera.position.set(0, 400, 700);
  cameraTarget = new THREE.Vector3(0, 150, 0);

  //场景
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000);
  scene.fog = new THREE.Fog(0x000000, 250, 1400);//雾

  //光
  const dirLight = new THREE.DirectionalLight(0xffffff, 0.4);
  dirLight.position.set(0, 0, 1).normalize();
  scene.add(dirLight);

  const pointLight = new THREE.PointLight(0xffffff, 4.5, 0, 0);
  pointLight.color.setHSL(Math.random(), 1, 0.5);
  pointLight.position.set(0, 100, 90);
  scene.add(pointLight);

  materials = [
    new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true }), // front
    new THREE.MeshPhongMaterial({ color: 0xffffff }), // side
  ];
  //字体组
  group = new THREE.Group();
  group.position.y = 100;
  scene.add(group);
  loadFont();
  //平面
  const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(10000, 10000),
    new THREE.MeshBasicMaterial({
      color: 0xffffff,
      opacity: 0.5,
      transparent: true,
    }),
  );
  plane.position.y = 100;
  plane.rotation.x = -Math.PI / 2;
  scene.add(plane);

  //渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);
}



function createText() {
  textGeo = new TextGeometry(text, {
    font: font,
    size: size,
    depth: depth,
    curveSegments: curveSegments,
    bevelThickness: bevelThickness,
    bevelSize: bevelSize,
    bevelEnabled: bevelEnabled,
  });

  textGeo.computeBoundingBox();

  const centerOffset =
    -0.5 * (textGeo.boundingBox.max.x - textGeo.boundingBox.min.x);

  textMesh1 = new THREE.Mesh(textGeo, materials);

  textMesh1.position.x = centerOffset;
  textMesh1.position.y = hover;
  textMesh1.position.z = 0;

  textMesh1.rotation.x = 0;
  textMesh1.rotation.y = Math.PI * 2;

  group.add(textMesh1);
  //倒影
  if (mirror) {
    textMesh2 = new THREE.Mesh(textGeo, materials);

    textMesh2.position.x = centerOffset;
    textMesh2.position.y = -hover;
    textMesh2.position.z = depth;

    textMesh2.rotation.x = Math.PI;
    textMesh2.rotation.y = Math.PI * 2;

    group.add(textMesh2);
  }
}

function refreshText() {
  group.remove(textMesh1);
  if (mirror) group.remove(textMesh2);

  if (!text) return;

  createText();
}
//载入字体模型
function loadFont() {
  const loader = new FontLoader();
  loader.load(
    'fonts/' + fontName + '_' + fontWeight + '.typeface.json',
    function (response) {
      font = response;

      refreshText();
    },
  );
}
function animate() {
  requestAnimationFrame(animate);//动起来
  render();//渲染
}

function render() {
  group.rotation.y += (targetRotation - group.rotation.y) * 0.05;
  camera.lookAt(cameraTarget);//相机朝向

  renderer.clear();//清除
  renderer.render(scene, camera);//渲染
}

05 载入载入3D模型

const loader = new GLTFLoader();

loader.load( 'path/to/model.glb', function ( gltf ) {
	scene.add( gltf.scene );
}, undefined, function ( error ) {
	console.error( error );
} );

进阶

01 场景中更新物体等

Object3D

Object3D对象默认会自动更新

const object = new THREE.Object3D();
scene.add( object );

//手动更新
object.matrixAutoUpdate = false;
object.updateMatrix();
BufferGeometry

不能调整 buffers 大小(这种操作开销很大,相当于创建了个新的geometry)。 但你可以更新 buffers的内容。必须预先分配足够大的buffer来容纳可能创建的任何新顶点。
重点

//更改第一次渲染后渲染的点数
line.geometry.setDrawRange( 0, newValue );
//在第一次渲染后更改position数值
positionAttribute.needsUpdate = true;

完整

//预先分配足够大的buffer
const MAX_POINTS = 500;

// geometry
const geometry = new THREE.BufferGeometry();

// attributes
const positions = new Float32Array( MAX_POINTS * 3 ); // 3 vertices per point
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );

// setDrawRange来改变
const drawCount = 2; // draw the first 2 points, only
geometry.setDrawRange( 0, drawCount );

// 材质
const material = new THREE.LineBasicMaterial( { color: 0xff0000 } );

// 线
const line = new THREE.Line( geometry, material );
scene.add( line );


//加点
const positionAttribute = line.geometry.getAttribute( 'position' );
let x = 0, y = 0, z = 0;
for ( let i = 0; i < positionAttribute.count; i ++ ) {
	positionAttribute.setXYZ( i, x, y, z );
	x += ( Math.random() - 0.5 ) * 30;
	y += ( Math.random() - 0.5 ) * 30;
	z += ( Math.random() - 0.5 ) * 30;
}
//更改第一次渲染后渲染的点数
line.geometry.setDrawRange( 0, newValue );
//在第一次渲染后更改position数值
positionAttribute.needsUpdate = true;
材质(Materials)
  • 所有uniforms值都可以自由改变(比如 colors, textures, opacity 等等),这些数值在每帧都发给shader。
  • GL状态相关参数也可以随时改变(depthTest, blending, polygonOffset 等)。
  • 在运行时无法轻松更改以下属性(一旦material被渲染了一次):uniforms的数量和类型、是否存在texture|fog|vertex colors|morphing|shadow map|alpha test| transparent
//需要设置
material.needsUpdate = true
纹理(Textures)
//如果更改了图像,画布,视频和数据纹理,需要设置
material.needsUpdate = true
相机(Cameras)

相机的位置和目标会自动更新。 如果你需要改变fov aspect near far,需要重新计算投影矩阵:

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

02 废置对象

BufferGeometry.dispose()
Material.dispose()
Texture.dispose()
WebGLRenderTarget.dispose()

03 创建VR内容

对于VR项目来说,使用的是setAnimationLoop调整动画循环

import { VRButton } from 'three/addons/webxr/VRButton.js';

renderer.xr.enabled = true;
renderer.setAnimationLoop( function () {
	renderer.render( scene, camera );
} );

04 后期处理

应用一个或多个图形效果,例如景深、发光、胶片微粒或是各种类型的抗锯齿等可以使用后期处理达成。EffectComposer(效果合成器)

const composer = new EffectComposer( renderer );

function animate() {
	requestAnimationFrame( animate );
	//不再调用WebGLRenderer的render方法,而是使用EffectComposer中对应的render方法
	composer.render();
}

//后期处理过程链
const renderPass = new RenderPass( scene, camera );
composer.addPass( renderPass );

const glitchPass = new GlitchPass();
composer.addPass( glitchPass );

const outputPass = new OutputPass();
composer.addPass( outputPass );

自定义后期处理着色器

import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { LuminosityShader } from 'three/addons/shaders/LuminosityShader.js';

// later in your init routine

const luminosityPass = new ShaderPass( LuminosityShader );
composer.addPass( luminosityPass );

05 矩阵变换

matrixAutoUpdate

默认情况下,matrixAutoUpdate属性设置为true,并且将自动重新计算矩阵。如果对象是静态的,或者您希望在重新计算时手动控制,则可以通过将属性设置为false来获得更好的性能

  1. 修改对象的position,quaternion和scale属性
object.position.copy( start_position );
object.quaternion.copy( quaternion );
object.matrixAutoUpdate = false;
object.updateMatrix();
  1. 直接修改对象的矩阵
object.matrix.setRotationFromQuaternion( quaternion );
object.matrix.setPosition( start_position );
object.matrixAutoUpdate = false;
对象和世界矩阵

一个对象的matrix存储了该对象 相对于 其Object3D.parent(父节点)的变换。要在 世界 坐标系中获取对象的转换,您必须访问该对象的Object3D.matrixWorld。

当父对象或子对象的变换发生更改时,可以通过调用[page:Object3D.updateMatrixWorld updateMatrixWorld()]来请求更新子对象的matrixWorld。

旋转和四元数

Three.js提供了两种表示3D旋转的方式:Euler angles(欧拉角)和Quaternions(四元数),以及两者之间的转换方法。 欧拉角有称为“万向节锁定”的问题,其中某些配置可能失去一定程度的自由度(防止物体绕一个轴旋转)。 因此,对象旋转 始终 存储在对象的quaternion中。

您应该使用setRotationFromEuler方法,该方法将更新四元数。

06 动画系统

let mesh;

// 新建一个AnimationMixer, 并取得AnimationClip实例列表
const mixer = new THREE.AnimationMixer( mesh );
const clips = mesh.animations;

// 在每一帧中更新mixer
function update () {
	mixer.update( deltaSeconds );
}

// 播放一个特定的动画
const clip = THREE.AnimationClip.findByName( clips, 'dance' );
const action = mixer.clipAction( clip );
action.play();

// 播放所有动画
clips.forEach( function ( clip ) {
	mixer.clipAction( clip ).play();
} );

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值