Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
Shape blur problem - Gideros Forum

Shape blur problem

rrraptorrrraptor Member
edited October 29 in General questions
So Im trying to achieve this effect:

Its working, yes, but there is some problems.
First, diferent blur levels:

And second, this mess:


Im doing it like in "blur" example that cames with gideros.
function GShape:initShader(blurLevel)
	local texw, texh = self:getSize()
	--Initial blur level
	self.blurShader:setConstant("fRad",Shader.CINT,1,blurLevel)
	--Initial texel size
	self.blurShader:setConstant("fTexelSize",Shader.CFLOAT4,1,{1/texw,1/texh,0,0})
 
	self.blurBG = RenderTarget.new(texw,texh,false)
	self.blurBG:draw(stage)
	self.blurIMG = Bitmap.new(self.blurBG)
	self.blurIMG:setShader(self.blurShader)
 
	self.RT = RenderTarget.new(texw,texh,true)
	self:setTexture(self.RT)
	self:setShader(self.blurShader)
end
--
function GShape:updateBlur()
	local x,y = self:getPosition()
	local offsetX, offsetY = 0, 0
	local texw, texh = self:getSize()
 
	-- circle is centerd, so we need to offset drawing position by its radius
	if (self.name == "circle") then 
		offsetX = self.r
		offsetY = self.r
	end
	-- render stage except this object
	self:setVisible(false)
	self.blurBG:draw(stage, -x+offsetX, -y+offsetY)
	self:setVisible(true)
 
	self.blurShader:setConstant("fTexelSize",Shader.CFLOAT4,1,{0,1/texh,0,0}) --Step 1: Vertical blur
	self.RT:draw(self.blurIMG)
	self.blurShader:setConstant("fTexelSize",Shader.CFLOAT4,1,{1/texw,0,0,0}) --Step 2: Horizontal blur
end
--
function GShape:setPosition(x, y)
	Path2D.setPosition(self, x, y)
	self:updateBlur()
end
I spent whole day trying to fix it, but i have no idea how to do it. Maybe I need to modify shader, but I dont know how to work with them. Maybe someone will have a solution.

Project atached.
blurLv.png
923 x 400 - 427K
rar
rar
ShapeBlur.rar
315K

