• 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_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 }

     

  • 相关阅读:
    SDUT 1488 数据结构实验:连通分量个数
    SDUT 3364 数据结构实验之图论八:欧拉回路
    SDUT 2413 n a^o7 !
    SDUT 3363 数据结构实验之图论七:驴友计划
    SDUT 3362 数据结构实验之图论六:村村通公路
    SDUT 2139 数据结构实验之图论五:从起始点到目标点的最短步数(BFS)
    POJ 3278 Catch That Cow
    SDUT 3361 数据结构实验之图论四:迷宫探索
    SDUT 2107 数据结构实验之图论二:图的深度遍历
    SDUT 2142 数据结构实验之图论二:基于邻接表的广度优先搜索遍历
  • 原文地址:https://www.cnblogs.com/anxin1225/p/5069289.html
Copyright © 2020-2023  润新知