2015.3.23優化修改,如今已經能達到穩定60幀了。。ide
本博客地址:http://www.cnblogs.com/wolfred7464/函數
創意來自於:http://ncase.me/sight-and-light/優化
我要介紹的,就是這樣的效果:(創意和素材都來自於上文的網址)this
因爲原文介紹的過於簡練,致使像我這樣的小白根本看不懂,因此我想要介紹的更易懂一點。。spa
在Cocos2d-x中,已經封裝了經過Opengl ES的畫線函數,只須要建立一個DrawNode對象,就能夠畫線了,畫幾條線段,就像這樣:3d
這裏須要一點點幾何知識了。(我也是惡補的)code
直線能夠用直線上的一點P0和方向向量v表示,直線上的全部點P知足 P = P0 + tv。對象
參數方程最方便的地方在於直線、射線、線段的方程形式是同樣的,區別在於參數t。直線的t沒有範圍限制,射線的t>0,線段的t在0~1之間(t >=0 && t <= 1)。blog
設直線分別爲 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最小),鏈接光源和這個點,就會獲得這樣的效果:
主要代碼:
1 bool HelloWorld::getIntersection(const Line& ray, const Line& segment, 2 Point& point, float& distance) 3 { 4 Vec2 v1(ray.p2 - ray.p1); 5 Vec2 v2(segment.p2 - segment.p1); 6 float cross = getCross(v1, v2); 7 if(cross == 0) { 8 return false; 9 } 10 Vec2 u(ray.p1 - segment.p1); 11 float t1 = getCross(v2, u) / cross; 12 float t2 = getCross(v1, u) / cross; 13 if(t1 < 0 || t2 < 0 || t2 > 1) { 14 return false; 15 } 16 point = v1 * t1 + ray.p1; 17 distance = t1; 18 return true; 19 }
3、以鼠標爲光源,畫射向線段端點的光線
改動一下剛纔的代碼,以鼠標做爲光源,畫射向每一個端點的光線,在每條光線的兩側同時畫出極角偏移1e-4的兩條光線,用來穿過線段端點,與端點後面的線段相交。看起來就像這樣:
上一步畫的光線表示出了光亮區域,還須要畫出填充多邊形來標記一下,可是opengl只能畫凸多邊形。因此爲了畫出須要的不規則多邊形,要分割成三角形來畫。
容易看出,任意相鄰的兩個交點與光源,能夠組成一個三角形,接下來就是找相鄰的點。因此極角排序一下,依次取相鄰的點就能夠了。畫完三角形後的效果就像這樣:
Cocos2d-x提供了ClippingNode類,能夠作出不規則的裁剪圖形,以上一步畫的多邊形爲模板裁剪就能夠了,很少贅述,代碼中有詳細。可是目前還有兩個問題:一、沒有實現出原文中的陰影效果 二、編譯到安卓看不到效果。
但願有大牛能指教一下。
1 #ifndef __LIGHTSCENE_H__ 2 #define __LIGHTSCENE_H__ 3 4 #include "cocos2d.h" 5 6 class Line 7 { 8 public: 9 cocos2d::Point p1; 10 cocos2d::Point p2; 11 Line(const cocos2d::Point& p1, const cocos2d::Point& p2) { 12 this->p1 = p1; 13 this->p2 = p2; 14 } 15 }; 16 17 class LightScene : public cocos2d::Layer 18 { 19 public: 20 static cocos2d::Scene* createScene(); 21 virtual bool init(); 22 CREATE_FUNC(LightScene); 23 private: 24 void initVertexs(); 25 void drawSegments(); 26 void initSingleVertexs(); 27 void calcAngles(const cocos2d::Point& touchPos); 28 29 float getCross(const cocos2d::Vec2& v1, const cocos2d::Vec2& v2); 30 bool getIntersection(const Line& ray, const Line& segment, 31 cocos2d::Point& point, float& distance); 32 33 void drawLight(const cocos2d::Vec2& pos); 34 35 cocos2d::DrawNode* _staticDraw; 36 cocos2d::DrawNode* _touchDraw; 37 cocos2d::ClippingNode* _clip; 38 39 std::vector<cocos2d::Point> _vertexs; 40 std::vector<Line> _segments; 41 std::vector<float> _angles; 42 }; 43 44 #endif
1 #include "LightScene.h" 2 3 USING_NS_CC; 4 5 Scene* LightScene::createScene() 6 { 7 auto scene = Scene::create(); 8 auto layer = LightScene::create(); 9 scene->addChild(layer); 10 return scene; 11 } 12 13 bool LightScene::init() 14 { 15 if (!Layer::init()) { 16 return false; 17 } 18 19 // 添加背景圖 20 auto visSize = Director::getInstance()->getVisibleSize(); 21 auto background = Sprite::create("background.png"); 22 background->setPosition(visSize.width / 2, visSize.height / 2); 23 addChild(background, 1); 24 25 _staticDraw = DrawNode::create(); 26 addChild(_staticDraw, 100); 27 _touchDraw = DrawNode::create(); 28 addChild(_touchDraw, 100); 29 30 _clip = ClippingNode::create(); 31 _clip->setInverted(false); 32 _clip->setAlphaThreshold(255.0f); 33 auto foreground = Sprite::create("foreground.png"); 34 foreground->setPosition(visSize.width / 2, visSize.height / 2); 35 _clip->addChild(foreground, 1); 36 _clip->setStencil(_touchDraw); 37 addChild(_clip, 101); 38 39 initVertexs(); 40 drawSegments(); 41 initSingleVertexs(); 42 43 // 觸摸監聽 44 auto listener = EventListenerTouchOneByOne::create(); 45 listener->onTouchBegan = [=](Touch* touch, Event* event) { 46 drawLight(touch->getLocation()); 47 return true; 48 }; 49 listener->onTouchMoved = [=](Touch* touch, Event* event) { 50 drawLight(touch->getLocation()); 51 }; 52 getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); 53 return true; 54 } 55 56 void LightScene::drawLight(const cocos2d::Vec2& pos) 57 { 58 Point tar(0, 0); // 光線的端點 59 Point cur(0, 0); // 光線與線段的交點 60 float distance = 0; // 光源與交點的距離 61 62 _touchDraw->clear(); 63 64 // 計算極角,添加兩個偏移1e-4的極角 65 calcAngles(pos); 66 67 // 極角排序 68 std::sort(_angles.begin(), _angles.end(), [](float x, float y) { 69 return x < y; 70 }); 71 72 // 找最近的交點 73 static std::vector<Point> vertex; 74 vertex.clear(); 75 for (auto angle : _angles) { 76 Vec2 dlt(cos(angle), sin(angle)); 77 float closest = -1; 78 for (auto s : _segments) { 79 if (getIntersection(Line(pos, pos + dlt), s, cur, distance)) { 80 if (closest == -1 || closest > distance) { 81 closest = distance; 82 tar = cur; 83 } 84 } 85 } 86 if (closest != -1) { 87 vertex.push_back(tar); 88 } 89 } 90 91 // 畫三角形 92 int limit = vertex.size() - 1; 93 for (int i = 0; i < limit; i++) { 94 _touchDraw->drawTriangle(pos, vertex[i], vertex[i + 1], Color4F::WHITE); 95 } 96 if (limit > 0) { 97 _touchDraw->drawTriangle(pos, vertex[limit], vertex[0], Color4F::WHITE); 98 } 99 } 100 101 void LightScene::initVertexs() { 102 int crd[] = { 103 0, 360, 840, 360, 104 840, 360, 840, 0, 105 840, 0, 0, 0, 106 0, 0, 0, 360, 107 100, 210, 120, 310, 108 120, 310, 200, 280, 109 200, 280, 140, 150, 110 140, 150, 100, 210, 111 100, 160, 120, 110, 112 120, 110, 60, 60, 113 60, 60, 100, 160, 114 200, 100, 220, 210, 115 220, 210, 300, 160, 116 300, 160, 350, 40, 117 350, 40, 200, 100, 118 540, 300, 560, 320, 119 560, 320, 570, 290, 120 570, 290, 540, 300, 121 650, 170, 760, 190, 122 760, 190, 740, 90, 123 740, 90, 630, 70, 124 630, 70, 650, 170, 125 600, 265, 780, 310, 126 780, 310, 680, 210, 127 680, 210, 600, 265 128 }; 129 for (int i = 0; i < 100; i += 2) { 130 _vertexs.push_back(Point(crd[i], crd[i + 1])); 131 } 132 } 133 134 // 畫線段 135 void LightScene::drawSegments() 136 { 137 for (int i = 0; i < _vertexs.size(); i += 2) { 138 _segments.push_back(Line(_vertexs[i], _vertexs[i + 1])); 139 _staticDraw->drawSegment(_vertexs[i], _vertexs[i + 1], 0.5f, Color4F::WHITE); 140 } 141 } 142 143 // 找不重複端點 144 void LightScene::initSingleVertexs() 145 { 146 std::vector<Point> singleVertexs; 147 for (int i = 0; i < _vertexs.size(); i++) { 148 bool has = false; 149 for (int j = 0; j < singleVertexs.size(); j++) { 150 if (_vertexs[i] == singleVertexs[j]) { 151 has = true; 152 break; 153 } 154 } 155 if (!has) { 156 singleVertexs.push_back(_vertexs[i]); 157 } 158 } 159 _vertexs.clear(); 160 for (auto v : singleVertexs) { 161 _vertexs.push_back(v); 162 } 163 } 164 165 // 計算極角 166 void LightScene::calcAngles(const Point& touchPos) 167 { 168 _angles.clear(); 169 const float eps = static_cast<float>(1e-4); 170 for (auto p : _vertexs) { 171 auto angle = atan2(p.y - touchPos.y, p.x - touchPos.x); 172 //_angles.push_back(angle); 173 _angles.push_back(angle - eps); 174 _angles.push_back(angle + eps); 175 } 176 } 177 178 // 向量的叉積 179 float LightScene::getCross(const Vec2& v1, const Vec2& v2) 180 { 181 return (v1.x * v2.y - v1.y * v2.x); 182 } 183 184 // 射線與線段的交點 185 bool LightScene::getIntersection(const Line& ray, const Line& segment, 186 Point& point, float& distance) 187 { 188 Vec2 v1(ray.p2 - ray.p1); 189 Vec2 v2(segment.p2 - segment.p1); 190 float cross = getCross(v1, v2); 191 if (cross == 0) { 192 return false; 193 } 194 Vec2 u(ray.p1 - segment.p1); 195 float t1 = getCross(v2, u) / cross; 196 float t2 = getCross(v1, u) / cross; 197 if (t1 < 0 || t2 < 0 || t2 > 1) { 198 return false; 199 } 200 point = v1 * t1 + ray.p1; 201 distance = t1; 202 return true; 203 }