three.js 笔记五 BufferGeometry

一、THREE.Geometry弃用

参考
Three.js 新旧版本创建几何对象差异
https://sbcode.net/threejs/geometry-to-buffergeometry/

从r125开始,THREE.Geometry就被弃用了。新版的Three.js不再提供Geometry对象,代之以BufferGeometry

1.老的
function createLine(){
        let start = new THREE.Vector3(7, 1.8, -2);
        let end = new THREE.Vector3(1.6,1.8, -2);

        // 就是这里创建的对象找不到 *****
        let geometry = new THREE.Geometry();
        // ****************************

        let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
        geometry.vertices.push(start);
        geometry.vertices.push(end);
        let line = new THREE.Line(lineGeometry, lineMaterial);
        scene.add(line);
        return line;
}
2.新的
function createLine(){
        let start = new THREE.Vector3(7, 1.8, -2);
        let end = new THREE.Vector3(1.6,1.8, -2);

        let lineMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
        let pointsArray = [start, end];
        let lineGeometry = new THREE.BufferGeometry().setFromPoints(pointsArray);
        let line = new THREE.Line(lineGeometry, lineMaterial);
        scene.add(line);
        return line;
}
二、BufferGeometry设置属性

参考
https://threejs.org/docs/#api/zh/core/BufferGeometry
三维空间中绘制点、线、面、UV贴图,万能的BufferGeometry(three.js实战4)
【Three.js】BufferGeometry 基础讲解(position、normal、color、index)

three.js 中提供了一系列绘制几何体的类,如BoxGeometry、SphereGeometry,PlaneGeometry、CircleGeometry、CylinderGeometry 等,使用这些类,可以快速创建对应的几何体。参考
十二、Three.js中的常用几何体
十三、Three.js中的高级几何

three.js同时提供了对应的 BoxBufferGeometry、SphereBufferGeometry,PlaneBufferGeometry、CircleBufferGeometry、CylinderBufferGeometry等直接使用缓存的构建几何体的 xxxBufferGeometry 类簇,这些类簇相比于 xxxGeometry 类簇,可以有效减少向 GPU 传输数据所需的开销,极大提高效率,但如果提供的这些几何都不能满足需求怎么办,这时就需要用到万能的 BufferGeometry ,通过它可以向缓存中传输几何体的顶点坐标、面索引、顶点颜色、顶点法向量、顶点UV甚至是自定义属性, 使用自定义属性和着色器材质配合使用,强大到无所不能。

关于Float32Array,可以参考jsmpeg系列一 基础知识 字符处理 ArrayBuffer TypedArray

1.设置点
let positions = new Float32Array([
    -10, 0, 0, // 0
    10, 0, 0, // 1
    0, 10, 0, // 2
    0, 0, 5, // 3
    0, 10, 5, // 4
    0, 0, 15 // 5
]);
geometry = new THREE.BufferGeometry();
geometry.attributes.position = new THREE.BufferAttribute(positions, 3);

其中,THREE.BufferAttribute(positions, 3) 中第二个参数 3 指的是数组 positions 中每三个元素构成一个点,分别表示x y z 值。

设置好后,此时如果我们构建网格模型,代码如下。此时,会自动以positions 数组中三行数据,即9个元素3个点,连接构成一个三角形。依次构成三角形,上面的六个点将构成两个三角形,效果如图:

    let mesh = new THREE.Mesh(
        geometry,
        new THREE.MeshBasicMaterial({ 
            color: 0xff0000,
            side: THREE.DoubleSide 
        })
        );
    scene.add(mesh);
image.png

如果使用点模型构建,则会以每3个元素创建一个点,代码、效果如下:六个点直接显示出来。

    let point = new THREE.Points(
        geometry,
        new THREE.PointsMaterial({
                color: 0x00ff00,
                size: 3
            })
        );
    scene.add(point);
image.png

如果使用线模型构建,则会按照点的顺序依次连接各个点,代码、效果如下:

    let line = new THREE.Line(
        geometry,
        new THREE.LineBasicMaterial({
            color: 0x00ffff
        })
        );
    scene.add(line);
image.png
2.设置点颜色

每一个点都可以设置颜色,颜色通过 rgb 值来设置,r g b 分别取 0-1之间的值。比如如下设置颜色,其中,每一行分别为一个点的颜色,每一行的三个元素分别是颜色的 rgb 值,下面设置了六个点的颜色。

    let colors = new Float32Array([
            1, 0, 0,
            0, 1, 0,
            0, 0, 1, 
            1, 1, 0, 
            0, 1, 1, 
            1, 0, 1
        ]);

定义好颜色数组后,添加到 BufferGeometry 中,并且设置到材质中,举例创建一个Mesh模型如下:

    geometry.addAttribute("color", new THREE.BufferAttribute(colors, 3));
 
    let mesh = new THREE.Mesh(
        geometry,
        new THREE.MeshBasicMaterial({ 
            vertexColors: THREE.VertexColors, // 该设置就是使用顶点设置颜色
            side: THREE.DoubleSide 
        })
        );
    scene.add(mesh);

可以看到网格、点、线设置顶点颜色后效果分别如下:


image.png

image.png

image.png
3.设置点法向量

