Hm… Real. String concatenation is 5.3 times slower than working with ColorSequence.
100k pixels test
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!
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
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
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).
Have you yet attempted to add parallel support for this module? it could be rlly cool for fast drawing images.
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.
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?
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
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
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.