Three 之 three.js (webgl)PostProcessing/shader/EffectComposer 屏幕渲染 之 屏幕pixel像素化效果的简单实现

Three 之 three.js (webgl)PostProcessing/shader/EffectComposer 屏幕渲染 之 屏幕pixel像素化效果的简单实现

目录

Three 之 three.js (webgl)PostProcessing/shader/EffectComposer 屏幕渲染 之 屏幕pixel像素化效果的简单实现

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码  


一、简单介绍

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

本节介绍, three.js (webgl)中有各种后期渲染效果的简单实现,不同屏幕渲染又有不同的效果,这里做简单的介绍,如果有不足之处,欢迎指出,或者你有更好的方法,欢迎留言。

后期处理

1、threejs 的后期处理通道包提供了各种强大的效果,有了这些效果会大大降低代码难度,并且可以直接使用内置的着色器包,避免了复杂的着色器代码编写。后期处理通道一般都按顺序执行,后加入的会覆盖前面的通道。

2、EffectComposer 用于在three.js中实现后期处理效果。该类管理了产生最终视觉效果的后期处理过程链。 后期处理过程根据它们添加/插入的顺序来执行,最后一个过程会被自动渲染到屏幕上。

3、后置处理通常是指应用到2d图像上的某种特效或者是滤镜。在ThreeJs的场景中,我们有的是由很多网格(mesh)构成的场景(scene)。我们将其渲染成2d图像。一般来说,图像被直接渲染成canvas然后在浏览器中被展示,然而在结果被输出到canvas之前,我们也可以通过另外的一个render target并应用一些后置效果。这被称为Post Processing,因为它发生在主场景渲染过程之后。

后置处理的示例 比如 Instagram 的滤镜,photoshop的滤镜。

工作方式:

1)你需要创建EffectComposer然后增加一些Pass对象。

2)每一个Pass阶段都可以增加一些后置处理特效,添加小插图,模糊,添加光晕,添加噪点,调整色相,饱和度,对比度等等。最终把效果渲染到canvas。

理解EffectComposer是如何工作的是有一点重要的

1)它创建两个render targets。让我们称他们为rtA和rtB

2)然后你调用EffectComposer.addPass按照你想要应用它们的顺序增加pass。然后它们就被向下图所示的被应用。举例说明,如下图:

 首先 你传入RenderPass的场景被渲染到rtA,不管rta的内容是啥,它继续向下一个pass传递。下一个pass将它作为输入做一些操作然后将其写入到rtB。然后rtB传到下一个pass,将rtB作为输入作一些操作然后在写回rtA。这个过程在整个pass过程中持续发生。

更多信息查看 Threejs 官网:Three.js – JavaScript 3D Library 

本节介绍,Threejs 中的屏幕渲染(后期渲染)添加对整个屏幕的 pixel 像素化效果进行动态调节的效果。像素化实现原理的简单说明:

(1)首先可以对UV坐标进行整体的放大几个倍数,然后把它变为整形,再缩小原来大小。这样一来,我们UV的精度就变化了,就会丢失一些精度,从而实现简单的像素化效果

(2)当然,这个过程也可以反过来,先缩小,在放大,过程中丢失一些精度(可以缩小的时候floor 向下取整,从而丢失一些精度),也会实现对应的像素化效果

二、实现原理

1、创建 EffectComposer ,并添加 RenderPass 通道,基础场景

2、添加 ShaderPass,主要添加 PixelShader,实现对屏幕整体像素化处理

3、PixelShader 把传入场景 2d图像 ,通过算法屏幕的2d 图像,进行像素化处理,如下的关键代码

            // pixelSize 像素大小, dxy 是 UV 放大缩小倍数
			vec2 dxy = pixelSize / resolution;
			// 像素化关键:把原有 UV 进行缩小,然后floor 向下取整,然后再放大回来;
			// 这样来回就使得 UV  丢失某些精度,从而实现简单像素化效果
			vec2 coord = dxy * floor( vUv / dxy );
			// 与处理过的 UV 进行 texture2D,得到像素化效果
			gl_FragColor = texture2D(tDiffuse, coord);

三、注意事项

1、场景不断变化,要持续的效果,需要办 EffectComposer.render() 渲染刷新

