• Three.js Demo源码分析1.MorphTargets与BufferGeometry


    Three.js主页上有很多绚丽的Demo,是学习的极佳素材。我正利用闲暇时间学习这些Demo,并将心得体会记录下来。

    老调重弹

    先睹为快

    空间中旋转的立方体板条箱,恐怕是主页上最简单的一个例子了。在WebGL原生API教程中就有这个例子,用Three.js实现起来更加方便了。但是,作为开始的开始,还是再重弹一遍老调吧。

    较简单的例子多采用这种一目了然的结构:

        // 在<body>尾部:
        // 一些全局变量
        var camera, scene, renderer;
        var mesh;
        // 初始化函数,仅运行一次
        function init(){
            // 初始化这些全局变量
        }
        // 动画函数,每一帧运行一次
        function animate(){
            requestAnimationFrame(animate);
            // 动画和绘图
        }
        init();
        animate();

     在init()中初始化相机,场景,渲染器,轻车熟路。

        renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
    
        camera = new THREE.PerspectiveCamera(70, window.innerWidth/window.innerHeight, 1, 1000);
        camera.position.z = 400;
    
        scene = new THREE.Scene();

    创建一个立方体mesh,贴上纹理,并加入到场景中。

        var geometry = new THREE.CubeGeometry(200, 200, 200);
    
        var texture = THREE.ImageUtils.loadTexture('code.JPG');
        texture.anisotropy = renderer.getMaxAnisotropy();
    
        var material = new THREE.MeshBasicMaterial({map:texture});
    
        mesh = new THREE.Mesh(geometry, material);
    
        scene.add(mesh);

    在animate()中渲染,结束。

        function animate(){
            requestAnimationFrame(animate);
    
            mesh.rotation.x += 0.005;
            mesh.rotation.y += 0.01;
    
            renderer.render(scene, camera);
        }

    这个例子就不多说了,代码放在这里只是作为最简单的开始。如果你之前使用过Three.js,一段时间不用后有所生疏,这个例子可以迅速将你唤醒。

    MorphTargets

    先睹为快

    MorphTargets允许物体发生变形。如果该物体的geometry有 $n$ 个顶点,那么MorphTargets允许你再指定 $n$ 个, $2n$ 个, $3n$ 个甚至更多个顶点(比如,$ p\cdot n$ 个),同时mesh对象提供一个数组morphTargetInfluences(公式中$ f_{j} $表示morphTargetInfluences[j]),具有 $p$ 个元素,每个元素取值在0-1之间。渲染这个物体的时候,某个顶点 $V_{i}$ 的位置其实变了,成了:

    $$V_{i}=V_{i}+\sum_{j=0}^{p}f_{j}\cdot (V_{j,i}-V_{i})$$

    举个简单的例子,一个立方体有8个顶点,MorphTargets又指定了8个顶点,立方体的一个顶点为(1,1,1),而在MorphTargets中与之对应的顶点为(2,2,2),那么当morphTargetInfluences[0]为0.5的时候,实际渲染的时候该顶点的位置就成了(1.5,1.5,1.5)。这样做的好处是显而易见的,你可以通过简单地调整morphTargetInfluences数组来使物体形变,只要之前你设置好了。

    向物体加入morphTargets的方法很简单:

        var geometry = new THREE.CubeGeometry(100,100,100);
        var material = new THREE.MeshLambertMaterial({color:0xffffff, morphTargets:true});
    
        var vertices = [];
        for(var i=0; i<geometry.vertices.length; i++)
        {
            var f = 2;
            vertices.push(geometry.vertices[i].clone());
            vertices[i].x *= f;
            vertices[i].y *= f;
            vertices[i].z *= f;
        }
        geometry.morphTargets.push({name:'target0', vertices:vertices});

     在其他什么地方(比如animate()或render()方法中)改变morphTargetInfluences,实在方便

    var s = 0;
    function render()
    {
        s += 0.03;
        mesh.morphTargetInfluences[0] = Math.abs(Math.sin(s));
        ...
    }

    最关键的问题是,我相信,这个功能是通过着色器来完成的。我阅读过一些简单的着色器,因此我发现在着色器中完成这件事实在太合适了。如果某个geometry有几千甚至上万个顶点,使用JavaScript逐个计算变形后顶点的位置会造成很大压力,而显卡大规模并行计算的能力很适合处理这个任务(毕竟每个顶点是独立地)。

    BufferGeometry

    先睹为快

    BufferGeometry是自由度最高的geometry类型了,你可以自由指定每个顶点的位置、颜色、法线(影响光照)。

    所谓Buffer,缓冲区,就是指顶点位置数组、顶点颜色数组等JavaScript二进制数组。这样定义一个BufferGeometry:

    var geometry = new THREE.BufferGeometry();
    geometry.attributes = {
        index:{
            itemSize:1,
            array:new Uint16Array(triangles*3),
            numItems:triangles*3
        },
        position:{
            itemSize:3,
            array:new Float32Array(triangles*3*3),
            numItems:triangles*3*3
        },
        normal:{
            itemSize:3,
            array:new Float32Array(triangles*3*3),
            numItems:triangles*3*3
        },
        color:{
            itemSize:3,
            array:new Float32Array(triangles*3*3),
            numItems:triangles*3*3
        }
    };

    这个例子通过好几个循环,在一个800宽度的立方体范围内随机构造了16万个尺寸1-12的小三角形,计算了法向,并按照位置赋值了颜色,指定了材质。最后的效果相当壮观。

    接着创建一个旁氏反射类型的材质和mesh。

    var material = new THREE.MeshPhongMaterial({
        color: 0xaaaaaa,
        ambient: 0xaaaaaa,
        specular: 0xffffff,
        shininess: 250,
        side: THREE.DoubleSide,
        vertexColors: THREE.VertexColors
    });
    mesh = new THREE.Mesh(geometry, material); 

    如果顶点的个数过多,超过了整型数index表示的范围(65536),则可以使用offset来处理。在这个例子中有16万个三角形,48万个顶点,使用了8个offset,每个offset相当于重置了index值。

    geometry.offsets = [];
    for(var i=0; i<offsets; i++){
        var offset = {
          start:i*chunkSize*3,
          index:i*chunkSize*3,
          count:Math.min(triangles-(i*chunkSize), chunkSize)*3
        };
        geometry.offsets.push(offset);
    }

    如果看过WebGL原生API教程,就会发现这与其中构建几何体的过程实在是太相似了。

    另两个类似的例子,是线类型和点类型的。

    先睹为快(线)

    先睹为快(点)

    这两种类型声明geometry时只需要position属性和color属性,不需要normal(没有面自然没有法向)属性。 

    geometry.attributes = {
        position:{
            itemSize:3,
            array:new Float32Array(segments*3),
            numItems:segments*3
        },
        color:{
            itemSize:3,
            array:new Float32Array(segments*3),
            numItems:segments*3
        }
    };

    用自己的逻辑填充完数组后,创建材质及相应的line对象或particleSystem对象,而不是mesh对象。

    对线类型:

    var material = new THREE.LineBasicMaterial({vertexColors:true});
    line = new THREE.Line(geometry, material);

    对点类型

    var material = new THREE.ParticleBasicMaterial({
        size:15,
        vertexColors:true
    });
    particleSystem = new THREE.ParticleSystem(geometry, material);

    这一篇先以一个最简单的例子“热身”,之后记录了MorphTargets和BufferGeometry的用法。

    作者:一叶斋主人
    出处:www.cnblogs.com/yiyezhai
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    013.Kubernetes认证授权
    012.Kubernetes的configmap和secret配置
    011.Kubernetes使用共享存储持久化数据
    010.kubernets的调度系统之daemonset
    009.kubernets的调度系统之污点和容忍
    008.kubernets的调度系统之标签选择器
    007.kubernets的headless service配置和ingress的简单配置
    Meterpreter初探
    渗透第一课:尝试渗透
    Metasploit漏洞扫描
  • 原文地址:https://www.cnblogs.com/yiyezhai/p/2863513.html
Copyright © 2020-2023  润新知