【Threejs】轮廓线、边框线、选中效果实现的N种方法以及性能评估(目前7种)

对于模型较小,面数较少,机器性能很好的情况下,用哪种方法都可以。

但是对于压缩后glb还有100m、200m,几百万面,机器配置又一般的情况下,对于效果和性能,就需要作出选择了。

备注:① example里https://jsfiddle.net开头的链接,需要VPN,能访问国外网络,才能打开;

​ ②所有的性能评估,我是以 机器配置:CPU i5-4460 3.20GHz 、内存 8.00 GB、显卡 NVIDIA GeForce GTX 745 (4096MB) 上 运行压缩后100M 面数150w面左右的glb,在chrome上运行测试的。

OutlinePass

image-20211129142212244

example https://threejs.org/examples/?q=outline#webgl_postprocessing_outline

优点:效果非常赞,可设置 边线大小,光晕,边缘浓度,呼吸闪烁等等效果

缺点:性能消耗double

主要代码片段

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

//初始化
// postprocessing
const size = renderer.getSize(new THREE.Vector2());
const _pixelRatio = renderer.getPixelRatio();
const _width = size.width;
const _height = size.height;
const renderTarget = new THREE.WebGLRenderTarget(
    _width * _pixelRatio,
    _height * _pixelRatio,
    {
        minFilter: THREE.LinearFilter,
        magFilter: THREE.LinearFilter,
        format: THREE.RGBAFormat,
        encoding: THREE.sRGBEncoding
    }
);
renderTarget.texture.name = "EffectComposer.rt1";
composer = new EffectComposer(renderer, renderTarget);
// composer = new EffectComposer(renderer);

const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
composer.addPass(outlinePass);

// const textureLoader = new THREE.TextureLoader();
// textureLoader.load('assets/tri_pattern.jpg', function (texture) {
//
//     outlinePass.patternTexture = texture;
//     texture.wrapS = THREE.RepeatWrapping;
//     texture.wrapT = THREE.RepeatWrapping;
//
// });

effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
composer.addPass(effectFXAA);

outlinePass.visibleEdgeColor.set(outlineColor[0]); // 边缘可见部分发颜色 sColor[0].color
outlinePass.hiddenEdgeColor.set(outlineColor[1]); // 边缘遮挡部分发光颜色 sColor[1].color
outlinePass.edgeStrength = Number(10.0); //边框的亮度
outlinePass.edgeGlow = Number(1); //光晕[0,1]
outlinePass.edgeThickness = Number(1.0); //边缘浓度
outlinePass.pulsePeriod = Number(1.8); //呼吸闪烁的速度 闪烁频率 ,默认0 ,值越大频率越低
outlinePass.usePatternTexture = false; //是否使用父级的材质
outlinePass.downSampleRatio = 2; // 边框弯曲度
outlinePass.clear = true;

// animate render里
renderer.render(scene, camera);
composer.render();

EdgesGeometry

image-20211129143940032

example https://jsfiddle.net/tgcf1u84/3/

https://threejs.org/examples/?q=help#webgl_helpers

优点:效果还行;性能比OutlinePass,要好一点,但是差距不是很大,大概5-10fps的样子;代码实现简单;

缺点:性能消耗较大,效果一般,线框的宽度不能大于1(官方是这样解释的:由于OpenGL Core Profile与 大多数平台上WebGL渲染器的限制,无论如何设置该值,线宽始终为1。)

主要代码片段

//添加
var edgeMaterial = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 5,} );
object.traverse( function( child ) {
		if ( child.isMesh ) {
			var edges = new THREE.EdgesGeometry( child.geometry, 45 );
			var line = new THREE.LineSegments( edges, edgeMaterial );
			child.add( line );
		}
} );

EdgesGeometry + LineSegments2

image-20211129145651190

example https://jsfiddle.net/5qv4Lmog/1/

优点:效果还行;线框宽度可大于1;性能比EdgesGeometry,要差一点,但是差距不是很大;

缺点:性能消耗较大,效果一般,不能在traverse时,直接add,否则会导致无尽的递归行为;

主要代码片段

//添加
edgeMaterial = new LineMaterial( { color: 0xff0000, linewidth: 5 } );
const meshes = [];
object.traverse( function( child ) {
    if ( child.isMesh ) meshes.push( child );
} );

for ( let mesh of meshes ) {
	var edges = new THREE.EdgesGeometry( mesh.geometry, 45 );
	var wideEdges = new LineSegmentsGeometry().fromEdgesGeometry( edges );
  	var line = new LineSegments2( wideEdges, edgeMaterial );
    mesh.add( line );
}

OutlineEffect

image-20211129153423662

example https://threejs.org/examples/?q=toon#webgl_materials_variations_toon (官方案例,单个scene)

https://jsfiddle.net/cwayd9v5/6/(多个scene,模拟实现特定对象出现边框线的效果)

优点:效果还行;性能也OK,代码实现很简单;

缺点:对于结构复杂的模型,不能实现特定对象出现边框线的功能效果(https://stackoverflow.com/questions/59997603/outline-effect-to-target-object/59998722#59998722 这个帖子上思路,能实现我提供的第二个效果,但是对于结构复杂模型,业务功能稍微复杂一些的,就不适用了);

主要代码片段

//引入
import { OutlineEffect } from './jsm/effects/OutlineEffect.js';

//初始化
let effect;
//renderer是webgl renderer
effect = new THREE.OutlineEffect(renderer,{
        defaultThickness: 0.015, //线条粗细
        defaultColor: [0.2,0.2,1], //线条颜色
        efaultAlpha: 0.9,
        // defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
});

//animate render里
effect.render( scene, camera ); //替换 renderer.render( scene, camera );

绘制一个对象两次;一次轮廓颜色对象,一次正常对象

image-20211129160902045

example https://jsfiddle.net/a4x0fwo7/47/

优点:效果还行;性能一般;

缺点:对于结构复杂的模型,会消耗较大的内存和性能;

主要代码片段

https://jsfiddle.net/a4x0fwo7/47/ 看这个链接里的源码

参考文章https://blog.mozvr.com/cartoon-outline-effect/

法线延伸

image-20211129161651781

example https://omarshehata.github.io/csb-l01dp/

优点:未实现,不清楚

缺点:未实现,不清楚

主要代码片段

https://github.com/OmarShehata/webgl-outlines 看这个链接里的源码

参考文章https://omar-shehata.medium.com/how-to-render-outlines-in-webgl-8253c14724f9

https://github.com/OmarShehata/webgl-outlines

BoxHelper

image-20211129161901827

example https://jsfiddle.net/maco6fqt/12/

https://threejs.org/examples/?q=box#webgl_helpers (官方案例)

优点:性能最好

缺点:效果比较差(这种效果只能算上选中效果,和边框线,轮廓线没关系)

主要代码片段

const box:any = new THREE.BoxHelper( object, '#00ffff');  //object 模型
object.attach(box);  
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