Not logged inGosu Forums
Forum back to Help Search Register Login
Up Topic Gosu / Extending Gosu / Ashton - shaders and framebuffers in Ruby
1 2 Previous Next  
Parent - - By lol_o2 Date 2012-08-23 08:20
I knew there's grayscale, but I had to learn how to compile library from GitHub. Now it's working.
Also, I saw the lighting example and the Y axis seems to be reversed :) (what you probably know)

Drawing tiles with shader, lags :(
Parent - - By Spooner Date 2012-08-23 12:55
Oh, I see, I missed your meaning there.

Whether or not I have released a gem is another issue, you can run directly from github, which downloads and compiles for you:

    gem "ashton", :git => "git://"

in your Gemfile if you want to run directly from the latest version, without having to build the latest gem. I'm trying to avoid release until I get a few more things settled; quite a few bugs I need to squash before it is useable, I think.

Yeah, that annoying bug in the lighting isn't as simple to fix as it might appear and I'm a bit stumped as to what is going wrong. It involves a series of shader effects that I've converted from someone else's code. It does, however, look like it will be really nice when it works :)

If you are drawing a lot of things using the same shader, you should do:

    @my_shader.use do
      @tiles.each {|t| t.draw x, y, z }

rather than the simpler, but massively slower:

    @tiles.each {|t| t.draw x, y, z, shader: @my_shader }

The reason for that is that in the former the shader gets bound and unbound once and in the latter it will be bound and unbound 1000 times. Binding a shader is really slow (it involves loading the compiled shader code and variable state into all the processors on the card). If you need different behaviour for each tile, then you can use uniforms to make the code run differently.

Also, when drawing tiles, you probably either want to render them into a large Texture or use #record (though, in truth, I have no idea if #record is compatible with shaders; something I need to check). Texture#draw is also a lot slower than Image#draw, so you probably should have your tiles as Image, not Texture (Textures can do things that images just can't do, but if you just need what Image can do, always use those instead since they are much faster).
Parent - - By lol_o2 Date 2012-08-23 14:48
Looks like I need to separate tiles from other objects. But this grayscale shader appears too dark.

I doubt you didn't know, but OpenGL uses reversed Y axis, which counts from down (and in Gosu it's from up). So if you use it for lighting, this might be the problem :)
Parent - - By Spooner Date 2012-08-23 15:38
Shouldn't be drawing tiles in with other objects anyway, since they are a bit different (they are usually drawn behind everything else and are generally static). The shader code is in lib/ashton/shaders/grayscale.frag - you can copy that and alter it to your liking (but you'll have to use the full path to your version, not just :grayscale, since it wouldn't then be an Ashton built-in shader).

