ios - 确定一组像素的表面法线的最佳方法?
<p><p>我目前的一项工作是为 iOS Cocos2D 创建一个 2D 可破坏地形引擎(参见 <a href="https://github.com/crebstar/PWNDestructibleTerrain" rel="noreferrer noopener nofollow">https://github.com/crebstar/PWNDestructibleTerrain</a>)。毫无疑问,它处于婴儿阶段,但自几周前开始以来,我已经取得了重大进展。但是,我在计算表面法线时遇到了一些性能障碍。</p>
<p>注意:对于我的可破坏地形引擎,alpha 为 0 被认为不是实心地面。 </p>
<p>下面发布的方法非常适用于小矩形,例如 n < 30。任何高于 30 的值都会导致帧速率下降。如果您接近 100x100,那么您不妨在 Sprite 尝试穿越地形时阅读一本书。目前,这是我能想到的最好的方法来改变 Sprite 在地形上漫游时的角度(要获得 Sprite 方向的角度,只需采用 100 * normal * (1,0) 向量的点积)。 </p>
<pre><code>-(CGPoint)getAverageSurfaceNormalAt:(CGPoint)pt withRect:(CGRect)area {
float avgX = 0;
float avgY = 0;
ccColor4B color = ccc4(0, 0, 0, 0);
CGPoint normal;
float len;
for (int w = area.size.width; w >= -area.size.width; w--) {
for (int h = area.size.height; h >= -area.size.height; h--) {
CGPoint pixPt = ccp(w + pt.x, h + pt.y);
if () {
if (color.a != 0) {
avgX -= w;
avgY -= h;
} // end inner if
} // end outer if
} // end inner for
} // end outer for
len = sqrtf(avgX * avgX + avgY * avgY);
if (len == 0) {
normal = ccp(avgX, avgY);
} else {
normal = ccp(avgX/len, avgY/len);
} // end if
return normal;
} // end get
</code></pre>
<p>我的问题是我的 Sprite 需要更大的矩形才能使其运动看起来更逼真。我考虑过缓存所有表面法线,但这会导致知道何时重新计算表面法线的问题,而且这些计算也非常昂贵( block 应该有多大?)。另一个较小的问题是我不知道如何正确处理长度 = 0 的情况。</p>
<p>所以我被困住了......来自社区的任何建议将不胜感激!我的方法是最好的吗?还是我应该重新考虑算法?我是游戏开发的新手,一直希望学习新的技巧和窍门。</p></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>有人在另一个论坛上回答了这个问题。我将发布修改后的 getSurfaceNormal 函数。这实现了 Nathan Reed 描述的第二种算法</p>
<p>-(CGPoint)getSurfaceNormalAt:(CGPoint)pt withSquareWidth:(int)area {
//这个方法只看表面像素</p>
<pre><code>int avgX = 0;
int avgY = 0;
CGPoint normal;
float len;
ccColor4B color = ccc4(0, 0, 0, 0);
for (int w = area; w >= -area; w--) {
int h = area;
do {
if () {
if (color.a != 0) {
if (w < 0) {
avgX -= w;
avgY -= h;
} else {
avgX += w;
avgY += h;
}
break;
} // end inner if
} // end outer if
h--;
} while (h >= -area);
} // end for
int perpX = -avgY;
int perpY = avgX;
len = sqrtf(perpX * perpX + perpY * perpY);
normal = ccp(perpX/len, perpY/len);
return normal;
}
</code></pre>
<p>这里也是这个人的原始帖子:感谢 Nathan Reed 的回答</p>
<p>我认为你的基本想法是正确的。我将总结您当前的代码在做什么。要获得一个点周围区域内的平均法线,您需要收集以该点为中心的矩形中的所有像素。对于矩形中的所有实心像素,您将平均从像素到查询点的向量。实际上,您正在计算从附近实体像素的质心到查询点的向量。</p>
<p>我有两个一般性建议来加快速度。首先是您不需要查看搜索区域中的<em>每个</em>像素。通过使用稀疏采样,您可能会得到一个很好的近似值:只看几个孤立的像素,均匀分布在搜索区域中。例如,您可以在循环中以 2 到 5 个像素为单位,而不是 1 个像素;这会给你一个稀疏的网格采样,这可能足以让你侥幸逃脱。 <a href="http://devmag.org.za/2009/05/03/poisson-disk-sampling/" rel="noreferrer noopener nofollow">Poisson disk sampling</a>也是一种常见的稀疏采样方法,尤其是在用于软阴影、SSAO 等的像素着色器中。您预先计算泊松磁盘模式(只需将点存储在代码中的静态数组中)并将模式缩放到所需搜索的大小运行时的区域。</p>
<p>第二个建议是,您可以将 2D 搜索替换为一系列 1D 搜索。如果我理解正确的话,您并不真正关心地下是什么,您只关心地面<em>surface</em> 的方向是什么。因此,您可以在搜索区域的顶部选择几个点,然后从每个起点向下进行一维搜索,直到找到一个实心像素。在 ASCII 艺术中,</p>
<pre><code>X X X X
| | | |
| | | ..*
| ..*...*....
*............
</code></pre>
<p>X 是起点,竖线是一维搜索,点是地面,星是搜索找到的地面点。一旦你有了这些点,你就可以计算从每个点到中心点的平均向量,但是对中心左侧的点的向量求反,而对于右侧的点则不理会它。这应该可以防止平均值出现为零,并会为您提供一个与地形相切的向量,指向右侧。计算垂直于这个向量的向量,你就会得到你的法线。</p></p>
<p style="font-size: 20px;">关于ios - 确定一组像素的表面法线的最佳方法?,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/17329741/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/17329741/
</a>
</p>
页:
[1]