2、可以改变 PixelShader 的参数,进行对应的效果变化

四、效果预览

五、实现步骤

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

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

gitcode:mirrors / mrdoob / three.js · GitCode

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

3、创建基础场景

 

 4、添加后期渲染,并且添加点屏幕效果通道 

 

5、添加 UI 调节像素化大小

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

六、关键代码  

1、PixelPostProcessingEffect.html

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>004PixelPostProcessingEffect</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';
			// GUI 
			import { GUI } from '../../jsm/libs/lil-gui.module.min.js';
			
			
			// 后期渲染关键库
			import { EffectComposer } from '../../jsm/postprocessing/EffectComposer.js';
			import { RenderPass } from '../../jsm/postprocessing/RenderPass.js';
			import { ShaderPass } from '../../jsm/postprocessing/ShaderPass.js';

			// shader脚本
			import { PixelShader } from './shaders/004PixelShader.js';

			let scene, camera, renderer, composer;
			let object;

			let effect;
			
			const params = {
				pixelSize: 1,
				enable: true
			
			};

			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();
				
				// 添加物体到场景中
				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)

				// 添加环境光
				scene.add( new THREE.AmbientLight( 0x222222 ) );
				
				// 添加方向光
				const light = new THREE.DirectionalLight( 0xffffff );
				light.position.set( 1, 1, 1 );
				scene.add( light );
				
				// 创建操作 UI
				createGUI();

				// 创建后期渲染通道
				composer = new EffectComposer( renderer );
				composer.addPass( new RenderPass( scene, camera ) );

				// 添加通道效果
				effect = new ShaderPass( PixelShader );
				effect.uniforms[ 'resolution' ].value = new THREE.Vector2( window.innerWidth, window.innerHeight );
				effect.uniforms[ 'resolution' ].value.multiplyScalar( window.devicePixelRatio );
				composer.addPass( effect );

				// 窗口尺寸变化监听
				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

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

				// 渲染器更新大小
				renderer.setSize( window.innerWidth, window.innerHeight );
				
				// 后期渲染更新大小
				composer.setSize( window.innerWidth, window.innerHeight );
				
				// 尺寸变化,像素 resolution 更新
				effect.uniforms[ 'resolution' ].value.set( window.innerWidth, window.innerHeight ).multiplyScalar( window.devicePixelRatio );

			}
			
			function createGUI() {

				const gui = new GUI( { name: 'Pixel setting' } );
				gui.add( params, 'pixelSize', 0.01, 20 ).step( 0.1 ).onChange(()=>{effect.uniforms[ 'pixelSize' ].value = params.pixelSize;});
				gui.add( params, 'enable' );
			
			}

			function animate() {

				requestAnimationFrame( animate );
				// 旋转效果
				// object.rotation.x += 0.005;
				// object.rotation.y += 0.01;
				
				if ( params.enable ) {
				
					// 后期效果渲染
					composer.render();
				
				} else {
					// 正常渲染
					renderer.render( scene, camera );
				}
			}
			
		</script>

	</body>
</html>

2、004PixelShader.js

/**
 * Pixelation shader
 */

const PixelShader = {

	uniforms: {

		'tDiffuse': { value: null },
		'resolution': { value: null },
		'pixelSize': { value: 1 },

	},

	vertexShader: /* glsl */`

		varying highp vec2 vUv;

			void main() {

				vUv = uv;
				gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

		}`,

	fragmentShader: /* glsl */`

		uniform sampler2D tDiffuse;
		uniform float pixelSize;
		uniform vec2 resolution;

		varying highp vec2 vUv;

		void main(){
			// pixelSize 像素大小, dxy 是 UV 放大缩小倍数
			vec2 dxy = pixelSize / resolution;
			// 像素化关键:把原有 UV 进行缩小,然后floor 向下取整,然后再放大回来;
			// 这样来回就使得 UV  丢失某些精度,从而实现简单像素化效果
			vec2 coord = dxy * floor( vUv / dxy );
			// 与处理过的 UV 进行 texture2D,得到像素化效果
			gl_FragColor = texture2D(tDiffuse, coord);

		}`

};

export { PixelShader };

猜你喜欢

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