WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法

栏目: JavaScript · 发布时间: 6年前

内容简介:MeshNormalMaterial是一种不受渲染时使用的颜色影响的材质,它只与自己每一个面从内到外的法向量有关。法向量在webgl中用处十分广泛,光的反射,以及三维图形的纹理映射都与这个有关。从图中可以看到,网格的每一面渲染的颜色都是不一样的,如果我们想要在物体表面添加法向量,我们可以使用的THREE.ArrowHelper去表示每一个法向量,它的参数为

WebGL学习----Three.js学习笔记(5)

点击查看demo演示

Demo地址: https://nsytsqdtn.github.io/d...

简单网格材质 MeshNormalMaterial

MeshNormalMaterial是一种不受渲染时使用的颜色影响的材质,它只与自己每一个面从内到外的法向量有关。法向量在webgl中用处十分广泛,光的反射,以及三维图形的纹理映射都与这个有关。

WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法

从图中可以看到,网格的每一面渲染的颜色都是不一样的,如果我们想要在物体表面添加法向量,我们可以使用的THREE.ArrowHelper去表示每一个法向量,它的参数为

THREE.ArrowHelper(dir, origin, length, color, headLength, headWidth)

**其中参数的意义为:

dir:方向,默认是法向量

origin:开始的坐标位置

length:辅助线的长度

color:辅助线的颜色

headLength:头部的长度

headWidth:头部的宽度**

对于一个球体,要描述它每一个面的法向量,首先需要对它的每一个面进行遍历,取出这个面上的三个顶点(因为webgl的面都是三角形,所以是三个顶点),通过divideScalar(3)这个函数计算它的中心位置,我们就可以在这个中心位置点上,从内向外引出一个ArrowHelper,来模拟法向量。

for(let i=0;i<sphereGeometry.faces.length;i++){//在每一个面上面循环
            let face = sphereGeometry.faces[i];//得到每个面的对象
            let centroid = new THREE.Vector3();
            //先创建一个vector3对象,要使用这个对象找到每个面的中心
            centroid.add(sphereGeometry.vertices[face.a]);
             // 将这该面的三个顶点的索引传给sphereGeometry.vertices找到其顶点的坐标
             //再添加进centroid
            centroid.add(sphereGeometry.vertices[face.b]);
            centroid.add(sphereGeometry.vertices[face.c]);
            centroid.divideScalar(3);//三角形的中心点坐标
            let arrow = new THREE.ArrowHelper(
                face.normal,//face这个面的法向量
                centroid,
                2,
                0xffcc55,
                0.5,
                0.5);//箭头辅助线,相当于把法向量用箭头表示出来
            sphere.add(arrow);
        }

其中,centroid.add(sphereGeometry.vertices[face.a])这段代码中的sphereGeometry.vertices存有几何体的所有顶点信息,通过[ ]索引可以取得其中的某一个顶点。face.a还有下面的face.b和c都是该面的顶点索引号,表示这个面是由顶点编号为face.a,face.b,face.c的三个顶点所构成的一个三角形(webgl的面都是三角形),然后我们再计算这三个顶点的中心点。

菜单面板的设置

在菜单面板中设置一些MeshNormalmaterial的一些属性,便于去测试这种材质的一些特质

其中:

**this.visible = meshMaterial.visible;//是否可见

this.wireframe = meshMaterial.wireframe;//是否以线框的方式渲染物体
        this.wireframeWidth = meshMaterial.wireframeLinewidth;//线框的宽度
        this.transparent = meshMaterial.transparent;//是否透明
        this.opacity = meshMaterial.opacity;//透明度,需要transparent为true才有效果
        this.side = "front";//边的渲染方式,有三种,前面,后面,还有双面
        this.selectMesh = "sphere";//当前选择的几何体
        this.shading = "smooth";//着色方式,有平面着色和平滑着色,对一个面很平的几何体几乎看不出区别,如正方体**
