你的位置:首页 > 操作系统

[操作系统]Cocos2dx实现光影效果的两种方式


Shader 和 RenderTexture

先贴上两张效果图

(Shader)

 

(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_SHADER102     initWithVertex("", "shaders/night.fsh");103     _resolution = Director::getInstance()->getOpenGLView()->getFrameSize();104 #endif105    106 #ifdef USING_RENDER_TEXTURE107     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 #endif127     128     129     success = true;130   } while (0);131   132   return success;133 }134 135 #ifdef USING_RENDER_TEXTURE136 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 #endif167 168 #ifdef USING_SHADER169 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 #endif176 177 #ifdef USING_SHADER178 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 }