Nothing would work in Ashton if I didn't know about the reversed Y-axis. You'll also notice that it is reversed only from -45 to +135 degrees, not for all directions (that is, direction of shadow-caster from light source). The light-casting shaders are pretty complex (three shaders are used, one after the other) and are mainly a port of someone else's shader system. Probably a simple fix to make it work, but I can't put my finger on it in the algorithms.
Parent - - By lol_o2 Date 2012-08-23 18:17
OK, I managed to change this shader and instead of using some ratio, it's now just changing other color channels to one with maximum value.
I also separated tiles and the're drawing apart from other objects. But I have another problem. shader.use flushes the drawing and now shadered tiles cover everything they shouldn't. Is there anything I can do?
Parent - - By Spooner Date 2012-08-23 18:52
Nope, just a limitation of how Gosu draws things in order. Just draw the tiles first (that is, in #update or early in #draw) and you'll be fine.
Parent - - By Spooner Date 2012-08-23 18:56
Oops, no, that only happens when you use Texture#render (it flushes at the start and end; something I can't avoid because of how Gosu sorts drawing actions). There shouldn't be any problem with using Shader#use unless you are trying to be tricky :)
Parent - - By lol_o2 Date 2012-08-23 19:29
But if I want to draw tiles in background and foreground? And there are object below foreground tiles and also above, so with this flushing, I need 4 arrays :/
Parent - - By Spooner Date 2012-08-23 21:07
Yeah, 4 arrays. It isn't like they'll change... Not sure why you are putting them all in a single array anyway, when they would just be easier to manage separately (for example, many objects probably need a #update calling on them, but tiles probably don't, so you are wasting a lot of #update calls that are not necessary).

One option you can use, if this is really painful for you, is rather than using Shader#use, to use Shader#enable(z) and Shader#disable(z), which you'd use like this:

    @tile_shader.enable 1 
    @tile_shader.enable 1001   
    @all_objects_including_tiles.each &:draw # With the tiles drawn at either z=1 or z=1001
    @tile_shader.disable 1
    @tile_shader.disable 1001

Or keeping things separate (which is probably what I'd suggest to do):

    @tile_shader.use 1 do
       @background_tiles.each &:draw # where z = 1

    @background_objects.each &:draw # where z is between 2 and 1000

    @tile_shader.use 1001 do
       @foreground_tiles.each &:draw # where z = 1001

    @foreground_objects.each &:draw # where z >= 1002

As I said earlier, however, I'd recommend just using Window#record (to pre-record) or Texture#render (to pre-render) to _vastly_ accelerate drawing your tiles.
Parent - - By lol_o2 Date 2012-08-24 07:30
Oh, thanks :) This enabling seems to work

And I have a not-related question: is Window#record fast enough to be used per-frame? I use GlReadPixels and GLDrawPixels to make mirrors and it's not so fast.
Parent - By Spooner Date 2012-08-24 13:16
No, there is no reason at all to use #record every frame. Recording itself takes about as long as regular drawing, though then drawing of the recorded images will be very fast. If you are going to display something for more than a couple of frames, such as your entire level tilemap (changes every time the level changes) then it is worthwhile.

You are right that glReadPixels and glDrawPixels are not fast, but #record is definitely not your answer. In vanilla Gosu, you would just draw the scene twice, each draw being inside a Window#clip_to and one of those draws being inside a Window#scale -1, 1 block (or Window#scale 1, -1, whichever axis you want to mirror).

A simple and fast Ashton solution would be to #render the scene into a Ashton#Texture, and draw that once normally then draw it mirrored in a Window#scale block (Sorry, I haven't got around to implementing scaling for Textures yet :$). This is probably as fast as a shader method (mirroring is quite a simple operation), so I wouldn't bother with the extra complication of using a shader. In any case, it will be significantly faster than your current method.

EDIT: I remember telling you to use TexPlay's Window#render_to_image, which is a simpler way than manually using glDrawPixels and glReadPixels (since that is what it uses internally). Shame you ignored that suggestion :P Ashton#Texture#render does the same thing, but the pixel data never leaves the graphics card, so it is considerably faster than either method (Window#render_to_image or glReadPixels/glDrawPixels).
Parent - - By daIT Date 2014-05-21 00:30
Would there be any advantages or disadvantages to using Texture#render over Window#record for this purpose ?
Parent - By Spooner Date 2014-05-21 00:36
Well, composing the image with Texture#render is going to be faster, since you'd only be rendering a single image (single draw call). Gosu#record is massively faster than manually drawing 1000s of sprites in Ruby, but it is only as fast as C would be at doing that (which is, admittedly, massively faster than Ruby at doing a lot of operations). Both have their place really. Gosu#record possibly uses a little less image memory, since you store just the sprites, which are repeatedly drawn, rather than a full rectangle that encompassed all.

Both are fine and either is vital to getting anything like a reasonable framerate in a non-trivial game.
- By Spooner Date 2012-09-25 18:53
I think I've finally licked the random flipping issues when using Texture#render and post-processing shaders. *fingers crossed*

Also added a global and per-draw control over smoothing/pixelating Textures AND made pixelated textures look better when drawn smaller, so they don't distort as they do with Gosu::Images. This functionality should really be in Gosu::Image, but the only way to add that would be to hack the Gosu C++ source itself (something I'm not prepared to do). In fact, I wish that all the Texture functionality would appear in Gosu::Image, but I am not terribly hopeful. There are enough things that Texture does, however, that are really needed in Ashton, that I can't just use Gosu::Images. Shame really!

The main issue now is the broken lighting system, but I think that is a dead loss for the time being!
- - By Spooner Date 2013-09-06 10:56
Just bumped this to v0.1.0, since I'm not likely to do any major work on this any time soon. Some advanced bits (lighting/particles) are still as broken as before, but you'll just have to ignore them for now; most things work just fine (render-to-texture and shaders, for example).
Parent - - By jlnr (dev) Date 2013-09-07 10:38
Thanks for bumping! When you're not working on Gosu-ish things, what is the best way to reach you? github? Just in case I or someone else had contributions to push :)
Parent - - By Spooner Date 2013-09-07 10:45
I get notifications from github issues/pull requests. You can probably PM/post me here too, which should notify me, as this post did.
Parent - - By bestguigui Date 2014-07-17 21:56
Hi Spooner, it's been such a long time !

I'm thinking about using Ashton to create a second FrameBuffer, and your Texture class seems to do the job pretty well. I just have a big issue with it, and I don't think I did something wrong, so... I'll just ask you there : using the Ashton::Texture#to_image method, the render is inaccurate. All the pixels that were drawn on that buffer are not in the resulting image.

All drawing operations seem to work, since I'm displaying the Texture on screen, and everything that was added during runtime is on it, without any problem. I can move around the Texture instance, no problem : everything is on it. But when I use the to_image method, there is nothing on the picture. It was easier to see when I called the .save method from the returned Gosu::Image : a blank image is all I got.

And it's really a pain ! I really need it. The rgba method always returns [0.0, 0.0, 0.0, 0.0], even when on screen there are a lot of colours drawn on those pixels. Can you help me out ? Thanks a lot !
Parent - - By Spooner Date 2014-07-17 22:00
If the Aston::Texture you create is larger than the maximum image size that Gosu supports, which is probable if the Ashton::Texture is window-sized, then it won't convert into a Gosu::Image properly.

What do you want to achieve exactly, in converting it to a Gosu::Image? Perhaps I can suggest a method of achieving the overall goal, if not how to convert it to an Image.

Well, if you ABSOLUTELY need it as an image, you could create smaller Textures, render the large texture into each one with an offset, then convert those to images. Really, it seems like a painful way to do it :D
Parent - - By bestguigui Date 2014-07-17 22:04
It was like the fastest answer ever ! :)

