three.js如何实现选中的材质边缘通道发光效果

632 阅读1分钟

前言

本篇给大家分享一下在threejs中如何实现材质边缘通道发光的效果

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

一、引入需要的API

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'

二、新加入两个方法

  1. 创建效果合成器:createEffectComposer
  2. 选择材质:onChangeModelMeaterial

创建效果合成器方法

	// 创建效果合成器
	createEffectComposer() {
		const { clientHeight, clientWidth } = this.container
		this.effectComposer = new EffectComposer(this.renderer)
		const renderPass = new RenderPass(this.scene, this.camera)
		this.effectComposer.addPass(renderPass)
		this.outlinePass = new OutlinePass(new THREE.Vector2(clientWidth, clientHeight), this.scene, this.camera)
		this.outlinePass.visibleEdgeColor = new THREE.Color('#4d57fd') // 可见边缘的颜色
		this.outlinePass.hiddenEdgeColor = new THREE.Color('#8a90f3') // 不可见边缘的颜色
		this.outlinePass.edgeGlow = 2.0 // 发光强度
		this.outlinePass.edgeThickness = 1 // 边缘浓度
		this.outlinePass.edgeStrength = 4 // 边缘的强度,值越高边框范围越大
		this.outlinePass.pulsePeriod = 100 // 闪烁频率,值越大频率越低
		this.effectComposer.addPass(this.outlinePass)
		// 抗锯齿
		let effectFXAA = new ShaderPass(FXAAShader)
		effectFXAA.uniforms.resolution.value.set(1 / clientWidth, 1 / clientHeight)
		this.effectComposer.addPass(effectFXAA)
	}
       // 选择材质方法
 onChangeModelMeaterial(name) {
		const mesh = this.model.getObjectByName(name)
		this.outlinePass.selectedObjects = [mesh]	
	}

三、修改动画帧函数渲染方式,使用effectComposer.render进行渲染

	sceneAnimation() {
		this.renderAnimation = requestAnimationFrame(() => this.sceneAnimation())
		this.effectComposer.render()
		this.controls.update()
	}

四、在页面中去使用

<script setup>
import { reactive, computed } from "vue";
const store = useMeshEditStore();
const config = reactive({
  meshName: null,
  color: null,
  wireframe: false,
  depthWrite: true,
  opacity: 1,
});

// 选择材质
const onChangeMaterialType = (mesh) => {
  const { name } = mesh
  config.meshName = name;
  const activeMesh = state.modelApi.onChangeModelMeaterial(name);
  const { color, wireframe, depthWrite, opacity } = activeMesh.material;
  Object.assign(config, {
    color: new THREE.Color(color).getStyle(),
    wireframe,
    depthWrite,
    opacity,
  });

  const originMaterial = state.originalMaterials.get(mesh.uuid)
  activeMeshMap.value = {
    url: getModelMaps(mesh),
    name: mesh.name,
    mapId: mesh.mapId,
    material: originMaterial
  }
};


// 获取模型自带贴图
const getModelMaps = (mesh) => {
  const originMaterial = state.originalMaterials.get(mesh.uuid)
  const materials = Array.isArray(originMaterial) ? originMaterial : [originMaterial]
  let textureMapUrl
  materials.forEach(texture => {
    if (texture.map && texture.map.image) {
      const canvas = document.createElement('canvas')
      const { width, height } = texture.map.image
      canvas.width = width
      canvas.height = height
      const context = canvas.getContext('2d')
      context.drawImage(texture.map.image, 0, 0)
      textureMapUrl = canvas.toDataURL('image/png', .5)
      canvas.remove()
    }
  })
  return textureMapUrl
}

<script>

五、完整的代码可参考:gitee.com/ZHANG_6666/…

六、界面效果对比

1.选中前

image.png

2.选中后

image.png