WebGL three.js学习笔记 自定义顶点建立几何体

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

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版本要一致,否则可能会报错。有一些教程的示例程序版本可能就比较旧了,如果直接拿来用可能会出问题,注意分辨一下。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会为您解答关于WebGL three.js的阴影与实现物体动画的问题。首先,让我们来了解一下WebGL three.js是什么。 WebGL three.js是一款基于WebGL的JavaScript 3D库,可以帮助我们快速搭建3D场景和应用。接下来我们来讲解阴影和实现物体动画的方法。 一、阴影 阴影是模拟物体之间的阴影效果,让3D场景更加真实。在three.js中,我们可以通过设置Mesh的castShadow和receiveShadow属性来实现阴影效果。 1. 首先,我们需要在场景中添加光源,例如SpotLight或DirectionalLight。 2. 然后,在需要投射阴影的物体上设置castShadow为true。 3. 最后,在需要接收阴影的物体上设置receiveShadow为true。 代码示例: ```javascript // 添加光源 const light = new THREE.SpotLight(0xffffff); light.position.set(0, 100, 0); light.castShadow = true; scene.add(light); // 添加需要投射阴影的物体 const cube = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshLambertMaterial({ color: 0xff0000 })); cube.castShadow = true; scene.add(cube); // 添加需要接收阴影的物体 const plane = new THREE.Mesh(new THREE.PlaneGeometry(200, 200, 1, 1), new THREE.MeshLambertMaterial({ color: 0xffffff })); plane.receiveShadow = true; plane.rotation.x = -Math.PI / 2; scene.add(plane); ``` 二、物体动画 在three.js中,我们可以通过Tween.js库来实现物体的动画效果。Tween.js是一款JavaScript动画库,可以帮助我们实现非常丰富的动画效果。 1. 首先,我们需要在HTML文件中引入Tween.js库文件。 2. 然后,在需要动画的物体上设置初始状态。 3. 最后,通过Tween.js库来设置物体的目标状态和动画效果,例如缓动动画(ease)或弹跳动画(bounce)。 代码示例: ```javascript // 引入Tween.js库文件 <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.min.js"></script> // 添加需要动画的物体 const cube = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshLambertMaterial({ color: 0xff0000 })); cube.position.set(0, 0, 0); scene.add(cube); // 设置初始状态 const start = { x: 0, y: 0, z: 0 }; // 设置目标状态 const end = { x: 50, y: 50, z: 50 }; // 设置动画效果 const tween = new TWEEN.Tween(start) .to(end, 1000) .easing(TWEEN.Easing.Quadratic.InOut) .onUpdate(() => { cube.position.set(start.x, start.y, start.z); }) .start(); ``` 以上是关于WebGL three.js阴影与实现物体动画的方法,希望能够对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值