About Monkey 2 › Forums › Monkey 2 Code Library › Simple image shader (GLSL)
Tagged: 2d, 3d, c64, commodore, GLSL, render target, rendertarget, Retro, retro fx, retrofx, shader, Shaders, speccy, spectrum, zx
This topic contains 12 replies, has 3 voices, and was last updated by
DruggedBunny
2 months, 1 week ago.
-
AuthorPosts
-
January 13, 2019 at 10:45 pm #15895
This is a very simple pixel shader for modifying images using a GLSL shader.
In this case, the shader changes each pixel’s colour to a greyscale (average) version:

It’s formed of two parts, the Monkey2 code and the GLSL shader code.
The file structure should be:
- the Monkey2 code and an assets folder next to each other;
- the GLSL file in a shaders folder within assets. (“assets/shaders/basic.glsl”)
A pre-made zip setup can be downloaded here.
Monkey2:
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122#Import "<std>"#Import "<mojo>"#Import "assets/"Using std..Using mojo..Class ImageRenderTargetField image:ImageField canvas:CanvasMethod New (width:Int, height:Int, centered:Bool = True)' Adapted from DoctorWhoof's Plane Demo -- thanks, DoctorWhoof!image = New Image (width, height, TextureFlags.Dynamic, Shader.Open ("basic"))If centeredimage.Handle = New Vec2f (0.5, 0.5)Endifimage.Material.SetInt ("Toggle", False)canvas = New Canvas (image)canvas.Clear (Color.Brown)canvas.TextureFilteringEnabled = FalseEndEndClass Demo Extends WindowConst WINDOW_WIDTH:Int = 960Const WINDOW_HEIGHT:Int = 540Const WINDOW_FLAGS:WindowFlags = WindowFlags.ResizableField x:FloatField y:FloatField width:Float = 2Field height:Float = 2Field xdir:Float = 1Field ydir:Float = 1Field toggle:BoolField target:ImageRenderTargetMethod New (title:String = "Image Shader Demo", width:Int = WINDOW_WIDTH, height:Int = WINDOW_HEIGHT, flags:WindowFlags = WINDOW_FLAGS)Super.New (title, width, height, flags)EndMethod OnCreateWindow () Overridetarget = New ImageRenderTarget (640, 480)Mouse.PointerVisible = FalseEndMethod OnRender (canvas:Canvas) Override' Ask app to render itself...RequestRender ()If Keyboard.KeyHit (Key.Escape) Then App.Terminate ()' Toggle B & W shader effect...If Keyboard.KeyHit (Key.Space)toggle = Not toggle' NOTE: The "Toggle" string is received in the shader code as m_Toggle, ie. is automatically prefixed with "m_".target.image.Material.SetInt ("Toggle", toggle)Endif' Simple movement code for the random-colour dot being drawn onto the render target...x = x + xdiry = y + ydirIf x < 0 Or x > target.canvas.Viewport.Widthxdir = -xdirx = x + xdirEndifIf y < 0 Or y > target.canvas.Viewport.Heightydir = -ydiry = y + ydirEndif' Draw onto render target canvas; shader will affect the result:target.canvas.Color = Color.Rnd ()target.canvas.DrawRect (x, y, width, height)target.canvas.Flush ()' Draw resulting image onto main window canvas:canvas.TextureFilteringEnabled = Falsecanvas.Clear (Color.Pine)canvas.DrawImage (target.image, canvas.Viewport.Width * 0.5, canvas.Viewport.Height * 0.5)', (Pi / 180.0) * angle)canvas.DrawText ("Space to toggle B & W shader", 20, 20)EndEndFunction Main ()New AppInstanceNew DemoApp.Run ()EndGLSL:
This code must be placed into assets/shaders/basic.glsl:
Monkey1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677// --------------------// BOILERPLATE BEGINS!// --------------------//@renderpasses 0varying vec2 v_TexCoord0;varying vec4 v_Color;//@vertexattribute vec4 a_Position;attribute vec2 a_TexCoord0;attribute vec4 a_Color;uniform mat4 r_ModelViewProjectionMatrix;uniform vec4 m_ImageColor;void main(){v_TexCoord0=a_TexCoord0;v_Color=m_ImageColor * a_Color;gl_Position=r_ModelViewProjectionMatrix * a_Position;}//@fragment// --------------------// BOILERPLATE ENDS!// --------------------uniform sampler2D m_ImageTexture0;// m_Toggle is filled-in by image.Material.SetInt ("Toggle", toggle)// m_ prefix is automatically added by mojo...uniform bool m_Toggle;void main(){// Store "this" pixel's original colour:vec4 color = texture2D(m_ImageTexture0, v_TexCoord0);// Average out the RGB values:float average_rgb = ((color.r + color.g + color.b) * 0.333);// Build a new vec4 value with RGB each set to average_rgb, and alpha of 1.0:vec4 bw = vec4(average_rgb, average_rgb, average_rgb, 1.0);// If Toggle = True, set to B & W version of pixel, else draw default.// (gl_FragColor is the resulting pixel colour.)if (m_Toggle){gl_FragColor = bw;}else{gl_FragColor = color;}}You just create an ImageRenderTarget, using the image and canvas fields like so:
- canvas to draw to the canvas;
- image for drawing the resulting shaded image.
To set/get shader variables, you use the image.Material‘s Set/Get methods, as in the SetInt (“Toggle”, toggle) seen in this example. The Toggle string here relates to the m_Toggle value in the GLSL code (notice that the GLSL variable name is formed from this string, auto-prefixed by mojo with “m_”).
Thanks to DoctorWhoof (aka. Ethernaut) for the render target code and PixelPaladin for the example GLSL interfacing code.
Next up, to finally do a workable ZX Spectrum shader on my Island demo (use L to toggle resolution), since I only need to operate on a tiny 256 x 192 image now, which is upscaled to the actual display size… (Done it before but it was far too slow as I was working on the entire display, and the attribute-clash part of the Spectrum shader is very intensive, checking 64 pixels around every single pixel for the most common colour!)
January 13, 2019 at 11:59 pm #15896Updated Island demo with the ImageRenderTarget code from above, now offers simple greyscale for starters. (L to toggle low-res, P to toggle greyscale in low-res mode.)
January 17, 2019 at 6:20 am #15936Update to Speccy Island!
Hit L to go Low-res, Spectrum-style, with attribute clash.
Hit P in low-res mode to toggle real colours. Full-screen it!
L gets back to normal res/colours.
Still WIP, but finally got a decent-performing shader that at least outputs legit Spectrum-compatible imagery, needs much tweaking still.
In theory, you could save a bunch of frames and display them for real on the Spectrum, as-is… which I’ll eventually try.
January 17, 2019 at 7:34 am #15937Which browser are you using? It’s not working on Chrome!
Failed to compile shader:ERROR: too many uniform
Working fairly nicely in FireFox!
January 17, 2019 at 8:23 am #15938Ah, only tried in FF, web not really being my main target, but had to do a few tweaks to get that to work. Get the impression cross-browser shaders are not a lot of fun!
January 17, 2019 at 1:31 pm #15940Hmm, weird, just installed Chrome and it’s working fine here! What platform/gfx card you on?
January 18, 2019 at 3:39 am #15947Well, 256 x 192 is rather a “challenging” candidate for turning a low-contrast, fairly washed-out, realtime rendered scene into 8 Spectrum colours (each with associated ‘dark’ version) with correct colour-clash, and without sampling every 8×8 block of pixels (a MASSIVE framerate hit since GLSL isn’t meant to read anything other than the ‘current’ pixel), it wasn’t feasible to select a suitable foreground and background colour that avoided constant flickering between the two as the scene changed, so I’ve amended the Spectrum’s display size.
Here’s how the scene would be rendered if the Spectrum’s display size was 1024 x 768 instead of 256 x 192, but still with correct colours and clash.
Full-screen it. (Only testing web version in Firefox, apologies.)

