GLSL Shaders in BlitzMAX

BlitzMax Forums/BlitzMax Programming/GLSL Shaders in BlitzMAX

TomToad(Posted 2016) [#1]
Been trying to learn shaders and incorporate them into Max2D. Been learning a lot about them. Came up with a type to help with them. The type is far from complete. I'll add to it as I learn more.

Shader Type.bmx


Usage is simple. First you must set BM to use OpenGL with
SetGraphicsDriver GLMax2DDriver()

Then set your graphics mode as usual.
Graphics Width, Height

Then you need to create your shader with TShader.Create()
Local Shader:TShader = TShader(VertexShaderSource,PixelShaderSource,Name)
VertexShaderSource and PixelShaderSource are strings containing the GLSL source of your shaders. Name is a name you give to this shader for the debug logging.

Now to use the shader, you just use

To stop shader processing, just use

If you need to send data to the shader from BMax, use
Local Location:Int = Shader.GetUniform(UniformName)

UniformName is the name of the variable in the shader. Currently, I have only implemented sending 1 float value at a time. Other types will be added over time.

That's about it. Look at my demo for an example of how to use the methods. :)

This post will be updated as I learn more and implement more into the Type.

BlitzSupport(Posted 2016) [#2]
That's pretty f-ing awesome, nice work!

TomToad(Posted 2016) [#3]
Getting textures to work. A fluttering flag example. Don't forget to copy the "Shader Type.bmx" file to the directory containing this source (or just Copy/Paste the whole thing where Include "Shader Type.bmx" is located).

BlitzSupport(Posted 2016) [#4]
Works well here. I realise it's very early days, but do you think it might be possible to do any of:

1) Apply shader to TImage or TPixel;
2) Apply to backbuffer (or copy of same)?
3) Include checks for necessary GL version?

That'd be my little wishlist anyway!

TomToad(Posted 2016) [#5]
Tried adding Klepto's RendertoTexture routine from here OpenGL Render2Texture (FBOs) to apply a shader to an entire rendered scene, but it seems to only render to half the frame. Any ideas?

TomToad(Posted 2016) [#6]
Ok, if I change line 71 from
glOrtho 0,Image.Width,Image.Width,0,-1,1
glOrtho 0,Image.Width,Image.Height,0,-1,1

then it renders as expected. A bug in the Render2Texture routine?

TomToad(Posted 2016) [#7]
Ok, also had to add the glScalef(1,-1,1) and the glScalfef(1,1,1) in the BindBuffer and UnbindBuffer methods respectively to avoid flipping the texture as per reply in the line above.

Endive(Posted 2016) [#8]
This is just wonderful.

Derron(Posted 2016) [#9]
Yes this is a bug in the routine, I had added the fix to the r2t-examples I gave to dw' in one of his threads (including a bit more "oop" for the types).

I think at least the glscalef-thing was appended to the codearchive-entry of klepto2.

Hope you enjoy the tinker time.


TomToad(Posted 2016) [#10]
Nice screensaver kinda thing :)
Decided to C&P the TShader type into the source, that way if I change the Type in post #1, I won't risk making all these samples unrunnable.

dw817(Posted 2016) [#11]
Great swimmers, TomToad ! This VertexSource and PixelSource is very new to me though.

I'm still waiting for full-screen random pixels with this new GL stuff. I might be able to understand it.

TomToad(Posted 2016) [#12]
full-screen random pixels

dw817(Posted 2016) [#13]
Okay, thank you ! Can you make them colored please ? R G B (0-255). Do that and I'll get my magnifying glass out and try to see what's going on there.

Oh, and before you get too busy, is this the fastest way to stuff colored pixels on the screen ? (Benchmarks ?)

Pingus(Posted 2016) [#14]
Awesome stuff there. Is it doable in Dx ?

TomToad(Posted 2016) [#15]
Okay, thank you ! Can you make them colored please ? R G B (0-255). Do that and I'll get my magnifying glass out and try to see what's going on there.

Yeah, a little bug in my program. GLSL doesn't allow for a static type. Globals are reinitialized every frame. So a proper Rnd() function cannot be created on GLSL. I did find one online, but you must provide the seed for every call. I just use the x,y coordinates added to a randomly generated float passed on the CPU side. That is the reason for the Random = Rnd(0,1) in the above code.

The bug comes in to the fact that I pass the exact same seed for red, green, blue components creating various shades of grey. If you replace
" gl_FragColor = vec4(rand(coord.xy),rand(coord.xy),rand(coord.xy),1.0);~n" + ..

" gl_FragColor = vec4(rand(coord.xy),rand(coord.xy+.01),rand(coord.xy+.02),1.0);~n" + ..
Then you will get a nice random color screen.

Oh, and before you get too busy, is this the fastest way to stuff colored pixels on the screen ? (Benchmarks ?)

Yes and No. As shown in many examples here, and examples in your Lights Fantastic thread, Doing per pixel calculations and pushing them to the screen can be quite fast. Unfortunately, there is no way to store a pixel table in the shader so pixels can be manipulated on CPU side. You could use a texture for a table, but as a texture is already a type of table anyway, updating them would be no faster than other, non shader versions. Problably some render2Texture code would be your best bet.

I did have an idea to just push a pixel queue (an array full of pixel data) to the shader, so if you are manipulating only a few pixels, there would be no need to push the entire texture across the graphics bus. It theoretically should be at least 2X faster even when changing all pixels in a texture as the transfer would be only one way. Unfortunately, GLSL 1,2 has no way of rendering to the texture directly that I can find. Maybe a higher version of GLSL will allow it, still reading and learning.

dw817(Posted 2016) [#16]
The more I am hearing about pushing pixels in a queue, Tom, it sounds very familiar to getting HAM graphics on the Commodore Amiga.

Gosh I hope it doesn't get this complex ! :/

Matty(Posted 2016) [#17]
Ultimately with pushing pixels they have to cross the cpu gpu barrier somewhere....unless you do all or most processing on the gpu itself with minimal instructions sent across.

Pete Rigz(Posted 2016) [#18]
This is really interesting, I've often wanted to dive into shaders, what are your reading/learning sources?

TomToad(Posted 2016) [#19]
[quoteThis is really interesting, I've often wanted to dive into shaders, what are your reading/learning sources?

And a bit of googling.

TomToad(Posted 2016) [#20]
Now messing with the vertex shaders. Made a nice sine scroller.

dw817(Posted 2016) [#21]
Trying out your code. ?? HOW are you doing this,Tom ? I see DrawText() and no other code in the main.

All I can figure is you somehow managed to reposition all the pixels on the screen to a wave pattern so anything that is plotted will be skewed from its original, meaning of course no straight lines via drawline().

Oddly enough trying to add any graphics around the DrawText() does not appear. Lines, rectangle, or ovals.

Tricky stuff. I remember in S2 I had an icon a picture of a fish and when you used it in your command set, the screen and sprites would become all wavy like that like you were underwater. :)

TomToad(Posted 2016) [#22]
In the Nice Screensaver Kinda Thing above, go to lines 116 and 117 and change the *20.0 in both strings to *2.0. Go to line 147 and change the Time :+ .001 to Time :+ .01.

Now you have a wavy underwater thing. :)

Reason why you don't see anything in the main program is because the code to modify the text is actually being run on the graphics card itself. When a shader is created, you effectively bypass the rendering pipeline. This gives you opportunity to modify the vertices and pixels before they actually reach the screen.

for the wavy text, the shader is moving the corners of each letter up and down on the y axis depending on its position on the x axis. The blue and white stripes are created by modifying the pixels based on its y position on the letter.

Reason why circles and lines are not showing up is because the shaders operate on 3D objects. Since TImages are simply 3D rectangles that are always facing the camera to appear 2D, shaders will work on them. Circles and lines are 2D constructs which shaders don't support (at least I have found none in the research I have done so far). In order to draw the 2D primitives, you must disable the running shaders to allow the normal pipeline to render them. A few small modifications to the main function will do.

dw817(Posted 2016) [#23]
Well now that you are mentioned it, Tom. Is there a simple way to take a TIMAGE or PIXMAP and display it as a parallelogram.

Where it appears something like this:


And would appear as if it were 3-dimensionally tilted away from the camera ?