function initDatGUI() {
        //设置菜单中需要的参数
        controls = new function () {
            this.rotationSpeed = 0.02;
            this.visible = meshMaterial.visible;//是否可见
            this.wireframe = meshMaterial.wireframe;//是否以线框的方式渲染物体
            this.wireframeWidth = meshMaterial.wireframeLinewidth;//线框的宽度
            this.transparent = meshMaterial.transparent;//是否透明
            this.opacity = meshMaterial.opacity;//透明度,需要transparent为true才有效果
            this.side = "front";//边的渲染方式,有三种,前面,后面,还有双面
            this.selectMesh = "sphere";//当前选择的几何体
            this.shading = "smooth";//着色方式,有平面着色和平滑着色,对一个面很平的几何体几乎看不出区别,如正方体
        };
        let gui = new dat.GUI();
        //将刚刚设置的参数添加到菜单中
        let F1 = gui.addFolder("Mesh");
        F1.add(controls, "rotationSpeed", 0, 0.1);
        F1.add(controls, "visible").onChange(function (e) {
            meshMaterial.visible = e;
        });
        F1.add(controls, "wireframe").onChange(function (e) {
            meshMaterial.wireframe = e;
        });
        F1.add(controls, "wireframeWidth",0,10).onChange(function (e) {
            meshMaterial.wireframeWidth = e;
        });
        F1.add(controls, "transparent").onChange(function (e) {
            meshMaterial.transparent = e;
        });
        F1.add(controls, "opacity",0,1).onChange(function (e) {
            meshMaterial.opacity = e;
        });
        F1.add(controls, "side",["front","back","double"]).onChange(function (e) {
            switch (e) {
                case "front":
                    meshMaterial.side = THREE.FrontSide;
                    break;
                case "back":
                    meshMaterial.side = THREE.BackSide;
                    break;
                case "double":
                    meshMaterial.side = THREE.DoubleSide;
                    break;
            }
            meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话
        });
        F1.add(controls, "selectMesh",["sphere","cube","plane"]).onChange(function (e) {
            //先把场景的物体清除,再来添加
            scene.remove(cube);
            scene.remove(sphere);
            scene.remove(plane);
            switch (e) {
                case "sphere":
                    scene.add(sphere);
                    break;
                case "cube":
                    scene.add(cube);
                    break;
                case "plane":
                    scene.add(plane);
                    break;
            }
        });
        F1.add(controls, "shading",["flat","smooth"]).onChange(function (e) {
            switch (e) {
                case "flat":
                    meshMaterial.shading = THREE.FlatShading;
                    break;
                case "smooth":
                    meshMaterial.shading = THREE.SmoothShading;
                    break;
            }
            meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话
        });
    }

**注意在程序运行过程中想要改变材质的属性,需要在改完以后,添加一句

meshMaterial.needsUpdate = true,这样才能更新成功。**

360度全景背景

360度全景背景能够让人有身临其境的感觉,所有这里的背景使用了全景背景

WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法

如果想要使用全景的背景,就需要6张6个方向的图片来合成一个完整的背景(也可以使用1张6方向的图片),然后把这些贴图赋值给 scene.background

let urls =[
            'image/posx.jpg',
            'image/negx.jpg',
            'image/posy.jpg',
            'image/negy.jpg',
            'image/posz.jpg',
            'image/negz.jpg'
        ];//引入6个方向的贴图
        let cubeMap = THREE.ImageUtils.loadTextureCube( urls );
        scene = new  THREE.Scene();
        scene.background = cubeMap;

这些图片的需要按照顺序摆放,右左上下后前,否则背景会错乱。

这里给一个全景图片的网站,里面有很多的360度风景图,都是6张类型的,下载下来解压后就可以直接引入

http://www.humus.name/index.p...

