Cocos2dx实现光影效果的两种方式

Shader 和 RenderTexture

先贴上两张效果图

Cocos2dx实现光影效果的两种方式_第1张图片

(Shader)

Cocos2dx实现光影效果的两种方式_第2张图片

 

(RenderTexture)

说一下实现的原因,因为项目中需要夜景,光影的效果。最初想到使用Shader来实现。实现之后。效果还不错。因为最初的测试是在Mac上跑的客户端,效果不错。但是放到手机端上之后。发现效率太低。超过3个光源之后,效率下降的太严重。不过既然做了,就拿出来分享一下。另一个则是用RenderTexture来实现的。效率则比Shader效率高很多。

Shader篇

思路讲解

  • Shader中的所有的数据都是真实像素点来进项渲染的,而程序中则更多的是逻辑像素点来进行的,所以代码中又关于Zoom相关的字段都是处理相关的位置转换的代码
  • 前文提到过关于光源超过三个的时候出现的问题。主要原因是,所有的渲染点都需要跟不同的光点位置进行运算。然后计算出他最后的使用哪个光电最亮(不透明度最低),然后取用这个值,所以一个光点运行一次,两个光点运行两次,以此类推。所以我做出的优化是减少判断的个数
  • 能够减少的位置有 全透明区域 全黑区域。其实全透明区域最容易判断,下文中的等于0就直接Break则是对于全透明区域的优化。全黑区域则相对复杂一些。
  • 全黑区域的优化。在C++代码中,我吧屏幕分成了24*16的网格,然后用C++代码与光点进行运算,初始化网格。然后把网格传入Shader中。Shader中则判定自己是不是处于全黑网格,如果处于全黑网格,则直接渲染为黑色不做后续处理
  • 其他内容应该非常容易理解了。不做赘述

Shader 代码

 1 #ifdef GL_ES
 2 precision highp float;
 3 #endif
 4 
 5 int screen_width = 24;
 6 int screen_height = 16;
 7 uniform float shader_zoom;
 8 uniform vec4 night_color;
 9 uniform vec2 light_pos[10];
10 uniform float light_lenght[10];
11 uniform float light_glare_lenght[10];
12 uniform float light_all_length[10];
13 uniform float light_all_length_sq[10];
14 uniform float light_glare_lenght_sq[10];
15 uniform int light_count;
16 uniform float screen_zoom;
17 uniform float screen_mapping[24 * 16];
18 //uniform sampler2D screen_mapping;
19 
20 float po_2_light_lenght[10];
21 
22 void main(void)
23 {
24     float f = 1.0;
25 
26     int i = 0;
27     vec2 p;
28     float color;
29     float color_f;
30     float length_sq;
31     float length_f;
32     
33     int type = 0;
34     
35     int x = int(gl_FragCoord.x / screen_zoom / shader_zoom);
36     int y = int(gl_FragCoord.y / screen_zoom / shader_zoom);
37     
38     while (i < light_count)
39     {
40         if(screen_mapping[y * screen_width + x] == 1.0)
41         {
42             break;
43         }
44         
45         if(f == 0.0)
46         {
47             break;
48         }
49         
50         p = gl_FragCoord.xy - light_pos[i].xy;
51 
52         length_sq = dot(p, p);
53         
54 
55         if(length_sq >= light_all_length_sq[i])
56         {
57             i++;
58             continue;
59         }
60         
61         if(length_sq <= light_glare_lenght_sq[i])
62         {
63             f = 0.0;
64             i++;
65             continue;
66         }
67         
68         color = length(p) - light_glare_lenght[i];
69         color_f = clamp(color / light_lenght[i], 0.0, 1.0);
70         
71         if(color_f < f)
72         {
73             f = color_f;
74         }
75         
76         i++;
77     }
78 
79     gl_FragColor = vec4(f * night_color);
80 }

 

