自定义顶点建立几何体与克隆

Three.js本身已经有很多的网格模型,基本已经够我们的使用,但是如果我们还是想自己根据顶点坐标来建立几何模型的话,Three.js也是可以的。

基本效果如图:

点击查看demo演示

demo演示:https://nsytsqdtn.github.io/demo/vertices/vertices

实际上出于性能的考虑,three.js是认为我们的几何体在整个生命周期中是不会改变的,但是我们还是想使用dat.gui.js去实时更新我们自定义几何体的顶点信息。

当顶点信息发生变化时,我们就需要使用
geometry.verticesNeedUpdate = true;
但是在每一帧渲染完后这个值又会变为false,所以我们需要每次渲染中都更新这个值。

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Three.js</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#canvas-frame {
            border: none;
            cursor: pointer;
            width: 100%;
            height: 850px;
            background-color: #333333;
        }
    </style>
</head>
<body onload="threeStart()">
<div id="canvas-frame"></div>
<script>
    let renderer, camera, scene;
    let controller;
    let controls;
    let vertices;
    let faces;
    let controlPoints = [];
    let geom;
    let mesh;

    //初始化渲染器
    function initThree() {
        renderer = new THREE.WebGLRenderer({
            antialias: true
        });//定义渲染器
        renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染的宽度和高度
        document.getElementById("canvas-frame").appendChild(renderer.domElement);//将渲染器加在html中的div里面
        renderer.setClearColor(0x333333, 1.0);//渲染的颜色设置
        renderer.shadowMapEnabled = true;//开启阴影,默认是关闭的,太影响性能
        renderer.shadowMapType = THREE.PCFSoftShadowMap;//阴影的一个类型

        camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight, 1, 10000);//perspective是透视摄像机,这种摄像机看上去画面有3D效果

        //摄像机的位置
        camera.position.x = 10;
        camera.position.y = 15;
        camera.position.z = 15;
        camera.up.x = 0;
        camera.up.y = 1;//摄像机的上方向是Y轴
        camera.up.z = 0;
        camera.lookAt(0, 0, 0);//摄像机对焦的位置
        //这三个参数共同作用才能决定画面

        scene = new THREE.Scene();

        let light = new THREE.SpotLight(0xffffff, 1.0, 0);//点光源
        light.position.set(-40, 60, -10);
        light.castShadow = true;//开启阴影
        light.shadowMapWidth = 8192;//阴影的分辨率,可以不设置对比看效果
        light.shadowMapHeight = 8192;
        scene.add(light);
        light = new THREE.AmbientLight(0xcccccc, 0.2);//环境光,如果不加,点光源照不到的地方就完全是黑色的
        scene.add(light);

        cameraControl();

         vertices = [
            new THREE.Vector3(1, 3, 1),
            new THREE.Vector3(1, 3, -1),
            new THREE.Vector3(1, -1, 1),
            new THREE.Vector3(1, -1, -1),
            new THREE.Vector3(-1, 3, -1),
            new THREE.Vector3(-1, 3, 1),
            new THREE.Vector3(-1, -1, -1),
            new THREE.Vector3(-1, -1, 1)
        ];//顶点坐标,一共8个顶点

         faces = [
             new THREE.Face3(0, 2, 1),
             new THREE.Face3(2, 3, 1),
             new THREE.Face3(4, 6, 5),
             new THREE.Face3(6, 7, 5),
             new THREE.Face3(4, 5, 1),
             new THREE.Face3(5, 0, 1),
             new THREE.Face3(7, 6, 2),
             new THREE.Face3(6, 3, 2),
             new THREE.Face3(5, 7, 0),
             new THREE.Face3(7, 2, 0),
             new THREE.Face3(1, 3, 4),
             new THREE.Face3(3, 6, 4),
        ];//顶点索引,每一个面都会根据顶点索引的顺序去绘制线条

        geom = new THREE.Geometry();
        geom.vertices = vertices;
        geom.faces = faces;
        geom.computeFaceNormals();//计算法向量,会对光照产生影响

        //两个材质放在一起使用
        let materials = [
            new THREE.MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true}),//透明度更改
            new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})//线条材质,让观察更直观一点

        ];
        //创建多材质对象,要引入SceneUtils.js文件,如果只有一个材质就不需要这个函数
        mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);
        mesh.children.forEach(function (e) {
            e.castShadow = true
        });
        scene.add(mesh);

        initDat();
    }
   //可视化面板
    function initDat() {
        function addControl(x, y, z) {
            controls = new function () {
                this.x = x;
                this.y = y;
                this.z = z;
            };
            return controls;
        }
        controlPoints.push(addControl(3, 5, 3));
        controlPoints.push(addControl(3, 5, 0));
        controlPoints.push(addControl(3, 0, 3));
        controlPoints.push(addControl(3, 0, 0));
        controlPoints.push(addControl(0, 5, 0));
        controlPoints.push(addControl(0, 5, 3));
        controlPoints.push(addControl(0, 0, 0));
        controlPoints.push(addControl(0, 0, 3));

        //克隆一个几何体
        let addClone = new function () {
            this.clone = function () {

                let clonedGeometry = mesh.children[0].geometry.clone();
                let materials = [
                    new THREE.MeshLambertMaterial({opacity: 0.6, color: 0xff44ff, transparent: true}),
                    new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})

                ];

                let mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry, materials);
                mesh2.children.forEach(function (e) {
                    e.castShadow = true
                });

                mesh2.translateX(Math.random()*4+3);
                mesh2.translateZ(Math.random()*4+3);
                mesh2.name = "clone";
                //删掉场景中已经存在的克隆体,再重新创建一个
                scene.remove(scene.getChildByName("clone"));
                scene.add(mesh2);

            }
        };

        let gui = new dat.GUI();

        gui.add(addClone, 'clone');

        for (let i = 0; i < 8; i++) {
            let f1 = gui.addFolder('Vertices ' + (i + 1));//把每个顶点的三个坐标都收拢在一个Folder里面,更加美观方便
            f1.add(controlPoints[i], 'x', -10, 10);
            f1.add(controlPoints[i], 'y', -10, 10);
            f1.add(controlPoints[i], 'z', -10, 10);

        }
    }

   // 摄像机的控制,可以采用鼠标拖动来控制视野
    function cameraControl() {
        controller = new THREE.OrbitControls(camera, renderer.domElement);
        controller.target = new THREE.Vector3(0, 0, 0);
    }

    let plane;

    //初始化物体
    function initObject() {
        //定义了一个地面
        let planeGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
        let planeMaterial = new THREE.MeshLambertMaterial({
            color: 0xffffff,
        });
        plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 15;
        plane.receiveShadow = true;//开启地面的接收阴影
        scene.add(plane);//添加到场景中
        // initCustomObj();
    }

    //定义的一个功能文件
    function initSetting() {
        loadAutoScreen(camera, renderer);
        loadFullScreen();
        loadStats();
    }

    //动画
    function render() {
        stats.update();
        //单材质几何体要更新顶点的话使用这一段语句
        // for (let i = 0; i < 8; i++) {
        //     console.log(mesh);
        //     mesh.geometry.vertices[i].set(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z);
        //     mesh.geometry.verticesNeedUpdate = true;
        //     mesh.geometry.computeFaceNormals();
        // }
        let vertices = [];
        for (let i = 0; i < 8; i++) {
            vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
        }
        mesh.children.forEach(function (e) {
            e.geometry.vertices = vertices;
            e.geometry.verticesNeedUpdate = true;//通知顶点更新
            e.geometry.elementsNeedUpdate = true;//特别重要,通知线条连接方式更新
            e.geometry.computeFaceNormals();
        });

        requestAnimationFrame(render);
        renderer.render(scene, camera);
    }
    //主函数
    function threeStart() {
        initThree();
        initObject();
        initSetting();
        render();
    }
