Cocos2dx - 根据背景高效率的生成模糊节点
2019-05-21 05:30:40 -0400

local vsh = [[
    attribute vec4 a_position;
    attribute vec2 a_texCoord;
    attribute vec4 a_color;

    #ifdef GL_ES
    varying lowp vec4 v_fragmentColor;
    varying mediump vec2 v_texCoord;
    #else
    varying vec4 v_fragmentColor;
    varying vec2 v_texCoord;
    #endif

    void main()
    {
        gl_Position = CC_PMatrix * a_position;
        v_fragmentColor = a_color;
        v_texCoord = a_texCoord;
    }
]]

local fsh = [[
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 v_fragmentColor;
    varying vec2 v_texCoord;

    uniform vec2 resolution;
    uniform float blurRadius;

    vec4 blur(vec2);

    void main(void)
    {
        vec4 col = blur(v_texCoord);
        gl_FragColor = vec4(col) * v_fragmentColor;
    }

    vec4 blur(vec2 p)
    {
        if (blurRadius > 0.0)
        {
            vec4 col = vec4(0);
            vec2 unit = 1.0 / resolution.xy;

            float r = blurRadius;
            float sampleStep = 1.0;

            float count = 0.0;

            for(float x = -r; x < r; x += sampleStep)
            {
                for(float y = -r; y < r; y += sampleStep)
                {
                    float weight = (r - abs(x)) * (r - abs(y));
                    col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)) * weight;
                    count += weight;
                }
            }

            return col / count;
        }

        return texture2D(CC_Texture0, p);
    }
]]

local function getRealNodes(node, tb)
    local nodeType = tolua.type(node)
    if nodeType == "cc.Sprite" then
        table.insert(tb, node)
    elseif nodeType == "ccui.Scale9Sprite" then
        getRealNodes(node:getSprite(), tb)
    elseif nodeType == "ccui.Text" then
        getRealNodes(node:getVirtualRenderer(), tb)
    elseif nodeType == "ccui.Button" then
        getRealNodes(node:getVirtualRenderer(), tb)
        getRealNodes(node:getTitleRenderer(), tb)
    elseif nodeType == "cc.Label" then
        node:updateContent()    --先刷新内部精灵
        getRealNodes(node:getChildren()[1], tb)
    elseif nodeType == "cc.RenderTexture" then
        getRealNodes(node:getSprite(), tb)
    else
        table.insert(tb, node)
    end
end

--[[
    @desc: 高斯模糊[shader实现,掉帧严重]
    author:BogeyRuan
    time:2019-05-15 14:26:45
    --@node: 要变模糊的节点
    --@[radius]: 模糊半径,默认10
    --@[resolution]: 采样粒度,大多数时间不用传
    @return:
]]
function display.makeBlur(node, radius, resolution)
    local nodes = {}
    getRealNodes(node, nodes)
    for _, node in pairs(nodes) do
        if resolution == nil then
            local size = node:getContentSize()
            if size.width == 0 or size.height == 0 then
                return
            end
            resolution = cc.p(size.width, size.height)
        end
        if radius == nil then
            radius = 10
        end

        local glProgram = cc.GLProgram:createWithByteArrays(vsh, fsh)
        local glProgramState = cc.GLProgramState:getOrCreateWithGLProgram(glProgram)
        glProgramState:setUniformVec2("resolution" , resolution)
        glProgramState:setUniformFloat("blurRadius", radius)
        node:setGLProgramState(glProgramState)
    end
end

--[[
    @desc: 截取一张指定节点指定位置的模糊截图
    author:BogeyRuan
    time:2019-05-21 15:56:15
    --@[node]: 指定的节点
    --@[rect]: 截取的范围,节点坐标
    @return: 
]]
function display.captureBlurNode(node, rect)
    local node = node or display.getRunningScene()
    -- 因为截取指定区域的函数的限制,得先把父节点和自己移动到左下角
    local parent = node:getParent()
    local parentPos
    if parent then
        parentPos = cc.p(parent:getPosition())
        local basePos = parent:convertToWorldSpace(cc.p(0, 0))
        parent:setPosition(cc.pSub(parentPos, basePos))
    end
    local nodePos = cc.p(node:getPosition())
    local nodeSize = node:getContentSize()
    local nodeAnchor = node:getAnchorPoint()
    node:setPosition(nodeAnchor.x * nodeSize.width, nodeAnchor.y * nodeSize.height)

    local pos = node:convertToWorldSpace(cc.p(0, 0))
    if rect then
        nodeSize = cc.size(rect.width, rect.height)
        pos = cc.pAdd(cc.p(rect.x, rect.y), nodePos)
    end
    local texture = cc.RenderTexture:create(nodeSize.width, nodeSize.height, cc.TEXTURE2_D_PIXEL_FORMAT_RGB_A8888, gl.DEPTH24_STENCIL8_OES)
    -- texture:setKeepMatrix(true)
    -- texture:setVirtualViewport(pos, cc.rect(0, 0, display.size), cc.rect(0, 0, cc.sizeMul(display.size, cc.Director:getInstance():getContentScaleFactor())))
    texture:beginWithClear(0, 0, 0, 0)
    node:visit()
    texture:endToLua()

    if parent then
        parent:setPosition(parentPos)
    end
    node:setPosition(nodePos)

    texture:pos(0, 0)
    local blurSp = texture:getSprite()
    local size = blurSp:getContentSize()
    blurSp:align(display.CENTER, size.width / 2, size.height / 2)
    display.makeBlur(blurSp)

    local texture2 = cc.RenderTexture:create(size.width, size.height, cc.TEXTURE2_D_PIXEL_FORMAT_RGB_A8888, gl.DEPTH24_STENCIL8_OES)
    texture2:beginWithClear(0, 0, 0, 0)
    blurSp:visit()
    texture2:endToLua()

    return texture2
end

踩坑指南

RenderTexture默认裁剪的位置之类的是没有问题的,setVirtualViewport却没有裁剪到正确的纹理是因为要裁剪的节点的父节点的位置问题,具体情况很复杂。

void Node::visit()
{
    auto renderer = Director::getInstance()->getRenderer();
    Mat4 parentTransform = Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    visit(renderer, parentTransform, true);
}

从Node的visit方法开始就出现坑了,默认返回的parentTransform和真实的transform是对不上的,导致要截图的节点的区域是错误的 所以修改起来就是直接把要截图的节点的位置和其父节点的位置都设置为相对世界坐标下的(0,0)点

«Newer      Older»
Comment:
Name:
Back to home

Subscribe | Register | Login | N