I’ll upload my code once I’m done messing about…
January 18, 2019 at 3:48 am #15948Also quite cool back in 256 x 192 but with colour clash turned off. (Separate link; again recommend full-screening it. Key L is still available to toggle low-res mode off.)
January 19, 2019 at 4:52 pm #15961January 25, 2019 at 5:43 am #16001Crude C64 resolution/palette shader. (No attribute clash implemented or planned!)

C64 Island [WebGL, Firefox-tested only!]
Will update GitHub probably tomorrow night (GMT) or over the weekend, once I’ve tweaked it more and implemented switching between Speccy and C64 modes…
Next up will have to be simple Amiga 32 and 256-colour modes, maybe 320 x 200 and/or 640 x 512, and SNES?
January 25, 2019 at 7:08 am #16002On safari, both don’t work
January 25, 2019 at 8:27 am #16003It’ll be the shaders needing tweaked, probably… I’m only testing on Firefox just for throwing up quick demos, but my own interest is desktop. As I mentioned, shaders and web browsers seem to be a minefield of misery.
I don’t think they do Safari for Windows these days, so…
February 10, 2019 at 11:10 pm #16062I’ve just updated this with some cool palette-selection/dither code to loosely represent a few different ‘retro’ systems…
Try it out at:
http://www.hi-toro.com/monkey2/retro_island/retro_island.html
Code at:
https://github.com/DruggedBunny/Retro-Island
Notes:
Hit R for retro mode: expect MASSIVE pause here on web, maybe 20-30 seconds or more! Need to somehow get this done during startup…
Hit [ and ] to cycle through a few cool ‘retro’ modes.
Hit H to hide text in retro mode.
Tested in Firefox, Chrome and Vivaldi on Windows 7 64-bit only. Download code from GitHub and build natively if it fails for you!




-
AuthorPosts
You must be logged in to reply to this topic.