调用Shader的代码(C++)

 1 void NightLayer::onDraw(const cocos2d::Mat4& transform, uint32_t flags)
 2 {
 3     int x, y, i;
 4     Vec2 postion;
 5     
 6     float screen_zoom = DataManager::getInstance()->getScreenZoom();
 7     for (i = 0; i < kScreenWidth * kScreenHeight; ++i)
 8     {
 9         _screen_mapping[i] = 1.f;
10     }
11     
12     
13     for (y = 0; y < kScreenHeight; ++y)
14     {
15         for (x = 0; x < kScreenWidth ; ++x)
16         {
17             for (i = 0; i < _light_count; ++i)
18             {
19                 postion.x = (x + 0.5f) * kShaderZoom * screen_zoom;
20                 postion.y = (y + 0.5f) * kShaderZoom * screen_zoom;
21                 
22                 if((postion - _light_pos[i]).lengthSquared() < pow((_light_all_length[i] + 14.2 * kShaderBaseZoom), 2))
23                 {
24                     _screen_mapping[y * kScreenWidth + x] = 0.f;
25                 }
26             }
27         }
28     }
29     
30     float w = _contentSize.width, h = _contentSize.height;
31     GLfloat vertices[12] = {0,0, w,0, w,h, 0,0, 0,h, w,h};
32     
33     auto glProgramState = getGLProgramState();
34     glProgramState->setVertexAttribPointer("a_position", 2, GL_FLOAT, GL_FALSE, 0, vertices);
35     
36     glProgramState->setUniformFloat("screen_zoom", screen_zoom);
37     glProgramState->setUniformFloat("shader_zoom", kShaderZoom);
38     glProgramState->setUniformInt("light_count", _light_count);
39     glProgramState->setUniformVec4("night_color", Vec4(0.055, 0.008, 0.008, 1));
40     glProgramState->setUniformVec2v("light_pos", _light_count, _light_pos);
41     glProgramState->setUniformFloatv("light_lenght", _light_count, _light_length);
42     glProgramState->setUniformFloatv("light_glare_lenght", _light_count, _light_glare_lenght);
43     glProgramState->setUniformFloatv("light_all_length_sq", _light_count, _light_all_length_sq);
44     glProgramState->setUniformFloatv("light_glare_lenght_sq", _light_count, _light_glare_lenght_sq);
45     glProgramState->setUniformFloatv("screen_mapping", kScreenWidth * kScreenHeight, _screen_mapping);
46     
47     glProgramState->apply(transform);
48     
49     glDrawArrays(GL_TRIANGLES, 0, 6);
50     
51     CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,6);
52 }

 

 

相关参数注解

  • int screen_width = 24; 映射屏幕的宽度
  • int screen_height = 16; 映射屏幕的高度
  • uniform float shader_zoom; Shader的缩放值
  • uniform vec4 night_color; 夜晚的颜色
  • uniform vec2 light_pos[10]; 光点的位置
  • uniform float light_lenght[10]; 光点强光圈长度
  • uniform float light_glare_lenght[10]; 光点弱光圈长度
  • uniform float light_all_length[10]; 光点的总长度(强光跟弱光距离相加)
  • uniform float light_all_length_sq[10]; 光电总长度的平方
  • uniform float light_glare_lenght_sq[10]; 光电弱光圈的平方
  • uniform int light_count; 光点的个数
  • uniform float screen_zoom; 屏幕缩放比例
  • uniform float screen_mapping[24 * 16]; 屏幕映射字典

ScreenZoom的计算方式

额,这个并不是所有的游戏都是这么计算的。需要跟游戏的适配方式配合

1 Size w_size = Director::getInstance()->getOpenGLView()->getFrameSize();
2 Size designResolutionSize = Director::getInstance()->getWinSize();
3 DataManager::getInstance()->setScreenZoom(w_size.height / designResolutionSize.height * Director::getInstance()->getOpenGLView()->getRetinaFactor());

我们的游戏适配方式

关键是第三个参数,以高度适配,所以上边的缩放计算也是以高度计算的

1 glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::FIXED_HEIGHT);

 

RenderTexture 篇

思路讲解

先比对Shader RenderTexture 则更加简单暴力。简单来说就是在RenderTexture上贴图,只要把对应的图片放到对应的位置,然后展示出来就好。

相关代码

 1 void NightLayer::update(float dt)
 2 {
 3     _render->clear(0, 0, 0, 1.f);
 4     _render->begin();
 5 //    _render->beginWithClear(0, 0, 0, 1.f, 0, 0);
 6     
 7     for (int i = 0; i < kPlayerMaxCount; ++i)
 8     {
 9         bool is_show = _light_player_count > i;
10         if(is_show)
11         {
12             _spr_lights_player[i]->setPosition(_light_player_pos[i]);
13             _spr_lights_player[i]->setScale(_light_player_length[i] / 300.f * 2.8f);
14             _spr_lights_player[i]->visit();
15         }
16     }
17     
18     for (int i = 0; i < kCandleMaxCount; ++i)
19     {
20         bool is_show = _light_candle_count > i;
21         if(is_show)
22         {
23             _spr_lights_candle[i]->setPosition(_light_candle_pos[i]);
24             _spr_lights_candle[i]->setScale(_light_candle_length[i] / 150.f);
25             _spr_lights_candle[i]->visit();
26         }
27     }
28     
29     _render->end();
30 }

 

 

全文件大放送

NightLayer.hpp

  1 //
  2 //  NightLayer.hpp
  3 //  4 //
  5 //  6 //
  7 //
  8 
  9 #ifndef NightLayer_hpp
 10 #define NightLayer_hpp
 11 
 12 #include "cocos2d.h"
 13 
 14 //#define USING_SHADER
 15 #define USING_RENDER_TEXTURE
 16 
 17 #include <stdio.h>
 18 
 19 class NightLayer : public cocos2d::Node
 20 {
 21 public:
 22     CREATE_FUNC(NightLayer);
 23     
 24     virtual bool init() override;
 25     
 26 #ifdef USING_RENDER_TEXTURE
 27     virtual void update(float dt) override;
 28 #endif
 29     
 30 #ifdef USING_SHADER
 31     virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
 32 #endif
 33     
 34 public:
 35 #ifdef USING_SHADER
 36     const static int kLightMaxCount = 10;
 37     cocos2d::Vec2 _light_pos[kLightMaxCount];
 38     float _light_length[kLightMaxCount];
 39     float _light_glare_lenght[kLightMaxCount];
 40     float _light_all_length[kLightMaxCount];
 41     float _light_all_length_sq[kLightMaxCount];
 42     float _light_glare_lenght_sq[kLightMaxCount];
 43     
 44     int _light_count;
 45 #endif
 46     
 47 #ifdef USING_RENDER_TEXTURE
 48     const static int kCandleMaxCount = 9;
 49     const static int kPlayerMaxCount = 1;
 50     
 51     cocos2d::Vec2 _light_candle_pos[kCandleMaxCount];
 52     cocos2d::Vec2 _light_player_pos[kPlayerMaxCount];
 53     
 54     float _light_candle_length[kCandleMaxCount];
 55     float _light_player_length[kPlayerMaxCount];
 56     
 57     int _light_candle_count;
 58     int _light_player_count;
 59     
 60 #endif
 61     
 62 protected:
 63     NightLayer()
 64 #ifdef USING_SHADER
 65     :_light_count(0)
 66 #endif
 67 #ifdef USING_RENDER_TEXTURE
 68     :_light_candle_count(0)
 69     ,_light_player_count(0)
 70     ,_render(nullptr)
 71 #endif
 72     { }
 73     virtual ~NightLayer();
 74     
 75     
 76 #ifdef USING_RENDER_TEXTURE
 77     cocos2d::RenderTexture * _render;
 78     cocos2d::Sprite * _spr_lights_candle[kCandleMaxCount];
 79     cocos2d::Sprite * _spr_lights_player[kPlayerMaxCount];
 80 #endif
 81     
 82 #ifdef USING_SHADER
 83     
 84     cocos2d::Vec2 _resolution;
 85     void onDraw(const cocos2d::Mat4& transform, uint32_t flags);
 86     
 87     bool initWithVertex(const std::string &vert, const std::string &frag);
 88     void loadShaderVertex(const std::string &vert, const std::string &frag);
 89     
 90     cocos2d::CustomCommand _customCommand;
 91     std::string _vertFileName;
 92     std::string _fragFileName;
 93     
 94     const static int kScreenWidth = 24;
 95     const static int kScreenHeight = 16;
 96     float _screen_mapping[kScreenWidth * kScreenHeight];
 97 #endif
 98 };
 99 