Comments

  • hey that's pretty nice thing you have here.
    What exactly is your problem I don't really get it?
    For your second question I think it's a mess because the shader is on top of another.
    I am sure you have read this https://wiki.giderosmobile.com/index.php/Writing_Shaders
  • and of course the top of the top http://forum.giderosmobile.com/discussion/6667
    but that's pretty hard core, so beware :o
  • rrraptorrrraptor Member
    edited October 30
    I have 0 experience writing shaders, but i gave it a try. All in all, im getting somewhere, but I have one question for now.
    @hgy29 how can I pass vec2 value to shader instead of vec4?
    For vec4 I must use this:
    {name="fDir",type=Shader.CFLOAT4,vertex=false}
    In shader:
    uniform vec4 fDir
    In Lua:
    shader:setConstant("fDir",Shader.CFLOAT4,1,{0,1,0,0})
    But I use same structure for vec2, and in shader code I only use xy component (e.g. fDir.xy).
    I tried to use Shader.DFLOAT, but it wont work, maybe I missed something...
    MoKaLux said:

    and of course the top of the top http://forum.giderosmobile.com/discussion/6667
    but that's pretty hard core, so beware :o

    I think its an overkill for just a blur shader :blush:
  • hgy29hgy29 Maintainer
    In theory you should use a Shader.CFLOAT2, but I am not completely sure I implemented it in gideros.

    Likes: rrraptor

    +1 -1 (+1 / -0 ) Share on Facebook
  • rrraptorrrraptor Member
    edited October 30
    Aaaah...seems like wiki is missing this one :smile:



    Also, maybe its better to rewrite Shader constatnts description to somethink like:
    Shader.CFLOAT2
    Uniform descriptor CFLOAT2 data type
    Equivalent to glsl "uniform vec2"
    333.png
    245 x 174 - 5K
  • rrraptorrrraptor Member
    edited October 30
    Awww, I made this to work..



    Whole problem was in background rendering. So I did this:
    function GShape:renderBG()
    	local x,y = self:getPosition()
    	local offsetX, offsetY = 0, 0
     
    	-- circle is centerd, so we need to offset drawing position by its radius
    	if (self.name == "circle") then 
    		offsetX = self.r
    		offsetY = self.r
    	end
     
    	local par = self:getParent()
    	if (not par) then return end
    	local ind = par:getChildIndex(self)
    	local l = par:getNumChildren()
    	print("Hide from " .. ind .. " to "..l)
    	for i = ind, l do 
    		local c = par:getChildAt(i)
    		c:setVisible(false)
    	end
    	self.blurBG:draw(stage, -x+offsetX, -y+offsetY)
    	for i = ind, l do 
    		local c = par:getChildAt(i)
    		c:setVisible(true)
    	end
    end
    --
    function GShape:updateBlur()
    	if (self.needToUpdate) then 
    		self:forceUpdate()
    	end
    end
    --
    function GShape:forceUpdate()
    	self:renderBG()	
    	-- vertical blur
    	shader:setConstant("fDir",Shader.CFLOAT2,1,{0,1})
    	self.RT:draw(self.blurIMG)
    	-- horizontal blur
    	shader:setConstant("fDir",Shader.CFLOAT2,1,{1,0})
    end
    --
    function GShape:setPosition(x, y)
    	Path2D.setPosition(self, x, y)
    	self.needToUpdate = true
    end
    Probably not the best solution, and I need to try to ptimize it, but a BIG progress there :blush:
    I also modifed the shader (not by myself ofcourse :smiley: ).
    Source link
    Project attached.

    And the final queston, how can a recolor the texture? I mean, equivalent to setColorTransform, that only applies to texture. I understand that to achieve this I need to edit fragment shader, but idk how)))

    Tried this:
    vec4 toGrayscale(in vec4 color)
    {
    	float average = (color.r + color.g + color.b) / 3.0;
    	return vec4(average, average, average, 1.0);
    }
     
    vec4 colorize(in vec4 grayscale, in vec4 color)
    {
    	return (grayscale * color);
    }
     
    void main() {
    	vec4 outColor = blur13(fTexture, fTexCoord, fResolution, fDir);
    	vec4 grayscale = toGrayscale(outColor);
    	vec4 finalColor = colorize(grayscale, fColorTransform);
    	gl_FragColor = finalColor;
    }
    But this is not what im looking for))
    I need you @hgy29 :blush:
    vJ1na8h0ySM.jpg
    264 x 236 - 22K
    zip
    zip
    ShapeBlur.zip
    317K
  • hgy29hgy29 Maintainer
    to mimic setColorTransform you would just need:
     vec4 finalColor=outColor*fColorTransform;
    but I am not sure that's what you want to achieve, btw your toGrayscale is wrong:
    float average = color.r*0.4+color.g*0.5+color.b*0.1;
    would be more correct, and those are only approximate coefficients.

    Likes: rrraptor

    +1 -1 (+1 / -0 ) Share on Facebook
  • hgy29hgy29 Maintainer
    More typical coefs: 0.2989, 0.5870, 0.1140 for R,G,B

    Likes: rrraptor

    +1 -1 (+1 / -0 ) Share on Facebook
  • Added shadows :o


    Blur works very strange. Every shape that is not rectangle offsets the background a bit (as you can see on a picture above). Need to fix it somehow... Probably texel size issue, but idk, need to do some tests.

    Source
  • hgy29hgy29 Maintainer
    About texel size, beware about gideros extending textures so that they are power of two sized. The texel size should be computed from the extended texture size.
  • rrraptorrrraptor Member
    edited October 31
    hgy29 said:

    About texel size, beware about gideros extending textures so that they are power of two sized.

    So what could posibly go wrong? :smiley: Out of memory?
    Also, to render shadow I downscale original shape, then render it to RenderTarget, apply blur, and upscale Bitmap to shape scale like that: 1/downScale. And for shadows I use different shader.
    hgy29 said:

    The texel size should be computed from the extended texture size.

    I use shape:getSize() and set texel size to a {1/w, 0} for horizontal blur, and {0, 1/h} for vertical, but something is wrong...

  • hgy29hgy29 Maintainer
    edited October 31
    rrraptor said:


    I use shape:getSize() and set texel size to a {1/w, 0} for horizontal blur, and {0, 1/h} for vertical, but something is wrong...

    Yes thats what I mean: if your w and h aren't power of two, then the texel size will be wrong! When you create the a rendertarget of 50x120 pixels (for exemple), gideros internals makes one of 64x128 to please the GPU. So your texel size will be 1/64 and 1/128, not 1/50 and 1/120

    EDIT: BTW gideros can compute the texel size for you with Shader.SYS TEXTUREINFO, with this attribute on a CFLOAT4 constant, gideros will fill it with the extent of the original texture in x and y, and the texel size in z and w
  • rrraptorrrraptor Member
    edited October 31
    hgy29 said:

    So your texel size will be 1/64 and 1/128, not 1/50 and 1/120

    Then why it works for rectangles? Their rendertargets w,h aren't power of two, but everything works.

    Btw, I added SYS_TEXTUREINFO flag, but they now flicking and look a bit strange:

    33.png
    468 x 388 - 219K
    33.png 218.6K
  • hgy29hgy29 Maintainer
    Ok, I tried your code and turns out that the rendering offset is not at all correct by texel size issue. Now I think about it, a texel size problem would just have made the blur cover and unexpected amount of texels, but not have changed the center position.

    Your issue is that you apply your blur to a Path2D shape, which only use texture coordinates for the 'filled' part, not for the outline. Since there is an outline in your case, the shape size is slightly bigger than the filled part (by the line thickness exactly). In theory, substracting the line width from the shape size before creating the render target should fix it.
  • hgy29hgy29 Maintainer
    Try this

    Likes: rrraptor

    lua
    lua
    GShape.lua
    10K
    +1 -1 (+1 / -0 ) Share on Facebook
  • rrraptorrrraptor Member
    edited October 31
    Hmmm
    It works even if I just use
    local tw,th = self.w,self.h
    Probably because self.w,self.h is not including stroke width.
    But i discovered a very strange bug.


    First, shape that is on top blures incorrectly (even with your fix), it dont have shadow, and its stroke is blured somehow.
    Second, the circle also dont have shadow, and also have blured stroke, BUT! Its texture is 100% correct

    Also, Im using Path2D because I didt find another way of applying some sort of mask on shader. Pixel and Bitmap are squares, Mesh is a black box for me, Shape... well I almost forgot about it, because we have more powerful and friendly Path2D :smiley:
    33.jpg
    401 x 297 - 48K
    33.jpg 48.2K
  • hgy29hgy29 Maintainer
    Ah, I forgot to remove the setAnchorPoint call around line 300 (for rrect)
  • Ah, ok :smiley:
    Now its all good, except this weird stoke blur effect...
  • rrraptorrrraptor Member
    edited October 31
    Also, any way to fix this?


    P.S. lol, looking like this cat xDDD

    1233.jpg
    36 x 31 - 2K
    e8ecd48d9f71e8d75c5c7057105afda4.jpg
    120 x 120 - 17K
  • hgy29hgy29 Maintainer
    Not sure, it could be due to using a radius too small as compared with the line width in your rrect
  • rrraptorrrraptor Member
    edited November 1
    In my first post you can see exactly same problem with rounded rectangle. It hapens at any radius/width/feather value. Even on a circle, but not that frequntly. Also, I noticed that this gaps appears only at 45 degrees step (0, 45, 90, 135, 180, 225, 270, 315).

    EDIT: Ok, i tried the Shape object aaaand i got this


    Stroke looks like a mess with virtices, but also texture itself is a small square (idk why).

    In initBlurShader I added this:
    		self.textureB = RenderTarget.new(tw*2,th*2,true)
    		-- remove Path2D
    		self:removeChild(self.shape)
    		self.shape = Shape.new()
    		self.shape:setFillStyle(Shape.TEXTURE, self.textureB)
    		self.shape:setLineStyle(10,0xffffff,1)
    		self.shape:setShader(self.blurShader)
     
    		self.shape:beginPath()
    		self.shape:moveTo(0,0)
    		self.shape:lineTo(tw,0)
    		self.shape:lineTo(tw*2,th)
    		self.shape:lineTo(0,th*2)
    		self.shape:lineTo(0,0)
    		self.shape:endPath()
     
    		self.shape:setAnchorPoint(self.ax, self.ay)
    		self:addChild(self.shape)
    223.png
    262 x 247 - 86K
    223.png 86.4K
  • rrraptorrrraptor Member
    edited November 1
    Updated.
    • Reworked shadows
    • Fixed anchor points
    • Shadow and blur now uses 1 shader
    • Added "updateRelativeXY" to correctly update blur if object is a child of another sprite
    • Overridden "setPosition", to apply blur when shape changes its position
    • Added setBlurColor (setBlurColorR, G, B, A) to mirror setColorTransform
    TODO:
    • Comments :wink:

    Likes: MoKaLux

    +1 -1 (+1 / -0 ) Share on Facebook
Sign In or Register to comment.