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

This isn’t the right module for that then, you’d have to make an image sequence, basically take a whole bunch of images from the recaps and stitch them together via code.

1 Like

This module is only helpful for the drawing part for your case. All you have to do is find a way to capture pixel data from your screen (and down scale the resolution) and for every frame, find a way to send every pixel data from your screen as color3 values and then draw every pixel to the canvas.

You may need third party tools to achieve this

1 Like

i have such a Encoder already just i need a good decoder inside roblox
i will have to cache around 50 frames and in background constantly preloading

How can I make a raytracing rendering engine with this? im trying to make realtime reflections…

2 Likes

is it possible to use this to make minimap? something like rorender

3 Likes

This module is criminally underrated, when looking at the title I thought this was one of those basic drawing tools like in drawing games, boy was I wrong, first few hours of figuring out how it works and already got videos of around 20 seconds working (95x190 res with 10 fps) might look into making watchable live streams next (That aren’t laggy) and a tutorial if anyone is interested

5 Likes

Sort of. But you would just have to project the whole world into the canvas but your probably better off using ViewportFrames for this case

3 Likes

You’d have to do your own math, I have a topic on that contains a link to some raytracing stuff the you could prob start from there

2 Likes

its already done lmao, All i need is to blur the pixels, something thats been a challenge

Heck even more of a challenge compared to adding shadows. I plan on doing Box kernel Blur, but im not sure if thats a good option.

Btw, I used GradientCanvas for this reflections project, thanks to that, there are less guis in use.

1 Like

Hi there! I gotta ask… may I take use this module, scan the Image functions so that i can write a similar system into my own upcoming module, but this time with some optimizations?

I’ve learnt a bunch of things that might help with the optimization part, Especially reducing the frame/instance count and etc.

1 Like

Yeah sure. But please do credit me somewhere in your module (a comment will be fine) for the image functions as I put a tone of effort into them and making them run well

3 Likes

Optimisation Update - v2.5.0

This update brings some very needed optimisations and new algorithms which will perform much better than before giving your pixel, line, or even polygon based games real-time results at high resolutions!


General Optimisations

  • Most of the drawing and pixel-based functions have been optimised or even rewritten completely. CanvasDraw now runs so well that I am running out of things to optimise.
    Here’s a list of functions that have be optimised/rewritten:
    • DrawTriangle()
    • DrawLine()
    • DrawRectangle()
    • DrawCircle()
    • DrawImage()

Lines

  • The DrawLine() function has been completely rewritten and now uses Bresenham’s Line Algorithm to draw lines (instead of my poor attempt at an algorithm)

  • There is also a new DrawLineXY() function which behaves similarly to DrawLine(), but takes a seperate X and Y coordinates to declare the line points.
    This function also runs much better than DrawLine() as it avoids general checks, rounding, table manipulation and vector constructing. This is a much more general, fast and raw method to drawing lines.

Triangles

  • The DrawTriangle() function has been completely rewritten and now uses a standard triangle algorithm which is extremely fast and efficient, compared to my old one which was a poor scan-line triangle algorithm.

  • There is also a new DrawTriangleXY() function which behaves similarly to DrawTriangle(), but takes seperate X and Y coordinates to declare the triangle points.
    This function also runs much better than the normal DrawTriangle() as it avoids general checks, rounding, table manipulation and vector constructing. This is a much more general, fast and raw method to drawing triangles.

  • This new triangle algorithm is so fast that I managed to get 60 FPS on my custom 3D Object Rasteriser rendering a cube at 200x200 with the DrawTriangleXY() function!


If you would like to see all the new features, then I recommend checking the API for this module

12 Likes

Small Optimisation Update - v2.5.1

This is a small update which heavily improves the performance of GetPixel() and GetPixelXY() functions.

The CanvasPixels property has also been removed as it is not that useful and has been replaced by the other pixel fetch methods

5 Likes

Hi there, um the image import just sometimes fails to import images, i noticed that when you try to import multiple frames, it will error on frames that are dark.

Okay so, i tried importing an animation that has multiple fade in and fade out effects. Then i tried to import, only to go through some errors. I realized that the module will struggle agaisnt frames that have a dark lighting or are completely black.

2 Likes

Oh really? Thats odd. Do you mind giving the png images that causes the error? I will fix the issues asap

lemme find a way to send you the folder, it has 300 pngs to upload, this one is prone tto the same error.

alright this link will lead you to a website called WeTransfer, it will allow u to download all of the png files:

I have made a temporary fix. There for some reason appears to be something wrong with the actual pixel data in the PNG file.

You should be able to import the images now though without any issues. If something does happen with other PNG images, please let me know.


Also, I have actually released a small secret update to the CanvasDraw module which heavily optimises the DrawImage() and DrawImageXY() function. Now your videos should be able to run much better at higher resolutions.

TIP: For maximum performance, use DrawImageXY for each frame in your video/GIF


Enjoy!


A nice crispy 30 FPS at 256x157

Could actually get a much higher framerate if i had something better than Boatbomber’s GreedyCanvas module to draw the pixels

4 Likes

Hi, I have a question about your 3D render engine. I’ve watched the youtube video that you have linked and I got to the end of the tutorial. The cube does show up on the CanvasDraw screen but the cube is squashed. I’m not sure if you actually based your 3D render engine code on the code of the video that you linked but if you did then maybe you know what I did wrong. Here’s my code and explorer:

--!strict

local ScreenWidth : number = 256
local ScreenHeight : number = 240 --// In tutorial 240

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

local Screen = script.Parent
local CanvasFrame = Screen.CanvasFrame

local CanvasDraw = require(Screen.CanvasDraw)
local VectorModule = require(Screen.VectorModule)

local RunService = game:GetService("RunService")

type vec3d = {["X"] : number, ["Y"] : number, ["Z"] : number, ["Magnitude"] : number}
type pArray = {[number] : vec3d}
type triangle = {["p"] : pArray}

type triangleArray = {[number?] : triangle}
type mesh = {["tris"] : triangleArray}

type matRow = {[number] : number}
type mat4x4 = {[number] : matRow}

--// Construct
local function Construct_vec3d(...) : vec3d
	if #{...} == 0 then return VectorModule.zero end
	return VectorModule.new(unpack{...})
end

local function Construct_triangle(...) : triangle
	local Params = {...}
	if #Params == 0 then return {p = {Construct_vec3d(), Construct_vec3d(), Construct_vec3d()}} end
	return {p = {Params[1] or Construct_vec3d(), Params[2] or Construct_vec3d(), Params[3] or Construct_vec3d()}}
end

local function Construct_mesh(...) : mesh
	local Params = {...}
	if #Params == 0 then return {tris = {}} end
	return {tris = Params}
end

local function Construct_mat4x4(x : number?) : mat4x4
	--// x is a default
	local d = 0
	if type(x) == "number" then d = x end
	local NewMatrix = {
		{d, d, d, d};	
		{d, d, d, d};	
		{d, d, d, d};	
		{d, d, d, d};	
	}
	return NewMatrix
end

--// Methods
local function MultiplyMatrixVector(i : vec3d, m : mat4x4) : vec3d
	local o : vec3d = Construct_vec3d()
	
	o.X = i.X * m[1][1] + i.Y * m[2][1] + i.Z * m[3][1] + m[4][1]
	o.Y = i.X * m[1][2] + i.Y * m[2][2] + i.Z * m[3][1] + m[4][2]
	o.Z = i.X * m[1][3] + i.Y * m[2][3] + i.Z * m[3][3] + m[4][3]
	local w : number = i.X * m[1][4] + i.Y * m[2][4] + i.Z * m[3][4] + m[4][4]
	
	if w ~= 0 then
		o.X /= w
		o.Y /= w
		o.Z /= w
	end
	
	return o
end

local function CopyTable(Target : any) : any
	local Result : any = {}
	for Key, Value in Target do
		Result[Key] = Value
	end
	return Result
end

local meshCube = Construct_mesh()
local matProj : mat4x4 = Construct_mat4x4()

local function Initialize() : nil --// main
	local Resolution = Vector2.new(ScreenWidth, ScreenHeight)
	CanvasDraw.CreateCanvas(CanvasFrame, Resolution, Color3.fromRGB(0,0,0), false)
	meshCube.tris = {
		--// South
		Construct_triangle(Construct_vec3d(0, 0, 0), Construct_vec3d(0, 1, 0), Construct_vec3d(1, 1, 0));
		Construct_triangle(Construct_vec3d(0, 0, 0), Construct_vec3d(1, 1, 0), Construct_vec3d(1, 0, 0));

		--// East
		Construct_triangle(Construct_vec3d(1, 0, 0), Construct_vec3d(1, 1, 0), Construct_vec3d(1, 1, 1));
		Construct_triangle(Construct_vec3d(1, 0, 0), Construct_vec3d(1, 1, 1), Construct_vec3d(1, 0, 1));

		--// North
		Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(1, 1, 1), Construct_vec3d(0, 1, 1));
		Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 1, 1), Construct_vec3d(0, 0, 1));

		--// West
		Construct_triangle(Construct_vec3d(0, 0, 1), Construct_vec3d(0, 1, 1), Construct_vec3d(0, 1, 0));
		Construct_triangle(Construct_vec3d(0, 0, 1), Construct_vec3d(0, 1, 0), Construct_vec3d(0, 0, 0));

		--// Top
		Construct_triangle(Construct_vec3d(0, 1, 0), Construct_vec3d(0, 1, 1), Construct_vec3d(1, 1, 1));
		Construct_triangle(Construct_vec3d(0, 1, 0), Construct_vec3d(1, 1, 1), Construct_vec3d(1, 1, 0));

		--// Bottom
		Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 0, 1), Construct_vec3d(0, 0, 0));
		Construct_triangle(Construct_vec3d(1, 0, 1), Construct_vec3d(0, 0, 0), Construct_vec3d(1, 0, 0));
	}
	return
end

--// OnUserCreate
Initialize()
local fNear = 0.1
local fFar = 1000
local fFov = 90
local fAspectRartio = ScreenHeight / ScreenWidth
local fFovRad = 1 / math.tan(math.rad(fFov * 0.5))

