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

Hm… Real. String concatenation is 5.3 times slower than working with ColorSequence.
image
100k pixels test

1 Like

Our only hope is a new UI instance that can hold more colours, or UIGradients get their cap of 20 colours lifted, OR someone discovers a new way to display and change pixels incredibly fast in a GUI

Option 2 seems the best option here. But something to make colorgradient optimized too.

As soon as I saw this module, I immediately fell in love with it!

I got some questions.

1. When i create a new canvas, i don’t have the autocompletion in code, how do i know what i should type?
2. I want to make 2D physics for canvas, how do i do it? I didn’t find any tutorial

Thanks! :slightly_smiling_face:

1 Like

well, for some reason, roblox just doesn’t apply the autocomplete thing in the script editor ever since I made this system object oriented. I have no idea why it doesn’t work.

to know what to type, you should open up the module itself to see the functions. It has a mini look-up API as large comments with descriptions and all.

I’ll try to fix this as soon as possible.

well, you should actually find tutorials on how to do this outside of roblox, as roblox has really never had a proper pixel canvas before. However, I found this tutorial on how to program sphere collisions with a pixel canvas.

It’s really good. Highly recommend checking this guy’s YouTube channel as he does tons of tutorials on custom game engines through a pixel canvas.

Module Bug Fix - v3.1.2

I have finally fixed the script editor autocompletion bug for the canvas class, and now you can see all the functions, properties and methods of the canvas in your code without having to go to the API all the time


@ABadScripter15 there you go

1 Like

Judging by the code in your video, you are still working with pixels, which means you dont need frames. Just use Canvas:SetPixel(X, Y, Colour) instead of PixelFrame.BackgroundColor.

local Part = workspace.Screen
local GuiFrame = Part.SurfaceGui.Frame

local CanvasDraw = require(script.CanvasDraw)

local ResX = 128
local ResY = 100

local Canvas = CanvasDraw.new(GuiFrame, Vector2.new(ResX, ResY))

-- Update/draw pixels
while true do
    for X = 1, ResX do
        for Y = 1, ResY do
            local RequestedColour = -- Request pixel colour through http services or something
            Canvas:SetPixel(X, Y, RequestedColour)
        end
    end
    task.wait(1/30) -- 30 FPS
end

I decided to use that method, it worked better because I didn’t have to compress the colours.

From what I can see here, you aren’t using CanvasDraw at all, and I also have no idea how to use HTTP services, so I can’t help at all with this.

well, i tested the code just then, and it works just fine

here’s a physical rbx place file of that test place that uses the module:
CanvasDraw Example.rbxl (59.3 KB)

Heya there!

As a fun little sideproject, I deided I want to touch into the suject of pixel rendering. I’m stuck on one thing though… how do you even do a raycast from a pixel?

I’ve been trying for a few hours now, but to no avail. The only thing I have is my “attempt” at doing so.

local SG = game:GetService("StarterGui")
local CD = require(script:WaitForChild("CanvasDraw"))
local FC = require(script.CanvasDraw:WaitForChild("FastCanvas"))

--@ config
local canvasSize = {20, 20}

local gui = script.Parent.Parent
local frame = gui:WaitForChild("Frame")
local canvas = CD.new(frame, Vector2.new(canvasSize[1], canvasSize[2]), Color3.new())
local camera = workspace.CurrentCamera

SG:SetCoreGuiEnabled(Enum.CoreGuiType.All, false)

while true do
	task.desynchronize()
	
	for y = 1, canvasSize[1] do
		for x = 1, canvasSize[2] do
			local ray = camera:ScreenPointToRay(x, y, .5) --@ aaaaahghhhhh help
			local hit = workspace:Raycast(ray.Origin, ray.Direction.Unit * 1000)

			if hit and hit.Instance then
				canvas:SetPixel(x, y, hit.Instance.Color)
			end
		end
	end
	
	task.synchronize()
	
	warn("test")
	task.wait(2)
end

Hey. Raycasting from a pixel is pretty easy once you understand how you use normalized coordinates.

what you are doing wrong here, is you are sampling colours from a range between a small pixel range that doesn’t work with the ScreenPointToRay function.

You are providing a much smaller resolution, which is from your canvas, rather than providing a screen coordinate which is a huge difference. e.g. (1920 × 1080) rather than (20 x 20)

All though this is a super easy fix on your end:

local ViewportSize = Gui.AbsoluteSize -- Screen size in pixels (1920 × 1080)

local Resolution = Vector2.new(20, 20) -- Canvas resolution

for X = 1, Resolution.X do
	for Y = 1, Resolution.Y do
		
		-- Normalise our screen points 
		--(so our coordinates range from a percentage)
		-- from 0 to 1
		local PointX = (X / Resolution.X)
		local PointY = (X / Resolution.Y)
		
		-- Get screen pixel point (from monitor)
		local ScreenX = PointX * ViewportSize.X
		local ScreenY = PointY * ViewportSize.Y
		
		-- Raycast to screen point
		local ScreenRay = Camera:ViewportPointToRay(ScreenX, ScreenY, 1)
		local RayOrigin = ScreenRay.Origin
		local RayDirection = ScreenRay.Direction

		local RaycastResult = workspace:Raycast(RayOrigin, RayDirection * RenderDistance)
		
		-- Set pixel colour
		if RaycastResult then
			local Colour = PrimaryRaycast.Instance.Color
			Canvas:SetPixel(X, Y, Colour)
		end
		
	end
end

EDIT: fixed up example code

1 Like

Thanks to this, and just a bit more research, I managed to pull off a realtime pixel renderer that has shadows! Plus, performance is pretty decent. Very neat.

Here’s the game for those who want to play it (it’s uncopylocked too).

11 Likes

Have you yet attempted to add parallel support for this module? it could be rlly cool for fast drawing images.

1 Like

made a 2.5 game this stuff is really cool!

5 Likes

I have, and quite a few times too. even with other modules. Problem is, every time I apply parallel lua to my scripts, i don’t ever seem to get a boost in performance. I could be missing something every time I try apply it to my games, or my computer simply doesn’t work with parallel lua for some odd reason.

So for now, parallel lua or multi-threading will not be used unfortunately.

2 Likes

I plan on using this for my cinema project that loads videos from nodejs, and I plan on updating a few hundred/thousand pixels per frame, however the documentation says this:

"

Canvas:SetPixel (X: number, Y: number, Colour: Color3)

  • This 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.
    "

So does this mean it will work badly when updating a max of 65536 (256 * 256) pixels per frame?

1 Like

Oh no, its saying that SetPixel is a much more preferred method for high resolution projects. DrawPixel is much slower than SetPixel. I may have to update the documentation there

1 Like

Thanks, this module is really usefull, and with some testing it seems like its way faster than just creating 120x80 frames and chaning the background color every frame

1 Like

how would i go with adjusting the transparency for each pixel?

As of this major version, changing pixel transparency isn’t possible atm.

Another thing too is that this makes use of UIGradients for pixels, and i really haven’t even tried messing around with making them transparent yet.

I might make a prototype version and see how performance might go for it.