图形学物体拾取:CPU VS GPU

一、CPU – raycaster

射线包围盒是一种常用的方法,在 CPU 中进行拾取,性能较好,但是精度较差。当拾取频率不高时,可以考虑使用像素级精度的帧缓冲拾取Framebuffer Picker.射线投射涉及将射线投射到场景中并检查对象和射线之间的碰撞。这样做有一些缺点;如果您有多个具有许多三角形的复杂网格形状,则计算碰撞可能会很昂贵,可以使用包围盒来判断,但是因为包围盒比较简单,拾取边缘误差较大。此外,您需要编写算法或使用第三方库来遍历您的场景并计算碰撞。这为小型纯图形项目增加了不必要的工作量。

射线拾取的原理就是坐标变换,相信熟悉图形流水线的都非常清楚:

物体坐标系->(模型矩阵)世界坐标系->(视角矩阵)view/camera坐标系->(投影矩阵)裁剪坐标系->(透视除法)NDC坐标系->(窗口变换)窗口坐标系;

所以射线拾取就是反过来:窗口坐标系->NDC坐标系 ->世界坐标系.

射线数学表达: Ray = o+kv (o原点,)

/**
     * 射线拾取函数
     * 选中的网格模型变为半透明效果
     */
    function ray() {
        var Sx = event.clientX;//鼠标单击位置横坐标
        var Sy = event.clientY;//鼠标单击位置纵坐标
        //屏幕坐标转标准设备坐标
        var x = ( Sx / window.innerWidth ) * 2 - 1;//标准设备横坐标
        var y = -( Sy / window.innerHeight ) * 2 + 1;//标准设备纵坐标
        var standardVector  = new THREE.Vector3(x, y, 0.5);//标准设备坐标
        //标准设备坐标转世界坐标
        var worldVector = standardVector.unproject(camera);
        //射线投射方向单位向量(worldVector坐标减相机位置坐标)
        var ray = worldVector.sub(camera.position).normalize();
        //创建射线投射器对象
        var raycaster = new THREE.Raycaster(camera.position, ray);
        //返回射线选中的对象
        var intersects = raycaster.intersectObjects([boxMesh,sphereMesh,cylinderMesh]);
        if (intersects.length > 0) {
            intersects[0].object.material.transparent = true;
            intersects[0].object.material.opacity = 0.6;
        }
    }

拾取Mesh上的图元比如三角形或者顶点:
在这里插入图片描述

二、 GPU – Framebuffer Picker

在将场景渲染到屏幕上时,GPU已经在进行该mesh所有必要的计算,以确定每个像素应该具有什么颜色。深度测试会丢弃看不见的片元。当点击位于a(x,y)的像素,这个像素的颜色实际上是特定场景中的Object。这种技术的想法是为在渲染每个对象时通过推送常量为它们提供唯一标识符,然后在填充颜色缓冲区的同时将其渲染到额外的帧缓冲区目标 glFramebufferRenderbuffer。渲染完成后,将纹理读回主机,并使用鼠标坐标查找对象标识符。OpenGL 提供了一个 glReadPixels 函数它是利用颜色的6位16进制表示,以颜色作为ID,在后台渲染出纹理后,根据鼠标坐标下的纹理颜色,进行ID的查询进行拾取操作.。当然,你不想把它渲染到屏幕上。像素信息将在缓冲区中可用,但不会显示在屏幕上。该技术的问题在于,需要再次将场景渲染为图像(FBO的RTT)。但是,对象拾取仅在用户单击鼠标按钮时完成,因此该性能损失仅在该时间发生在绘图中,这是带有几个对象的原始场景。每个对象都将指定一种唯一的颜色。

流程如下:

  1. 准备好两份数据,一份渲染输出到屏幕,一份渲染到FBO,同时把每个物体的信息存起来。
  2. 创建一个webglRenderTarget()(FBO,不直接输出到屏幕)。
  3. 渲染FBO,通过获取到的颜色位换算回ID值,判断点击了那个物体。
  4. 通过ID值获取到点击的物体的信息,在生成一个正方体套在点击物体外面,表示高亮。
  5. 最后正常渲染场景,输出到颜色缓冲区(屏幕)。

GPU的picking优化降低显存消耗:

