RbxShader: A robust shader engine, for everyone


Latest Release: v1.5.3

:exclamation:   Heads Up

I am no rendering expert, just a complete hobbyist who happened to develop a deep interest for shaders and graphics programming because of two things, Teardown, and Acerola.

Consequently, this also means that the shader might be lacking some necessary optimizations and other features. If you wish for more features to be added, or have an eye for possible optimizations on how the shader could be better in any way, please feel free to contribute on my GitHub repository as this project is open-source and is open for contribution.

NOTE: This program utilizes Roblox’s beta feature EditableImage, meaning that this cannot be used by published games for the meantime.

The shader can run virtually any shader program that can run on Shadertoy, and porting is relatively easy due to their code structuring being similar.

If you wish to test the abilities of this shader, you can use the sample shader program provided, which are also ports of pre-existing shader programs in Shadertoy :blush:. Alternatively, you can go view the showcase below, albeit please beware that the video results are not of great quality due to compression and conversion.

Features the shader provides:

  • The ability to interlace your renders
  • Partial re-implementation of GLSL functions (no swizzling tho)
  • Multithreading
  • Multiple buffers
  • A culling system for programs that are displayed on SurfaceGuis or BillboardGuis

Features that are planned to be implemented:

  • Make rendering go more brr
  • Input channels
     

Showcase

All video footage are from a laptop with an 11th Gen Intel i3 Core, and 8 GB of RAM
*The footage is actually a bit more smoother an less grainier live than in video

Voxel Shader

ported from @Xor on Shadertoy

180 by 120 pixels, with dual interlacing by the factor of 2
avg. 40-45 FPS

Voxel Tunnel

ported from @Elsio on Shadertoy

180 by 120 pixels, with dual interlacing by the factor of 2
avg. 24-27 FPS (Not so bad!? :blush:)

Shader Art

ported from @kishimisu on Shadertoy

180 by 120 pixels, with dual interlacing by the factor of 2
avg. 40-45 FPS

Gravity Sucks

ported from @mrange on Shadertoy

180 by 120 pixels, with dual interlacing by the factor of 2
avg. 25-30 FPS (ohh?! :kissing:)

Resources

Learn about the latest release: v1.5.3
Learn how to use the module here: RbxShader Tutorial

Learn about the API Change that happened in v1.4.1

 


 
This shader also utilizes @Ethanthegrand14 's CanvasDraw module v4.1.1.b, lots of thanks to him for provide such a wonderful resource!

You can download the shader by going to the GitHub repository and downloading the latest release. I do not expect this resource to be of use to a lot of people as of now. But at most I hope I can attract some shader nerds like me or really clever individuals (possibly you?) in making this project grow and mature. Thanks for reading, lovelots! :heartpulse:

54 Likes

Probably the best shader module ive seen yet. Might pick up my raytracing project again using this.

2 Likes

Amazing! To think this Super Optimised Pixel Raytracer with Textures! would become free material! I did try out the stuff it’s a little confusing and didn’t get it to work but I will wait for the instructions! It’s cool to see this kind of thing be used in Roblox. I can’t wait for more stuff like this since I myself am not capable I’ll leave it to you guys! I can see more stuff like this in the future when coders know implement this kind of stuff more, unlike other software (unity? I am not making fun of them) where they come with shaders like this or so I heard (VHS style games). Not sure if any of that made sense, but can’t wait for more!

3 Likes

Hi! Thank you for your appreciation and sorry for the late reply! I was busy making the tutorial for the module and is now available. I hope this fixed your problem!

Finally someone doing this :sob:
Godspeed man, i hope the performance issues gets well over time.

1 Like

Thanks for crediting me for CanvasDraw! Just a couple of hints to help improve performance however!

local function PerFrame ( iTimeDelta : number )

		local iTime = iTime()
		if Step > InterlaceFactor then Step = 1 end

		for y = Step, Canvas.Resolution.Y, InterlaceFactor do
			for x = 1, Canvas.Resolution.X do
				ImageBuffer:SetRGBA(x, y, Shader.mainImage(
					Vector3.new(ImageBuffer:GetRGB(x, y)),
					Vector2.new(x + CanvasOffset.X, y + CanvasOffset.Y),
					iTime,
					iTimeDelta,
					iResolution
				))
			end
		end

		Canvas:DrawImage(ImageBuffer)
		Step += 1
	end

