我们都知道,three.js库里面内置了很多着色器通道对象供我们渲染场景,本文将对EffectComposer、RenderPass、FilmPass这三个通道进行学习和实现:
1.RenderPass这个通道会在当前场景(scene)和摄像机(camera)的基础上渲染出一个新场景,新建:
let renderPass = new THREE.RenderPass(scene, camera);
2.FilmPass这个通道通过扫描线和失真模拟电视屏幕效果,实现的效果超有时代感,新建:
/*四个参数分别为粗糙程度,扫描线强度,扫描线数量,是否转换为灰度图*/ let effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); //将渲染结果输出到屏幕 effectFilm.renderToScreen = true;
3.EffectComposer可以理解为着色器通道容器,着色器通道按照先后顺序添加进来并执行,新建:
/*渲染效果组合器,每个通道都按照传入的顺序执行*/ let composer = new THREE.EffectComposer(renderer); composer.addPass(renderPass); composer.addPass(effectFilm);
本文实现的demo基于three.js_r86(请自行下载),代码所用js文件和图片都在下载的那个包里面,请读者自行引用。
实现效果:
代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>shader_2_earth</title> <style> body{ margin: 0; overflow: hidden; } </style> </head> <body> <script src="build/three.js"></script> <script src="js/libs/stats.min.js"></script> <script src="js/libs/dat.gui.min.js"></script> <script src="js/controls/OrbitControls.js"></script> <script src="js/Detector.js"></script> <script src="js/postprocessing/EffectComposer.js"></script> <script src="js/postprocessing/ShaderPass.js"></script> <script src="js/postprocessing/MaskPass.js"></script> <script src="js/postprocessing/FilmPass.js"></script> <script src="js/postprocessing/BloomPass.js"></script> <script src="js/postprocessing/RenderPass.js"></script> <script src="js/shaders/CopyShader.js"></script> <script src="js/shaders/FilmShader.js"></script> <div id="stats"></div> <div id="container"></div> <script> //检测webgl的兼容性 if(!Detector.webgl) Detector.addGetWebGLMessage(); let scene; let camera, renderer, sphere, controls, stats; let ambientLight, spotLight; let composer; let clock; main(); render(); function main() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.set(-10, 15, 25); camera.lookAt(new THREE.Vector3(0, 0, 0)); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setClearColor(new THREE.Color(0,0,0)); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMapEnabled = true; controls = new THREE.OrbitControls(camera); controls.autoRotate = false; clock = new THREE.Clock(); ambientLight = new THREE.AmbientLight(0x181818); scene.add(ambientLight); spotLight = new THREE.SpotLight(0xffffff); spotLight.position.set(550, 100, 550); spotLight.intensity = 0.6; scene.add(spotLight); //创建地球 sphere = createMesh(new THREE.SphereGeometry(10, 60, 60)); scene.add(sphere); document.getElementById("container").appendChild(renderer.domElement); /** * 添加渲染通道 */ //在当前场景和摄像机的基础上渲染一个新场景 let renderPass = new THREE.RenderPass(scene, camera); //通过扫描线和失真来实现模拟电视屏幕的效果 let effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); //将渲染结果输出到屏幕 effectFilm.renderToScreen = true; //渲染效果组合器,每个通道都按照传入的顺序执行 composer = new THREE.EffectComposer(renderer); composer.addPass(renderPass); composer.addPass(effectFilm); //菜单栏元素 let guiFields = { "扫描线数量": 256, "灰度图像": false, "扫描线强度": 0.3, "粗糙程度": 0.8, "updateEffectFilm": function () { effectFilm.uniforms.grayscale.value = guiFields.灰度图像; effectFilm.uniforms.nIntensity.value = guiFields.粗糙程度; effectFilm.uniforms.sIntensity.value = guiFields.扫描线强度; effectFilm.uniforms.sCount.value = guiFields.扫描线数量; } }; //新建一个菜单栏 let gui = new dat.GUI(); gui.add(guiFields, "扫描线数量", 0, 2048).onChange(guiFields.updateEffectFilm); gui.add(guiFields, "扫描线强度", 0, 1).onChange(guiFields.updateEffectFilm); gui.add(guiFields, "粗糙程度", 0, 3).onChange(guiFields.updateEffectFilm); gui.add(guiFields, "灰度图像").onChange(guiFields.updateEffectFilm); stats = initStats(); } //创建一个Mesh function createMesh(geometry) { //初始化纹理加载器 let textureLoader = new THREE.TextureLoader(); //加载图片 let uniforms = { planetTexture:{value:textureLoader.load("textures/planets/earth_atmos_2048.jpg")}, specularTexture:{value:textureLoader.load("textures/planets/earth_specular_2048.jpg")}, normalTexture:{value:textureLoader.load("textures/planets/earth_normal_2048.jpg")} }; //创建phong材料,并进行相应图片的贴图 let planetMaterial = new THREE.MeshPhongMaterial(); planetMaterial.specularMap = uniforms.specularTexture.value; planetMaterial.specular = new THREE.Color(0x4444aa); planetMaterial.normalMap = uniforms.normalTexture.value; planetMaterial.map = uniforms.planetTexture.value; //新建一个mesh let mesh = new THREE.SceneUtils.createMultiMaterialObject(geometry, [planetMaterial]); return mesh; } //渲染更新场景 function render() { stats.update(); let delta = clock.getDelta(); controls.update(delta); sphere.rotation.y += 0.002; requestAnimationFrame(render); //没有着色器通道系统默认为WebGLRenderer.render //使用着色器通道后,应使用使用composer.render composer.render(delta); } //左上角帧显示 function initStats() { let stats = new Stats(); stats.setMode(0); stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.top = '0px'; document.getElementById("stats").appendChild(stats.domElement); return stats; } </script> </body> </html>