local fTheta = 0

matProj[1][1] = fAspectRartio * fFovRad
matProj[2][2] = fFovRad
matProj[3][3] = fFar / (fFar - fNear)
matProj[4][3] = (-fFar * fNear) / (fFar - fNear)
matProj[3][4] = 1
matProj[4][4] = 0

RunService.RenderStepped:Connect(function() --// OnUserUpdate
	CanvasDraw.ClearCanvas()
	
	local matRotZ : mat4x4, matRotX : mat4x4 = Construct_mat4x4(), Construct_mat4x4()
	fTheta += 0.01
	
	--// Rotation Z
	matRotZ[1][1] = math.cos(fTheta)
	matRotZ[1][2] = math.sin(fTheta)
	matRotZ[2][1] = -math.sin(fTheta)
	matRotZ[2][2] = math.cos(fTheta)
	matRotZ[3][3] = 1
	matRotZ[4][4] = 1
	
	--// Rotation X
	matRotX[1][1] = 1
	matRotX[2][2] = math.cos(fTheta * 0.5)
	matRotX[2][3] = math.sin(fTheta * 0.5)
	matRotX[3][2] = -math.sin(fTheta * 0.5)
	matRotX[3][3] = math.cos(fTheta * 0.5)
	matRotX[4][4] = 1
	
	--// Draw Triangles
	for _, tri : triangle in meshCube.tris do
		local triProjected : triangle = Construct_triangle()
		local triRotatedZ : triangle = Construct_triangle()
		local triRotatedZX : triangle = Construct_triangle()
		
		triRotatedZ.p[1] = MultiplyMatrixVector(tri.p[1], matRotZ)
		triRotatedZ.p[2] = MultiplyMatrixVector(tri.p[2], matRotZ)
		triRotatedZ.p[3] = MultiplyMatrixVector(tri.p[3], matRotZ)
		
		triRotatedZX.p[1] = MultiplyMatrixVector(triRotatedZ.p[1], matRotX)
		triRotatedZX.p[2] = MultiplyMatrixVector(triRotatedZ.p[2], matRotX)
		triRotatedZX.p[3] = MultiplyMatrixVector(triRotatedZ.p[3], matRotX)
		
		local triTranslated : triangle = Construct_triangle(
			Construct_vec3d(triRotatedZX.p[1].X, triRotatedZX.p[1].Y, triRotatedZX.p[1].Z),
			Construct_vec3d(triRotatedZX.p[2].X, triRotatedZX.p[2].Y, triRotatedZX.p[2].Z), 
			Construct_vec3d(triRotatedZX.p[3].X, triRotatedZX.p[3].Y, triRotatedZX.p[3].Z)
		)
		
		triTranslated.p[1].Z = triRotatedZX.p[1].Z + 3
		triTranslated.p[2].Z = triRotatedZX.p[2].Z + 3
		triTranslated.p[3].Z = triRotatedZX.p[3].Z + 3
		
		triProjected.p[1] = MultiplyMatrixVector(triTranslated.p[1], matProj)
		triProjected.p[2] = MultiplyMatrixVector(triTranslated.p[2], matProj)
		triProjected.p[3] = MultiplyMatrixVector(triTranslated.p[3], matProj)
		
		--// Scale into view
		triProjected.p[1].X += 1; triProjected.p[1].Y += 1
		triProjected.p[2].X += 1; triProjected.p[2].Y += 1
		triProjected.p[3].X += 1; triProjected.p[3].Y += 1
		
		triProjected.p[1].X *= 0.5 * ScreenWidth; triProjected.p[1].Y *= 0.5 * ScreenHeight
		triProjected.p[2].X *= 0.5 * ScreenWidth; triProjected.p[2].Y *= 0.5 * ScreenHeight
		triProjected.p[3].X *= 0.5 * ScreenWidth; triProjected.p[3].Y *= 0.5 * ScreenHeight
		
		CanvasDraw.DrawTriangle(Vector2.new(triProjected.p[1].X , triProjected.p[1].Y ), -- You have to shift everything by 1 on both axes because the canvas ...
			-- actually starts on 1, 1 and not 0, 0 :(
			Vector2.new(triProjected.p[2].X , triProjected.p[2].Y ),
			Vector2.new(triProjected.p[3].X , triProjected.p[3].Y ),
			Color3.fromRGB(255,255,255), false
		)
	end
end)

Beautiful video of a squashed cube:

2 Likes

Yeah the issue isn’t related to CanvasDraw itself. But judging by your code and the video, it seems mostly fine, all though I think there might be something wrong with your projection or matrix translations, because it only squashes when it rotates a certain amount.

Check your translations and maybe double check with the video (I had to rewatch the tutorial a good 4 times before I made my 3D rasteriser).

If you have any more questions, please do ask. I am more than happy to help!

1 Like

I think I found the issue. One of the triangles (the first bottom triangle) actually has a Y position of 1 and that’s obviously wrong. I must have copied the values over incorrectly. :sweat_smile: Ok so after fixing it the cube has lost it’s height. :confused: This is so annoying to deal with. :laughing:

1 Like