您的位置:首页 > Web前端

how to create 2D visibility/shadow effects for your game

2015-09-29 13:55 567 查看
以下翻译转载自:indienova

原文:http://ncase.github.io/sight-and-light/

注意:手机浏览可能会不能加载内嵌网页

 

今天,我要介绍大家制作如下这个效果:(请在下面的区域内移动鼠标,如果没有反应请点击一下该区域再尝试)

这个效果被用在了我最新的开源游戏 Nothing
To Hide 里面。您可能知道,有不少游戏都有类似的效果,比如《摩纳哥:你的就是我的(Monaco:
What’s Yours Is Mine)》、《爆破杰克(Dynamite
Jack)》以及《史莱姆吉什(Gish)》等……说不定也会出现在你的游戏中!



我会将自己的实现步骤以及在开发过程中犯过的错误展示出来。首先,是一些预热的内容,下面的演示展示了绘制一些线段并且跟踪鼠标的位置。(提示:一个方形由四条线段组成,以此类推)

接下来是比较数学的一部分。别担心,只是回忆一下之前学过的代数。

我们需要找到视线(也可以是光线,我们用视线来统一代替)和这些线段之间最近的交点。任何一条线都可以这样来表示:

1Point + Direction * T
这样,我们就可以得到关于视线和线段的四个等式

1

2

3

4

5

6

7

Ray
X
=
r_px+r_dx*T1

 

Ray
Y
=
r_py+r_dy*T1

 

Segment
X
=
s_px+s_dx*T2

 

Segment
Y
=
s_py+s_dy*T2

注意:在我们开始之前,要注意检查视线和相对应的线段是否平行,也就是方向是否相同。如果平行,就没有交点,不用处理。

如果它们(视线和相对应的线段)相交,那么交点的 X 和 Y 应该相等,也就是:

123r_px+r_dx*T1 = s_px+s_dx*T2 r_py+r_dy*T1 = s_py+s_dy*T2
我们将用下面的方法取得 T1 和 T2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// Isolate T1 for both equations, getting rid of T1

 

T1
=
(s_px+s_dx*T2-r_px)/r_dx
=
(s_py+s_dy*T2-r_py)/r_dy

 

// Multiply both sides by r_dx * r_dy

 

s_px*r_dy
+
s_dx*T2*r_dy
-
r_px*r_dy
=
s_py*r_dx
+
s_dy*T2*r_dx
-
r_py*r_dx

 

// Solve for T2!

 

T2
=
(r_dx*(s_py-r_py)
+
r_dy*(r_px-s_px))/(s_dx*r_dy
-
s_dy*r_dx)

 

// Plug the value of T2 to get T1

 

T1
=
(s_px+s_dx*T2-r_px)/r_dx

要确保 T1>0 和 0<T2<1。如果不是这样,那么预计中的交点并不在视线或者线段上,其实也就是无交点。如果由交点,那很好!你找到一个交点。现在用同样一条视线和所有线段计算,以便找到最近的交点(应该是 T1 值最小的那个)。

下面是看起来的样子:(请在下面的区域内移动鼠标)

很好,现在让我们发射出 50 条视线来:

然后,我想,我只要连接起这些交点来,就可以得到一个看起来不错的多边形,结果看起来是这样:

见鬼。就算我发出 360 条视线,也还是看起来不对劲儿。这个问题困扰了我半天,直到我意识到:我不用向所有方向发出视线,我只需要向每个线段的端点发出视线就可以。

针对每一条(不同的)线段的端点,我直接发出视线,另外增加两条偏移量为 +/- 0.00001 弧度的视线。这两条额外的视线用来去和线段后面的墙来相交。

接下来,我按照这些视线的角度将这些交点排序,这样我就可以简单的按照顺时针顺序将它们连接起来,可以绘制出一个看起来满意的多边形。

最后!看起来确实不错了。我又添加了一些效果,看起来就像下面这样,带有一些模糊的阴影效果。红点代表的是 11 个初始点——是的,11 个多边形。

最后,做了些改进,我放了两幅图。然后,将这两幅图混合,就成了我们在开始的时候看到的那个效果那样。

然后我又添加了额外的光源,就成了下面这样





多光源、投射阴影、激光感应炸弹、显示你或者敌人的可视范围……这种 2D 的效果有很强的可扩展性,如果应用得当,加上好的创意,可以大大增强你的游戏的吸引力

 

 


Cocos2dx实现过程

这个效果最初是在 indienova 看到的相关介绍,于是就开始琢磨如何在cocos2dx中实现这个效果。

第一次尝试:

这个效果是由 背景图(骷髅)和前景(美女)图 叠加形成的,在光线变动的情况下背景保持不变,而前景根据光线的明暗相应的显示对应部分的明暗度,而没有被光线照射到的地方隐藏掉。

首先想到的自然是 DrawNode 绘画出光照射到的多边形区域,然后用ClippingNode 设置DrawNode 为模板来绘制前景图。

但是从效果中可以看出影子中还会区分透明度,越偏离视线的方向的光线越暗,并与前景叠加,前景被分成了几个层次的透明度。

ClippingNode在绘制模板的时候会把模板扣抠掉,如果可以保留模板的绘制的话,就可以将前景与模板的颜色做blend处理,形成多层次透明度。

查阅 ClippingNode 代码,发现ClippingNode在做模板设置的时候设置了 模板测试 从不通过,所以模板怎么都不会被绘制出来。

12glStencilFunc(GL_NEVER, mask_layer, mask_layer);glStencilOp(!_inverted ? GL_REPLACE : GL_ZERO, GL_KEEP, GL_KEEP);
可以修改为

1

2

glStencilFunc(GL_GEQUAL,
mask_layer,
mask_layer);

glStencilOp(GL_KEEP,
GL_KEEP,
!_inverted
?
GL_REPLACE
:
GL_ZERO);

然后设置前景的blend func 为

1

2

3

auto
fore
=
Sprite::create("foreground.png");

BlendFunc
func
=
{GL_DST_COLOR,
GL_NONE};

fore->setBlendFunc(func);

此时测试模板和前景是能很好的叠加效果的。

但是接下来使用DrawNode绘制多边形的时候却出现了问题,DrawNode对于处理复杂多边形并不能胜任,画出来的形状都是乱飞的。

 

第二次尝试:

这次尝试了使用了 前一篇文章 介绍的nanovg
来绘制多边形,首先建了个NvgNode,重载draw 里面写绘制函数,将这个node作为模板,最后发现新问题:

1.nanovg 绘制的效率比较低,简单测试同为使用多边形填充整个屏幕,nanovg 30帧,DrawNode 60帧,差的还是比较多。

2.nanovg 处理多边形是 也是 使用 模板来处理 填充颜色,修改了一番还是不能够很好的和cocos结合,没有再往深处继续研究。

 

第三次尝试:

这次还是回到了DrawNode上,对于opengl 绘制 复杂多边形上进行资料搜索,

看到几种解决方案(参考):

第一种解决方案:多边形网格化法

对于非简单多边形、非凸多边形或有洞的多边形,OpenGL在GLU库中提供了一个多边形网格化对象GLUtesselator,对多边形进行网格化————将它们分解成一组简单的、能够进行渲染的OpenGL多边形。

经测试这种方法对凹凸多边形和自交、带孔多边形都能正确的渲染。

第二种解决方案:模板缓冲法stencil Buffer

可以参考 nanovg中的实现方法

第三种解决方案: 凹多边形凸分解法

思路: 使用算法将凹多边形分解为多个凸多边形或一系列的三角形,然后进行渲染。

 

这里 列出了许多三角形划分多边形算法的实现,这里选了一个比较靠谱的
 Triangulate 来使用。

将最后输出的多边形分解为三角形后调用DrawNode::drawTriangle 来绘制,成功!

--------------------------------------------------------------华丽的分割线--------------------------------------------------------
这个效果我亲自做出了多个阴影,但是发现存在某些bug:每次启动App的时候,一旦加上光源,会出现色彩完全失真现象,还没找到原因,如有知道的大神,还望留言告知,谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: