Shader 和 RenderTexture
先贴上两张效果图
(Shader)
(RenderTexture)
说一下实现的原因,因为项目中需要夜景,光影的效果。最初想到使用Shader来实现。实现之后。效果还不错。因为最初的测试是在Mac上跑的客户端,效果不错。但是放到手机端上之后。发现效率太低。超过3个光源之后,效率下降的太严重。不过既然做了,就拿出来分享一下。另一个则是用RenderTexture来实现的。效率则比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 }
相关参数注解
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 }