Three.js+TypeScript+Webpack学习记录(五)

使用环境参考

Node.js v16.19.1

正文

为模型做展示资料板

在 three.js 中是可以传入 canvas 渲染内容作为纹理进行展示的,这里举个简单的例子,用 canvas 绘制文字,然后贴在一个 box 模型上:

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass'
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'
import { initCamera, initScene, initWebGLRenderer } from './init'
import { loadGLTF } from './load'
import { AnimationManager } from './animation'

class Game {
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    orbitControls: OrbitControls

    raycaster = new THREE.Raycaster()
    mouse = new THREE.Vector2()

    animationManager = new AnimationManager()

    composer: EffectComposer
    outlinePass: OutlinePass

    constructor() {
        this.scene = initScene()
        this.camera = initCamera()
        this.scene.add(this.camera)
        this.renderer = initWebGLRenderer()

        this.orbitControls = this.addOrbitControls(this.camera, this.renderer)
        this.addModel()
        this.addCanvasModel()
        this.addResizeEventListener()
        this.addClickEvent()

        this.composer = new EffectComposer(this.renderer)
        const renderPass = new RenderPass(this.scene, this.camera)
        this.composer.addPass(renderPass)

        const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera)
        outlinePass.edgeStrength = 4
        outlinePass.edgeGlow = 1
        outlinePass.edgeThickness = 1
        outlinePass.visibleEdgeColor.set('#ffffff')
        outlinePass.hiddenEdgeColor.set('#190a05')
        this.composer.addPass(outlinePass)

        this.outlinePass = outlinePass
    }

    addClickEvent() {
        this.renderer.domElement.addEventListener('pointermove', (ev) => {
            this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1
            this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1
            this.raycaster.setFromCamera(this.mouse, this.camera)
            const intersects = this.raycaster.intersectObject(this.scene, true)
            if (intersects.length) {
                this.outlinePass.selectedObjects = [intersects[0].object]
            } else {
                this.outlinePass.selectedObjects = []
            }
        })
    }

    addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
        const controls = new OrbitControls(camera, renderer.domElement)
        controls.autoRotate = true
        controls.enableDamping = true
        controls.update()
        return controls
    }

    addCanvasModel() {
        // 创建一个 canvas 元素,并在其中绘制内容
        const canvas = document.createElement('canvas')
        canvas.width = 256
        canvas.height = 256
        const context = canvas.getContext('2d')
        context.fillStyle = 'rgba(255, 255, 255, 0)' // 设置透明背景色
        context.fillRect(0, 0, canvas.width, canvas.height) // 绘制透明矩形
        context.font = '42px Arial'
        context.fillStyle = '#ffffff'
        context.fillText('kuokuo的椅子', 0, 100)
        context.fillText('售价:666元', 0, 160)
        // 将 canvas 元素创建为纹理,并应用到模型上
        const texture = new THREE.CanvasTexture(canvas)
        // 创建一个正方体模型
        const geometry = new THREE.BoxGeometry(0.65, 0.65, 0.001)
        const material = new THREE.MeshBasicMaterial({
            map: texture,
            transparent: true
        })
        const cube = new THREE.Mesh(geometry, material)
        cube.position.set(0, 1.2, 0)
        this.scene.add(cube)
    }

    async addModel() {
        const model = await loadGLTF('gltf/SheenChair.glb')
        const geometryArray = []
        const materialArray = []
        model.traverse(obj => {
            if (obj instanceof THREE.Mesh) {
                geometryArray.push(obj.geometry)
                materialArray.push(obj.material)
            }
        })
        const mergeGeo = mergeBufferGeometries(geometryArray, true)
        const group = new THREE.Mesh(mergeGeo, materialArray)
        this.scene.add(group)
    }

    addResizeEventListener() {
        window.addEventListener('resize', () => {
            this.camera.aspect = window.innerWidth / window.innerHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(window.innerWidth, window.innerHeight)
        })
    }

    startMainLoop() {
        // 等待一帧用于初始化
        Promise.resolve().then(() => {
            this.step()
        })
    }

    step() {
        requestAnimationFrame(this.step.bind(this))
        this.orbitControls && this.orbitControls.update()
        this.animationManager.step()
        // this.renderer.render(this.scene, this.camera)
        this.composer.render()
    }
}

const game = new Game()
game.startMainLoop()

做更炫酷的图表展示

既然可以引入 canvas 作为渲染纹理,那要说图表展示,引入 Echarts 岂不是更爽!

安装 npm i echarts,然后是声明文件 npm i @types/echarts --save-dev

import * as THREE from 'three'
import * as echarts from 'echarts'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass'
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'
import { initCamera, initScene, initWebGLRenderer } from './init'
import { loadGLTF } from './load'
import { AnimationManager } from './animation'

class Game {
    scene: THREE.Scene
    camera: THREE.PerspectiveCamera
    renderer: THREE.WebGLRenderer
    orbitControls: OrbitControls