100 #endif /* NightLayer_hpp */

 

NightLayer.cpp

  1 //
  2 //  NightLayer.cpp
  3 //  4 //
  5 //  6 //
  7 //
  8 
  9 #include "NightLayer.hpp"
 10 #include "DataManager.h"
 11 
 12 USING_NS_CC;
 13 
 14 
 15 namespace
 16 {
 17 #ifdef USING_SHADER
 18     const float kShaderBaseZoom = 4.f;
 19     const float kShaderZoom = kShaderBaseZoom * 10.f;
 20 #endif
 21 }
 22 
 23 #ifdef USING_SHADER
 24 
 25 bool NightLayer::initWithVertex(const std::string &vert, const std::string &frag)
 26 {
 27     _vertFileName = vert;
 28     _fragFileName = frag;
 29 #if CC_ENABLE_CACHE_TEXTURE_DATA
 30     auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){
 31         this->setGLProgramState(nullptr);
 32         loadShaderVertex(_vertFileName, _fragFileName);
 33     });
 34     
 35     _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
 36 #endif
 37     
 38     loadShaderVertex(vert, frag);
 39     
 40     scheduleUpdate();
 41     
 42     Size size = Director::getInstance()->getWinSize();
 43     setContentSize(size);
 44     setAnchorPoint(Vec2(0.5f, 0.5f));
 45     
 46     
 47     return true;
 48 }
 49 
 50 void NightLayer::loadShaderVertex(const std::string &vert, const std::string &frag)
 51 {
 52     auto fileUtiles = FileUtils::getInstance();
 53     
 54     // frag
 55     auto fragmentFilePath = fileUtiles->fullPathForFilename(frag);
 56     auto fragSource = fileUtiles->getStringFromFile(fragmentFilePath);
 57     
 58     // vert
 59     std::string vertSource;
 60     if (vert.empty()) {
 61         vertSource = ccPositionTextureColor_vert;
 62     } else {
 63         std::string vertexFilePath = fileUtiles->fullPathForFilename(vert);
 64         vertSource = fileUtiles->getStringFromFile(vertexFilePath);
 65     }
 66     
 67     auto glprogram = GLProgram::createWithByteArrays(vertSource.c_str(), fragSource.c_str());
 68     auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
 69     setGLProgramState(glprogramstate);
 70 }
 71 
 72 #endif
 73 
 74 NightLayer::~NightLayer()
 75 {
 76 #ifdef USING_RENDER_TEXTURE
 77     
 78     for (int i = 0; i < kCandleMaxCount; ++i)
 79     {
 80         CC_SAFE_RELEASE(_spr_lights_candle[i]);
 81     }
 82     
 83     for (int i = 0; i < kPlayerMaxCount; ++i)
 84     {
 85         CC_SAFE_RELEASE(_spr_lights_player[i]);
 86     }
 87     
 88 #endif
 89 }
 90 
 91 bool NightLayer::init()
 92 {
 93     bool success = false;
 94     
 95     do {
 96         if(!Node::init())
 97         {
 98             break;
 99         }
100         
101 #ifdef USING_SHADER
102         initWithVertex("", "shaders/night.fsh");
103         _resolution = Director::getInstance()->getOpenGLView()->getFrameSize();
104 #endif
105       
106 #ifdef USING_RENDER_TEXTURE
107         Size size = Director::getInstance()->getWinSize();
108         
109         for (int i = 0; i < kCandleMaxCount; ++i)
110         {
111             _spr_lights_candle[i] = Sprite::create("imgs/light_candle.png");
112             _spr_lights_candle[i]->retain();
113             _spr_lights_candle[i]->setBlendFunc({GL_DST_COLOR, GL_ZERO});
114         }
115         
116         for (int i = 0; i < kPlayerMaxCount; ++i)
117         {
118             _spr_lights_player[i] = Sprite::create("imgs/light_player.png");
119             _spr_lights_player[i]->retain();
120             _spr_lights_player[i]->setBlendFunc({GL_DST_COLOR, GL_ZERO});
121         }
122         
123         
124         _render = RenderTexture::create(size.width, size.height, Texture2D::PixelFormat::RGBA4444, GL_DEPTH24_STENCIL8);;
125         this->addChild(_render);
126 #endif
127         
128         
129         success = true;
130     } while (0);
131     
132     return success;
133 }
134 
135 #ifdef USING_RENDER_TEXTURE
136 void NightLayer::update(float dt)
137 {
138     _render->clear(0, 0, 0, 1.f);
139     _render->begin();
140 //    _render->beginWithClear(0, 0, 0, 1.f, 0, 0);
141     
142     for (int i = 0; i < kPlayerMaxCount; ++i)
143     {
144         bool is_show = _light_player_count > i;
145         if(is_show)
146         {
147             _spr_lights_player[i]->setPosition(_light_player_pos[i]);
148             _spr_lights_player[i]->setScale(_light_player_length[i] / 300.f * 2.8f);
149             _spr_lights_player[i]->visit();
150         }
151     }
152     
153     for (int i = 0; i < kCandleMaxCount; ++i)
154     {
155         bool is_show = _light_candle_count > i;
156         if(is_show)
157         {
158             _spr_lights_candle[i]->setPosition(_light_candle_pos[i]);
159             _spr_lights_candle[i]->setScale(_light_candle_length[i] / 150.f);
160             _spr_lights_candle[i]->visit();
161         }
162     }
163     
164     _render->end();
165 }
166 #endif
167 
168 #ifdef USING_SHADER
169 void NightLayer::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags)
170 {
171     _customCommand.init(_globalZOrder, transform, flags);
172     _customCommand.func = CC_CALLBACK_0(NightLayer::onDraw, this, transform, flags);
173     renderer->addCommand(&_customCommand);
174 }
175 #endif
176 
177 #ifdef USING_SHADER
178 void NightLayer::onDraw(const cocos2d::Mat4& transform, uint32_t flags)
179 {
180     int x, y, i;
181     Vec2 postion;
182     
183     float screen_zoom = DataManager::getInstance()->getScreenZoom();
184     for (i = 0; i < kScreenWidth * kScreenHeight; ++i)
185     {
186         _screen_mapping[i] = 1.f;
187     }
188     
189     
190     for (y = 0; y < kScreenHeight; ++y)
191     {
192         for (x = 0; x < kScreenWidth ; ++x)
193         {
194             for (i = 0; i < _light_count; ++i)
195             {
196                 postion.x = (x + 0.5f) * kShaderZoom * screen_zoom;
197                 postion.y = (y + 0.5f) * kShaderZoom * screen_zoom;
198                 
199                 if((postion - _light_pos[i]).lengthSquared() < pow((_light_all_length[i] + 14.2 * kShaderBaseZoom), 2))
200                 {
201                     _screen_mapping[y * kScreenWidth + x] = 0.f;
202                 }
203             }
204         }
205     }
206     
207     float w = _contentSize.width, h = _contentSize.height;
208     GLfloat vertices[12] = {0,0, w,0, w,h, 0,0, 0,h, w,h};
209     
210     auto glProgramState = getGLProgramState();
211     glProgramState->setVertexAttribPointer("a_position", 2, GL_FLOAT, GL_FALSE, 0, vertices);
212     
213     glProgramState->setUniformFloat("screen_zoom", screen_zoom);
214     glProgramState->setUniformFloat("shader_zoom", kShaderZoom);
215     glProgramState->setUniformInt("light_count", _light_count);
216     glProgramState->setUniformVec4("night_color", Vec4(0.055, 0.008, 0.008, 1));
217     glProgramState->setUniformVec2v("light_pos", _light_count, _light_pos);
218     glProgramState->setUniformFloatv("light_lenght", _light_count, _light_length);
219     glProgramState->setUniformFloatv("light_glare_lenght", _light_count, _light_glare_lenght);
220     glProgramState->setUniformFloatv("light_all_length_sq", _light_count, _light_all_length_sq);
221     glProgramState->setUniformFloatv("light_glare_lenght_sq", _light_count, _light_glare_lenght_sq);
222     glProgramState->setUniformFloatv("screen_mapping", kScreenWidth * kScreenHeight, _screen_mapping);
223     
224     glProgramState->apply(transform);
225     
226     glDrawArrays(GL_TRIANGLES, 0, 6);
227     
228     CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,6);
229 }
230 
231 #endif

