Skip to content

后处理-描边效果

🕒 Published at:

所谓threejs后期处理,就像ps一样,对threejs的渲染结果进行后期处理,比如添加发光效果。

OutlinePass.js:高亮发光描边

OutlinePass.js扩展库提供的类OutlinePass就可以给一个模型添加一个高亮发光描边

引入EffectComposer.js

js
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';

大家都知道three.js WebGL渲染器执行渲染方法.render()会得到一张图像,如果你需要对一个webgl渲染器的渲染结果进行后期处理,就把它作为EffectComposer的参数。

js
// 创建后处理对象EffectComposer,WebGL渲染器作为参数
const composer = new EffectComposer(renderer);

渲染器通道RenderPass

js
// 引入渲染器通道RenderPass
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';

通过EffectComposer(renderer)指定了需要后处理的渲染器WebGLRenderer,渲染器通道RenderPass的作用是指定后处理对应的相机camera和场景scene。

js
// 创建一个渲染器通道,场景和相机作为参数
const renderPass = new RenderPass(scene, camera);

给EffectComposer添加一个渲染器通道RenderPass。

js
// 设置renderPass通道
composer.addPass(renderPass);

OutlinePass通道

OutlinePass可以给指定的某个模型对象添加一个高亮发光描边效果。

js
// 引入OutlinePass通道
import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js';

创建OutlinePass通道

js
// OutlinePass第一个参数v2的尺寸和canvas画布保持一致
const v2 = new THREE.Vector2(window.innerWidth, window.innerHeight);
// const v2 = new THREE.Vector2(800, 600);
const outlinePass = new OutlinePass(v2, scene, camera);

OutlinePass属性.selectedObjects

three.js场景中有多个模型的话,你希望给哪个模型对象设置发光描边效果,就可以通过OutlinePass的选择对象属性.selectedObjects设置。

js
// 一个模型对象
outlinePass.selectedObjects = [mesh];
// 多个模型对象
outlinePass.selectedObjects = [mesh1,mesh2,group];

设置OutlinePass通道

最后把创建好的OutlinePass通道添加到后处理composer中。

js
// 设置OutlinePass通道
composer.addPass(outlinePass);

渲染循环执行EffectComposer.render()

渲染循环中后处理EffectComposer执行.render(),会调用webgl渲染器执行.render(),也就是说renderer.render(scene, camera)不用再执行。

