这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
three.js 基础
Three.js 现学现卖 这篇文章会让你对 Three.js 有全面的了解。
实战经验
实时绘制
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
通过 requestAnimationFrame 实现了 Three.js 的渲染帧,只要我们改变场景类物体的属性,渲染帧就能实时绘制出属性变化后的物体。
几何体 Geometry
尺寸和位置
几何体 Geometry 提供了 .scale ( x, y, z ) 和 .scale.x = xxx 来实现尺寸的缩放,他们的区别主要在缩放的参照物。
一般来说,调用方法的都是基于几何体的当前的属性,直接更改属性值的基于几何体创建时的原始属性:
.scale()缩放基于当前已缩放过的尺寸.scale.x = xxx缩放基于原始的尺寸
使用 .translate(x, y, z) 和 .translate.x === xxx 移动以及``.rotateX()和.rotate.x` 旋转时也遵循这个规律。
计算尺寸
一旦进行了缩放,物体的尺寸就发生了变化,我们可以通过以下方式获取最新的尺寸信息:
geometry.computeBoundingBox() // 获取几何体的包围盒
let { y: height, x: width, z: depth } = geometry.boundingBox.getSize()
材质 Material
更改材质颜色
Material 的 color 属性都是 THREE.Color 的实例,它拥有多种改变颜色的方法,推荐如下方式:
material.color.setStyle(newColor)
newColor 可以是: "rgb(250, 0,0)", "rgb(100%, 0%, 0%)", "hsl(0, 100%, 50%)", "#ff0000", "#f00", or "red"
注意:不支持 rgba
透明颜色
首先,Three.js 不支持透明颜色(存疑,待验证)。目前使用的是透明材质+颜色的方式:
material = new this.THREE.MeshLambertMaterial({
color: 0xff3c60,
transparent: true
});
material.color.setStyle(color)
material.opacity = opacity
替换材质
其实更简单就是, 更改 Mesh 的 Material 属性
let cube = new THREE.Mesh(geometry, material)
cube = newMaterial // 更改为新的材质
立方体贴图
let materials = [
rightMaterial, // right
leftMaterial, // left
topMaterial, // top
bottomMaterial, // bottom
frontMaterial, // front
backMaterial, // back
]
let cube = new THREE.Mesh(geometry, material)
场景等比缩放
一般调整窗口大小时我们用下文的 resize 方法实现
// 创建渲染器
let renderer = new THREE.WebGLRenderer();
let camera = new THREE.PerspectiveCamera(75, width / height, 1, 800)
// 调整大小
function resize (width, height) {
camera.aspect = width / height
renderer.setSize(width, height);
}
要想做到物体在场景内占有的比率一定,我们可以使用如下方式:
// 以下为伪代码
const designW = W
const designH = H
const trueW = W1
const trueH = W2
let renderer = new THREE.WebGLRenderer();
let camera = new THREE.PerspectiveCamera(75, designW / designH, 1, 800)
// 绘制物体按照 designW 下对应的尺寸绘制
// 适配到当前场景宽高
camera.aspect = trueW / trueH
renderer.setSize(trueW, trueH);
动画
简单动画
在渲染帧内改变物体的属性就可以形成动画
function animate() {
requestAnimationFrame(animate);
// 改变物体的属性
boxMesh.rotateY(speed)
renderer.render(scene, camera);
}
Tween.JS 动画
利用 Tween.JS 我们可以轻松实现许多动画,详情参见 Tween.JS 文档
let ani = new TWEEN.Tween({ rotation: totalRotation1 })
.to({ rotation: totalRotation2 }, 1000)
.onUpdate(({rotation}) => {
boxMesh.rotation.y = rotation
})
.onComplete(() => {
console.log('ani complete')
})
.start()
function animate() {
requestAnimationFrame(animate);
// 改变物体的属性
ani.update()
renderer.render(scene, camera);
}
光源种类
光源和阴影
不能产生投影的光源有:环境光(AmbientLight)、半球光(HemisphereLight)
// 渲染器启用阴影
renderer.shadowMap.enabled = true
// 指定哪个光源能产生阴影
spotLight.castShadow = true
// 指定哪个物体能投射阴影,哪个物体能接受阴影
plane.receiveShadow = true
cube.receiveShadow = true
// 更改阴影质量
// 更改渲染器的投影类型,默认值是 THREE.PCFShadowMap
renderer.shadowMap.type = THREE.PCFSoftShadowMap
// 更改光源的阴影质量,默认值是 512
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
canvas 纹理
精灵文字
// 绘制canvas
let { fontSize, fontFamily, fontBold, color } = style
const canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
const fontStyle = `${fontBold ? 'bold' : 'normal'} ${fontSize}px ` + fontFamily
ctx.font = fontStyle
const { width } = ctx.measureText(text)
canvas.width = width
canvas.height = fontSize
ctx.font = fontStyle
ctx.fillStyle = color;
ctx.fillText(text, 0, fontSize);
// canvas 材质
let texture = new THREE.CanvasTexture(canvas)
// 生成纹理
渐变色的实现
let context = canvas.getContext( '2d' );
let gradient = context.createLinearGradient( 0, 0, 0, canvas.height);
gradient.addColorStop( 0, c1 );
gradient.addColorStop( 1, c2 );
context.fillStyle = gradient;
context.fillRect( 0, 0, canvas.width, canvas.height );
let canvasTexture = new this.THREE.CanvasTexture( canvas );
let canvasMaterial = new this.THREE.MeshLambertMaterial( { map: canvasTexture } );
3D 模型
加载 3D 模型
默认内置的模型都是 json 形式的,如果要加载其它格式的,需要自行按照官方示例引入对应的 Loader。
模型中心点
模型的中心点可能不在其几何中心,通过如下方式,可以重置中心点并将模型移动到三维坐标 (0,0,0) 处:
geometry.center()
若模型为 Three.Group 则要进行复杂的计算。
模型加载后纹理为纯黑色的处理
loader.load( 'xxx.obj', function ( object ) {
//需要添加的部分
object.scene.traverse(function ( child ) {
if ( child.isMesh ) {
child.material.emissive = child.material.color;
child.material.emissiveMap = child.material.map ;
}
});
scene.add( gltf.scene );
})
相关资料:
点击物体
let raycaster = new THREE.Raycaster()
let mouse = new THREE.Vector2()
onClick = (event) => {
// x, y 为左上点坐标
this.mouse.x = ((event.clientX - x) / width) * 2 - 1;
this.mouse.y = -((event.clientY - y) / height) * 2 + 1;
// update the picking ray with the camera and mouse position
this.raycaster.setFromCamera( this.mouse, this.camera.camera );
// calculate objects intersecting the picking ray
let intersects = this.raycaster.intersectObjects(this.boxGroup.children );
}
参考资料:ThreeJS中的点击与交互——Raycaster的用法
其他细节
- 在同一位置绘制多个平行平面时,会出现交错失真现象,可以调整数值,使平面在它的法线方向错开位置。
参考资料
-
Three.js 开发指南