Shader 和 RenderTexture

先贴上两张效果图

(Shader)

(RenderTexture)

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

Shader篇

思路讲解

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

Shader 代码

 #ifdef GL_ES
precision highp float;
#endif int screen_width = ;
int screen_height = ;
uniform float shader_zoom;
uniform vec4 night_color;
uniform vec2 light_pos[];
uniform float light_lenght[];
uniform float light_glare_lenght[];
uniform float light_all_length[];
uniform float light_all_length_sq[];
uniform float light_glare_lenght_sq[];
uniform int light_count;
uniform float screen_zoom;
uniform float screen_mapping[ * ];
//uniform sampler2D screen_mapping; float po_2_light_lenght[]; void main(void)
{
float f = 1.0; int i = ;
vec2 p;
float color;
float color_f;
float length_sq;
float length_f; int type = ; int x = int(gl_FragCoord.x / screen_zoom / shader_zoom);
int y = int(gl_FragCoord.y / screen_zoom / shader_zoom); while (i < light_count)
{
if(screen_mapping[y * screen_width + x] == 1.0)
{
break;
} if(f == 0.0)
{
break;
} p = gl_FragCoord.xy - light_pos[i].xy; length_sq = dot(p, p); if(length_sq >= light_all_length_sq[i])
{
i++;
continue;
} if(length_sq <= light_glare_lenght_sq[i])
{
f = 0.0;
i++;
continue;
} color = length(p) - light_glare_lenght[i];
color_f = clamp(color / light_lenght[i], 0.0, 1.0); if(color_f < f)
{
f = color_f;
} i++;
} gl_FragColor = vec4(f * night_color);
}

调用Shader的代码(C++)

 void NightLayer::onDraw(const cocos2d::Mat4& transform, uint32_t flags)
{
int x, y, i;
Vec2 postion; float screen_zoom = DataManager::getInstance()->getScreenZoom();
for (i = ; i < kScreenWidth * kScreenHeight; ++i)
{
_screen_mapping[i] = .f;
} for (y = ; y < kScreenHeight; ++y)
{
for (x = ; x < kScreenWidth ; ++x)
{
for (i = ; i < _light_count; ++i)
{
postion.x = (x + 0.5f) * kShaderZoom * screen_zoom;
postion.y = (y + 0.5f) * kShaderZoom * screen_zoom; if((postion - _light_pos[i]).lengthSquared() < pow((_light_all_length[i] + 14.2 * kShaderBaseZoom), ))
{
_screen_mapping[y * kScreenWidth + x] = .f;
}
}
}
} float w = _contentSize.width, h = _contentSize.height;
GLfloat vertices[] = {,, w,, w,h, ,, ,h, w,h}; auto glProgramState = getGLProgramState();
glProgramState->setVertexAttribPointer("a_position", , GL_FLOAT, GL_FALSE, , vertices); glProgramState->setUniformFloat("screen_zoom", screen_zoom);
glProgramState->setUniformFloat("shader_zoom", kShaderZoom);
glProgramState->setUniformInt("light_count", _light_count);
glProgramState->setUniformVec4("night_color", Vec4(0.055, 0.008, 0.008, ));
glProgramState->setUniformVec2v("light_pos", _light_count, _light_pos);
glProgramState->setUniformFloatv("light_lenght", _light_count, _light_length);
glProgramState->setUniformFloatv("light_glare_lenght", _light_count, _light_glare_lenght);
glProgramState->setUniformFloatv("light_all_length_sq", _light_count, _light_all_length_sq);
glProgramState->setUniformFloatv("light_glare_lenght_sq", _light_count, _light_glare_lenght_sq);
glProgramState->setUniformFloatv("screen_mapping", kScreenWidth * kScreenHeight, _screen_mapping); glProgramState->apply(transform); glDrawArrays(GL_TRIANGLES, , ); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(,);
}

相关参数注解

  • 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的计算方式

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

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

我们的游戏适配方式

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

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

RenderTexture 篇

思路讲解

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

