Three 之 three.js (webgl)物体描边效果(outline)三种实现方式的简单整理(后期渲染/MeshBasicMaterial/法线扩展)

Three 之 three.js (webgl)物体描边效果(outline)三种实现方式的简单整理

目录

Three 之 three.js (webgl)物体描边效果(outline)三种实现方式的简单整理

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码


一、简单介绍

Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。

本节介绍, three.js (webgl) 中实现描边效果 outline 的三种方法实现的简单介绍,其中,包括:1)后期渲染进行描边效果;2)使用 MeshBasicMaterial 略微放大比例,从而形成描边效果;3)点位置法线扩展,从而实现描边效果。其中,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。

二、实现原理

后期渲染实现描边:

1、具体可参见博文:

Three 之 three.js (webgl)PostProcessing/shader/EffectComposer 屏幕渲染 之 指定物体添加描边 outline 效果

MeshBasicMaterial 描边效果

1、要描边的 geometry ,然后与 MeshMeshBasicMaterial 创建一个新的 Mesh

2、新的 Mesh 适当放大比例,略大于原物体

3、把 MeshMeshBasicMaterial 的 side 设置为 THREE.BackSide,如果还没有达到效果,可以在通过合理的渲染顺序,从而视觉上形成描边效果

/**简单使用 MeshBasicMaterial 进行描边处理
			 * @param {Object} mesh 要描边的物体
			 * @param {Object} scene
			 */
			function outlineMeshBasicMaterial(mesh, scene){
				var outlineMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff,side: THREE.BackSide } );
				var outlineMesh = new THREE.Mesh( mesh.geometry, outlineMaterial );
				outlineMesh.position.set(mesh.position.x,mesh.position.y,mesh.position.z)
				outlineMesh.scale.set(1.2,1.2,1.2)
				scene.add( outlineMesh );
			}

法线扩展描边效果

1、构建 进行发现扩展的 shader

2、要描边的 geometry ,然后与 法线扩展shader 创建一个新的 Mesh

3、通过传入的描边宽度、颜色、透明度,来显示描边效果

              uniform float lineWidth;
			  void main()
			  {
			    gl_Position = projectionMatrix * modelViewMatrix * vec4( position + normal * lineWidth, 1.0 );
			  }

三、注意事项

1、如果要描边的物体比较复杂,含多个 geometry ,可能需要组合多个 geometry  来创建描边效果

2、后期渲染描边,效果官方已经给了样例,效果可以,可能性能消耗较大

3、MeshMeshBasicMaterial  进行描边,如果描边要添加透明度效果,则可能需要描边的物体也要处理设置 transparent 为 true,且需要合理物体的 renderOrder (Renderer 中 sortObjects 为true) ,来控制渲染顺序

4、法线扩展的方式描边,可能对于棱角分明的物体,描边效果会出现断裂的效果

四、效果预览

五、实现步骤

后期处理进行描边的参见博文:Three 之 three.js (webgl)PostProcessing/shader/EffectComposer 屏幕渲染 之 指定物体添加描边 outline 效果

1、为了方便学习,这里是基于 Github 代码,进行开发的,大家可以下载官网代码,很多值得学习的案例

GitHub - mrdoob/three.js: JavaScript 3D Library.

gitcode:mirrors / mrdoob / three.js · GitCode

2、在上面的基础上,添加一个 html ,用来实现案例效果,引入相关包    

3、 创建基础场景

4、实现 outlineMeshBasicMaterial 描边方法

5、实现法线扩展描边方法

6、场景添加描边的物体,然后给物体分别添加描边效果

7、 完成其他的基础代码编写,运行脚本,效果如下   

六、关键代码

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>22OutlineMeshBasicMaterial</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
	</head>
	<body>

		<!-- Import maps polyfill -->
		<!-- Remove this when import maps will be widely supported -->
		<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

		<script type="importmap">
			{
				"imports": {
					"three": "../../../build/three.module.js"
				}
			}
		</script>

		<script type="module">
			// 引入 three 基础库
			import * as THREE from 'three';
			import Stats from '../../jsm/libs/stats.module.js';
			import { OrbitControls } from './../../jsm/controls/OrbitControls.js';
			

			let camera, renderer, scene,controls;
			let object;
			let stats;
			
			const params = {
				enableFpsRender: false,
				enableRightNowRender: false
			
			};

			init();
			animate();

			function init() {

				// 渲染器
				renderer = new THREE.WebGLRenderer();
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				renderer.setClearColor('#cccccc');
				document.body.appendChild( renderer.domElement );
				
				// camera 
				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
				camera.position.z = 8;
				
				// 场景
				scene = new THREE.Scene();

				// 添加环境光
				scene.add( new THREE.AmbientLight( 0x222222 ) );

				// 添加方向光
				const light = new THREE.DirectionalLight( 0xffffff );
				light.position.set( 1, 1, 1 );
				scene.add( light );

				// 窗口尺寸变化监听
				window.addEventListener( 'resize', onWindowResize );
				
				stats = new Stats();
				document.body.appendChild( stats.dom );

				controls = new OrbitControls( camera, renderer.domElement );
				
				// 添加物体到场景中
				object = new THREE.Object3D();
				scene.add( object );
				const geometry = new THREE.SphereGeometry( 1, 4, 4 );
				const material = new THREE.MeshPhongMaterial( { color: '#ff3399', flatShading: true } );
				const material2 = new THREE.MeshPhongMaterial( { color: '#880088' } );
				const mesh = new THREE.Mesh( geometry, material );
				mesh.position.set(-2,0,0)
				object.add(mesh)
				const mesh2 = new THREE.Mesh( geometry, material2 );
				mesh2.position.set(2,0,0)
				object.add(mesh2)

				// 两种方式描边
				outlineMeshBasicMaterial(mesh,scene)
				OutLineNormalMaterial(mesh2,scene)
			}
			
			/**简单使用 MeshBasicMaterial 进行描边处理
			 * @param {Object} mesh 要描边的物体
			 * @param {Object} scene
			 */
			function outlineMeshBasicMaterial(mesh, scene){
				var outlineMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff,side: THREE.BackSide } );
				var outlineMesh = new THREE.Mesh( mesh.geometry, outlineMaterial );
				outlineMesh.position.set(mesh.position.x,mesh.position.y,mesh.position.z)
				outlineMesh.scale.set(1.2,1.2,1.2)
				scene.add( outlineMesh );
			}
			
			/**
			 * 简单使用 法线 进行描边处理
			 * @param {Object} mesh 要描边的物体
			 * @param {Object} scene
			 */
			function OutLineNormalMaterial(mesh, scene) {
				var outlineMaterial = getOutLineNormalMaterial(0.1);
				var outlineMesh = new THREE.Mesh( mesh.geometry, outlineMaterial );
				outlineMesh.position.set(mesh.position.x,mesh.position.y,mesh.position.z)
				outlineMesh.scale.set(1.2,1.2,1.2)
				scene.add( outlineMesh );
			}
			
			/**
			 * 简单发现扩展描边
			 * @param {float} lineWidth 描边宽带
			 * @param {float} lineAlpha 描边的透明度
			 * @param {Object} lineColor 描边颜色
			 */
			function getOutLineNormalMaterial(lineWidth=1.0,lineAlpha = 1.0,lineColor={r:1.0,g:1.0,b:0}){
			  let uniforms = {
			    lineWidth:{value:lineWidth},
			    lineColor:{value: new THREE.Color(lineColor.r,lineColor.g,lineColor.b)},
			    lineAlpha:{value: lineAlpha},
			    timer:{value:3.14}
			  }
			
			  let vertexShader = `
			  uniform float lineWidth;
			  void main()
			  {
			    gl_Position = projectionMatrix * modelViewMatrix * vec4( position + normal * lineWidth, 1.0 );
			  }
			  `
			  let fragmentShader = `
			  uniform vec3 lineColor;
			  uniform float lineAlpha;
			  uniform float timer;
			  void main(){
			    float factor = (sin(timer * 10.0 + 3.14 / 2.0) + 1.0) / 2.0;
			    float alpha = lineAlpha * factor;
			    gl_FragColor = vec4( lineColor , alpha);
			  }`
			  let material = new THREE.ShaderMaterial({
			      uniforms:uniforms,
			      vertexShader: vertexShader,
			      fragmentShader: fragmentShader,
			      side: THREE.BackSide,
			      transparent: true
			  });
			  return material
			}

			function onWindowResize() {

				// camera 更新 aspect
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				// 渲染器更新大小
				renderer.setSize( window.innerWidth, window.innerHeight );

			}
			

			function animate() {

				requestAnimationFrame( animate );
				renderer.render( scene, camera );
				stats.update();
				controls.update()
			}
			
			

		</script>
	</body>
</html>

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/127011773