RetroRaster | The Real-time Raytracing Solution for Pixelating Roblox!

Nice job! looks amazing! What are you planning to use for it? I’m pretty curious.

5 Likes

At the moment, im not planning to use it at all. I just plan to get this to look good and run at 60 FPS.

Depending on how this turns out, I might even open source this for others

12 Likes

Nice. thats a good item. you should sell it if you do it well.

7 Likes

Have you multithreaded it? If you do, and haven’t already, it will give you even more performance.

5 Likes

I have no idea how to multithread, or how to do it effectively

6 Likes

Well essentially if you have four cores, you’d 2-4x performance (not all cores are equal), roughly, depends on factors. Often times we have more.

So it would be worth it.

But essentially in roblox, we use an actor system. What you want to do is use multiple actors to raycast, so if you had 4 cores, divide the screen into 4 sections, have each actor raycast for that section only, in desynchronized mode, or, do every Nth pixel. Etc. Best to do a lot of actors tho so it works good on computers with a lot of cores.

Btw, what you’re doing is called raytracing, not pixelating, if you were not aware, some other people myself included have also messed around with this before so there should be some resources you can find on the devforum here that are either uncopylocked or explain how to do multithreaded raytracing.

Here is a staff post (warning, old, but it is open source) from when parallel luau came out in 2020.

The biggest performance improvement possible is multithreading.

There are some other techniques that can optimize it (btw i like your idea of using interlacing and pixel sleeping, that was smart!) however multithreading is the holy grail here.

8 Likes

I did some research, and im currently trying to implement it, and its the most painful thing i’ve ever had to do. Im having so many issues. And why do I have to do it across multiple scripts and actors?

This might take awhile…

5 Likes

OMG I FIGURED IT OUT!

It might have taken over a year of me actually getting any kind of multithreading working for any of my pixel-based projects, but thanks to this documentation featuring cloning one script across actors to recycle code with multi-threading, I DID IT!

We now have multi-threading on this bad boy!


Here are some tests at 200x200

BEFORE MULTI-THREADING:

Approximately 13 FPS

AFTER MULTI-THREADING (10 actors):

Approximately 31 FPS


That’s an insanely large performance boost. That’s about 2.3x faster!!

11 Likes

Omg this is amazing, I would LOVE to buy this! Why? Trust me, the stuff people would DO with this is amazing. I love making horror games and this would def be awesome! I am unsure how to help you, but keep at it! It looks amazing! Can’t wait to see the finish product!

6 Likes

I think it can be faster. How does your code look like? I am not asking for the Lua code but more of how do you actually send information between actors and things like that.

4 Likes

I can make lego island with this

4 Likes

The first video looks way better because yours has those weird visual bugs. You should probably fix that before you use it / sell it

5 Likes

The multithreading part was half yoinked from this on the roblox documentation.

Here’s their code:

-- Parallel execution requires the use of actors
-- This script clones itself; the original initiates the process, while the clones act as workers

local actor = script:GetActor()
if actor == nil then
	local workers = {}
	for i = 1, 32 do
		local actor = Instance.new("Actor")
		script:Clone().Parent = actor
		table.insert(workers, actor)
	end
	
	-- Parent all actors under self
	for _, actor in workers do
		actor.Parent = script
	end
	
	-- Instruct the actors to generate terrain by sending messages
	-- In this example, actors are chosen randomly
	task.defer(function()
		local rand = Random.new()
		local seed = rand:NextNumber()
		
		local sz = 10
		for x = -sz, sz do
			for y = -sz, sz do
				for z = -sz, sz do
					workers[rand:NextInteger(1, #workers)]:SendMessage("GenerateChunk", x, y, z, seed)
				end
			end
		end
	end)
	
	-- Exit from the original script; the rest of the code runs in each actor
	return
end

function makeNdArray(numDim, size, elemValue)
	if numDim == 0 then
		return elemValue
	end
	local result = {}
	for i = 1, size do
		result[i] = makeNdArray(numDim - 1, size, elemValue)
	end
	return result
end

function generateVoxelsWithSeed(xd, yd, zd, seed)
	local matEnums = {Enum.Material.CrackedLava, Enum.Material.Basalt, Enum.Material.Asphalt}
	local materials = makeNdArray(3, 4, Enum.Material.CrackedLava)
	local occupancy = makeNdArray(3, 4, 1)
	
	local rand = Random.new()
	
	for x = 0, 3 do
		for y = 0, 3 do
			for z = 0, 3 do
				occupancy[x + 1][y + 1][z + 1] = math.noise(xd + 0.25 * x, yd + 0.25 * y, zd + 0.25 * z)
				materials[x + 1][y + 1][z + 1] = matEnums[rand:NextInteger(1, #matEnums)]
			end
		end
	end
	
	return {materials = materials, occupancy = occupancy}
end

-- Bind the callback to be called in parallel execution context
actor:BindToMessageParallel("GenerateChunk", function(x, y, z, seed)
	local voxels = generateVoxelsWithSeed(x, y, z, seed)
	local corner = Vector3.new(x * 16, y * 16, z * 16)
	
	-- Currently, WriteVoxels() must be called in the serial phase
	task.synchronize()
	workspace.Terrain:WriteVoxels(
		Region3.new(corner, corner + Vector3.new(16, 16, 16)),
		4,
		voxels.materials,
		voxels.occupancy
	)
end)

I basically used the exact same concept. I have 10 actors which each render a 10th of the screen (for a 100x100 resolution, that’s 100x10 each)

As for what code is contained in the parralel execution, that’s the main raytracing, pixel and colour management, and then a bindable event is fired for every pixel back to the core script to draw the pixel to the canvas

5 Likes

Those artefects/visuals are part of the reason why the FPS is so high. I’m only doing about 30% the calculations compared to the previous video

4 Likes

I think you should avoid sending many data. Can you change the pixels after the parallel phase without the need to send the data to Main script?

4 Likes

Could buffer serialization help at all?

4 Likes

Unfortunately not. I have found that this is the best method. And also, not using the bindable event at all and not even setting the pixel hardly changes FPS at all. It’s like a 1% difference, so that part cant be improved much.

4 Likes

Next up… Textures!

6 Likes

Buffer serialisation is slower than just reading/writing to an array as far as I know

5 Likes

It’s incredible. I wanted to try making pixel games, but after reading this, I got some hope. Are you going to sell them or are you going to distribute them for free?
I’m Korean and I used a translator, so it might be hard to understand. Sorry. If there’s anything you don’t understand, please let me know. Thank you

3 Likes