Firstly, your method of using a blank ImageData object as a buffer isn’t needed. A canvas is it’s own buffer!

You are technically setting pixels twice (one in the image data, one in the canvas when drawing said image)

You can disable AutoRendering (CanvasDraw.AutoRender = false)

and then use Canvas:SetRBG() instead of ImageData:SetRGB(),

and then instead of Canvas:DrawImage(), use Canvas:Render()!

This will maximize per pixel efficiency!


Secondly, you should use more actors than threads in your CPU or whatever CPU core amount you’re targetting. In your example, you have it set to 4. Setting this to like double with help spread out the workflow better and slightly increase FPS.

6 Likes

Hi! Thank you for reaching out to me! Sorry for the late reply, I have trying to implement what you’ve said but I can’t seem to do so because the array I’m producing keeps being larger than Size.X*Size.Y*4, I know it’s an indexing problem but I can’t seem to figure out why can you help me point out the problem here?

NVM, I was being stupid and made the for-loops run adjusted to conform to it’s position on the “global canvas”, but I forgot the canvas are subcanvases (smaller canvases that make up the whole scene) so it should really just be local positions. :triumph:

Anyways, thank you so much for pointing it out. because of this, I managed to squeeze out an extra 2-4 frames (when interlacing is set to 1, and canvas size is 180 by 120)! Also, I am aware that having more actors can help spread out the workflow, I only set mine to 4 because it doesn’t really do that much of a difference on my device; but thank you for mentioning it!

3 Likes

The new release, v1.3.0 is out. It features a new configuration parameter called DualAxisInterlacing which makes the renderer interlace for both axises of the screen when set to true.

A canvas size of 180 by 120 and having your configuration set to:

{
	InterlaceFactor = 2 ,
	DualAxisInterlacing = true ,
	ScreenDivision = 16
}

can now give you an average of 25 frames on most shaders (on dual-core processors atleast).

Does this work on older drivers? (P1000 Notebook)

I’m not sure, but you could go ahead and try!

Yeaaaahhh it bluescreeened… In my offense i have a VERY VERY old laptop like its older than my brother.

1 Like

I have 0 experience in graphics area, so what is this, what is a shader engine, is this for like playing videos, im confused.

For playing shader programs, you can view examples of such by going to Shadertoy. if you wish to know more, you can do more research about shaders or graphics programming in general.

1 Like

v1.4.1 has been release.

And we’ve got few things to cover. Buffers are now supported. Additionally, for everyone that is currently using it, there will be a bit of an API change. Any questions and feedback regarding these changes and additions are welcome.

I. API Changes

First and foremost, you now have the ability to put the shaders to any GuiBase of your choice via the Screen parameter, as originally, they were only restricted to ScreenGui’s.

RbxShader.new() now holds a new 6th parameter called ShaderID, this allows for multiple shaders to be ran for a single host script as previously, whatever was the host/parent of the script would automatically be the parent of the shader workers threads, which would be problematic for people running multiple shader programs (though I doubt anyone actually would do this) for the same host.

Consequently, this means that RbxShader.run() and RbxShader.stop() now takes in a ShaderID instead of the host of the program.

Added new functionalities, such as RbxShader.pause() and RbxShader.resume() which as you’ve guessed, pauses and resumes a shader program. The difference between these functions and .run() and .stop() is that it preserves the iTime of the program, which most shaders often rely on for moving scenes, etc.

A new function, called RbxShader.set() has also been created. Which sets a shader program’s canvas size, interlacing factor, and subdivisions (equivalent to the amount of worker threads) to whatever you want. It also preserves iTime, as it uses .pause().

Lastly, we have RbxShader.clear(), which wipes off the shader program by the given ShaderID from the host. Useful if you only ever need to display the program for a short period or when it’s too far too be rendered becoming a waste of processing power.

II. Buffers

Buffers are a way to make multiple passes on a single frame. Useful for structuring and organizing your shader program.

You can now make buffers but only ranging from A to Z. This is by design, as having too many buffers is absurd. This means that proper function name for buffers could only be "bufferA, ..., bufferZ" else they will not be recognized as buffers (but having everything to be in lowercase is allowed).

III. Input Handling

Added a new sixth parameter for mainImage and buffers called iMouse, which acts as a Vector2 that represents the mouse’s position on the screen (with respect to the UI inset). This value only ever changes to wherever the mouse moves when the left mouse button is held down inside the display of the program.


Here is a short video showcasing some of the new features mentioned.

4 Likes

oh damn this thing is awesome!!
it might not be that performant but i cloned an existing shader and it works like a charm: original shader

2 Likes

How come I haven’t heard of this? Is this more performant than Hexo’s shader system and that other one? Considering that it says it has multithread and Hexo’s doesn’t and it also has interlacing (Hexo’s also has interlacing) which the shader system made by someone who’s name I don’t remember doesn’t have interlacing. The best of 2 worlds! Tho it EditableImages so no live servers.

same

just use canvasdraw so it can be used in live servers but so it also can use editableimages when your in studio
edit: wait your using the beta but wait shouldnt it work in live servers but actualy im wrong i thought the new beta canvasdraw automatically uses the old create a bunch of colored frames method or the uigradient method well i actualy havent used canvasdraw before also just so you know you can change the canvasdraw canvas color and blur you should make it an option to change the canvas color or blur

I also wish it ran on the GPU so I can run this at 144 FPS but ROBLOX doesn’t let you do that hope ROBLOX adds shader support (but coregui will draw over shaders so you can’t hide them for security reasons)
Also CPU shaders do kindof exist they’re not really shaders but it is rendering an example is Blender Cycles on CPU setting or Blender Compositor on CPU setting they are much more optimized it is probably possible to make this run most shaders at a smooth 60 fps at 180 by 120 with interlacing 2 or even 1920 by 1080 with no interlacing but ROBLOX’s lua is not that fast

edit: I also would like it if alpha was added so i can see through the canvas tho im pretty sure thats not in canvasdraw yet. (edit: it does but rbxshader makes the background opaque) also i did get this running also interlacing is not working
edit: i got interlacing to work

	- there is something very wrong with how I calculate
	  material reflectance

instead of

local Direction = g.reflect(RaycastResult.Position, RaycastResult.Normal)

do

local Direction = g.reflect(RaycastResult.Position-Raycast.Origin, RaycastResult.Normal)
1 Like

Hey there, thank you for checking out my resource!

I am not sure, about that. Benchmarks could be made, but I don’t want to be the one to do it as I don’t want to look too competitive. In fact, I also have seen Hexo’s shader system before I embarked on this project. I tried to use it but the way how you make a shader on that module felt really unintuitive to me + he didn’t have proper documentation on how to use it :cry:. ( But I do understand that the design choice was meant to leverage performance—especially for a single-threaded shader system )


I am infact using the beta—of CanvasDraw v4, which is a version that supports EditableImage. You could modify the source of the code at the RbxShader.Worker file so that it uses CanvasDraws’ Canvas object instead to render the shader, then you’ll be able to publish it for live production.


Same, if they’ll never make an explicit interface to interact with the graphics library or whatever, at the very least I wish they could make Luau something like Bend (a high-level massively parallel programming language), where “Anything that can run in parallel, will run in parallel”.


Could you elaborate how? I got ran my test place up and the interlacing is working perfectly fine for me; did you set shader configurations properly?


I really thought that I had already properly implement this, but it turns out I haven’t.
10 minutes later, I have found a solution. After some tinkering, I learned that returning colors that have a custom alpha like this:

-- # `v3_rgb` means that we're splitting a Vector3 into 3 numbers (RGB channels)
return g.v3_rgb(col), 0.5

—doesn’t work because what Luau actually does is it takes the first element from the tuple, then returns it alongside 0.5 ( weird I know ). To resolve this issue, I’ve added two new methods at the graphics library, albeit sorta hacky:

-- # these split their respective value into RGB values, which they return
--   along with the alpha value given so that the tuple doesn't get cut
g.v3_rgba( v : Vector3 , alpha : number )
g.c3_rgba( c : Color3 , alpha : number )

This change will be a part of v1.5.2, which I’ll be releasing this afternoon.

just a question but, has Roblox added a way to use Viewport frames as buffers?

What do you mean by this? Could you possibly elaborate?

not sure how to explain this but i mean like getting what you see in a Viewport frame as an editable image. or getting it as a rendering buffer like you can in shadertoy with buffer A buffer B and so on.