js
// 渲染循环
function render() {
    composer.render();
    // renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

此时看到这里描边的效果就已经实现了

再介绍几个描边的属性,用来控制描边的效果

描边颜色.visibleEdgeColor

js
//模型边缘高亮边框颜色,默认白色  
outlinePass.visibleEdgeColor.set(0xffff00);

描边厚度.edgeThickness

outlinePass.edgeThickness表示高亮发光描边的厚度,默认值1

js
// 轮廓边缘描边厚度设置
outlinePass.edgeThickness = 4.0;

console.log('描边厚度',outlinePass.edgeThickness);

描边亮度.edgeStrength

outlinePass.edgeThickness表示高亮描边的发光强度,默认值3

js
outlinePass.edgeStrength = 6;

描边闪烁.pulsePeriod

outlinePass.pulsePeriod的功能是控制描边的闪烁频率,默认0不闪烁。

js
outlinePass.pulsePeriod = 2;

完整代码

html
<template>
  <div id="canvas"></div>
</template>

<script setup>
import * as THREE from 'three';
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
// 引入渲染器通道RenderPass
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { OutlinePass } from 'three/addons/postprocessing/OutlinePass.js';

import { DotScreenPass } from 'three/examples/jsm/postprocessing/DotScreenPass.js';

import { SMAAPass } from 'three/examples/jsm/postprocessing/SMAAPass.js';

import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

import { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';

const route = useRoute();

onMounted(() => {
  // 创建场景
  const scene = new THREE.Scene();
  //创建一个长方体几何对象Geometry
  const geometry = new THREE.BoxGeometry(50, 50, 50);

  //材质对象Material
  const material = new THREE.MeshLambertMaterial({
    color: 0x00ffff //设置材质颜色
  });

  // 创建网格模型
  const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh

  //设置网格模型在三维空间中的位置坐标,默认是坐标原点
  mesh.position.set(0, 10, 0);
  scene.add(mesh); //网格模型添加到场景中

  // AxesHelper:辅助观察的坐标系
  const axesHelper = new THREE.AxesHelper(100);
  scene.add(axesHelper);

  // tip: 环境光
  //   环境光AmbientLight:没有特定的光源位置,整体改变场景的光照明暗度
  const ambient = new THREE.AmbientLight(0xffffff, 0.4);
  scene.add(ambient);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
  // 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
  directionalLight.position.set(100, 60, 50);
  // 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0
  // directionalLight.target = mesh;
  scene.add(directionalLight);

  const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000);
  scene.add(dirLightHelper);

  /**
   * 光源设置
   */
  //点光源
  //   const pointLight = new THREE.PointLight(0xffffff, 1.0);
  //   pointLight.decay = 0.0; //光源光照强度不随距离改变衰减
  //   //点光源位置
  //   // pointLight.position.set(400, 0, 0);//点光源放在x轴上
  //   pointLight.position.set(400, 200, 300); //偏移光源位置,观察渲染效果变化
  //   scene.add(pointLight); //点光源添加到场景中

  // width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
  const width = window.innerWidth; //宽度
  const height = window.innerHeight; //高度

  /**
   * 透视投影相机设置
   */
  // 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
  const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
  camera.position.set(292, 223, 185); //相机在Three.js三维坐标系中的位置
  camera.lookAt(0, 0, 0); //相机观察目标指向Three.js坐标系原点
  // OutlinePass第一个参数v2的尺寸和canvas画布保持一致
  const v2 = new THREE.Vector2(window.innerWidth, window.innerHeight);
  // const v2 = new THREE.Vector2(800, 600);
  const outlinePass = new OutlinePass(v2, scene, camera);
  outlinePass.selectedObjects = [mesh];

  //模型描边颜色,默认白色
  outlinePass.visibleEdgeColor.set(0xffff00);
  //高亮发光描边厚度
  outlinePass.edgeThickness = 4;
  //高亮描边发光强度
  outlinePass.edgeStrength = 6;
  //模型闪烁频率控制,默认0不闪烁
  outlinePass.pulsePeriod = 2;

  const renderPass = new RenderPass(scene, camera);

  /**
   * 创建渲染器对象
   */
  const renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
  renderer.render(scene, camera); //执行渲染操作
  // 创建后处理对象EffectComposer,WebGL渲染器作为参数
  const composer = new EffectComposer(renderer);

  // 开启后处理 像素处理
  const dotScreenPass = new DotScreenPass();
  const smaaPass = new SMAAPass();

  // 开启后处理 发光处理
  const unrealBloomPass = new UnrealBloomPass();

  // 开启后处理 闪烁处理
  const glitchPass = new GlitchPass();

  // 强度
  // unrealBloomPass.strength = 0;
  // 曝光强度
  // renderer.toneMapping = THREE.ACESFilmicToneMapping; //老旧的电影的感觉
  // renderer.toneMappingExposure = 2;

  composer.addPass(renderPass);
  composer.addPass(outlinePass);
  // 关闭
  dotScreenPass.enabled = false;
  composer.addPass(dotScreenPass);
  // 加上这个就没有锯齿感了
  composer.addPass(smaaPass);
  composer.addPass(glitchPass);

  // 开启发光
  composer.addPass(unrealBloomPass);

  document.getElementById('canvas').appendChild(renderer.domElement);

  // 设置相机控件轨道控制器OrbitControls
  const controls = new OrbitControls(camera, renderer.domElement);

  function render() {
    // renderer.render(scene, camera); //执行渲染操作
    composer.render();

    requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
  }
  render();

  // 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
  //   controls.addEventListener('change', function () {
  //     renderer.render(scene, camera); //执行渲染操作
  //     // 浏览器控制台查看相机位置变化
  //     console.log('camera.position', camera.position);
  //   }); //监听鼠标、键盘事件

  window.onresize = () => {
    if (route.query.type === '1') {
      // 重置渲染器输出画布canvas尺寸
      renderer.setSize(window.innerWidth, window.innerHeight);
      return;
    } else if (route.query.type === '2') {
      // 重置渲染器输出画布canvas尺寸
      renderer.setSize(window.innerWidth, window.innerHeight);
      camera.aspect = window.innerWidth / window.innerHeight;
      // 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
      // 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
      // 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      camera.updateProjectionMatrix();
    }
  };
});
</script>