相关代码

 void NightLayer::update(float dt)
{
_render->clear(, , , .f);
_render->begin();
// _render->beginWithClear(0, 0, 0, 1.f, 0, 0); for (int i = ; i < kPlayerMaxCount; ++i)
{
bool is_show = _light_player_count > i;
if(is_show)
{
_spr_lights_player[i]->setPosition(_light_player_pos[i]);
_spr_lights_player[i]->setScale(_light_player_length[i] / .f * 2.8f);
_spr_lights_player[i]->visit();
}
} for (int i = ; i < kCandleMaxCount; ++i)
{
bool is_show = _light_candle_count > i;
if(is_show)
{
_spr_lights_candle[i]->setPosition(_light_candle_pos[i]);
_spr_lights_candle[i]->setScale(_light_candle_length[i] / .f);
_spr_lights_candle[i]->visit();
}
} _render->end();
}

全文件大放送

NightLayer.hpp

 //
// NightLayer.hpp
// //
// //
// #ifndef NightLayer_hpp
#define NightLayer_hpp #include "cocos2d.h" //#define USING_SHADER
#define USING_RENDER_TEXTURE #include <stdio.h> class NightLayer : public cocos2d::Node
{
public:
CREATE_FUNC(NightLayer); virtual bool init() override; #ifdef USING_RENDER_TEXTURE
virtual void update(float dt) override;
#endif #ifdef USING_SHADER
virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
#endif public:
#ifdef USING_SHADER
const static int kLightMaxCount = ;
cocos2d::Vec2 _light_pos[kLightMaxCount];
float _light_length[kLightMaxCount];
float _light_glare_lenght[kLightMaxCount];
float _light_all_length[kLightMaxCount];
float _light_all_length_sq[kLightMaxCount];
float _light_glare_lenght_sq[kLightMaxCount]; int _light_count;
#endif #ifdef USING_RENDER_TEXTURE
const static int kCandleMaxCount = ;
const static int kPlayerMaxCount = ; cocos2d::Vec2 _light_candle_pos[kCandleMaxCount];
cocos2d::Vec2 _light_player_pos[kPlayerMaxCount]; float _light_candle_length[kCandleMaxCount];
float _light_player_length[kPlayerMaxCount]; int _light_candle_count;
int _light_player_count; #endif protected:
NightLayer()
#ifdef USING_SHADER
:_light_count()
#endif
#ifdef USING_RENDER_TEXTURE
:_light_candle_count()
,_light_player_count()
,_render(nullptr)
#endif
{ }
virtual ~NightLayer(); #ifdef USING_RENDER_TEXTURE
cocos2d::RenderTexture * _render;
cocos2d::Sprite * _spr_lights_candle[kCandleMaxCount];
cocos2d::Sprite * _spr_lights_player[kPlayerMaxCount];
#endif #ifdef USING_SHADER cocos2d::Vec2 _resolution;
void onDraw(const cocos2d::Mat4& transform, uint32_t flags); bool initWithVertex(const std::string &vert, const std::string &frag);
void loadShaderVertex(const std::string &vert, const std::string &frag); cocos2d::CustomCommand _customCommand;
std::string _vertFileName;
std::string _fragFileName; const static int kScreenWidth = ;
const static int kScreenHeight = ;
float _screen_mapping[kScreenWidth * kScreenHeight];
#endif
}; #endif /* NightLayer_hpp */

NightLayer.cpp

 //
