RbxShader: A robust shader engine, for everyone


Latest Release: v1.4.1

:exclamation:   Heads Up

I am no rendering expert, just a complete hobbyist who happened to develop a deep interest into 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.

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.

The shader features/provides:

  • The ability to interlace your renders
  • Partial re-implementation of GLSL functions (no swizzling tho)
  • Multithreading
  • Multiple buffers

Features that are planned to be implemented:

  • Make rendering go more brr
  • Input channels
     

* As of May 18, 2024, the shader only contains a fragment shader. But even so, you can still do a lot. If you wish for more features to be added, or have an eye for possible optimizations or 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.

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 newest version: v1.4.1
Learn how to use the module here: RbxShader Tutorial

A resource elaborating the new API’s methods and parameters will be coming soon!

 


 
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:

37 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!

2 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.

5 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.

3 Likes