HEngine
  • 引擎编译
  • 未来工作
  • 使用文档
    • 代码规范
    • Shader规范
    • config 配置
  • Python脚本系统
    • 技术分析
    • 使用api
  • Cpp脚本系统
    • 技术分析
  • 技术文档 & 心得体会
    • 数学
      • 坐标系、UV与深度范围
      • 行主序 与 列主序
    • 路径管理
    • 构建系统
    • 自定义资源格式
    • UI
      • undo/redo
      • 分辨率处理
    • ECS 系统
    • 插件系统
    • 数学库
    • Ref Counted
    • 音频
    • ChatGPT
    • Mesh、Vertex 等组织关系
    • 编辑器热更新方案
    • Profile
    • NLP
    • RenderDoc
    • CodeGen与反射系统
    • 安装包
    • 物理引擎
      • PhysX集成
      • Bullet集成
    • 动画
  • 图形后端
    • 坐标系差异
    • Feature差异
    • RHI 封装
    • Shader与Constant Buffer
    • DX12
    • Vulkan
    • Render Graph
    • 渲染整体架构
  • 图形Feature
    • 毛发
    • 鼠标拾取
    • 实例化渲染
  • Shader
    • Shader 交叉编译
    • Shader Toy
    • Shader 热更新
    • PBR
  • 打包
    • 打包
  • Bug 记录 & 解决过程
    • 闪退记录汇总
    • 导入图片显示混乱
    • D3D下glfw+imgui失效
  • 其他资料
    • 总结集合
Powered by GitBook
On this page
  • pixel方法
  • 物理方法
  1. 图形Feature

鼠标拾取

Previous毛发Next实例化渲染

Last updated 1 year ago

可以用物理或者pixel方法。

对OpenGL有glReadBuffer方法。

对dx11可以参考:

https://stackoverflow.com/questions/13479259/read-pixel-data-from-render-target-in-d3d11

https://www.gamedev.net/forums/topic/594722-reading-one-pixel-from-texture-to-cpu-in-dx11/

对dx来说,我们必须弄一张 D3D11_USAGE_STAGING 格式的纹理,然后用 ID3D11DeviceContext::Map() 方法。因为只有这种格式cpu才可读:

pixel方法

有几个缺陷:

  1. 必须增加一个绘制id的pass,不过可以用mrt的手段,例如对于glsl可以指定两个输出(下面的用于绘制id),因此我认为问题不大:

    out vec4 FragColor;
    out int color2;
  2. 强行增加回读阶段。我们知道gpu一般落后cpu两三帧,强行从gpu搬数据回读给cpu是消耗巨大的。对应ogl有方法glReadPixels

优点是简单方便,并且绝对准确。但是因为上面的原因效率低下,尤其是顶点缓冲区可能会影响最终发布(前两点可以用宏轻易绕开),因此目前几乎所有引擎都是做的物理的拾取方法。

物理方法

物理方法一般是使用射线检测,一般做法是和物理引擎绑定起来。但是我还没决定allin physx or bullet,因此打算换用其他的库。这里我选择的是 embree 这个库,对于embree而言,要想对一个geometry使用transform,必须要用instance才行。逻辑是把一个 geometry 的数据(这里我选择的是三角面数据)RTCGeometry geom = rtcNewGeometry(gEmbreeDevice, RTC_GEOMETRY_TYPE_TRIANGLE) 这个geom需要绑定到一个他专门的 instance scene 中,同时再用 rtcNewGeometry(gEmbreeDevice, RTC_GEOMETRY_TYPE_INSTANCE) 来创建一个 instance 的geom,这个instance的geom需要和全局的scene绑定,并且调用 rtcSetGeometryInstancedScene 方法和 instance scene 绑定起来。

因此逻辑是对每个mesh可以弄个instance scene,实例化优化的时候就也要在这个 instance scene 里头创建多个这样的 mesh。

最后射线检测的时候记得要 rayhit.ray.mask = -1; 才行。

射线检测的原点即摄像机位置,射线方向我们可以根据鼠标点击在屏幕空间的位置,在ndc空间构造一个向量,然后通过逆变换反推出世界空间的向量当作射线方向,可以参考:

https://antongerdelan.net/opengl/raycasting.html

顶点缓冲区由于带了id的信息,必须每帧更新,也就导致必须每帧从cpu端重传(因为id是从cpu端传给gpu端的)。这就导致对于dx原本可以用default的缓冲区(至多用UpdateSubresource更新)现在变成不得不用dynamic每次map unmap来更新了。 来自https://directx11.tech/#/part1/02

https://directx11.tech/#/part1/02