2015.3.23优化修改,现在已经能达到稳定60帧了。。

本博客地址:http://www.cnblogs.com/wolfred7464/

创意来自于:http://ncase.me/sight-and-light/

我要介绍的,就是这样的效果:(创意和素材都来自于上文的网址)

由于原文介绍的过于简练,导致像我这样的小白根本看不懂,所以我想要介绍的更易懂一点。。

一、画线段

在Cocos2d-x中,已经封装了通过Opengl ES的画线函数,只需要创建一个DrawNode对象,就可以画线了,画几条线段,就像这样:

二、画射线和线段的交点及轨迹。

这里需要一点点几何知识了。(我也是恶补的)

直线的参数表示:

直线可以用直线上的一点P0和方向向量v表示,直线上的所有点P满足 P = P0 + tv。

参数方程最方便的地方在于直线、射线、线段的方程形式是一样的,区别在于参数t。直线的t没有范围限制,射线的t>0,线段的t在0~1之间(t >=0 && t <= 1)。

直线交点:

设直线分别为 P+t1v 和 Q+t2w,设向量u=QP,设cross(x, y)为向量x和y的叉积,则:

t1 = cross(w, u) / cross(v, w)

t2 = cross(v, u) / cross(v, w)

当cross(v, w) == 0时,两直线平行,无交点。

所以把屏幕中心作为光源,方向指向鼠标所在的位置,画一条射线,t即是光源与交点的距离,选一个最近的交点(即t最小),连接光源和这个点,就会得到这样的效果:

主要代码:

 bool HelloWorld::getIntersection(const Line& ray, const Line& segment,
Point& point, float& distance)
{
Vec2 v1(ray.p2 - ray.p1);
Vec2 v2(segment.p2 - segment.p1);
float cross = getCross(v1, v2);
if(cross == ) {
return false;
}
Vec2 u(ray.p1 - segment.p1);
float t1 = getCross(v2, u) / cross;
float t2 = getCross(v1, u) / cross;
if(t1 < || t2 < || t2 > ) {
return false;
}
point = v1 * t1 + ray.p1;
distance = t1;
return true;
}

射线与线段的交点

三、以鼠标为光源,画射向线段端点的光线

改动一下刚才的代码,以鼠标作为光源,画射向每个端点的光线,在每条光线的两侧同时画出极角偏移1e-4的两条光线,用来穿过线段端点,与端点后面的线段相交。看起来就像这样:

四、画多边形,标记出光亮区域

上一步画的光线表示出了光亮区域,还需要画出填充多边形来标记一下,但是opengl只能画凸多边形。所以为了画出需要的不规则多边形,要分割成三角形来画。

容易看出,任意相邻的两个交点与光源,可以组成一个三角形,接下来就是找相邻的点。所以极角排序一下,依次取相邻的点就可以了。画完三角形后的效果就像这样:

五、实现本文开头的效果

Cocos2d-x提供了ClippingNode类,可以做出不规则的裁剪图形,以上一步画的多边形为模板裁剪就可以了,不多赘述,代码中有详细。但是目前还有两个问题:1、没有实现出原文中的阴影效果 2、编译到安卓看不到效果。

希望有大牛能指教一下。

六、附上Cocos2d-x写的主要代码

 #ifndef __LIGHTSCENE_H__
#define __LIGHTSCENE_H__ #include "cocos2d.h" class Line
{
public:
cocos2d::Point p1;
cocos2d::Point p2;
Line(const cocos2d::Point& p1, const cocos2d::Point& p2) {
this->p1 = p1;
this->p2 = p2;
}
}; class LightScene : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(LightScene);
private:
void initVertexs();
void drawSegments();
void initSingleVertexs();
void calcAngles(const cocos2d::Point& touchPos); float getCross(const cocos2d::Vec2& v1, const cocos2d::Vec2& v2);
bool getIntersection(const Line& ray, const Line& segment,
cocos2d::Point& point, float& distance); void drawLight(const cocos2d::Vec2& pos); cocos2d::DrawNode* _staticDraw;
cocos2d::DrawNode* _touchDraw;
cocos2d::ClippingNode* _clip; std::vector<cocos2d::Point> _vertexs;
std::vector<Line> _segments;
std::vector<float> _angles;
}; #endif