本例子的完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Depth Material Test</title>
    <script src="../../import/three.js"></script>
    <script src="../../import/stats.js"></script>
    <script src="../../import/Setting.js"></script>
    <script src="../../import/OrbitControls.js"></script>
    <script src="../../import/dat.gui.min.js"></script>
    <script src="../../import/SceneUtils.js"></script>
    <style type="text/css">
        div#WebGL-output {
            border: none;
            cursor: pointer;
            width: 100%;
            height: 850px;
            background-color: #333333;
        }
    </style>
</head>
<body onload="Start()">
<div id="WebGL-output"></div>
<script>
    let camera, renderer, scene, light;
    let controller;
    let controls;
    let cube, sphere, plane, meshMaterial;

    function initThree() {
        //渲染器初始化
        renderer = new THREE.WebGLRenderer({
            antialias: true
        });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0x333333);
        document.getElementById("WebGL-output").appendChild(renderer.domElement);//将渲染添加到div中

        //初始化摄像机,这里使用透视投影摄像机
        camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 10, 100);
        camera.position.set(0, 40, 60);
        camera.up.x = 0;//设置摄像机的上方向为哪个方向,这里定义摄像的上方为Y轴正方向
        camera.up.y = 1;
        camera.up.z = 0;
        camera.lookAt(0, 0, 0);

        //初始化场景
        let urls =[
            'image/posx.jpg',
            'image/negx.jpg',
            'image/posy.jpg',
            'image/negy.jpg',
            'image/posz.jpg',
            'image/negz.jpg'
        ];//引入6个方向的贴图
        let cubeMap = THREE.ImageUtils.loadTextureCube( urls );
        scene = new  THREE.Scene();
        scene.background = cubeMap;

        //相机的移动
        controller = new THREE.OrbitControls(camera, renderer.domElement);
        controller.target = new THREE.Vector3(0, 0, 0);


        light = new THREE.AmbientLight(0x0c0c0c);
        scene.add(light);

        // add spotlight for the shadows
        light = new THREE.SpotLight(0xffffff);
        light.position.set(0, 30, 30);
        scene.add(light);

    }

    //初始化菜单面板
    function initDatGUI() {
        //设置菜单中需要的参数
        controls = new function () {
            this.rotationSpeed = 0.02;
            this.visible = meshMaterial.visible;//是否可见
            this.wireframe = meshMaterial.wireframe;//是否以线框的方式渲染物体
            this.wireframeWidth = meshMaterial.wireframeLinewidth;//线框的宽度
            this.transparent = meshMaterial.transparent;//是否透明
            this.opacity = meshMaterial.opacity;//透明度,需要transparent为true才有效果
            this.side = "front";//边的渲染方式,有三种,前面,后面,还有双面
            this.selectMesh = "sphere";//当前选择的几何体
            this.shading = "smooth";//着色方式,有平面着色和平滑着色,对一个面很平的几何体几乎看不出区别,如正方体
        };
        let gui = new dat.GUI();
        //将刚刚设置的参数添加到菜单中
        let F1 = gui.addFolder("Mesh");
        F1.add(controls, "rotationSpeed", 0, 0.1);
        F1.add(controls, "visible").onChange(function (e) {
            meshMaterial.visible = e;
        });
        F1.add(controls, "wireframe").onChange(function (e) {
            meshMaterial.wireframe = e;
        });
        F1.add(controls, "wireframeWidth",0,10).onChange(function (e) {
            meshMaterial.wireframeWidth = e;
        });
        F1.add(controls, "transparent").onChange(function (e) {
            meshMaterial.transparent = e;
        });
        F1.add(controls, "opacity",0,1).onChange(function (e) {
            meshMaterial.opacity = e;
        });
        F1.add(controls, "side",["front","back","double"]).onChange(function (e) {
            switch (e) {
                case "front":
                    meshMaterial.side = THREE.FrontSide;
                    break;
                case "back":
                    meshMaterial.side = THREE.BackSide;
                    break;
                case "double":
                    meshMaterial.side = THREE.DoubleSide;
                    break;
            }
            meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话
        });
        F1.add(controls, "selectMesh",["sphere","cube","plane"]).onChange(function (e) {
            //先把场景的物体清除,再来添加
            scene.remove(cube);
            scene.remove(sphere);
            scene.remove(plane);
            switch (e) {
                case "sphere":
                    scene.add(sphere);
                    break;
                case "cube":
                    scene.add(cube);
                    break;
                case "plane":
                    scene.add(plane);
                    break;
            }
        });
        F1.add(controls, "shading",["flat","smooth"]).onChange(function (e) {
            switch (e) {
                case "flat":
                    meshMaterial.shading = THREE.FlatShading;
                    break;
                case "smooth":
                    meshMaterial.shading = THREE.SmoothShading;
                    break;
            }
            meshMaterial.needsUpdate = true;//要在程序中让材质更新需要添加这一句话
        });
    }

    function initObject() {
        //创建正方体,球和地面的几何体
        let cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
        let sphereGeometry = new THREE.SphereGeometry(10, 20, 20);
        let planeGeometry = new THREE.PlaneGeometry(10, 10, 1, 1);
        //创建一个法向量材质
        meshMaterial = new THREE.MeshNormalMaterial();

        cube = new THREE.Mesh(cubeGeometry, meshMaterial);
        sphere = new THREE.Mesh(sphereGeometry, meshMaterial);
        plane = new THREE.Mesh(planeGeometry, meshMaterial);
        //把三者的位置统一
        cube.position.set(0,0,0);
        sphere.position = cube.position;
        plane.position = cube.position;

        //在球的每一个面上显示一个法向量,方便观测这种法向量材质的渲染方式
        for(let i=0;i<sphereGeometry.faces.length;i++){//在每一个面上面循环
            let face = sphereGeometry.faces[i];//得到每个面的对象
            let centroid = new THREE.Vector3();//先创建一个vector3对象,要使用这个对象找到每个面的中心,
            centroid.add(sphereGeometry.vertices[face.a]);
            // 将这该面的三个顶点的索引传给sphereGeom.vertices找到其顶点的坐标,再添加进centroid
            centroid.add(sphereGeometry.vertices[face.b]);
            centroid.add(sphereGeometry.vertices[face.c]);
            centroid.divideScalar(3);//三角形的中心点坐标
            let arrow = new THREE.ArrowHelper(
                face.normal,
                centroid,
                2,
                0xffcc55,
                0.5,
                0.5);//箭头辅助线,相当于把法向量用箭头表示出来
            sphere.add(arrow);
        }
        scene.add(sphere);
    }

    function rotation() {
        scene.traverse(function (e) {
            if (e instanceof THREE.Mesh) {
                e.rotation.y += controls.rotationSpeed;
            }
        })
    }

    //渲染函数
    function render() {
        rotation();
        stats.update();
        renderer.clear();
        requestAnimationFrame(render);
        renderer.render(scene, camera);
    }

    //功能函数
    function setting() {
        loadFullScreen();
        loadAutoScreen(camera, renderer);
        loadStats();
    }

    //运行主函数,敲代码的时候老是敲错,所以改了一个名字,叫Start更方便
    function Start() {
        initThree();
        initObject();
        initDatGUI();
        setting();
        render();
    }
</script>
</body>
</html>

以上所述就是小编给大家介绍的《WebGL three.js学习笔记 法向量网格材质MeshNormalMaterial的介绍和创建360度全景天空盒的方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

产品型社群

产品型社群

李善友 / 机械工业出版社 / 2015-3-1 / CNY 69.00

传统模式企业正在直面一场空前的“降维战争”, 结局惨烈,或生或死。 传统模式很难避免悲惨下场, 诺基亚等昔日庞然大物轰然倒塌, 柯达发明了数码成像技术却依然破产, 新商业的兴起到底遵循的是什么模式? 微信轻而易举干掉了运营商的短信业务, “好未来”为何让传统教育不明觉厉? 花间堂为什么不是酒店,而是入口? 将来不会有互联网企业与传统企业之分, ......一起来看看 《产品型社群》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器