GPU的RTT Pick有一些小技巧,比如把RTT的尺寸设置为4个像素,降低渲染一帧的负担之类的,makeFrustum可以设置一下视窗的位置和尺寸,调整一下尺寸。相应的代码也得改一下,主要是像素位置要对应上。具体实现:就是调节framebuffer的尺寸,最后你要返回一个Image来解译ID。这个Image可以很小。我记得是4x4(至于为什么最小是4x4,因为在GPU中的帧缓存或者说纹理的数据结构就是平铺多维数组,就是俗称的分块,最小是4x4);回归正题,在设置RTT的时候,要设置一下Framebuffer object的大小,这个尺寸可以通过camera的投影矩阵来设置,RTT要attach一个纹理,这个纹理尺寸就是可以是4x4。然后通过设置makeFrustrum,来设置RTT Camera的透视投影为你想要的像素位置,然后将针对该像素左下4x4这个大小区域进行绘制。当然具体makeFrustrum的参数不是4x4,而是反算到你的NDC Space下的,大概是的。你绘制的场景不变,只是绘制的投影矩阵变了,拾取只关心鼠标周围的像素ID,不关心距离很远的那些像素,这样就不用消耗过多的显存。特别注意!是渲染的场景不变,不是渲染的窗口不变。渲染的窗口通过设置投影矩阵来聚焦到鼠标周围,然后绘制的纹理要设置到一个小尺寸下,绘制出来后,图像的00点就是你鼠标位置处的那个像素。如果你设置了100x100的纹理,你就会看到从鼠标位置开始,左下100x100那么大的图像。

这个思想在分屏渲染里用的很多实际上就是假设这一个屏幕是一个4x4的窗口了,另外用的是RTT技术而已。想象一下你现在有一个10x10个屏幕,然后你想渲染1000x1000这么大的图像。那么每个图像就是100x100。你怎么去渲染这单个图像呢?其实就是你每次移动就渲染其他新的,但是永远也不可能说能看完一整张,也不用看完一整张图!本人最近也在看GAMES104游戏引擎架构,当你要实现一个小引擎或者工作实操上有很多小trick,比如可以延迟查询来降低回传同步造成的CPU卡顿,比如交互操作用不着实时渲染查询,每一秒渲染一次也就够了,这就可以降低大量的数据传输卡顿了。读到这相信你能跟我一样理解什么事工业界什么事学术界的区别~

三、拓展 – 深度值拾取

第三种方法前辈大佬说08年测绘院就有写过,看来我还是在玩泥巴哈哈哈哈~~~鼠标位置可以转换到gl_FragCoord.xy,深度值是FragCoord.z,这就是Projection后的[-1,1]区间坐标值(opengl是-1到1,而DX是0到1),然后invVP得到世界坐标的值。这就是一个普通的转换,如果是从gbuffer返回来,那就说的高级一点就是coordinate regeneration但是换汤不换药。

该方法的工作原理如下:沿鼠标射线获取整个深度范围,并将其存储为有限的固定大小。 我选择 DEPTH_ARRAY_SCALE 32用于演示目的,但实际上应该在 1000 左右。 在片段着色器中,我们将实体 ID 写入相应的深度桶 bucket中。为了创建存储bucket.,使用绑定到片元着色器的写到存储缓冲区。

#define DEPTH_ARRAY_SCALE 32

layout(set=0, binding = 3) buffer writeonly s_Write_t
{
    uint data[DEPTH_ARRAY_SCALE];
} s_Write;

我们通过 push_constant 以 uniform 的形式将UNIQUE_ID和MOUSE_POS传递给片段着色器

layout(push_constant) uniform PushConsts
{
    vec2 MOUSE_POS;
    uint UNIQUE_ID;
} pushC;

然后在片段着色器中,我们得到顶点着色器使用 gl_FragCoord.z 计算的当前深度值。 这个值应该在 0 和 1 之间。我们通过将它乘以数组的长度 (DEPTH_ARRAY_SCALE) 来放大它。 这给了我们深度桶 bucket的索引。 如果我们当前着色的像素接近当前鼠标坐标,我们将唯一 ID 写入该索引位置。

// get the depth and scale it up by
// the total number of buckets in depth array
uint zIndex = uint(gl_FragCoord.z * DEPTH_ARRAY_SCALE);

if( length( pushC.MOUSE_POS - gl_FragCoord.xy) < 1)
{
    s_Write.data[zIndex] = pushC.UNIQUE_ID;
}

以上是片元着色器~
在主机端,你可以做两件事之一,要么使用 HOST_VISIBLE 存储缓冲区,并保持它的持久映射。 或者,使用 DEVICE_LOCAL 存储缓冲区,并在写入片段后执行 bufferCopy。 我选择了前者,因为它更容易。
您现在在主机上拥有的是一个数组,其中数组中的每个索引都代表鼠标射线上的某个深度。 我们遍历数组并找到最接近的非零值。

