如何解决2D栅格Center Of Mass算法的GLSL GPU移植问题?
现有实现的核心问题
- 阈值判断逻辑错位:后续迭代分支里,用当前像素的阈值状态决定是否累加所有邻域的backbuffer值,等于只要当前像素是无效像素,哪怕邻域有10个有效像素的累积值也完全不加,和邻域求和的设计目标完全相反。
- 提前做除法破坏累积过程:原代码里每轮迭代直接输出
sumCoord/counter,把累积值换成了局部质心,后续邻域采样拿到的不是块级累积的坐标和、计数,已经被缩小的质心值,多次累加下来根本得不到全局总和。 - 邻域遍历漏加当前像素:3x3邻域循环没有正确把中心位置(当前像素自身上一轮的backbuffer值)纳入有效累加,也就是你复盘发现的漏加当前fragCoord贡献的问题,会导致每轮迭代都丢失当前点的累积值。
- 语法硬伤:代码里texture采样语句缺失右括号、if分支闭合大括号缺失,GLSL编译都无法通过。
PingPong实现的正确逻辑
这个场景用PingPong做全局质心,本质是靠3x3邻域的逐层扩散,把所有有效像素的坐标、计数贡献逐次传播到整个形状区域,每帧backbuffer存的必须是局部累积的(sumX, sumY, count)三元组,绝对不能提前计算质心。
分阶段流程:
- 初始化帧:所有超过阈值的有效像素,backbuffer存自身屏幕坐标和计数1;未达阈值的像素存全0,无额外贡献。
- 迭代帧:每轮对当前像素的3x3邻域,把所有邻域存的累积值直接求和,输出到当前backbuffer,全程不做除法。
- 收敛判断:当连续两帧同位置的累积值差小于精度阈值时停止迭代。此时所有有效像素位置存的都是全形状的sumX、sumY、总计数,直接在有效位置做除法得到质心即可。
修正后的核心代码
vec2 pixSize = 1.0 / resolution; vec4 originalTex = texture(myTex, uv); // 初始化帧逻辑 if(frame == 0) { if(originalTex.r > threshold) { // 存储格式:x=sumX, y=sumY, z=count gl_FragColor = vec4(gl_FragCoord.xy, 1.0, 0.0); } else { gl_FragColor = vec4(0.0); } return; } // 迭代帧的3x3邻域求和 vec3 accumulated = vec3(0.0); for(int y = -1; y <= 1; y++) { for(int x = -1; x <= 1; x++) { // 补全原代码缺失的右括号,覆盖x=0,y=0的当前像素位置 vec4 neighborData = texture(backbuffer, uv + pixSize * vec2(x, y)); // 直接累加邻域的累积值,不需要额外阈值判断——所有无效像素存的是0,加了不影响结果 accumulated += neighborData.xyz; } } // 直接输出累积值,禁止提前做除法 gl_FragColor = vec4(accumulated, 0.0);
实操注意事项
- 纹理采样设置:给backbuffer设置
GL_CLAMP_TO_EDGE的采样模式,避免边缘位置邻域采样越界拉到错误数值。 - 迭代次数估算:对单连通形状,3x3邻域的扩散速度是每轮向外扩1个像素,需要的迭代次数基本等于形状外接矩形的对角线长度,比如100x100的形状大概50次迭代就能完全收敛,不需要盲目增加迭代次数。
- 质心提取:收敛后随便选一个originalTex高于阈值的像素点,采样backbuffer的xyz通道,用
centroid = vec2(data.x / data.z, data.y / data.z)计算最终质心即可——此时所有有效位置的累积值都是一致的全局总和,选哪个点计算结果都一致。
内容的提问来源于stack exchange,提问作者Felipe Gutierrez
相关产品推荐
相关产品推荐

