three.js如何设置和修改模型材质的:贴图、颜色、透明度、网格,材质类型等参数信息

1,285 阅读3分钟

前言

本篇将给大家分享一下在three.js中如何去修改一个模型材质的贴图、颜色、透明度、网格,材质类型等参数信息

在上一篇 Three.js加载外部glb,fbx,gltf,obj 模型文件 的文章基础上

一.新增以下函数方法:

  1. 获取当前模型材质:getModelMeaterialList
  2. 设置材质属性:onSetModelMaterial
  3. 设置模型贴图:onSetModelMap
  4. 设置模型贴图:onSetSystemModelMap
  5. 切换材质类型:onChangeModelMeshType
  6. 重置模型材质数据:initModelMaterial
  7. 设置材质显隐: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.修改前:

image.png 2.修改后:

image.png