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中的下方代码进行解决,根据自己实际情况进行旋转。
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)
}
}
})