LightScene.h

 #include "LightScene.h"

 USING_NS_CC;

 Scene* LightScene::createScene()
{
auto scene = Scene::create();
auto layer = LightScene::create();
scene->addChild(layer);
return scene;
} bool LightScene::init()
{
if (!Layer::init()) {
return false;
} // 添加背景图
auto visSize = Director::getInstance()->getVisibleSize();
auto background = Sprite::create("background.png");
background->setPosition(visSize.width / , visSize.height / );
addChild(background, ); _staticDraw = DrawNode::create();
addChild(_staticDraw, );
_touchDraw = DrawNode::create();
addChild(_touchDraw, ); _clip = ClippingNode::create();
_clip->setInverted(false);
_clip->setAlphaThreshold(255.0f);
auto foreground = Sprite::create("foreground.png");
foreground->setPosition(visSize.width / , visSize.height / );
_clip->addChild(foreground, );
_clip->setStencil(_touchDraw);
addChild(_clip, ); initVertexs();
drawSegments();
initSingleVertexs(); // 触摸监听
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = [=](Touch* touch, Event* event) {
drawLight(touch->getLocation());
return true;
};
listener->onTouchMoved = [=](Touch* touch, Event* event) {
drawLight(touch->getLocation());
};
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
return true;
} void LightScene::drawLight(const cocos2d::Vec2& pos)
{
Point tar(, ); // 光线的端点
Point cur(, ); // 光线与线段的交点
float distance = ; // 光源与交点的距离 _touchDraw->clear(); // 计算极角,添加两个偏移1e-4的极角
calcAngles(pos); // 极角排序
std::sort(_angles.begin(), _angles.end(), [](float x, float y) {
return x < y;
}); // 找最近的交点
static std::vector<Point> vertex;
vertex.clear();
for (auto angle : _angles) {
Vec2 dlt(cos(angle), sin(angle));
float closest = -;
for (auto s : _segments) {
if (getIntersection(Line(pos, pos + dlt), s, cur, distance)) {
if (closest == - || closest > distance) {
closest = distance;
tar = cur;
}
}
}
if (closest != -) {
vertex.push_back(tar);
}
} // 画三角形
int limit = vertex.size() - ;
for (int i = ; i < limit; i++) {
_touchDraw->drawTriangle(pos, vertex[i], vertex[i + ], Color4F::WHITE);
}
if (limit > ) {
_touchDraw->drawTriangle(pos, vertex[limit], vertex[], Color4F::WHITE);
}
} void LightScene::initVertexs() {
int crd[] = {
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , , ,
, , ,
};
for (int i = ; i < ; i += ) {
_vertexs.push_back(Point(crd[i], crd[i + ]));
}
} // 画线段
void LightScene::drawSegments()
{
for (int i = ; i < _vertexs.size(); i += ) {
_segments.push_back(Line(_vertexs[i], _vertexs[i + ]));
_staticDraw->drawSegment(_vertexs[i], _vertexs[i + ], 0.5f, Color4F::WHITE);
}
} // 找不重复端点
void LightScene::initSingleVertexs()
{
std::vector<Point> singleVertexs;
for (int i = ; i < _vertexs.size(); i++) {
bool has = false;
for (int j = ; j < singleVertexs.size(); j++) {
if (_vertexs[i] == singleVertexs[j]) {
has = true;
break;
}
}
if (!has) {
singleVertexs.push_back(_vertexs[i]);
}
}
_vertexs.clear();
for (auto v : singleVertexs) {
_vertexs.push_back(v);
}
} // 计算极角
void LightScene::calcAngles(const Point& touchPos)
{
_angles.clear();
const float eps = static_cast<float>(1e-);
for (auto p : _vertexs) {
auto angle = atan2(p.y - touchPos.y, p.x - touchPos.x);
//_angles.push_back(angle);
_angles.push_back(angle - eps);
_angles.push_back(angle + eps);
}
} // 向量的叉积
float LightScene::getCross(const Vec2& v1, const Vec2& v2)
{
return (v1.x * v2.y - v1.y * v2.x);
} // 射线与线段的交点
bool LightScene::getIntersection(const Line& ray, const Line& segment,
Point& point, float& distance)
{
Vec2 v1(ray.p2 - ray.p1);
Vec2 v2(segment.p2 - segment.p1);
float cross = getCross(v1, v2);
if (cross == ) {
return false;
}
Vec2 u(ray.p1 - segment.p1);
float t1 = getCross(v2, u) / cross;
float t2 = getCross(v1, u) / cross;
if (t1 < || t2 < || t2 > ) {
return false;
}
point = v1 * t1 + ray.p1;
distance = t1;
return true;
}

LightScene.cpp

