Integrating LiquidFun with Cocos2d-x: Part II
In Part I I described to how integrated LiquidFun with Cocos2d-x. In this part (part II) I’ll describe how to render the particles using a basic water effect. Part I uses just one glDrawArrays(GL_POINTS, 0, total); to draw the particles. And although that works to draw “particles”, it is not enough to draw “water”. Drawing “water” requires a more complex rendering algorithm, like the one used in this example. And implementing an algorithm similar that one is what this article describes. The algorithm works more or less like this: Choose a white circle and blur it. You can blur the circle at runtime Or you can blur it off-line. Create an a new frame-buffer (think of a clean off-screen buffer where you can render whatever you want) Render the particles into the newly created frame-buffer using the blurred circle Now render the frame-buffer into the main color-buffer using a threshold. The threshold could be something like this: If pixel.r < 0.1, discard the pixel (the pixel won’t be drawn) If pixel.r < 0.2, draw a blue pixel (for the border, although this is optional) else draw a white pixel (the inner part of the water) How to do it using Cocos2d-x and LiquidFun Let’s take the LFParticleSystemNode from Part I, and “evolve” it: The first thing to do is to add the “off-screen” frame-buffer into the LFParticleSystemNode class. In Cocos2d-x, the “off-screen” buffers are created with the RenderTexture class. Example: [code language=“cpp”] bool LFParticleSystemNode::init(b2ParticleSystem* particleSystem, float ratio) { … // create an off-screen frame-buffer with the size of the screen auto s = Director::getInstance()->getWinSize(); _renderTexture = cocos2d::RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888); this->addChild(_renderTexture); _renderTexture->setAnchorPoint(Point::ANCHOR_MIDDLE); _renderTexture->setPosition(Point(s.width/2, s.height/2)); // Change the default shader. Use a the threshold shader auto program = GLProgram::createWithByteArrays(_renderTextureShaderVert, _renderTextureShaderFrag); auto programState = GLProgramState::getOrCreateWithGLProgram(program); programState->setUniformFloat(“u_threshold_discard”, 0.15); programState->setUniformFloat(“u_threshold_border”, 0.3); … } [/code] And, as mentioned earlier, the RenderTexture (the off-screen frame-buffer) needs a shader with a threshold. The threshold shader should look like the following: [code language=“cpp”] varying vec4 v_fragmentColor; varying vec2 v_texCoord; uniform float u_threshold_discard; uniform float u_threshold_border; void main() { vec4 color = v_fragmentColor * texture2D(CC_Texture0, v_texCoord); if( color.r < u_threshold_discard) // black or discard color = vec4(0,0,0,0); else if( color.r < u_threshold_border) // blue for the border color = vec4(0.2,0.2,0.9,1); else // white for the center color = vec4(1,1,1,1); gl_FragColor = color; } [/code] The values u_threshold_discard, and u_threshold_border are defined at runtime. In the example, they are set at 0.15 and 0.3 respectively. The next thing to do is, to render the particles in the RenderTexture. [code language=“cpp”]void LFParticleSystemNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t transformFlags) { // tell RenderTexture to “capture” the particles _renderTexture->beginWithClear(0,0,0,0); _customCommand.init(_globalZOrder); _customCommand.func = CC_CALLBACK_0(LFParticleSystemNode::onDraw, this, transform, transformFlags); renderer->addCommand(&_customCommand); // tell RenderTexture to stop “capturing” the particles _renderTexture->end(); } [/code] The result is the following