// NightLayer.cpp
// //
// //
// #include "NightLayer.hpp"
#include "DataManager.h" USING_NS_CC; namespace
{
#ifdef USING_SHADER
const float kShaderBaseZoom = .f;
const float kShaderZoom = kShaderBaseZoom * .f;
#endif
} #ifdef USING_SHADER bool NightLayer::initWithVertex(const std::string &vert, const std::string &frag)
{
_vertFileName = vert;
_fragFileName = frag;
#if CC_ENABLE_CACHE_TEXTURE_DATA
auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){
this->setGLProgramState(nullptr);
loadShaderVertex(_vertFileName, _fragFileName);
}); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
#endif loadShaderVertex(vert, frag); scheduleUpdate(); Size size = Director::getInstance()->getWinSize();
setContentSize(size);
setAnchorPoint(Vec2(0.5f, 0.5f)); return true;
} void NightLayer::loadShaderVertex(const std::string &vert, const std::string &frag)
{
auto fileUtiles = FileUtils::getInstance(); // frag
auto fragmentFilePath = fileUtiles->fullPathForFilename(frag);
auto fragSource = fileUtiles->getStringFromFile(fragmentFilePath); // vert
std::string vertSource;
if (vert.empty()) {
vertSource = ccPositionTextureColor_vert;
} else {
std::string vertexFilePath = fileUtiles->fullPathForFilename(vert);
vertSource = fileUtiles->getStringFromFile(vertexFilePath);
} auto glprogram = GLProgram::createWithByteArrays(vertSource.c_str(), fragSource.c_str());
auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
setGLProgramState(glprogramstate);
} #endif NightLayer::~NightLayer()
{
#ifdef USING_RENDER_TEXTURE for (int i = ; i < kCandleMaxCount; ++i)
{
CC_SAFE_RELEASE(_spr_lights_candle[i]);
} for (int i = ; i < kPlayerMaxCount; ++i)
{
CC_SAFE_RELEASE(_spr_lights_player[i]);
} #endif
} bool NightLayer::init()
{
bool success = false; do {
if(!Node::init())
{
break;
} #ifdef USING_SHADER
initWithVertex("", "shaders/night.fsh");
_resolution = Director::getInstance()->getOpenGLView()->getFrameSize();
#endif #ifdef USING_RENDER_TEXTURE
Size size = Director::getInstance()->getWinSize(); for (int i = ; i < kCandleMaxCount; ++i)
{
_spr_lights_candle[i] = Sprite::create("imgs/light_candle.png");
_spr_lights_candle[i]->retain();
_spr_lights_candle[i]->setBlendFunc({GL_DST_COLOR, GL_ZERO});
} for (int i = ; i < kPlayerMaxCount; ++i)
{
_spr_lights_player[i] = Sprite::create("imgs/light_player.png");
_spr_lights_player[i]->retain();
_spr_lights_player[i]->setBlendFunc({GL_DST_COLOR, GL_ZERO});
} _render = RenderTexture::create(size.width, size.height, Texture2D::PixelFormat::RGBA4444, GL_DEPTH24_STENCIL8);;
this->addChild(_render);
#endif success = true;
} while (); return success;
} #ifdef USING_RENDER_TEXTURE
void NightLayer::update(float dt)
{
_render->clear(, , , .f);
_render->begin();
// _render->beginWithClear(0, 0, 0, 1.f, 0, 0); for (int i = ; i < kPlayerMaxCount; ++i)
{
bool is_show = _light_player_count > i;
if(is_show)
{
_spr_lights_player[i]->setPosition(_light_player_pos[i]);
_spr_lights_player[i]->setScale(_light_player_length[i] / .f * 2.8f);
_spr_lights_player[i]->visit();
}
} for (int i = ; i < kCandleMaxCount; ++i)
{
bool is_show = _light_candle_count > i;
if(is_show)
{
_spr_lights_candle[i]->setPosition(_light_candle_pos[i]);
_spr_lights_candle[i]->setScale(_light_candle_length[i] / .f);
_spr_lights_candle[i]->visit();
}
} _render->end();
}
#endif #ifdef USING_SHADER
void NightLayer::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags)
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(NightLayer::onDraw, this, transform, flags);
renderer->addCommand(&_customCommand);
}
#endif #ifdef USING_SHADER
void NightLayer::onDraw(const cocos2d::Mat4& transform, uint32_t flags)
{
int x, y, i;
Vec2 postion; float screen_zoom = DataManager::getInstance()->getScreenZoom();
for (i = ; i < kScreenWidth * kScreenHeight; ++i)
{
_screen_mapping[i] = .f;
} for (y = ; y < kScreenHeight; ++y)
{
for (x = ; x < kScreenWidth ; ++x)
{
for (i = ; i < _light_count; ++i)
{
postion.x = (x + 0.5f) * kShaderZoom * screen_zoom;
postion.y = (y + 0.5f) * kShaderZoom * screen_zoom; if((postion - _light_pos[i]).lengthSquared() < pow((_light_all_length[i] + 14.2 * kShaderBaseZoom), ))
{
_screen_mapping[y * kScreenWidth + x] = .f;
}
}
}
} float w = _contentSize.width, h = _contentSize.height;
GLfloat vertices[] = {,, w,, w,h, ,, ,h, w,h}; auto glProgramState = getGLProgramState();
glProgramState->setVertexAttribPointer("a_position", , GL_FLOAT, GL_FALSE, , vertices); glProgramState->setUniformFloat("screen_zoom", screen_zoom);
glProgramState->setUniformFloat("shader_zoom", kShaderZoom);
glProgramState->setUniformInt("light_count", _light_count);
glProgramState->setUniformVec4("night_color", Vec4(0.055, 0.008, 0.008, ));
glProgramState->setUniformVec2v("light_pos", _light_count, _light_pos);
glProgramState->setUniformFloatv("light_lenght", _light_count, _light_length);
glProgramState->setUniformFloatv("light_glare_lenght", _light_count, _light_glare_lenght);
glProgramState->setUniformFloatv("light_all_length_sq", _light_count, _light_all_length_sq);
glProgramState->setUniformFloatv("light_glare_lenght_sq", _light_count, _light_glare_lenght_sq);
glProgramState->setUniformFloatv("screen_mapping", kScreenWidth * kScreenHeight, _screen_mapping); glProgramState->apply(transform); glDrawArrays(GL_TRIANGLES, , ); CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(,);
} #endif

night.fsh

 #ifdef GL_ES
precision highp float;
#endif int screen_width = ;
int screen_height = ;
uniform float shader_zoom;
uniform vec2 resolution;
uniform vec4 night_color;
uniform vec2 light_pos[];
uniform float light_lenght[];
uniform float light_glare_lenght[];
uniform float light_all_length[];
uniform float light_all_length_sq[];
uniform float light_glare_lenght_sq[];
uniform int light_count;
uniform float screen_zoom;
uniform float screen_mapping[ * ];
//uniform sampler2D screen_mapping; float po_2_light_lenght[]; void main(void)
{
float f = 1.0; int i = ;
vec2 p;
float color;
float color_f;
float length_sq;
float length_f; int type = ; int x = int(gl_FragCoord.x / screen_zoom / shader_zoom);
int y = int(gl_FragCoord.y / screen_zoom / shader_zoom); // f = screen_mapping[y * screen_width + x]; while (i < light_count)
{
if(screen_mapping[y * screen_width + x] == 1.0)
{
break;
} if(f == 0.0)
{
break;
} p = gl_FragCoord.xy - light_pos[i].xy; length_sq = dot(p, p); if(length_sq >= light_all_length_sq[i])
{
i++;
continue;
} if(length_sq <= light_glare_lenght_sq[i])
{
f = 0.0;
i++;
continue;
} color = length(p) - light_glare_lenght[i];
color_f = clamp(color / light_lenght[i], 0.0, 1.0); if(color_f < f)
{
f = color_f;
} i++;
} gl_FragColor = vec4(f * night_color);
}

Cocos2dx实现光影效果的两种方式的更多相关文章

  1. cocos2d-x的lua脚本加载CocostudioUI两种方式

    前言 当前版本使用的是quick cocos2dx lua 3.3.UI使用cocostudio编辑器1.6.0.我们在程序里面可以使用两种方式进行解析UI.开始的时候用的是quick的方法, 结果遇 ...

  2. cocos2dx 帧动画的两种创建方式

    看了好几天cocos2dx的帧动画,现在才有点眉目,为了高效期间我们一般会用到 精灵帧缓存(CCSpriteFrameCache) 和动画缓存(CCAnimationCache) .大体的操作步骤: ...

  3. Cocos2d-x学习笔记(五岁以下儿童) 精灵两种方式播放动画

     这几天在看控件类,临时没有想好实际运用的方向.单纯的创建网上已经有非常多这方面的样例,我就不写了.接下来是学习精灵类.精灵类若是单独学习也是非常easy.于是我加了一些有关动画方面的知识点与精灵 ...

  4. Struts2实现ajax的两种方式

    基于Struts2框架下实现Ajax有两种方式,第一种是原声的方式,另外一种是struts2自带的一个插件. js部分调用方式是一样的: JS代码: function testAjax() { var ...

  5. CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking)

    CSharpGL(18)分别处理glDrawArrays()和glDrawElements()两种方式下的拾取(ColorCodedPicking) 我在(Modern OpenGL用Shader拾取 ...

  6. 两种方式实现java生成Excel

    Web应用中难免会遇到需要将数据导出并生成excel文件的需求.同样,对于本博客中的总结,也是建立在为了完成这样的一个需求,才开始去了解其实现形式,并且顺利完成需求的开发,先将实现过程总结于此.本博文 ...

  7. Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)

    Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚 使用场景: 1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动 ...

  8. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  9. (转)SqlServer 数据库同步的两种方式 (发布、订阅),主从数据库之间的同步

    最近在琢磨主从数据库之间的同步,公司正好也需要,在园子里找了一下,看到这篇博文比较详细,比较简单,本人亲自按步骤来过,现在分享给大家. 在这里要提醒大家的是(为了更好的理解,以下是本人自己理解,如有错 ...

随机推荐

  1. Zero Copy I: User-Mode Perspective

    By now almost everyone has heard of so-called zero-copy functionality under Linux, but I often run i ...

  2. 体验phonegap3.0

    网上有各种各样的phonegap环境搭建资料,鉴于学习和整理的考虑,我还是把我搭建的过程整理出来 这篇文章中将涉及到的内容 PhoneGap环境需要的组件 Node环境 JDK Android SDK ...

  3. js问题杂记

    1.如何把字符串数组 转成数组对象? eval妙用 var str = "[\"UserName=1,Pwd=1\",\"UserNmae=1,Pwd=1,Sa ...

  4. MySQL mysqldump数据导出详解

    介绍 在日常维护工作当中经常会需要对数据进行导出操作,而mysqldump是导出数据过程中使用非常频繁的一个工具:它自带的功能参数非常多,文章中会列举出一些常用的操作,在文章末尾会将所有的参数详细说明 ...

  5. 使用C#给Linux写Shell脚本(下篇)

    在上篇的<使用C#给Linux写Shell脚本>结尾中,我们留下了一个关于C#如何调用BashShell的问题.在文章发布之后,我留意到有读者留言推荐使用“Pash”(一款类PowerSh ...

  6. Windows Azure Storage (19) 再谈Azure Block Blob和Page Blob

    <Windows Azure Platform 系列文章目录> 请读者在参考本文之前,预习相关背景知识:Windows Azure Storage (1) Windows Azure St ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  8. JQuery图片切换动画效果

    由于博主我懒,所以页面画的比较粗糙,但是没关系,因为我主要讲的是如何实现图片动画切换. 思路:想必大家都逛过淘宝或者其他的一些网站,一般都会有图片动画切换的效果,那是怎样实现的呢?博主我呢,技术不是很 ...

  9. python 日期计算案例

    一.计算两个日期内的所有月 def get_month_interval(start_str, end_str): start_year, start_month = list(map(int, st ...

  10. easyuidatagrid中load,reload,loadData的区别

    摘要:datagrid中有load,reload,loadData那三个方式,皆是加载数据的,但又有差别.下面让我们一起来看看: 首先,load方法,比如我已经定义一个datagrid的id为grid ...