CanvasDraw - A powerful pixel-based graphics library (Draw pixels, lines, triangles, read/modify image data, and much more!)

Well, all i had to do was read some .OBJ file data, and then store the vertices into an array, and from there you can project the vertices onto your screen just like any other 3D engine. As for drawing the polygons, I just used the DrawTriangle() function.

Just be thankful that .OBJ files are in a human friendly readable format.

I learnt all this from a small tutorial series I found on youtube. The videos are super easy to follow and understand. Highly recommend checking them out:

21 Likes

Literally watched that video a while back xd, great channel!

7 Likes

Small Update - v2.1.0

This update consists of a few new features, bug fixes, and some small changes to this thread page. The API reference for this update also now has a bunch of example codes. So hopefully it should it make easier to learn functions and such from the module. So let’s now start form the top with all the changes to the module:

Fixed a horrible precision bug for canvases with non-square aspect ratios

  • This bug was an issue as all pixels were stored in a table and relied on converting points to the index for said pixel. Turns out that the math behind was incorrect and causes pixels to end up being draw in the wrong place on the canvas. Thankfully that is now fixed!

Added a simple event to CanvasDraw: CanvasDraw.Heartbeat

  • This event is exactly the same as RunService.Heartbeat. This event was added as there are so many situations where I use RunService once and it’s only for the heartbeat event so I can manually draw and then update the canvas afterwards. This event just makes simple code even simpler and more tidy.
    CanvasDraw also relies on the Heartbeat event for updating the canvas every frame when CanvasDraw.AutoUpdate is set to true
CanvasDraw.Heartbeat:Connect(SomeFunction)

Added a Fill parameter to the functions; DrawRectange, DrawCircle, and DrawTriangle

  • There are many situations where you want an empty shape that isn’t filled. Well this update gives all these functions a new parameter that lets you choose if you wish to fill your shapes with colour or not.
  • If the parameter is left empty, then filling will be enabled by default.
    Also having this parameter set to false saves heaps of performance for high resolution canvases and renderers.
CanvasDraw.DrawCircle(
   Vector2.new(25, 25), -- Place the circle on the canvas at 25, 25
   10, -- Give the circle a pixel radius of 10
   Color3.new(1, 0, 0), -- Colour the circle
   false -- Don't fill the circle
)

Added a new drawing function: DrawDistortedImage()

  • This function works similarly to DrawImage(), all though difference being is that there are heaps of parameters that let you customise how the image is drawn to the screen. This function lets you do lots to the image. These new parameters allow for:

    • Skewing
    • Stretching
    • And tinting the image colour
  • This function was intended for use in pseudo 3D renderering or for situations where you may want to upscale or downscale or stretch images.

CanvasDraw.DrawDistortedImage(
    ImageData, -- The image data we want to load
    Vector2.new(50, 50), -- Place the image at point 50, 50
    Vector2.new(2, 2), -- Double the size of the image
    Vector2.new(45, 45), -- Skew the image in a 45 degree angle for both directions.
    true, -- Enable alpha transparency
    Color3.new(1, 0, 0), -- Tint the image red
    0.5 -- Only tint the image red by 50%
)
7 Likes

Sorry for stupid quastion. It’s possible create up and down rotate camera on CanvasDraw?

9 Likes

What do you mean by this?
Do you mean having a camera and rendering a scene according to it’s position and orientation? Because all that’s possible with CanvasDraw.
CanvasDraw is pretty well built for this sort of stuff.

It also depends on how you render your scene and what that scene is. I find it easier to render top-down 2D maps and project them to the screen with pseudo 3D methods.

But you do need some knowledge on this sort of stuff. There heaps of videos and tutorials online that are easy to follow and learn for basic computer graphics by using nothing but a canvas system.

CanvasDraw also has a lot of functions that lets you create scenes much easier.

For example;

  • DrawImage (can be used in a 2D game)
  • DrawTriangle (can be used in 3D rendering)
  • GetPixelFromImage (sampling texture data)
  • And much more!