    raycaster = new THREE.Raycaster()
    mouse = new THREE.Vector2()

    animationManager = new AnimationManager()

    composer: EffectComposer
    outlinePass: OutlinePass

    updateTexture: THREE.Texture

    constructor() {
        this.scene = initScene()
        this.camera = initCamera()
        this.scene.add(this.camera)
        this.renderer = initWebGLRenderer()

        this.orbitControls = this.addOrbitControls(this.camera, this.renderer)
        this.addModel()
        this.addCanvasModel()
        this.addResizeEventListener()
        this.addClickEvent()

        this.composer = new EffectComposer(this.renderer)
        const renderPass = new RenderPass(this.scene, this.camera)
        this.composer.addPass(renderPass)

        const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera)
        outlinePass.edgeStrength = 4
        outlinePass.edgeGlow = 1
        outlinePass.edgeThickness = 1
        outlinePass.visibleEdgeColor.set('#ffffff')
        outlinePass.hiddenEdgeColor.set('#190a05')
        this.composer.addPass(outlinePass)

        this.outlinePass = outlinePass
    }

    addClickEvent() {
        this.renderer.domElement.addEventListener('pointermove', (ev) => {
            this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1
            this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1
            this.raycaster.setFromCamera(this.mouse, this.camera)
            const intersects = this.raycaster.intersectObject(this.scene, true)
            if (intersects.length) {
                this.outlinePass.selectedObjects = [intersects[0].object]
            } else {
                this.outlinePass.selectedObjects = []
            }
        })
    }

    addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {
        const controls = new OrbitControls(camera, renderer.domElement)
        controls.autoRotate = true
        controls.enableDamping = true
        controls.update()
        return controls
    }

    addCanvasModel() {
        // 创建一个 canvas 元素,并在其中绘制内容
        const canvas = document.createElement('canvas')
        canvas.width = 256
        canvas.height = 256

        const data = []
        for (let i = 0; i < 5; ++i) {
            data.push(Math.round(Math.random() * 200))
        }

        const dataArr1 = [1, 2, 3, 2, 1, 3]
        const dataArr2 = [2, 1, 1, 2, 1, 2]

        const chart = echarts.init(canvas)
        const option: echarts.EChartOption<{ name: string, type: string, data: number[] }> = {
            title: {
                text: 'World Population'
            },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    type: 'shadow'
                }
            },
            legend: {},
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            xAxis: {
                type: 'value',
                boundaryGap: [0, 0.01]
            },
            yAxis: {
                type: 'category',
                data: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World']
            },
            series: [
                {
                    name: '2011',
                    type: 'bar',
                    data: dataArr1
                },
                {
                    name: '2012',
                    type: 'bar',
                    data: dataArr2
                }
            ]
        }
        chart.setOption(option)

        // 定时器变化
        setInterval(() => {
            // 随机加减
            const newArr1 = dataArr1.map(v => (v + Math.random()))
            const newArr2 = dataArr1.map(v => (v + Math.random()))
            option.series[0].data = newArr1
            option.series[1].data = newArr2
            chart.setOption(option)
        }, 600)

        // 将 canvas 元素创建为纹理,并应用到模型上
        const texture = new THREE.CanvasTexture(canvas)
        this.updateTexture = texture
        // 创建一个正方体模型
        const geometry = new THREE.BoxGeometry(1, 0.65, 0.001)
        const material = new THREE.MeshBasicMaterial({
            map: texture,
            transparent: true
        })
        const cube = new THREE.Mesh(geometry, material)
        cube.position.set(0, 1.2, 0)
        this.scene.add(cube)
    }

    async addModel() {
        const model = await loadGLTF('gltf/SheenChair.glb')
        const geometryArray = []
        const materialArray = []
        model.traverse(obj => {
            if (obj instanceof THREE.Mesh) {
                geometryArray.push(obj.geometry)
                materialArray.push(obj.material)
            }
        })
        const mergeGeo = mergeBufferGeometries(geometryArray, true)
        const group = new THREE.Mesh(mergeGeo, materialArray)
        this.scene.add(group)
    }

    addResizeEventListener() {
        window.addEventListener('resize', () => {
            this.camera.aspect = window.innerWidth / window.innerHeight
            this.camera.updateProjectionMatrix()
            this.renderer.setSize(window.innerWidth, window.innerHeight)
        })
    }

    startMainLoop() {
        // 等待一帧用于初始化
        Promise.resolve().then(() => {
            this.step()
        })
    }

    step() {
        requestAnimationFrame(this.step.bind(this))
        this.updateTexture && (this.updateTexture.needsUpdate = true)
        this.orbitControls && this.orbitControls.update()
        this.animationManager.step()
        // this.renderer.render(this.scene, this.camera)
        this.composer.render()
    }
}

const game = new Game()
game.startMainLoop()

更多文章与分享

Three 学习项目链接:https://github.com/KuoKuo666/threejs-study

个人网站:www.kuokuo666.com

2023!Day Day Up!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KUOKUO众享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值