auto * v = ... get mapped memory ...
auto u = static_cast<uint32_t*>(v);


uint32_t SELECTED_OBJECT_ID = 0;


for(size_t i=0;i<DEPTH_ARRAY_SCALE;i++)
{
    if( u[i] != 0)
    {
        SELECTED_OBJECT_ID = u[i];
        break;
    }
}
// we have to zero out the memory each frame
std::memset(v, 0, DEPTH_ARRAY_SCALE * sizeof(uint32_t));

效果图如下:
在这里插入图片描述

四、总结

优缺点及其应用场景:
Framebuffer Picker :

  • 消耗性能低;
  • 对于射线碰撞,没有精度问题;
  • 但是只能拾取Mesh这种粒度;

raycaster:

  • 除了拾取Mesh还可以拾取图元,比如三角形;
  • 依靠物理系统,消耗性能比较高;
  • 会有精度问题,比如图纸放大缩小上百倍很难拾取(本人因放大缩小图纸感受到了精度问题);

打通了引擎Runtime和编辑器开发的桥梁,通过物体的拾取就可以挂载其他辅助的组件,例如Gizmos,进而编辑场景。或者通过脚本来调用raycast对场景的物体进行射线检测或者动画拾取。不会言拾取,必称射线检测,不同的方法有不同的适用范围,可以按需选择,甚至两者混用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/527425.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8s技术全景:架构、应用与优化

一、介绍 Kubernetes的历史和演进 Kubernetes&#xff08;简称K8s&#xff09;是一个开源的容器编排系统&#xff0c;用于自动化应用程序的部署、扩展和管理。它最初是由Google内部的Borg系统启发并设计的&#xff0c;于2014年作为开源项目首次亮相。 初始阶段 Kubernetes的诞生…

网站想使用https安全协议,必须要安装ssl证书吗?

ssl证书作为保护网站数据传输安全的重要工具&#xff0c;被广泛应用于网站的安全加密通信中。很多人在初次接触ssl证书时&#xff0c;有一个常见的疑问&#xff1a;网站使用https协议必须要ssl证书吗&#xff1f; 答案是肯定的。   HTTPS是一种通过计算机网络进行安全通信的…

计算机网络 网络命令的使用

一、实验内容 1.PING网络命令的实验 ping 127.0.0.1(内部回环测试)ping 本主机的IP地址ping 默认网关地址ping远端目的地的IP地址ping localhostping域名 2.其他网络命令实验 命令用途ipconfig/all 显示当前系统网络配置&#xff0c;包括IP地址、子网掩码、默认网关等trace…

Unity MySql安装部署与Unity连接

1.前言 最近项目用到MySql&#xff0c;记录一下安装部署过程。 数据量过大或者需要管理用户数据的时候用mysql的话数据结构比较清晰明了&#xff0c;便于管理。 2.安装MySql Unity版本&#xff1a;2019.4.16 MySql版本&#xff1a;8.2.0 下载地址&#xff1a;MySql 下载…

LLM - 大语言模型(LLM) 的 应用技术

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/137503579 大语言模型(LLM) 的应用技术范围非常广泛,即: LangChain:开发框架,专为大型语言模型设计,以提高开发人工智能应用的效率,允许开发者将语言模…

Day17_学点JavaEE_转发、重定向、Get、POST、乱码问题总结

1 转发 转发&#xff1a;一般查询了数据之后&#xff0c;转发到一个jsp页面进行展示 req.setAttribute("list", list); req.getRequestDispatcher("student_list.jsp").forward(req, resp);2 重定向 重定向&#xff1a;一般添加、删除、修改之后重定向到…

整理的微信小程序日历(单选/多选/筛选)

一、日历横向多选&#xff0c;支持单日、双日、三日、工作日等选择 效果图 wxml文件 <view class"calendar"><view class"section"><view class"title flex-box"><button bindtap"past">上一页</button&…

AWS入门实践-在EC2上部署Wordpress网站

在AWS EC2上部署WordPress涉及到几个步骤&#xff0c;包括启动EC2实例、配置数据库、安装WordPress等。以下是详细的步骤和相应的命令脚本 第一步: 启动 EC2 实例 登录 AWS 控制台,进入 EC2 服务启动一个新的 EC2 实例,选择 Amazon Linux 2 AMI选择合适的实例类型(例如 t2.mi…

⼿机客户端画K线图流程