15 Likes

Small Update - v2.2.0

This update consists of some optimisations to the drawing side of things and some new functions!

New function: SetPixel() - The performant version of DrawPixel()

  • This function behaves similarly to DrawPixel() , all though main difference being is that this a much more performant method for colouring a pixel to the screen as it avoids constructing a vector and avoids checks to the canvas. So this function assumes your X and Y values are already whole numbers and are within the canvas.

  • If you are not doing high-resolution real-time rendering, then the DrawPixel() function will work just fine in most cases, and is in-fact, much more recommended as it it already pretty performant and consists of safe checks to ensure you don’t have problems when colouring a pixel on canvas.

  • This function is only recommended if you are having performance issues from creating lots and lots of pixels with high resolutions, or you have guaranteed coordinates for pixels and checks are never necessary for the canvas.

-- Creates a red pixel at point 25, 25
CanvasDraw.SetPixel(25, 25, Color3.new(1, 0, 0))

New function: GetPointFromMouse()

  • This function allows you get a point on the canvas from the client’s mouse!

  • This functions gives you heaps of possibilities for cool things such as drawing programs!

  • NOTE: This function only works on the client or locally. If you are using this function from the server, it will throw a warning in the console/output.

This function will also only work for GUIs. Currently, this will not work for SurfaceGuis as this function assumes everything is in screen space.

-- Get the mouses point on the canvas and draws a pixel.
local Point = CanvasDraw.GetPointFromMouse()

if Point then
    CanvasDraw.DrawPixel(Point, Color3.new(1, 0, 0))
    print("Red pixel drawn!")
else
    print("The mouse is not the within the canvas!")
end

Optimised some of the pixel functions

Some of the drawing and fetch functions for CanvasDraw has been optimised to further allow maximum performance.

Here’s a list of them of the functions that have been optimised:

  • GetPixel()
  • GetPixels()
  • DrawPixel()
  • DrawImage()
  • DrawDistortedImage()
  • GetPixelFromImage()
6 Likes

You can make it work for surface guis pretty easily using some basic projection math

5 Likes

GetPointFromMouse? SurfaceGuis? Damn i would love to add that, but i have already gotten sick of trying to get the transformations for the mouse coordinates to work, let alone project the thing.

But if you have ideas on how i can do this simply and efficiently, please let me know. Cause i have no clue how to do this

5 Likes

This transforms a 2d position on a surface gui to a 3d point

function guiPointToWorld(gui, point)
	local adornee = assert(gui.Adornee)

	local normalizedPoint = point/gui.AbsoluteSize
	local dir = Vector3.FromNormalId(gui.Face)

	local projY = math.abs(dir.z + dir.x)
	local projZ = dir.x + math.abs(dir.y)

	local origin = Vector3.new(
		dir.x - dir.y - dir.z,
		dir.y + projY,
		dir.z + projZ)*0.5
	
	local surfX = Vector3.new(dir.z, 0, -projZ)*normalizedPoint.x
	local surfY = Vector3.new(dir.y, -projY, 0)*normalizedPoint.y

	local pos = origin + surfX + surfY

	return adornee.CFrame*(adornee.Size*pos)
end

Do that conversion for the 4 corners, a,b,c,d, and using those push 4 more relative to the canvas’s normal. You essentially have a cube now and then from there you just do a point-in-box test for the mouses hit. If it is inside you can then just transform the coordinate space by mapping it from world space to basis vectors AB and AC (look up lookAt matrices). Your point should now be a 2d point with values from 0 to 1

7 Likes

Plugin update

The CanvasDraw Image Importer plugin has been updated. You can now mass import images from your computer. This means you can now select and import more than one image when the plugin prompts images from your computer to create multiple image save objects at once!

This can be very handy in situations where you need to store heaps of images for things such as frames for videos and GIFs


9 Likes

Wait, can we actually scroll through each frame that we mass imported into this plugin?

