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)点