用Cocos2d-x实现2D光线效果的更多相关文章

  1. [原创]cocos2d-x研习录-第一阶 背景介绍 之 cocos2d家族史

    Cocos2D是一个2D开源游戏引擎,它最早是由Ricardo Quesada(阿根廷人,社区简称Riq)和他的朋友们用Python开发的,用于开发2D游戏和基于2D图形的任何应用.最早引擎的名字源自 ...

  2. Cocos2D研究院之CCNode详解(三)

    http://www.xuanyusong.com/archives/950 上一章我们了解了cocos2d的项目路径以及工作原理,这次作者要真刀真枪地讲解代码了,咱们先来看看cocos2d最常用.也 ...

  3. Index

    我主要在研究.NET/C# 实现 PC IMERP 和 Android IMERP ,目的在解决企业通信中遇到的各类自动化问题   分布式缓存框架: Microsoft Velocity:微软自家分布 ...

  4. GitHub上整理的一些工具

    技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应用,关注软件开发领域 ...

  5. GitHub上整理的一些工具[转载]

    Source:http://segmentfault.com/q/1010000002404545 技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddi ...

  6. Mac下cocos2dx-3.0打包Android时,提示&quot;SimpleAudioEngine.h&quot;not found的解决方法

    前段时间触控公布cocos2dx-3.0,在升级之后试过之后,在最初的不习惯之后,感觉比之前的好用了不少,在下之前一直是用xCode模板创建,这回算是一口气升到顶了. 之后再一次编程时须要用到Sima ...

  7. GitHub 开源工具整理

    技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应用,关注软件开发领域 ...

  8. GitHub上整理的一些工具,求补充

    http://segmentfault.com/q/1010000002404545 技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MS ...

  9. GitHub上整理的一些资料(转)

    技术站点 Hacker News:非常棒的针对编程的链接聚合网站 Programming reddit:同上 MSDN:微软相关的官方技术集中地,主要是文档类 infoq:企业级应用,关注软件开发领域 ...

随机推荐

  1. C#_IComparer实例 - 实现ID或者yearOfscv排序

    调用LIST的Sort的时候会调用IComparer的默认实现,quicksort会调用每个元素的CompareTo的IComparable实现 using System; using System. ...

  2. Fixflow引擎解析(二)(模型) - BPMN2.0读写

    Fixflow引擎解析(四)(模型) - 通过EMF扩展BPMN2.0元素 Fixflow引擎解析(三)(模型) - 创建EMF模型来读写XML文件 Fixflow引擎解析(二)(模型) - BPMN ...

  3. Java基础知识强化之网络编程笔记13:TCP之TCP协议上传图片并给出反馈

    1. TCP协议上传图片并给出反馈: (1)客户端: package cn.itcast_13; import java.io.BufferedInputStream; import java.io. ...

  4. PS基础

    1.仿制图章工具:[小  ]大   建立一个新的图层,可以进行图片某个部分的复制,完全复制之后,还可以调整大小(ctrl+t), 颜色(ctrl+u打开色相饱和度的菜单)等. 2.修复画笔工具:与仿制 ...

  5. Cadence画封装的步骤

    画封装的步骤 打开 pad designer       through 通孔       single  表贴      在焊盘设置时,soldermask层要比pastmask大0.1毫米     ...

  6. 关于Git中的一些常用的命令

    深入了解git的checkout命令 检出命令(git checkout)是Git最常用的命令之一,同时也是一个很危险的命令. 因为这条命令会重写工作区.检出命令的用法如下: 用法一: git che ...

  7. 虚反矩阵指令pinv之应用

    pinv指令     在多数解的例子中,有时并不是仅要将其中一变数设定为零之解.为使整个系统得到最佳化,亦可利用pinv指令求得最小模组之合理解.pinv(A)又称为虚反矩阵(pseudoinvers ...

  8. HTTPS协议学习总结

    目录 一:什么是HTTPS协议?二:客户端与服务端如何建立HTTPS协议连接?三:证书.加密协议.信息摘要.签名算法概念解释与关系梳理四:低版本操作系统作为客户端发送HTTPS失败分析五:参考资料   ...

  9. dedecms自定义函数(二次开发)

    一些功能可能dedecms没有,这个时候可以自己写一些函数: 1.打开inlude->extend.func.php,将函数写到里面 比如:前台: [field:id function=&quo ...

  10. content = "IE=edge,chrome=1" 详解

    content = "IE=edge,chrome=1" 详解 < meta http-equiv = "X-UA-Compatible" content ...