Can we configure where the imported files go?

3 Likes

Yep. You sure can.

I have managed to easily create GIFs thanks to the mass import update:

Yeah, when you import the images, just select an instance or a folder or something in the explorer of where you want all the SaveObjects to be parented to.

(These save objects uses attributes to store the pixel data of the images)

5 Likes

hol up, wait a minute. Did this dude just made a brand new way to efficiently make a GIF/Video in roblox? Thats just insane, CanvasDraw at this point is the most useful module for rendering 3D models, Images and Videos in roblox to date. Even I am too impressed that i am also very excited to use this module in most of my projects.

But btw, Isnt it better to bypass the rendering problem of too many gui objects by simply using RichText? With as you can actually put in multiple pixel colors in 1 textlabel doing so.

This will probs allow for higher resolutions like 512x512 or even 720x1280.

6 Likes

You can get a lot higher resolution and videos to the point where they last for minutes at a time by using this method + texture atlasing. The real gem is trying to figure out the best way to store them so that you can have them be in datastore.

Over my research the past year I found that a mix of bit packing, base64 encoding, as well as serialization, was the best way to make the storage small for RGB images. There is a better way than RichText, and it is to take advantage of caching. For GUIs you’d just use something like a lookup table for the colors whereas if you wanted to take the parts approach, you layout in your world a pallet of color cubes then change their CFrame on call (by using a CFrame cache). There are problems with both methods, GUIs can only go so high as they are inefficient as you’ll need to create constant UIGradients whereas with parts it is very efficient in terms of CPU usage, but you will use loads of memory with the sheer amount of parts in the cache.

That is why there are basically two methods for images for stuff like art games in Roblox, and because most users want it for an art game, where they can extrapolate it to a surface GUI and still run for mobile users, it is why most people tend to use the UIGradient exploit.

4 Likes

pretty cool stuff, works like black magic :flushed:
here’s osu! (lazer) being streamed at 5-2 fps (slow because of the studio window not being focused):

10 Likes

Gui caching is actually a wonderful idea, but I would love to have a visual example if you don’t mind lol.

1 Like

Amazing! No words, but amazing.

3 Likes

Yep. I sure did. Image processing on this has been around for a month and a bit now, but I honestly don’t think anyone even knows that it existed. half of the capabilities of this module isn’t even know! Maybe my thread on this thing doesn’t say or show enough.

I honestly don’t think it will make that big of a difference in performance since we are still colouring different parts of the ui objects. If anything, and honestly, I believe the rendering process for richtext colours is probably more expensive than just changing frame colours or UI gradients which are probably less expensive in rendering.

I hate to say it, but we will most likely never be getting any real time rendering for any custom canvas with a resolution that high. I would like to point out that the main performance problems are the pixel calculations. Not the actual frames with gradients.

4 Likes

Small Update - v2.3.0

Image SaveObject compression and resolution limit doubled!

This update contains some very impressive features for SaveObjects on CanvasDraw.


Firstly, the resolution limit for image SaveObjects has been increased from 128x128 to 256x256

Wow, now that’s a high quality image for CanvasDraw


Now secondly, the actual method for storing pixel data of images on SaveObjects has been heavily optimised thanks to string compression methods.

Before, SaveObjects used to be able to get as big as 120KB!! Now that’s a really big file size for an instance in your game. But now, SaveObjects have become 80-95% smaller!

Now you can have as many SaveObjects in your place as you want without having to worry about file size!


WARNING:

If you are using SaveObjects in your place that were made before this update, then you will need to delete them and re-import new SaveObjects as the old SaveObjects will not work with CanvasDraw 2.3.0 and above.

You will also need to update the CanvasDraw Image Importer plugin as well.

5 Likes

huh i realized loading a 1 minute video that has a 144p resolution takes a long time to load into roblox for some reason… I wish it was a bit… faster… If i remember correctly, it took longer than 30 minutes to load in the video.

2 Likes