【5月实习总结】0基础上手threejs一个月,这期间我遇到过的问题

1、threejs贴图顺序问题

threejs贴图顺序为:x轴(正方向,负方向)、y轴(正方向、负方向)、z轴(正方向、负方向)。【先正方向,后反方向】

例子:

(1)假设我们项目里面,x轴是水平方向、y轴是垂直方向、z轴是垂直屏幕正对着我们的方向。那么贴图顺序为:右左、上下、前后

(2)如果项目里面,X轴是垂直屏幕方向正对着我们、y轴是垂直方向、z轴是水平方向。那么贴图顺序为:后前、上下、右左

 

2、threejs贴图异步加载问题

Three.js 中的异步加载问题主要源于资源(如纹理、模型、音频)的加载是非阻塞的。如果处理不当,可能导致资源未完全加载就被使用,从而引发渲染异常(如黑屏、材质缺失)。

下面,我们使用promise解决异步加载问题。

2.1 Promise简约代码

看下逻辑还是比较简单的

function loadTexture(url) {
    return new Promise((resolve, reject) => {
        const loader = new THREE.TextureLoader();
        loader.load(
            url,
            (texture) => resolve(texture),
            undefined,
            (error) => reject(error)
        );
    });
}

// 并行加载多个纹理
async function createCubeWithTextures() {
    try {
        const [texture1, texture2] = await Promise.all([
            loadTexture('texture1.jpg'),
            loadTexture('texture2.jpg')
        ]);
        
        const materials = [
            new THREE.MeshStandardMaterial({ map: texture1 }),
            new THREE.MeshStandardMaterial({ map: texture2 })
        ];
        
        const cube = new THREE.Mesh(geometry, materials);
        scene.add(cube);
    } catch (error) {
        console.error('纹理加载出错:', error);
    }
}

// 调用异步函数
createCubeWithTextures();

2.2 本地项目代码

const textureLoader = new THREE.TextureLoader()

let smallMaterial = []
// 顺序不可乱:后前、上下、右左
let arr = ['../../src/direction_image/back.jpg','../../src/direction_image/front.jpg',  '../../src/direction_image/top.jpg',
    '../../src/direction_image/bottom.jpg', '../../src/direction_image/right.jpg', '../../src/direction_image/left.jpg']

// 加载所有纹理并创建材质
Promise.all(arr.map(url => {
    return new Promise((resolve, reject) => {
        const texture = textureLoader.load(url, () => resolve(texture), error => reject(error));
    });
})).then((textures) => {
    // 对顶部和底部纹理进行特殊处理
    const topTexture = textures[0]; // 顶部纹理
    topTexture.rotation = Math.PI; // 旋转180度
    topTexture.center.set(0.5, 0.5); // 设置旋转中心

    smallMaterial = textures.map((texture, index) => {
        if (index === 0) {
            return new THREE.MeshBasicMaterial({
                map: texture,
                side: THREE.DoubleSide
            });
        }   
        return new THREE.MeshBasicMaterial({ map: texture });
    })

    const smallMesh = new THREE.Mesh(smallGeometry, smallMaterial);

3、threejs贴图上,图片的方向问题

解决方案:使用2中的下方代码进行解决,根据自己实际情况进行旋转。

具体在threejs小案例——贴图翻转_threejs如何将贴图上下翻转-CSDN博客有讲解。

4、threejs兼容性问题 webgl1和webgl2兼容性问题

解决webg1和webg2的兼容性问题;

threejs在 163版本之后,不在支持webg1。

如果你的项目里面的threejs版本 <= 163版本,那么你是需要进行兼容性处理的,具体的处理方法如下:

const canvas = document.createElement('canvas')
    var gl;

    function isWebGL2Supported() {
        gl = canvas.getContext('webgl2');
        return gl !== null;
    }

    if (isWebGL2Supported()) {
        renderer = new THREE.WebGLRenderer({
            canvas: canvas,
            logarithmicDepthBuffer: true
        })
        console.log('使用webgl2')
    } else  {
        gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
        renderer = new THREE.WebGLRenderer({
            canvas: canvas,
            context: gl
        }) 
        console.log('强制使用 wegl1..')
    }

 测试:给浏览器快捷键,右键→属性。给属性结尾里面添加  --disable-webgl2 --enable-webgl  可以用来模拟webgl1,来进行测试。

【火狐浏览器兼容性测试】:

5、各部件计算位置配置文件(我使用nodejs写的一个简便的计算,可直接导出json格式)

下面导出json数据中,将jsonData换成自己的数据即可。

我们有这个需求的原因是:

        项目里面有各个部件的位置,是需要通过计算得到的,我们在下方的配置文件里面,只需要输入,二维平面上的x轴和y轴的位置坐标,就可以计算出在三维坐标系里面的位置。

因为需求需要,我们使用画的部件的左下角为原点要计算位置,所以,

每个部件的三维坐标,除了垂直平面的z轴变化为1/2

x轴和y轴需要相同的变化,即:box.width /2 - obj.XX - part.width/2

(为什么要除2呢,因为threejs中,默认是以盒子的中心为原点的。我们的需求是要以盒子的左下角为原点计算位置。)

其中:        

        box 是指大盒子

        obj.xx 是二维坐标的x轴值

        part 是指其中某个部件。

const fs = require('fs');
const jsonData = { name: "John", age: 30, city: "New York" };
 
fs.writeFile('data.json', JSON.stringify(jsonData, null, 2), (err) => {
  if (err) throw err;
  console.log('JSON数据已成功保存到data.json');
});

6、意识到版权问题,因为公司项目是商用的,icon不能随便使用。

阿里icon,是涉及到版权的。不能商用

可以使用如下菜鸟图标:菜鸟图标,免费商用矢量图标库

7、 三维空间向量偏移向量计算。

8、代码优化,将共用的边框线,封装成函数;

// 线框生成函数
function borderLine(geometry, mesh, color_) {
    const edges = new THREE.EdgesGeometry(geometry);
    const lineMaterial = new THREE.LineBasicMaterial({ color: color_ ? color_ : 'red', linewidth: 2 });
    const line = new THREE.LineSegments(edges, lineMaterial);

    line.position.copy(mesh.position); // 线框位置与网格同步
    scene.add(line);
    // mesh.add(line)
    return line;
}

9、控制台显示:app.bundle.js:468 WARNING: Too many active WebGL contexts. Oldest context will be lost.

10、局部刷新

描述(要解决的问题):

在threejs创建的画布上面,上面绘制的各个部件都是通过接口获得数据,然后显示的。现在我们在旋转结束后,想刷新画布,看是否有更新,但是又不想让创建的盒子重置回第一次打开的状态。也就是说,我们想要只更新数据,而不更新 场景、相机、渲染器。

解决方案:

创建updateComponents 函数,用来处理局部刷新,也就是用来处理更新的数据,也就是,触发这个函数的时候,重新调用接口数据,来渲染模型。点击刷新按钮后,模型的场景和相机是不是发生变化的,变化的只有数据和渲染

简洁版代码如下。

$scope.isClickRefresh = false
// 初始化
function initFn(data){
    const {smalldata,bigdata} = data
    sceneFn()       // 场景
    cameraFn()      // 相机

    smallBox(smalldata)   // 小盒子
    bigBox(bigdata)       // 大盒子
    renderFn()      // 渲染器
    animateFn()   // 动画
}

// 局部刷新
function updateComponents(data){
    $scope.isClickRefresh = true
    const {smalldata,bigdata} = data
    smallBox(smalldata)   // 小盒子
    bigBox(bigdata)       // 大盒子
    renderFn()     
    animateFn()   
}

// 刷新按钮  点击事件
let element = document.getElementById('refreshButton')
element.addEventListener('click',function(){
    $scope.isClickRefresh = true
})


// 页面监听
$scope.watch('data',function(newValue,oldValue){
    if(newValue && JSON.stringify(newValue) != JSON.stringify(oldValue)){
        if(!$scope.isClickRefresh){
            initFn(data)
        }else{
            updateComponents(data)
        }
    }
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

熙熙攘攘Re

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

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

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

打赏作者

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

抵扣说明:

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

余额充值