</script>
</body>
</html>

特别要注意的是

在顶点发生变化时,如果是多材质对象的话,需要使用遍历每一个子对象来进行更新顶点数据。并且不仅要更新顶点,还要更新线条的连接方式geometry.elementsNeedUpdate = true,否则是没有效果的。(甚至尝试了一下不更新顶点,只更新线条也是可以达到实时更新的效果)

let vertices = [];
        for (let i = 0; i < 8; i++) {
            vertices.push(new THREE.Vector3(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z));
        }
        mesh.children.forEach(function (e) {
            e.geometry.vertices = vertices;
            e.geometry.verticesNeedUpdate = true;//通知顶点更新
            e.geometry.elementsNeedUpdate = true;//特别重要,通知线条连接方式更新
            e.geometry.computeFaceNormals();
        });

如果是单一的材质几何体,就不需要去遍历每一个子物体,直接把几何体的每一个顶点值更改,然后在通知顶点更新,就可以了。

     //单材质几何体要更新顶点的话使用这一段语句
         for (let i = 0; i < 8; i++) {
             console.log(mesh);
            mesh.geometry.vertices[i].set(controlPoints[i].x, controlPoints[i].y, controlPoints[i].z);
            mesh.geometry.verticesNeedUpdate = true;
            mesh.geometry.computeFaceNormals();
        }

注:

老版本的three.js,SceneUtils是没有单独拿出来作为一个js文件的,是直接写在three.js里。
而且使用69版本的three.js时,不需要更新线条的连接方式也可以实现实时更新。但是103版本试了很多次,都不行。
另外,使用的OrbitControls.js和dat.gui.min.js最好都是和自己用的Three.js版本要一致,否则可能会报错。有一些教程的示例程序版本可能就比较旧了,如果直接拿来用可能会出问题,注意分辨一下。