night.fsh

 1 #ifdef GL_ES
 2 precision highp float;
 3 #endif
 4 
 5 int screen_width = 24;
 6 int screen_height = 16;
 7 uniform float shader_zoom;
 8 uniform vec2 resolution;
 9 uniform vec4 night_color;
10 uniform vec2 light_pos[10];
11 uniform float light_lenght[10];
12 uniform float light_glare_lenght[10];
13 uniform float light_all_length[10];
14 uniform float light_all_length_sq[10];
15 uniform float light_glare_lenght_sq[10];
16 uniform int light_count;
17 uniform float screen_zoom;
18 uniform float screen_mapping[24 * 16];
19 //uniform sampler2D screen_mapping;
20 
21 float po_2_light_lenght[10];
22 
23 void main(void)
24 {
25     float f = 1.0;
26 
27     int i = 0;
28     vec2 p;
29     float color;
30     float color_f;
31     float length_sq;
32     float length_f;
33     
34     int type = 0;
35     
36     int x = int(gl_FragCoord.x / screen_zoom / shader_zoom);
37     int y = int(gl_FragCoord.y / screen_zoom / shader_zoom);
38     
39 //    f = screen_mapping[y * screen_width + x];
40 
41     while (i < light_count)
42     {
43         if(screen_mapping[y * screen_width + x] == 1.0)
44         {
45             break;
46         }
47         
48         if(f == 0.0)
49         {
50             break;
51         }
52         
53         p = gl_FragCoord.xy - light_pos[i].xy;
54 
55         length_sq = dot(p, p);
56         
57 
58         if(length_sq >= light_all_length_sq[i])
59         {
60             i++;
61             continue;
62         }
63         
64         if(length_sq <= light_glare_lenght_sq[i])
65         {
66             f = 0.0;
67             i++;
68             continue;
69         }
70         
71         color = length(p) - light_glare_lenght[i];
72         color_f = clamp(color / light_lenght[i], 0.0, 1.0);
73         
74         if(color_f < f)
75         {
76             f = color_f;
77         }
78         
79         i++;
80     }
81 
82     gl_FragColor = vec4(f * night_color);
83 }

 

 

你可能感兴趣的:(Cocos2dx实现光影效果的两种方式)