ThreeJS從創(chuàng)建場(chǎng)景到使用功能實(shí)例詳解
前言
最近公司要做一個(gè)2.5D插件,然后自己學(xué)旋轉(zhuǎn)角度不太好,然后就使用了THREEJS, 用起來(lái)還是比較繁瑣的,整體支持不太好,整體都是自己研究,看到寫的不好地方勿怪
創(chuàng)建場(chǎng)景以及相機(jī)
首先,要?jiǎng)?chuàng)建一個(gè)場(chǎng)景,以及一個(gè)相機(jī)(相機(jī)分為透視相機(jī)和正交攝像機(jī),區(qū)別在后面會(huì)解釋),代碼如下
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) // 場(chǎng)景 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)建一個(gè)平面
相機(jī)和平面創(chuàng)建完成,接來(lái)下我這邊是直接創(chuàng)建一個(gè)平面放到場(chǎ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方法,重新渲染場(chǎng)景 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)建線
首先要說(shuō),因?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)對(duì)點(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)
// 縮放功能對(duì)應(yīng)mesh進(jìn)行縮放,每個(gè)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文件,而且還需要中文配置,所以使用起來(lái)占用內(nèi)存會(huì)比較大,所以當(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)用
簡(jiǎn)單來(lái)說(shuō)
- 正交攝像機(jī)的特點(diǎn)就是:場(chǎng)景中遠(yuǎn)處的物體和近處的物體是一樣大的
- 透視攝像機(jī)的特點(diǎn)就是:場(chǎng)景中物體遵循近大遠(yuǎn)小的擺列,如果物體在最近,物體相對(duì)就會(huì)比較大
下面就是怎么使用這兩個(gè)相機(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
屬性介紹(以下都是個(gè)人理解,如果有不清楚的歡迎指出):
- fov 攝像機(jī)視錐體垂直視野角度 (就是從攝像機(jī)看視角的角度有多大)
- aspect 攝像機(jī)視錐體長(zhǎng)寬比 (通常就是你整個(gè)場(chǎng)景的長(zhǎng)寬比)
- near 攝像機(jī)視錐體近端面 (就是攝像機(jī)最近看到的距離)
- far 攝像機(jī)視錐體遠(yuǎn)端面 (攝像機(jī)最遠(yuǎn)看到的距離,和near組合起來(lái)就相當(dāng)于你攝像機(jī)從某個(gè)位置到某個(gè)位置的整體能看到的一個(gè)面)
正交攝像機(jī)OrthographicCamera
屬性介紹:
- left 攝像機(jī)視錐體左側(cè)面。
- right 攝像機(jī)視錐體右側(cè)面。
- top 攝像機(jī)視錐體上側(cè)面。
- bottom 攝像機(jī)視錐體下側(cè)面。
- 上面四個(gè)屬性推薦配置為場(chǎng)景的長(zhǎng)款比,如代碼所示(使這個(gè)等式成立: | left / right | = 1,| top / buttom | = 1),如果不成立,可能看到的效果不太一樣
- near
- far
- 以上兩個(gè)屬性通透視攝像機(jī)原理
角度計(jì)算:
如果設(shè)計(jì)剛好給你出了一個(gè)圖,表示3d的位置等,這塊需要一個(gè)角度計(jì)算,就需要改動(dòng)攝像機(jī)的位置,以及l(fā)ookAt屬性:
this.camera.position.set(x, y, z) this.camera.lookAt(x, y, z)
這個(gè)屬性的設(shè)置需要自己設(shè)置(目前算法還不太了解,之后可能了解了會(huì)更新一下),把自己想象成一個(gè)攝像機(jī),擺在哪里看到的效果都是不一樣的,然后lookAt就是你眼睛看哪個(gè)位置,可以看的偏移一點(diǎn)這樣的效果
總結(jié)
到此這篇關(guān)于ThreeJS從創(chuàng)建場(chǎng)景到使用功能的文章就介紹到這了,更多相關(guān)ThreeJS創(chuàng)建場(chǎng)景到使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js如何獲取訪問IP、地區(qū)、當(dāng)前操作瀏覽器
這篇文章主要介紹了js如何獲取訪問IP、地區(qū)、當(dāng)前操作瀏覽器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07使用post方法實(shí)現(xiàn)json往返傳輸數(shù)據(jù)的方法
今天小編就為大家分享一篇關(guān)于使用post方法實(shí)現(xiàn)json往返傳輸數(shù)據(jù)的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03Bootstrap時(shí)間選擇器datetimepicker和daterangepicker使用實(shí)例解析
這篇文章主要為大家詳細(xì)解析了Bootstrap時(shí)間選擇器datetimepicker和daterangepicker使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09JavaScript實(shí)現(xiàn)圖形驗(yàn)證碼完整代碼
很多小伙伴都在學(xué)習(xí)JavaScript,可能也會(huì)有老師提出這樣一個(gè)問題,如何用js編寫一個(gè)簡(jiǎn)單的驗(yàn)證碼,這里就和大家分享一下,這篇文章主要給大家介紹了關(guān)于JavaScript實(shí)現(xiàn)圖形驗(yàn)證碼的相關(guān)資料,需要的朋友可以參考下2024-01-01javascript類型系統(tǒng) Array對(duì)象學(xué)習(xí)筆記
這篇文章主要介紹了javascript類型系統(tǒng)之Array對(duì)象,整理關(guān)于Array對(duì)象的學(xué)習(xí)筆記,感興趣的小伙伴們可以參考一下2016-01-01js 獲取網(wǎng)絡(luò)圖片的高度和寬度的實(shí)現(xiàn)方法(變通了下)
簡(jiǎn)單地說(shuō)就是把圖片放入一個(gè)自動(dòng)伸縮的DIV中,然后獲取DIV的寬和高!這個(gè)不錯(cuò)的變通,大家可以參考下。2009-10-10JS判斷當(dāng)前頁(yè)面是否在微信瀏覽器打開的方法
這篇文章主要介紹了JS判斷當(dāng)前頁(yè)面是否在微信瀏覽器打開的方法,涉及JavaScript針對(duì)客戶端的相關(guān)判定技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-12-12