优质博文&#xff1a;IT-BLOG-CN 一、什么是K线流程 K线图是一种用于展示金融市场价格走势的图表。它通常由四个关键价格点组成&#xff0c;即开盘价、收盘价、最高价和最低价。K线图的流程可以简单概括为以下几个步骤&#xff1a; 【1】收集数据&#xff1a; 首先&#xff0c…

GPT-4对多模态大模型在多模态预训练、 理解生成上的启发

传统人工智能 模型往往依赖大量有标签数据的监督训练,而且一个模型一般只能解决一个任务,仅适用于单一场景, 这使得人工智能的研发和应用成本高,场景适应能力弱,难以规模化应用。 常见的多模态任务大致可以分为两类: 多模态理解任务,如视频 分类、视觉问答、跨模态检索、指代…

数据库——实验6 视图的创建与使用

1. 视图的定义 视图是根据需要以一个表或多个表为基础&#xff0c;选择满足一定条件的行或列数据的静态定义。它是一种逻辑对象&#xff0c;是一种虚拟表。视图并不生成行或列的永久副本&#xff0c;并不占用存储空 间&#xff0c;也就是说&#xff0c;视图就是保存在数据库中…

LLMs之FreeGPT35:FreeGPT35的简介、安装和使用方法、案例应用之详细攻略

LLMs之FreeGPT35&#xff1a;FreeGPT35的简介、安装和使用方法、案例应用之详细攻略 目录 FreeGPT35的简介 FreeGPT35的安装和使用方法 1、部署和启动服务 Node 2、使用 Docker 部署服务&#xff1a; 运行 Docker 容器以部署服务 使用 Docker Compose 进行更方便的容器化…

TCP/IP协议、HTTP协议和FTP协议等网络协议简介

文章目录 一、常见的网络协议二、TCP/IP协议1、TCP/IP协议模型被划分为四个层次2、TCP/IP五层模型3、TCP/IP七层模型 三、FTP网络协议四、Http网络协议1、Http网络协议简介2、Http网络协议的内容3、HTTP请求协议包组成4、HTTP响应协议包组成 一、常见的网络协议 常见的网络协议…

Docker之数据卷和Dockerfile

目录 一、Docker数据管理 二、数据卷 创建数据卷 查看数据卷 删除数据卷 挂载数据卷 三、数据卷容器 创建数据卷容器 测试数据卷容器 备份数据卷容器 还原数据卷容器 四、Dockerfile 什么是Dockerfile 基本结构 常用指令 快速入门 编写Dockerfile文件 构建镜…

Java-Tomcat

一、web补充技术 ①&#xff1a;B/S架构 主流的方式&#xff0c;只要有浏览器即可。编程方式直接基于socket即可 ②&#xff1a;javascript 简称js&#xff0c;早期只是实现在客户端的浏览器的动态效果&#xff0c;但服务端不会解释运行&#xff0c;所以本质上是静态资源。 …

使用新版FLIR (FLIR_ADAS_v2) 数据集创建yolo格式数据集(目标检测)

FLIR在2022.1.19发布了新版的FLIR_ADAS_v2&#xff0c;有着更多的类别和数量更丰富的图像。数据集同步注释热图像和无注释RGB图像供参考。本文章主要介绍如何使用FLIR_ADAS_v2中的rgb图像和thermal图像来制作yolo格式数据集。 1.官方数据集下载&#xff1a;FLIR_ADAS_v2数据集…

Leetcode 6. Z 字形变换

将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1a; P A H N A P L S I I G Y I R 之后&#xff0c;你的输出需要从左往右逐行读取&#xff0…

【鸿蒙开发】ArkTS和组件

1. 初识ArkTS语言 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性。 当前&#xff0c;ArkTS在TS的基础上主要扩展了如下能力&#xff1a; 基本语法&#xff1a;ArkTS定义了声明式UI描述、自…

AI日报:北大Open Sora视频生成更强了;文心一言可以定制你自己的声音;天工 SkyMusic即将免费开放;

&#x1f916;&#x1f4f1;&#x1f4bc;AI应用 北大Open Sora视频生成更强了!时长可达10秒&#xff0c;分辨率更高 【AiBase提要:】 ⭐️ Open-Sora-Plan v1.0.0模型发布 显著提升视频生成质量和文本控制能力 ⭐️ 支持华为昇腾910b芯片&#xff0c;提升运行效率和质量。 ⭐…

智慧安防系统EasyCVR视频汇聚平台接入大华设备无法语音对讲的原因排查与解决

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xff0c;视频画面1、4、9、16个可选&#xff0c;支持自定义视频轮播。EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标…