GPU像素级对象选取:ShapeUp着色器编码点击拾取技巧全解析 GPU像素级对象选取ShapeUp着色器编码点击拾取技巧全解析【免费下载链接】ShapeUp-publicA 3D Modeler Made in a Week项目地址: https://gitcode.com/gh_mirrors/sh/ShapeUp-publicShapeUp 是一款一周建成的 GPU 体素建模工具用着色器实现建模、渲染与对象选取。本文带你拆解它最巧妙的功能——GPU 像素级点击拾取无需网格碰撞只靠一段动态生成的选取着色器和一次像素回读就能精准判断鼠标点中了哪个对象。 适合读者对 GPU 编程、着色器开发、拾取算法Picking感兴趣的新手与 3D 应用开发者。什么是 ShapeUp一周写成的 GPU 建模器ShapeUp 的作者只用了一周时间就写出了这个基于构造实体几何CSG的 3D 建模工具。它的核心理念非常反传统没有网格整个场景由一组圆角方块RoundBox通过 SDF有向距离场布尔运算拼成全 GPU 渲染每个像素都用光线步进Ray Marching在着色器里实时求解场景代码即模型场景本身就是 C 代码任何改动都会重新拼装着色器源码并热重载。正是最后一点让它的点击选中对象功能有了极其优雅的解法。拾取难题为什么传统方案在这里行不通在常规 3D 引擎里点击选中物体通常靠两种方式网格射线相交——场景必须有三角形网格ShapeUp 里根本没有网格深度缓冲 对象 ID——需要对每个对象做深度测试而 SDF 是连续场无法直接输出 ID。ShapeUp 选择了第三条路着色器编码Shader Encoding 离屏渲染 像素回读。这也是所有场渲染体积渲染、SDF 渲染、噪声渲染应用中最通用的拾取方案。核心思路把对象编号画进颜色里整套方案的精髓一句话让 GPU 渲染一张不可见的图图中每个像素的 R 通道 该像素命中的对象编号 1然后 CPU 回读这一个像素。关键实现在 src/main.c 的object_at_pixel()函数int object_index ((int)pixels[(x w*(h-y))*4]) - 1;R 通道读出i1减 1 就是对象下标读到0时结果为-1表示点空了。点击拾取流程三步走拆解第 1 步动态拼装选取专用着色器每次拾取时程序把四个片段拼成一段全新的片元着色器源码然后从内存直接编译SHADER_VERSION_PREFIX // GLSL 版本头 shader_prefix_fs // SDF 数学函数库见 src/shader_prefix.fs append_map_function(...) // 把全部对象翻译成着色器代码 selection_fs // 选取入口见 src/selection.fs其中append_map_function()src/main.c是灵魂它遍历场景里每一个对象生成一个signed_distance_field()函数把每个对象的R 通道颜色写成自身编号use_color_as_index true时r i1。这样光线命中谁谁就把自己的编号染到像素上。第 2 步只渲染鼠标周围的 3×3 像素这是最见功力的优化。程序并不重画整屏而是创建一个全屏离屏渲染目标RenderTexture只提交一个以鼠标位置为中心、3×3 像素的小四边形src/main.c。着色器里castRay()从相机出发射线、最多迭代 64 次穿过 SDFsrc/selection.fs与主渲染管线共用同一套几何——保证看到的和选中的永远是同一套数学。⚡ 只渲染 9 个像素却调用了完整的 SDF 求交这就是 GPU 拾取看似昂贵实则极快的原因。程序甚至会打印picking object took Xms告诉你耗时。第 3 步回读像素解码编号uint8_t *pixels rlReadTexturePixels(...); int object_index ((int)pixels[(x w*(h-y))*4]) - 1;用rlReadTexturePixels把 GPU 显存中的像素搬回 CPU取(x, y)处的第一个字节R 通道减 1即得对象下标。随后主循环src/main.c只需比较若new_selection ! selected_sphere→ 更新选中对象置needs_rebuild true触发场景着色器重建手柄用户按 R1 键时同样调用该函数选取屏幕正中心的对象逻辑完全复用src/main.c。源码导读关键文件一览文件作用src/main.c主程序object_at_pixel()拾取、场景到着色器的翻译器append_map_function()src/selection.fs选取片元着色器单像素光线步进输出对象编号src/shader_prefix.fsSDF 数学库圆角盒、平滑并集、平滑差集、对称、旋转src/shader_base.fs主渲染管线着色、软阴影、环境光遮蔽MakefilemacOS 构建依赖 lib/raylib-4.5.0_macoswebbuild.shWebAssembly 一键构建脚本一个值得注意的对照主渲染着色器中选中对象是动态的——append_map_function()接受dynamic_index参数被选中对象改用selectionValues_uniform 数组实时驱动位置与角度于是拖拽该对象时不需要重新编译着色器src/main.c。静态编码拾取 动态 uniform 编辑两条路径在同一套 SDF 代码上无缝切换是整份代码里最精巧的设计。实战借鉴这套方案怎么用在你自己的项目里如果你想在自己的 SDF/体积渲染项目里实现同样的点击拾取记住这个通用四步模板编号编码为每个对象分配唯一 ID写入其材质颜色的 R 通道或单独一张 ID 通道离屏渲染用与主渲染完全相同的几何数学只渲染目标像素区域到 FBO像素回读glReadPixels/rlReadTexturePixels取回目标像素解码判定ID-1 为命中对象0 为背景。⚠️ 新手常见坑数学不一致拾取着色器和主渲染着色器必须共用同一套 SDF 源码否则会出现看得见却点不中每帧回读整张图性能杀手。ShapeUp 只画 3×3 像素区域把回读量压到最低忘了 -1R 通道从i1开始编码正是为了和未命中 0区分开。常见问题 FAQQ为什么不用射线与包围盒相交来拾取AShapeUp 的主循环里其实两者都有——控制手柄缩放 gizmo用的是射线包围盒碰撞CPU 侧而点中任意对象本体才走 GPU 像素拾取。CPU 快但只能测简单体GPU 拾取才能精确匹配 SDF 的任意复杂形态。Q拾取会卡顿吗A会有一次 GPU→CPU 的同步开销所以程序专门打印耗时。ShapeUp 的做法是只在鼠标左键释放的瞬间才拾取一次而不是每帧轮询交互流畅度完全无感。QWebAssembly 版本支持吗A支持。同一份 src/main.c 通过PLATFORM_WEB宏适配网页端见 Makefile.Web 与 webbuild.sh像素回读逻辑保持不变。结语ShapeUp 用不到 200 行核心代码一个函数 一个 45 行的着色器就解决了 SDF 建模器里最棘手的点击拾取问题。它的思路——把答案编码进颜色让 GPU 替你判卷——是所有程序化场渲染应用的通用武器。下次你的项目里出现点中了什么的难题时不妨先想想这条 GPU 像素级拾取路线。 想动手研究克隆仓库后 macOS 直接makeWeb 端运行./webbuild.sh重点阅读object_at_pixel()与selection.fs配合本文流程拆解即可快速上手。【免费下载链接】ShapeUp-publicA 3D Modeler Made in a Week项目地址: https://gitcode.com/gh_mirrors/sh/ShapeUp-public创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考