WebGL three.js学习笔记 自定义顶点建立几何体的更多相关文章

  1. WebGL three.js学习笔记 加载外部模型以及Tween.js动画

    WebGL three.js学习笔记 加载外部模型以及Tween.js动画 本文的程序实现了加载外部stl格式的模型,以及学习了如何把加载的模型变为一个粒子系统,并使用Tween.js对该粒子系统进行 ...

  2. WebGL three.js学习笔记 6种类型的纹理介绍及应用

    WebGL three.js学习笔记 6种类型的纹理介绍及应用 本文所使用到的demo演示: 高光贴图Demo演示 反光效果Demo演示(因为是加载的模型,所以速度会慢) (一)普通纹理 计算机图形学 ...

  3. WebGL three.js学习笔记 使用粒子系统模拟时空隧道(虫洞)

    WebGL three.js学习笔记 使用粒子系统模拟时空隧道 本例的运行结果如图: 时空隧道demo演示 Demo地址:https://nsytsqdtn.github.io/demo/sprite ...

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

    WebGL学习----Three.js学习笔记(5) 点击查看demo演示 Demo地址:https://nsytsqdtn.github.io/demo/360/360 简单网格材质 MeshNor ...

  5. WebGL three.js学习笔记 创建three.js代码的基本框架

    WebGL学习----Three.js学习笔记(1) webgl介绍 WebGL是一种3D绘图协议,它把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的 ...

  6. Angular JS 学习笔记(自定义服务:factory,Promise 模式异步请求查询:$http,过滤器用法filter,指令:directive)

    刚学没多久,作了一个小项目APP,微信企业号开发与微信服务号的开发,使用的是AngularJS开发,目前项目1.0版本已经完结,但是项目纯粹为了赶工,并没有发挥AngularJS的最大作用,这几天项目 ...

  7. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

  8. WebGL three.js学习笔记 阴影与实现物体的动画

    实现物体的旋转.跳动以及场景阴影的开启与优化 本程序将创建一个场景,并实现物体的动画效果 运行的结果如图: 完整代码如下: <!DOCTYPE html> <html lang=&q ...

  9. JS学习笔记 - 自定义右键菜单、文本框只能输入数字

    <script> // 事件总共有2个部分, //1.点击鼠标右键的表现 oncontextmenu 2.点击鼠标左键的表现(即普通点击onclick) // 点击右键,div位置定位到鼠 ...

随机推荐

  1. lintcode 链表求和

    题目要求 你有两个用链表代表的整数,其中每个节点包含一个数字.数字存储按照在原来整数中相反的顺序,使得第一个数字位于链表的开头.写出一个函数将两个整数相加,用链表形式返回和. 样例 给出两个链表 3- ...

  2. python库pandas简介

    pandas是基于numpy的数据分析模块,提供了大量标准模型和高效操作大型数据集所需要的工具. pandas主要提供了3种数据结构:1.Series,带标签的一维数组:2.DataFrame,带标签 ...

  3. 导出excel记录

    前言: 记录这篇使用记录,是为了方便以后学习查阅和让没有使用过的人了解一下,其中不足还请见谅.不是很全的文章,大神请绕行. 在项目中我们或多或少的会遇到数据导出到excel表格以便线下查看或者记录一些 ...

  4. ThreadPoolExcuter源码解析(一)

    1.ThreadPoolExcuter原理说明 首先我们要知道为什么要使用ThreadPoolExcuter,具体可以看看文档中的说明: 线程池可以解决两个不同问题:由于减少了每个任务的调用开销,在执 ...

  5. Java 标准 I/O 介绍

    一.Java标准I/O知识体系图: 二.I/O是什么 I/O 是Input/Output(输入.输出)的简称,输入流可以理解为向内存输入,输出流是从内存输出. 三.Java I/O 用途与对应的流一览 ...

  6. SSM-SpringMVC-24:SpringMVC异常高级之自定义异常

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 自定义异常,大家都会,对吧,无非就是继承异常类等操作,很简单,我就不多扯皮了,但是在xml配置文件中有个不同的 ...

  7. Socket Connect问题

    一.非阻塞Connect对于Select时应注意的问题二.linux客户端socket非阻塞connect编程 一.非阻塞Connect对于Select时应注意的问题 对于面向连接的socket(SO ...

  8. C++关于Union使用的部分总结

    说明:未加说明,struct均指C++语言中的struct(可以有成员函数,可以定义访问属性) 1 什么是Union? (1)与class,struct相似用于定义数据结构:union 可以说是一种特 ...

  9. 架构选型之Nodejs与Java

    前言: 身边越来越多的同事谈论Nodejs,谈其异步IO.事件回调.前后台统一一门语言,创业的朋友的第一个创业项目也选择了Nodejs,期望能够使用一种语言节省成本快速完成需求开发.与其他项目组的同事 ...

  10. 找不到 blog.csdn.net 的服务器 DNS 地址

    找不到 blog.csdn.net 的服务器 DNS 地址 csdn的博客用win7的电脑打不开是怎么回事?手机可以正常打开,csdn的bbs 下载什么的都可以正常使用. blog.csdn.net显 ...