three.js使用gpu选取物体并计算交点位置


本文摘自PHP中文网,作者angryTom,侵删。

光线投射法

使用three.js自带的光线投射器(Raycaster)选取物体非常简单,代码如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

var raycaster = new THREE.Raycaster();

var mouse = new THREE.Vector2();

function onMouseMove(event) {  

 // 计算鼠标所在位置的设备坐标

    // 三个坐标分量都是-1到1

    mouse.x = event.clientX / window.innerWidth * 2 - 1;

    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;

}

function pick() {   

    // 使用相机和鼠标位置更新选取光线   

    raycaster.setFromCamera(mouse, camera);   

    // 计算与选取光线相交的物体

    var intersects = raycaster.intersectObjects(scene.children);

}

【相关课程推荐:JavaScript视频教程】

它是采用包围盒过滤,计算投射光线与每个三角面元是否相交实现的。

但是,当模型非常大,比如说有40万个面,通过遍历的方法选取物体和计算碰撞点位置将非常慢,用户体验不好。

但是使用gpu选取物体不存在这个问题。无论场景和模型有多大,都可以在一帧内获取到鼠标所在点的物体和交点的位置。

使用GPU选取物体

实现方法很简单:

1. 创建选取材质,将场景中的每个模型的材质替换成不同的颜色。

2. 读取鼠标位置像素颜色,根据颜色判断鼠标位置的物体。

具体实现代码:

1. 创建选取材质,遍历场景,将场景中每个模型替换为不同的颜色。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

let maxHexColor = 1;// 更换选取材质

scene.traverseVisible(n => {   

if (!(n instanceof THREE.Mesh)) {

        return;

    }

    n.oldMaterial = n.material;

        if (n.pickMaterial) { // 已经创建过选取材质了

        n.material = n.pickMaterial;

                return;

    }

    let material = new THREE.ShaderMaterial({

        vertexShader: PickVertexShader,

        fragmentShader: PickFragmentShader,

        uniforms: {

            pickColor: {

                value: new THREE.Color(maxHexColor)

            }

        }

    });

    n.pickColor = maxHexColor;

    maxHexColor++;

    n.material = n.pickMaterial = material;

});

  

PickVertexShader:

void main() {

    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

}

  

PickFragmentShader:

uniform vec3 pickColor;void main() {

    gl_FragColor = vec4(pickColor, 1.0);

}

2. 将场景绘制在WebGLRenderTarget上,读取鼠标所在位置的颜色,判断选取的物体。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

let renderTarget = new THREE.WebGLRenderTarget(width, height);

let pixel = new Uint8Array(4);// 绘制并读取像素

renderer.setRenderTarget(renderTarget);

renderer.clear();

renderer.render(scene, camera);

renderer.readRenderTargetPixels(renderTarget, offsetX, height - offsetY, 1, 1, pixel); // 读取鼠标所在位置颜色

// 还原原来材质,并获取选中物体

const currentColor = pixel[0] * 0xffff + pixel[1] * 0xff + pixel[2];

let selected = null;

 

scene.traverseVisible(n => {

    if (!(n instanceof THREE.Mesh)) {

            return;

    }

        if (n.pickMaterial && n.pickColor === currentColor) {

         // 颜色相同

 

        selected = n; // 鼠标所在位置的物体   

        }

        if (n.oldMaterial) {

            n.material = n.oldMaterial;        delete n.oldMaterial;

        }

});

说明:offsetX和offsetY是鼠标位置,height是画布高度。readRenderTargetPixels一行的含义是选取鼠标所在位置(offsetX, height - offsetY),宽度为1,高度为1的像素的颜色。

pixel是Uint8Array(4),分别保存rgba颜色的四个通道,每个通道取值范围是0~255。

完整实现代码:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/event/GPUPickEvent.js

阅读剩余部分

相关阅读 >>

js是什么意思?

js原型链是什么

浏览器的事件循环

js中如何利用正则匹配多个全部数据

three.js是什么?

js如何实现盒子拖拽效果?(附代码)

js如何实现递归函数

js怎么设置css高度

js实现页面跳转的方法

js中字符串转数组的方法

更多相关阅读请进入《three.js》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...