What I need is to be able to draw an OpenGL scene into another FrameBuffer. So, what I have in mind is to insert a @texture_buffer.render bloc inside a Gosu::Window#draw gl bloc. And the purpose of that is to be able to use the rgba method you provide to get the pixel color at x,y position into that render.

I hope you understand me, my english is still awful...
Parent - - By Spooner Date 2014-07-17 22:22
Not sure what that has to do with Ashton::Texture#to_image though.

So anyway, you render into an Ashton::Texture, which displays perfectly with Ashton::Texture#draw(), but Ashton::Texture#rgba doesnt give the result you expect, nor does converting #to_image. Seems the PixelCache object (written in C) associated with the texture is not getting updated (it should lazy-load the pixel data from the GL texture).


Try forcing a refresh of the cache and check different values:

p texture.rgba(10, 15)
p, 15)

Note that you can't access the pixel cache until after the Ashton::Texture#render block (though I assume you realise that).
Parent - - By bestguigui Date 2014-07-17 22:30
My point has nothing to do with the .to_image method, that was just a solution I thought about, because I could use Texplay to read the pixel. But then I saw that there was a rgba method directly inside of the Texture class, which is much better for me.

Anyway, it still doesn't work :(

I don't see where the mistake is. You'll see that I ask for the pixel oustide of the render block.

Result :

>ruby init.rb

[0, 0, 0, 0]

>Exit code: 0

Parent - - By Spooner Date 2014-07-17 22:38
That is a pretty simple example. Looks fine, though I'd only render to the buffer if the mouse button is down :) So you are able to see the changes to the buffer on-screen, but not via the PixelCache. Not a clue here, I'm afraid - sorry!

puts @buffer.rgba(10, 15).inspect

can be written more simply as:

p @buffer.rgba(10, 15)
Parent - By bestguigui Date 2014-07-17 22:43
Thanks for the tip, didn't know about that "p" thing (I always use print or puts).

The example is really simple because I only wanted to see if that would do it or not, into the simplest program possible ! :)

So you don't know ? :'(
It would really have been great, since I can't figure out a way to create another buffer using the Ruby binding of OpenGL. I guess I will still have to draw / pick color / clear / redraw each frame. Thanks for your time, anyway ! :)

And I changed my Texture, for a 64*64 one, the rgba method now returns correct values.
Up Topic Gosu / Extending Gosu / Ashton - shaders and framebuffers in Ruby
1 2 Previous Next  

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill