前言
本篇将给大家分享一下在three.js中如何去修改一个模型材质的贴图、颜色、透明度、网格,材质类型等参数信息
在上一篇 Three.js加载外部glb,fbx,gltf,obj 模型文件 的文章基础上
一.新增以下函数方法:
- 获取当前模型材质:getModelMeaterialList
- 设置材质属性:onSetModelMaterial
- 设置模型贴图:onSetModelMap
- 设置模型贴图:onSetSystemModelMap
- 切换材质类型:onChangeModelMeshType
- 重置模型材质数据:initModelMaterial
- 设置材质显隐:onSetMeshVisibe
/**
* @describe 材质模块方法
* @function getModelMeaterialList 获取当前模型材质
* @function onSetModelMaterial 设置材质属性(网格,透明度,颜色,深度写入)
* @function onSetModelMap 设置模型贴图(模型自带)
* @function onSetSystemModelMap 设置模型贴图(系统贴图)
* @function onChangeModelMeshType 切换材质类型
* @function initModelMaterial 重置模型材质数据
* @function onSetMeshVisibe 设置材质显隐
*/
import * as THREE from 'three'
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import { useMeshEditStore } from '@/store/meshEditStore'
const store = useMeshEditStore()
// 获取当前模型材质
function getModelMeaterialList() {
this.modelMaterialList = []
let i = 0;
this.model.traverse((v) => {
if (v.isMesh) {
v.castShadow = true
v.frustumCulled = false
if (v.material) {
i++;
const newMesh = v.clone()
Object.assign(v.userData, {
rotation: newMesh.rotation,
scale: newMesh.scale,
position: newMesh.position,
})
const newMaterial = v.material.clone()
v.mapId = v.name + '_' + i
v.material = newMaterial
const { mapId, uuid, userData, type, name, isMesh, visible } = v
const { color, wireframe, depthWrite, opacity } = v.material
const meshMaterial = { color, wireframe, depthWrite, opacity }
const mesh = {
mapId, uuid, userData, type, name, isMesh, visible, material: meshMaterial
}
this.modelMaterialList.push(mesh)
const cloneMesh = v.material.clone()
cloneMesh.userData.mapId = v.name + '_' + i
this.originalMaterials.set(v.uuid, cloneMesh);
}
}
})
}
// 设置材质属性
function onSetModelMaterial(config) {
const { color, wireframe, depthWrite, opacity } = JSON.parse(JSON.stringify(config))
const uuid = store.selectMesh.uuid
const mesh = this.scene.getObjectByProperty('uuid', uuid)
if (mesh && mesh.material) {
const { name, map } = mesh.material
Object.assign(mesh.material, {
map,
name,
transparent: true,
color: new THREE.Color(color),
wireframe,
depthWrite,
opacity
})
}
// 修改当前材质列表的参数
const listMesh = this.modelMaterialList.find((v) => v.uuid == uuid) || {};
Object.assign(listMesh.material, {
color: new THREE.Color(color),
wireframe,
depthWrite,
opacity
})
}
// 修改材质显隐
function onSetMeshVisibe(config) {
const mesh = this.scene.getObjectByProperty('uuid', config.uuid)
mesh.visible = config.visible
}
// 设置模型贴图(模型自带)
function onSetModelMap({ mapId, meshName }) {
const uuid = store.selectMesh.uuid
const mesh = this.scene.getObjectByProperty('uuid', uuid)
const originMaterial = this.originalMaterials.get(uuid)
mesh.material = originMaterial.clone()
mesh.mapId = mapId
// 设置当前材质来源唯一标记值key 用于预览处数据回填需要
mesh.meshFrom = meshName
}
// 设置模型贴图(系统贴图)
function onSetSystemModelMap({ id, url }) {
return new Promise((reslove) => {
const uuid = store.selectMesh.uuid
const mesh = this.scene.getObjectByProperty('uuid', uuid)
const texture = new THREE.TextureLoader().load(url)
const newMaterial = mesh.material.clone()
newMaterial.map = texture
newMaterial.map.wrapS = THREE.MirroredRepeatWrapping;
newMaterial.map.wrapT = THREE.MirroredRepeatWrapping;
newMaterial.map.flipY = false
newMaterial.map.colorSpace = THREE.SRGBColorSpace
newMaterial.map.minFilter = THREE.LinearFilter;
newMaterial.map.magFilter = THREE.LinearFilter;
mesh.material = newMaterial
mesh.mapId = id
// 设置当前材质来源唯一标记值key 用于预览处数据回填需要
mesh.meshFrom = id
texture.dispose()
reslove()
})
}
// 设置模型贴图 (外部)
function onSetStorageModelMap(url, type) {
return new Promise(async (reslove) => {
const uuid = store.selectMesh.uuid
const mesh = this.scene.getObjectByProperty('uuid', uuid)
// 根据 图片类型选择不同的加载器
let loader
let texture
if (type == 'hdr') {
loader = new RGBELoader()
} else {
loader = new THREE.TextureLoader()
}
texture = await loader.loadAsync(url)
const newMaterial = mesh.material.clone()
newMaterial.map = texture
newMaterial.map.wrapS = THREE.MirroredRepeatWrapping;
newMaterial.map.wrapT = THREE.MirroredRepeatWrapping;
newMaterial.map.flipY = false
newMaterial.map.colorSpace = THREE.SRGBColorSpace
newMaterial.map.minFilter = THREE.LinearFilter;
newMaterial.map.magFilter = THREE.LinearFilter;
mesh.material = newMaterial
texture.dispose()
reslove()
})
}
// 设置材质类型
function onChangeModelMeshType(activeMesh) {
this.model.traverse(v => {
if (v.isMesh && v.material) {
const { name, color, map, wireframe, depthWrite, opacity } = v.material
if (activeMesh.type) {
v.material = new THREE[activeMesh.type]({
map,
transparent: true,
color,
name,
})
} else {
const originalMaterial = this.originalMaterials.get(v.uuid);
v.material = originalMaterial;
}
depthWrite ? v.material.depthWrite = depthWrite : ''
opacity ? v.material.opacity = opacity : ''
wireframe ? v.material.wireframe = wireframe : ''
v.material.side = THREE.DoubleSide
}
})
}
// 重置模型材质数据
function initModelMaterial() {
this.model.traverse(v => {
if (v.isMesh && v.material) {
// 获取原始材质类型
const originalMaterial = this.originalMaterials.get(v.uuid);
v.material = originalMaterial.clone();
v.mapId = originalMaterial.userData.mapId
v.visible = true
}
});
this.modelMaterialList.forEach((v) => {
v.visible = true
const originalMaterial = this.originalMaterials.get(v.uuid);
v.mapId = originalMaterial.userData.mapId
const { color, wireframe, depthWrite, opacity } = originalMaterial
Object.assign(v.material, {
color, wireframe, depthWrite, opacity
})
})
store.selectMeshAction({})
}
二.在页面中去使用
<script setup>
import { reactive, computed } from "vue";
import { ElMessage } from "element-plus";
const store = useMeshEditStore();
const config = reactive({
meshName: null,
color: null,
wireframe: false,
depthWrite: true,
opacity: 1,
});
const state = reactive({
modelMaterialList: computed(() => store.modelApi.modelMaterialList),
originalMaterials: computed(() => store.modelApi.originalMaterials),
modelApi: computed(() => store.modelApi),
selectMeshUuid: computed(() => store.selectMeshUuid),
});
const onChangeMeaterial = () => {
state.modelApi.onSetModelMaterial(config);
};
// 设置材质显隐
const onSetMeshVisibe = (mesh) => {
mesh.visible = !mesh.visible;
state.modelApi.onSetMeshVisibe(mesh);
};
//修改当前材质贴图
const onChangeModelMap = (map) => {
activeMapId.value = map.mapId;
state.modelApi.onSetModelMap(map);
ElMessage.success("当前材质贴图修改成功");
};
// 修改当前材质贴图
const onChangeSystemModelMap = async (map) => {
activeMapId.value = map.id;
// 修改当前材质列表的贴图ID
const mesh = state.modelMaterialList.find((v) => v.uuid == store.selectMeshUuid) || {};
mesh.mapId = map.id
state.modelApi.onSetSystemModelMap(map);
ElMessage.success("当前材质贴图修改成功");
};
// 上传外部贴图
const onUploadTexture = async (file) => {
const filePath = URL.createObjectURL(file.raw);
await state.modelApi.onSetStorageModelMap(filePath, getFileType(file.name));
URL.revokeObjectURL(filePath)
ElMessage.success("当前材质贴图修改成功");
}
<script>
三.完整的代码可参考:gitee.com/ZHANG_6666/…
四.界面效果:
1.修改前:
2.修改后: