Android客户端下载:sight_and_light-debug.rar
以下翻译转载自:indienova
原文:http://ncase.github.io/sight-and-light/
注意:手机浏览可能会不能加载内嵌网页
今天,我要介绍大家制作如下这个效果:(鼠标移动可看不同效果,可在原文中尝试交互效果)
这个效果被用在了我最新的开源游戏《Nothing To Hide》里面。您可能知道,有不少游戏都有类似的效果,比如《摩纳哥:你的就是我的(Monaco: What’s Yours Is Mine)》、《爆破杰克(Dynamite Jack)》以及《史莱姆吉什(Gish)》等。说不定也会出现在你的游戏中!
我会将自己的实现步骤以及在开发过程中犯过的错误展示出来。首先,是一些预热的内容,下面的演示展示了绘制一些线段并且跟踪鼠标的位置。(提示:一个方形由四条线段组成,以此类推)
接下来是比较数学的一部分。别担心,只是回忆一下之前学过的代数。
我们需要找到视线(也可以是光线,我们用视线来统一代替)和这些线段之间最近的交点。任何一条线都可以这样来表示:
1
|
Point+Direction*T
|
这样,我们就可以得到关于视线和线段的四个等式:
RayX=r_px+r_dx*T1
RayY=r_py+r_dy*T1
SegmentX=s_px+s_dx*T2
SegmentY=s_py+s_dy*T2
|
注意:在我们开始之前,要注意检查视线和相对应的线段是否平行,也就是方向是否相同。如果平行,就没有交点,不用处理。
如果它们(视线和相对应的线段)相交,那么交点的 X 和 Y 应该相等,也就是:
r_px+r_dx*T1=s_px+s_dx*T2
r_py+r_dy*T1=s_py+s_dy*T2
|
我们将用下面的方法取得 T1 和 T2
//IsolateT1forbothequations,gettingridofT1
T1=(s_px+s_dx*T2-r_px)/r_dx=(s_py+s_dy*T2-r_py)/r_dy
//Multiplybothsidesbyr_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
//SolveforT2!
T2=(r_dx*(s_py-r_py)+r_dy*(r_px-s_px))/(s_dx*r_dy-s_dy*r_dx)
//PlugthevalueofT2togetT1
T1=(s_px+s_dx*T2-r_px)/r_dx
|
要确保 T1>0 和 0<T2<1。如果不是这样,那么预计中的交点并不在视线或者线段上,其实也就是无交点。如果由交点,那很好!你找到一个交点。现在用同样一条视线和所有线段计算,以便找到最近的交点(应该是 T1 值最小的那个)。
下面是看起来的样子:(移动鼠标的效果)
很好,现在让我们发射出 50 条视线来:
然后,我想,我只要连接起这些交点来,就可以得到一个看起来不错的多边形,结果看起来是这样:
见鬼。就算我发出 360 条视线,也还是看起来不对劲儿。这个问题困扰了我半天,直到我意识到:我不用向所有方向发出视线,我只需要向每个线段的端点发出视线就可以。
针对每一条(不同的)线段的端点,我直接发出视线,另外增加两条偏移量为 +/- 0.00001 弧度的视线。这两条额外的视线用来去和线段后面的墙来相交。
接下来,我按照这些视线的角度将这些交点排序,这样我就可以简单的按照顺时针顺序将它们连接起来,可以绘制出一个看起来满意的多边形。
最后!看起来确实不错了。我又添加了一些效果,看起来就像下面这样,带有一些模糊的阴影效果。红点代表的是 11 个初始点——是的,11 个多边形。
最后,做了些改进,我放了两幅图。然后,将这两幅图混合,就成了我们在开始的时候看到的那个效果那样。
然后我又添加了额外的光源,就成了下面这样
多光源、投射阴影、激光感应炸弹、显示你或者敌人的可视范围……这种 2D 的效果有很强的可扩展性,如果应用得当,加上好的创意,可以大大增强你的游戏的吸引力。
Cocos2d-x实现过程
这个效果最初是在indienova看到的相关介绍,于是就开始琢磨如何在Cocos2d-x中实现这个效果。
第一次尝试:
这个效果是由 背景图(骷髅)和前景(美女)图 叠加形成的,在光线变动的情况下背景保持不变,而前景根据光线的明暗相应的显示对应部分的明暗度,而没有被光线照射到的地方隐藏掉。
首先想到的自然是 DrawNode 绘画出光照射到的多边形区域,然后用ClippingNode 设置DrawNode 为模板来绘制前景图。
但是从效果中可以看出影子中还会区分透明度,越偏离视线的方向的光线越暗,并与前景叠加,前景被分成了几个层次的透明度。
ClippingNode在绘制模板的时候会把模板扣抠掉,如果可以保留模板的绘制的话,就可以将前景与模板的颜色做blend处理,形成多层次透明度。
查阅 ClippingNode 代码,发现ClippingNode在做模板设置的时候设置了 模板测试 从不通过,所以模板怎么都不会被绘制出来。
可以修改为:
然后设置前景的blend func 为:
autofore=Sprite::create(
"foreground.png"
);
BlendFuncfunc={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 来绘制,成功!
项目地址:https://github.com/2youyouo2/sight_and_night