[POC] Optimized Frame Image Rendering With UIGradients

Hello! I recently came up with a method to reduce the number of frames needed to draw an image pixel by pixel using UIGradients for (currently) up to 20x (18x by default in 1.1) less laggy image rendering. I’ve created this proof of concept module as well as a demo included. I would not recommend using this in a large scale game due to it’s hackiness and potential instability.

DISCLAIMER

Do NOT render inappropriate things with this POC code and DO NOT allow inappropriate things to be drawn with this POC code. It is not my fault if your game is moderated due to inappropriate images being displayed by your code and it is up to you to ensure that that doesn’t happen.

How does it work?

Note: By default in 1.1 this is reduced to 1x18 for an ~18x performance boost to get rid of artifacting (this was provided by DataBrain in a reply below)
Your image is passed into the Generate function as a 2D array of pixels. This array even supports transparent pixels! It is then converted into a bunch of 1x20 columns (frames) which include a UIGradient in them. This UIGradient contains 20 colors (one for each individual pixel). This is currently the only limiting factor of how many vertical pixels can be drawn (ColorSequences only support a maximum of 20 values). Finally, each row is scaled and positioned to match the correct location of the input image and the frame containing the generated image is returned.

Usage

One function is included on the module, the generate function. This takes a 2D array of colors/alphas. (See usage within the included scripts for more info)

Additionally WritePixels, WritePixel, and ReadPixel are included for image manipulation (1.1)

Example & Demo (V1.1 fixes artifacting by default)

Here’s 100x100 pixels of perlin noise generated entirely by this module! Notice the artifacting. The image seams are drawn in red on the right. This weird seam issue is due to the way UIGradients are interpolated and it’s the main downside to using this method.
image

Current Version: 1.1
If you’d like to test this demo code and see some example image generation here’s the demo code with the module included: RenderTest.rbxl (24.5 KB)

23 Likes

What a clever method!

You can get rid of these artifacts completely by shifting the color sequence around so that each pixel aligns with the center of each interval. The tradeoff is that you can only have 18 pixels per frame rather than 20. In this case, I colored the end keypoints red to show that they don’t affect the final result

On the left is your version, and on the right is the modified version (Both are perlin noise)
image

Here’s a diff showing what portions of code I modified:

6 Likes

This is great! I’ve gone ahead and added an option to generate images using this. I’ve additionally added some functions to write/read pixels in a generated image. That should allow for efficient live rendering!

I’ll make sure to update the post when I’ve finished the update.

2 Likes

Updated demo/module to version 1.1:

  • Added the noArtifacting option which fixes artifacting issues at the cost of performance (defaults to true, thanks @DataBrain :smile:)
  • Added ReadPixel
  • Added WritePixels
  • Added WritePixel
    Note: I highly recommend using WritePixels due to its batching abilities! Using WritePixel to fill an image will take a huge huge hit on your FPS.
  • Demo now allows for automatically refreshing the rendered image
1 Like

Am I missing something? I tried to create it with a structure of
[x][y] = {
{Color3},
{0}
}
and I get this error
attempt to perform arithmetic (sub) on nil and number - Client - PixOptimize:39

I haven’t touched this in ages, and, its a proof of concept, so I never really released anything proper on it. Probably something to do with your input, check the script at the error location and see I suppose.

1 Like