Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
Desaturate Sprite - Gideros Forum

Desaturate Sprite

totebototebo Member
edited July 2016 in General questions
Is there a way to desaturate a Sprite now when RenderTarget can access pixels?
My Gideros games: www.totebo.com

Comments

  • n1cken1cke Maintainer
    edited July 2016 Accepted Answer
    --~~~~~~~~~~~~~~~~~~~~~~~~~software shading~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--
    -- 'render' is RenderTarget instance
    -- 'shader' is a soft-shader function (r, g, b, a, x, y, ...) -> r, g, b, a
    -- '...' is for constants which will be passed to 'shader' function
    -- NOTE: each color component should be an integer in 0..255 range
    local function applyShader(render, shader, ...)
    	local w, h = render:getWidth(), render:getHeight()
    	local pixels = render:getPixels(1, 1, w, h)
    	for i = 1, #pixels, 4 do
    		local n = (i - 1) / 4
    		local y = math.floor(n / w) + 1
    		local x = (n - y * w + w) + 1
    		local r, g, b, a = pixels:byte(i, i+3)
    		r, g, b, a = shader(r, g, b, a, x, y, ...)
    		render:clear(r * 65536 + g * 256 + b, a / 255, x, y, 1, 1)
    	end
    end
    --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--
     
    local texture = Texture.new "sample.png"
    local w, h = texture:getWidth(), texture:getHeight()
    local image = Bitmap.new(texture)
    local render = RenderTarget.new(w, h)
    render:draw(image)
    local bitmap = Bitmap.new(render)
    stage:addChild(bitmap)
     
    local function grayscale(r, g, b, a, x, y)
    	local avr = math.floor((r + g + b) / 3)
    	return avr, avr, avr, a
    end
     
    applyShader(render, grayscale)

    Likes: pie, totebo

    +1 -1 (+2 / -0 ) Share on Facebook
  • n1cken1cke Maintainer
    However `RenderTarget.clear` method is not effective (8 seconds to process 512x512 pixels image). Gideros needs C++ implementation of above applyShader method because Lua strings and tables have much slower access.
  • totebototebo Member
    Cool, thanks! Shame about the performance, but will give it a whirl.
    My Gideros games: www.totebo.com
  • hgy29hgy29 Maintainer
    BTW, a real shader would be more efficient. What is a desaturation ?
  • n1cken1cke Maintainer
    edited July 2016
    Desaturation = reduction of colorfulness.
    Real shader is hard to write for beginners and it constantly consumes videocard resources. This one-shot soft-shader is useful for image processing and you can later save the result because I don't think `getPixel` will return modified texture if you apply real shader to it (need to check).
    If Lua-C API supports effective iteration over arrays with Lua function application (i.e. "map") this can be very fast processing too.
  • hgy29hgy29 Maintainer
    ... and it constantly consumes videocard resources.
    No it doesn't, you can apply it once and release it afterwards, and yes getPixel() will return processed pixels, since applying a shader is the exact purpose of a RenderTarget, wether the shader aims is create graphics or image processing.

    However I agree on the fact that shaders are less easy to deal with than lua functions, but on all aother points they are more effective, because the GPU is designed to do just that task.
  • hgy29hgy29 Maintainer
    So is it like turning an image to greyscale or is there some mathematical subtile difference ? If it is greyscale, then http://giderosmobile.com/forum/discussion/6442/turn-a-colour-bitmap-greyscale-at-runtime/p1 still applies :)
  • n1cken1cke Maintainer
    @hgy29, that's awesome!
    Then we only need additional simplified API for pixel shaders.
    Nothing special, just a template with predefined `ps.glsl`, `vs.glsl` and `Shader.new`. String with pixel shader code could be inserted into `void main() {...}`.
    In the end a user should be able to write something like this:
    renderTarget:applyShader [[
    //convert to grayscale using NTSC conversion weights
    float gray = dot(gl_Color.rgb, vec3(0.299, 0.587, 0.114));
    gl_FragColor = vec4(gray, gray, gray, gl_Color.a);
    ]]
    I think it's much better for image processing than 3 files with shader description.

    Likes: totebo

    +1 -1 (+1 / -0 ) Share on Facebook
  • n1cken1cke Maintainer
    edited July 2016
    @hgy29, I checked and it doesn't work works. You cannot get modified pixels from rendertargets because each time pixel shader applies to source texture (not previously modified one).
    local pixels1 = render:getPixels(1, 1, w, h)
     
    local effect = Shader.new("vs","ps",0,
    {
    {name="g_MVPMatrix",type=Shader.CMATRIX,sys=Shader.SYS_WVP, vertex=true},
    {name="g_Color",type=Shader.CFLOAT4,mult=1,sys=Shader.SYS_COLOR},
    {name="lightPos",type=Shader.CFLOAT4,mult=1,vertex=false},
    {name="g_Texture",type=Shader.CTEXTURE,mult=1,vertex=false}
    },
    {
    {name="POSITION0",type=Shader.DFLOAT,mult=3,slot=0,offset=0},
    {name="vColor",type=Shader.DUBYTE,mult=0,slot=1,offset=0},
    {name="TEXCOORD0",type=Shader.DFLOAT,mult=2,slot=2,offset=0}
    })
     
    bitmap:setShader(effect)
     
    render:draw(bitmap) --> this was missing!
     
    local pixels2 = render:getPixels(1, 1, w, h)
     
    assert(pixels1 ~= pixels2)
  • totebototebo Member
    edited July 2016
    Gah! I knew I had posted something similar before, but couldn't find it in the forum search.

    Would it be counterintuitive to create an abstracted Lua class with common uses, such as blur and desaturation? Shaders still scare me, so for me that would be very useful. I am working on a Sprite:setBlur method, but it's still a way off working as it should.
    My Gideros games: www.totebo.com
  • hgy29hgy29 Maintainer
    I don't really understand what you mean. If you want to keep your rendertarget's unmodified after the first draw call, then just don't draw on it again ? And you can use the rendertarget as source texture for subsequent calls without applying the shader again.
  • hgy29hgy29 Maintainer
    @totebo, yes that's a good idea. We plan to allow shader's to be loaded from lua string instead of files, that way we will be able to hide shader stuff inside .lua libraries, plus shader's could be customised at run time for even better efficiency.
    +1 -1 (+3 / -0 ) Share on Facebook
  • n1cken1cke Maintainer
    @hgy29, I mean with `getPixels` function you can't get pixels of rendertarget modified by `setShader` effect. I can't load images from disk, apply shader effect (like grayscale) and then save the result (grayscaled image) to disk.
  • hgy29hgy29 Maintainer
    Yes you can, the same way you apply shaders for drawing.
    In your example you miss the
    render:draw(bitmap)
    call, no ?
    +1 -1 (+2 / -0 ) Share on Facebook
  • SinisterSoftSinisterSoft Maintainer
    @n1cke,

    1. load your source image texture, point a bitmap to it, apply the shader to the bitmap.

    2. make a rendertarget. draw the bitmap to the rendertarget (once)

    3. read the pixels from the rendertarget.

    Likes: antix

    +1 -1 (+1 / -0 ) Share on Facebook
  • n1cken1cke Maintainer
    @hgy29, yes, sorry, missed that call.
  • totebototebo Member
    edited July 2016
    I have tried to do the greyscale with shaders, but I can't get it to work. All that happens is all sprites disappear for a frame. :)

    Could someone post actual code to make a sprite greyscale with shaders? I'm missing something and and actual compilable example would help a lot.
    My Gideros games: www.totebo.com
  • n1cken1cke Maintainer
    Accepted Answer
    Open "Normal mapping" example and replace `ps.glsl` "void main {...}"-part with:
    void main()
    {
    	highp vec4 color = texture2D(g_Texture, texCoord).rgba;
    	highp float m = (color.r + color.g + color.b) / 3;
    	gl_FragColor = vec4(m, m, m, color.a);
     
    }
    First line gives you 4 color components as vector (vec4). It also sets precision as 'high' for it (this affects performance).
    Second line does some maths to get greyscaled color.
    Third line creates new color vector and assigns it to texture coordinate.

    This template just returns unmodified color if you want to experiment further:
    highp vec4 color = texture2D(g_Texture, texCoord).rgba;
    gl_FragColor = vec4(color.r, color.g, color.b, color.a);

    Likes: chuz0

    +1 -1 (+1 / -0 ) Share on Facebook
  • totebototebo Member
    Awesome, got it. I hadn't dared look inside the ps.glsl file before. Thanks!
    My Gideros games: www.totebo.com
  • antixantix Member
    Example project please :)
    Follow me on FaceBook Check out my DevBlog, my GitHub, and try my games...
    Falling Animals | Breaky Wall | Exetor | Mini Putt Golfing
  • totebototebo Member
    Will make one once I get this working. First attempt of plugging it into my game removed the sprite instead of making it grey scale, so next step is to make a minimal project and take it from there.
    My Gideros games: www.totebo.com
    +1 -1 (+1 / -0 ) Share on Facebook
  • totebototebo Member
    edited May 2017
    Project attached!
    zip
    zip
    ShaderGreyscaleTest.zip
    6K
    My Gideros games: www.totebo.com
    +1 -1 (+4 / -0 ) Share on Facebook
  • antixantix Member
    Almost a year later. ;)
    Better late than never? :D
    Follow me on FaceBook Check out my DevBlog, my GitHub, and try my games...
    Falling Animals | Breaky Wall | Exetor | Mini Putt Golfing
    +1 -1 (+3 / -0 ) Share on Facebook
  • totebototebo Member
    edited May 2017
    I cheated and used Photoshop in the other game. Now I had a chance to get back to it. Patience is a virtue, guys! :)

    Still, shaders are a twilight zone of mystery, magic and infinite pain and suffering. So I'm not there often.
    My Gideros games: www.totebo.com
Sign In or Register to comment.