normal 的设置主要和光照相关,我们知道光照在物体的表面,由于光线与表面夹角角度的不一样,会导致亮度等也不一样。在 three.js 中,我们要知道一个物体的光照效果,需要知道物体表面每一个位置的法向方向,而 threejs 中的物体是由一个个点构成的,我们就需要知道每一个的法向量,这里的两个三角形法线方向分别是 z 轴和 x 轴。注意:法向量必须归一化,Vector3 中有方法可以直接调用。这里我们直接声明法向量数组:

    let normals = new Float32Array([
            0, 0, 1, 
            0, 0, 1, 
            0, 0, 1, 
            1, 0, 0, 
            1, 0, 0, 
            1, 0, 0
        ]);
 
    geometry.addAttribute("normal", new THREE.BufferAttribute(normals, 3));
4.设置 index

可以理解 index 为 positions 数组中的第n-1个点,如下每一行注释后面就是该点的 index 值。我们知道在 Mesh 模型中,threejs 直接按顺序没三个点一组创建一个三角形,index 就是为了帮助我们指定哪三个点构成一个三角形,并且这些点还可以重复使用。

let positions = new Float32Array([
    -10, 0, 0, // 0
    10, 0, 0, // 1
    0, 10, 0, // 2
    0, 0, 5, // 3
    0, 10, 5, // 4
    0, 0, 15 // 5
]);

如下,注意:这里用的是 Uint16Array,在这里,我们就可以通过6个点创建3个三角形,并且这些三角形的顶点都是我们自己指定的。而且,这里的 THREE.BufferAttribute(indexs, 1) 的第二个参数为 1 ,表示这里数组 indexs 中每一个元素表示一个点。

        let indexs = new Uint16Array([
            0, 1, 2,
            3, 4, 5,
            2, 4, 5
        ]);
 
        geometry.index = new THREE.BufferAttribute(indexs, 1);

效果如下,三个三角形:


image.png
5.绘制自定义UV贴图

关于uv的概念,可以参考图形学笔记七 纹理和贴图 AO

纹理坐标系

添加uv属性前先来捋一下uv坐标与顶点坐标的关系
image.png

上图是uv坐标与梯形顶点坐标的关系图,只需要按这个顺添加uv属性即可,需要注意的数uv坐标是一个Vector2类型,设置的时候需要注意,创建BufferAttribute时第二个参数为2,具体实现代码如下:

function drawPlaneByBufferGeometryUV() {
  //创建BufferGeometry实例
  const bufferGeom = new THREE.BufferGeometry();

  //初始化存放顶点信息的序列化数组
  const positions = new Float32Array([
    -5.0, 3.0, 0.0, //point0
    5.0, 3.0, 0.0, //point1
    6.0, -3.0, 0.0, //point2
    -6.0, -3.0, 0.0, //point3

  ]);

  //设置顶点信息
  bufferGeom.setAttribute('position', new THREE.BufferAttribute(positions, 3));

  //初始化存放颜色信息的序列化数组
  const colors = new Float32Array([
    0.5, 0.3, 0.6,
    0.5, 0.3, 0.6,
    0.5, 0.3, 0.6,
    0.5, 0.3, 0.6,

  ]);
  //设置颜色信息
  bufferGeom.setAttribute('color', new THREE.BufferAttribute(colors, 3));


  const indexs = new Uint16Array([
    0, 1, 2,
    0, 2, 3,
    4, 5, 6,
    4, 6, 7
  ]);


  //设置画面的索引
  bufferGeom.index = new THREE.BufferAttribute(indexs, 1);

  const uvs = new Uint16Array([
    0, 1,
    1, 1,
    1, 0,
    0, 0,

  ]);
  //设置UV
  bufferGeom.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));

  const planetTexture = new THREE.TextureLoader().load("../assets/textures/test.png");

  //创建材质
  const material = new THREE.MeshBasicMaterial({
    map: planetTexture,
    vertexColors: THREE.VertexColors, //使用缓存中的颜色
    side: THREE.DoubleSide
  });

  const mesh = new THREE.Mesh(bufferGeom, material);
  scene.add(mesh);
}
image.png
6.addAttribute和setAttribute(新版本用这个)
THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().

    /**
     * @deprecated Use {@link BufferGeometry#setAttribute .setAttribute()} instead.
     */
    addAttribute(
        name: string,
        attribute: BufferAttribute | InterleavedBufferAttribute
    ): BufferGeometry;
三、THREE.BufferAttribute

参考
Object3D、Geometry和BufferAttribute的关系
【ThreeJS基础教程-高级几何体篇】2.6 BufferGeometry与BufferAttribute
深入BufferAttribute,它最重要的成员就是array: TypedArray,他就是用来给GPU发送数据的数字数组。

let positions = new Float32Array([
    -10, 0, 0, // 0
    10, 0, 0, // 1
    0, 10, 0, // 2
    0, 0, 5, // 3
    0, 10, 5, // 4
    0, 0, 15 // 5
]);
geometry = new THREE.BufferGeometry();
geometry.attributes.position = new THREE.BufferAttribute(positions, 3);

从直觉上来说,对数字数组中的顶点属性进行处理时,会有很多额外的计算,比如说,第二个顶点的位置,存在array[3]array[4]array[5],而不是array[1]。直接处理会很麻烦,所以,three.js抽象出BufferAttribute就是解决这个痛点的。

在创建BufferAttribute时,需要一个itemSize参数,这个就是用来指定array中,几个元素表示一个顶点。对于上面的例子,每个position需要xyz三个分量,所以itemSize就是3。

BufferAttribute提供了一系列方法,按顶点来处理数据,关注点是每一个顶点,而不是数组中某一个下标的某个值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,812评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,626评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,144评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,052评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,925评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,035评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,461评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,150评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,413评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,501评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,277评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,159评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,528评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,868评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,143评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,407评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,615评论 2 335

推荐阅读更多精彩内容