使用three.js的着色器通道渲染地球模型一

我们都知道,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>



猜你喜欢

转载自blog.csdn.net/qq_37338983/article/details/80289631
今日推荐