ThreeJS從創(chuàng)建場景到使用功能實(shí)例詳解
前言
最近公司要做一個2.5D插件,然后自己學(xué)旋轉(zhuǎn)角度不太好,然后就使用了THREEJS, 用起來還是比較繁瑣的,整體支持不太好,整體都是自己研究,看到寫的不好地方勿怪
創(chuàng)建場景以及相機(jī)
首先,要創(chuàng)建一個場景,以及一個相機(jī)(相機(jī)分為透視相機(jī)和正交攝像機(jī),區(qū)別在后面會解釋),代碼如下
export default class ThreeComponent extends React.Component<any, any> {
private mount: any
private camera: any
private scene: any
private renderer: any
componentDidMount() {
this.init()
this.renders()
}
init = () => {
// 相機(jī)
this.camera = new THREE.PerspectiveCamera(30, this.mount.clientWidth / this.mount.clientHeight, 1, 2500)
this.camera.position.set(500, 800, 1300)
this.camera.lookAt(30, 0, 0)
// 場景
this.scene = new THREE.Scene()
this.scene.background = new THREE.Color(0x000000)
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
this.renderer.setClearColor(0xEEEEEE, 0.0)
this.renderer.setPixelRatio(window.devicePixelRatio)
this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight)
this.mount.appendChild(this.renderer.domElement)
window.addEventListener('resize', () => this.onWindowResize.bind(this))
}
onWindowResize = () => {
this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight)
this.renders()
}
renders = () => {
this.renderer.render(this.scene, this.camera)
}
render() {
return (
<div id='canvas'
style={{ width: '100%', height: '100%' }}
ref={(mount) => {
this.mount = mount
}}/>
)
}
}創(chuàng)建一個平面
相機(jī)和平面創(chuàng)建完成,接來下我這邊是直接創(chuàng)建一個平面放到場景中,代碼如下
const geometry = new THREE.PlaneGeometry(800, 400)
// 設(shè)置透明以及顏色
const material = new THREE.MeshBasicMaterial({ color: 0x091A20, transparent: true, opacity: 0.8 })
const plane = new THREE.Mesh(geometry, material)
// 這邊操作的是旋轉(zhuǎn)還是位置
plane.rotation.x = 300.1
plane.rotation.y = 0
plane.rotation.z = 49.8
plane.rotation.y = 0
plane.position.x = 120
plane.position.y = 200
this.scene.add(plane)添加圖片
const image = require('../../assets/images/test.png').default
// 因?yàn)樘砑訄D片加載是異步的,所以在load方法中操作,每次加載之后都要執(zhí)行一遍renders方法,重新渲染場景
new THREE.TextureLoader().load(image, (texture) => {
// 設(shè)置透明度,以及基礎(chǔ)材質(zhì)的map
const mat = new THREE.MeshBasicMaterial({ map: texture, transparent: true })
const geom = new THREE.BoxGeometry(100, 100)
const mesh = new THREE.Mesh(geom, mat)
mesh.receiveShadow = true
mesh.rotation.z = 19.7
mesh.position.x = 0
mesh.position.y = -30
// 往plane平面中添加,這樣就可以直接放到plane中,位置就是plane的位置
plane.add(mesh)
this.renders()
})創(chuàng)建線
首先要說,因?yàn)檎hreeJs的line不能設(shè)置線寬,所以要用到的MeshLine,github地址為: MeshLine
// 這里引入MeshLine
import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline'
const mat = new THREE.MeshBasicMaterial({ map: texture1, transparent: true })
const boxGeom = new THREE.BoxGeometry(60, 150)
const mesh = new THREE.Mesh(boxGeom, mat)
const mat1 = new THREE.MeshBasicMaterial({ map: texture2, transparent: true })
const boxGeom1 = new THREE.BoxGeometry(60, 150)
const mesh1 = new THREE.Mesh(boxGeom1, mat1)
const point = []
point.push(mesh.position) // mesh的位置
point.push(mesh1.position) // mesh1的位置
// 點(diǎn)對點(diǎn)的線
const line = new MeshLine()
line.setPoints(point)
const lineMaterial = new MeshLineMaterial({
color: new THREE.Color(0xffffff),
lineWidth: 10,
transparent: true,
opacity: 0.5
})
// 添加線
const lineMesh = new THREE.Mesh(line.geometry, lineMaterial)
plane.add(mesh)
plane.add(mesh1)
plane.add(lineMesh)
// 更新完之后在執(zhí)行一遍render,把東西渲染到畫布中
this.renders()添加軸線
const axesHelper = new THREE.AxesHelper(800)
this.scene.add(axesHelper)縮放、定位、以及旋轉(zhuǎn)
// 縮放功能對應(yīng)mesh進(jìn)行縮放,每個mesh添加后都有固定的position, rotation, scale 屬性
mesh.position.set(x, y, z)
mesh.rotation.set(x, y, z)
mesh.scale.set(x, y, z)
// 也可以這樣, scale, rotation 都可以這么設(shè)置
mesh.position.x = 0
mesh.position.y = 0
mesh.position.z = 0添加文字
添加文字使用threeJS官方的添加文字需要導(dǎo)入json文件,而且還需要中文配置,所以使用起來占用內(nèi)存會比較大,所以當(dāng)前項(xiàng)目中使用的是Canvas導(dǎo)入文字圖片
//創(chuàng)建canvas
const canvas = document.getElementById('text-canvas') as HTMLCanvasElement
const ctx = canvas?.getContext('2d') as any
canvas.width = 100
canvas.height = 100
ctx.fillStyle = 'transparent'
ctx.fillRect(0, 0, 100, 100)
ctx.fillStyle = '#FFFFFF'
ctx.font = `normal ${attr.fontSize ?? 14}px "楷體"`
ctx.fillText(text.length > 5 ? text.substr(0, 5) + '...' : text, 0, 40)
// 導(dǎo)出圖片路徑
const url = canvas.toDataURL('image/png')
// 設(shè)置圖片位置等信息
new THREE.TextureLoader().load(url, (texture: any) => {
const textGeom = new THREE.PlaneGeometry(200, 200)
const mat1 = new THREE.MeshBasicMaterial({
map: texture,
transparent: true
})
const mesh1 = new THREE.Mesh(textGeom, mat1)
mesh1.position.set(attr.x, attr.y, attr.z)
if (attr.rotation !== undefined) {
mesh1.rotation.set(attr.rotation.x, attr.rotation.y, attr.rotation.z)
}
mesh1.scale.set(0.8, 0.8, 0.8)
if (attr.group !== undefined) {
attr.group.add(mesh1)
plane.add(attr.group)
} else {
plane.add(mesh1)
}
this.renders()
})正交攝像機(jī)和透視攝像機(jī)的區(qū)別
這邊畫圖的話我就不畫了,這塊只是稍微的解釋一下,具體的可以看一下搜到的文章:正交相機(jī)的應(yīng)用
簡單來說
- 正交攝像機(jī)的特點(diǎn)就是:場景中遠(yuǎn)處的物體和近處的物體是一樣大的
- 透視攝像機(jī)的特點(diǎn)就是:場景中物體遵循近大遠(yuǎn)小的擺列,如果物體在最近,物體相對就會比較大
下面就是怎么使用這兩個相機(jī):
// 透視攝像機(jī)
this.camera = new THREE.PerspectiveCamera(30, this.mount.clientWidth / this.mount.clientHeight, 1, 2500)
// 正交攝像機(jī)
this.camera = new THREE.OrthographicCamera(width / -4, width / 4, height / 4, height / -4, -100, 10000)透視攝像機(jī)PerspectiveCamera屬性介紹(以下都是個人理解,如果有不清楚的歡迎指出):
- fov 攝像機(jī)視錐體垂直視野角度 (就是從攝像機(jī)看視角的角度有多大)
- aspect 攝像機(jī)視錐體長寬比 (通常就是你整個場景的長寬比)
- near 攝像機(jī)視錐體近端面 (就是攝像機(jī)最近看到的距離)
- far 攝像機(jī)視錐體遠(yuǎn)端面 (攝像機(jī)最遠(yuǎn)看到的距離,和near組合起來就相當(dāng)于你攝像機(jī)從某個位置到某個位置的整體能看到的一個面)
正交攝像機(jī)OrthographicCamera屬性介紹:
- left 攝像機(jī)視錐體左側(cè)面。
- right 攝像機(jī)視錐體右側(cè)面。
- top 攝像機(jī)視錐體上側(cè)面。
- bottom 攝像機(jī)視錐體下側(cè)面。
- 上面四個屬性推薦配置為場景的長款比,如代碼所示(使這個等式成立: | left / right | = 1,| top / buttom | = 1),如果不成立,可能看到的效果不太一樣
- near
- far
- 以上兩個屬性通透視攝像機(jī)原理
角度計(jì)算:
如果設(shè)計(jì)剛好給你出了一個圖,表示3d的位置等,這塊需要一個角度計(jì)算,就需要改動攝像機(jī)的位置,以及l(fā)ookAt屬性:
this.camera.position.set(x, y, z)
this.camera.lookAt(x, y, z)這個屬性的設(shè)置需要自己設(shè)置(目前算法還不太了解,之后可能了解了會更新一下),把自己想象成一個攝像機(jī),擺在哪里看到的效果都是不一樣的,然后lookAt就是你眼睛看哪個位置,可以看的偏移一點(diǎn)這樣的效果
總結(jié)
到此這篇關(guān)于ThreeJS從創(chuàng)建場景到使用功能的文章就介紹到這了,更多相關(guān)ThreeJS創(chuàng)建場景到使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript setAttribute, getAttribute 在不同瀏覽器上的不同表現(xiàn)
該方法把指定的屬性設(shè)置為指定的值。如果不存在具有指定名稱的屬性,該方法將創(chuàng)建一個新屬性。2010-08-08
原生js實(shí)現(xiàn)查詢天氣小應(yīng)用
這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)查詢天氣的小應(yīng)用,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
webpack學(xué)習(xí)教程之publicPath路徑問題詳解
這篇文章主要給大家介紹了webpack學(xué)習(xí)教程之publicPath路徑問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-06-06
深入學(xué)習(xí)JavaScript執(zhí)行上下文
這篇文章主要介紹了深入學(xué)習(xí)JavaScript執(zhí)行上下文,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下,希望對你的學(xué)習(xí)有所幫助2022-08-08

