ASCII visualization shader optimization issues

I’m currently coding a ‘shader’ for visualizing with ASCII characters, however it runs extremely poorly and I wanted a second opinion on what might be possible to implement

Here is the entire code line for anyone who wants it, I’ll release it more officially if I ever figure out this optimization crisis

local run_service = game:GetService("RunService")

local template = script.Template

local value_table = string.split("@&%QWNM0gB$#DR8mHXKAUbGOpV4d9h6PkqwSE2]ayjxY5Zoen[ult13If}C{iF|(7J)vTLs?z/*cr!+<>;=^,_:'-.` ","")

local screen_gui = script.Parent
local frame = screen_gui.Frame
local ui_grid = frame.UIGridLayout

local viewport = frame.AbsoluteSize

local pixel_size_x = ui_grid.CellSize.X.Offset
local pixel_size_y = ui_grid.CellSize.Y.Offset
local resolution = Vector2.new(viewport.X/pixel_size_x,viewport.Y/pixel_size_y)

local wait_time = 0

local create_pixels = function(resolution)
	for i = 1,resolution.y do
		for v = 1,resolution.x do
			local pixel = template:Clone()
			pixel.Parent = frame
			pixel.Name = "row "..i.." pixel "..v
			pixel.LayoutOrder = v + ((i-1)*resolution.x)
		end
	end
	return "finished"
end

local clear_pixels = function()
	for i,child in pairs(frame:GetChildren()) do
		if child:IsA("TextLabel") then
			game.Debris:AddItem(child, 0)
		end
	end
end

print(create_pixels(resolution))
task.wait(2)

workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
	local new_resolution = Vector2.new(viewport.X/pixel_size_x,viewport.Y/pixel_size_y)
	
	clear_pixels()
	create_pixels(new_resolution)
end)

local get_ray_from_pixel = function(pixel)
	local ray = workspace.CurrentCamera:ScreenPointToRay(pixel.AbsolutePosition.X, pixel.AbsolutePosition.Y, 0)
	local result = workspace:Raycast(ray.Origin, ray.Direction*#value_table)
	
	if result then
		return math.floor(result.Distance), result.Instance
	else
		return #value_table
	end
end

local translate_distance_to_ascii = function(distance)
	if value_table[distance] ~= nil then
		return value_table[distance]
	else
		return value_table[#value_table]
	end
end

local t = tick()
run_service.RenderStepped:Connect(function()
	if (tick() - t) >= (1/20) then
		t = tick()
		
		for i,pixel in pairs(frame:GetChildren()) do
			if pixel:IsA("TextLabel") then
				local distance, instance = get_ray_from_pixel(pixel)
				local new_value = translate_distance_to_ascii(distance)
				pixel.Text = new_value
				
				local intensity = 1 - (distance / #value_table)
				intensity = math.clamp(intensity, 0, 1)
				
				if instance ~= nil then
					if instance.Parent:FindFirstChild("Humanoid") or instance.Parent:IsA("Accessory") then
						pixel.TextColor3 = Color3.new(intensity, 0, 0)
					else
						pixel.TextColor3 = Color3.new(intensity, intensity, intensity)
					end
				else
					pixel.TextColor3 = Color3.new(intensity, intensity, intensity)
				end
			end
		end
	end
end)

Preview


2 Likes

Use a single textlabel only, use inconsolata font so all characters will have same width, enable RichText property to put specific color for each character

1 Like

If you’re not sure where to start looking to optimise your code, use debug.profilebegin(“PutAnyLabelNameHere”) and debug.profileend() around particular areas of code. That gives you a way of measuring its performance via Roblox’s microprofiler, of which then you can take more incremental steps in whittling down the time taken to process.

2 Likes

This looks highly parallelizable.

A way I would implement this is by breaking the screen into multiple text label segments that support richtext(so each pixel can have a different color). That way you don’t need to load different text labels for each specific pixel but rather a label for each “collection” of pixels. A collection can be a few lines of pixels, for example three lines of pixels(you need to ensure the text label length limit wont be hit, I think richtext matters in this, so you can optimize how you represent richtext properties as well).

After breaking the screen into collections what I would do is take advantage of actors and give each actor a specific amount of text labels to process/render. This allows you to utilize more of your CPU cores to increase overall processing speed(by making each core focus on different segments of the screen).

Lastly you don’t need to constantly clear and recreate the pixels/segments of the screen. Instead you can create them once, then edit their values on each frame. Instancing tends to be heavy so its better to avoid doing it often.

2 Likes

ascii-text.rbxl

1 Like

i have no idea how to implement this, maybe one day ill improve on it but for now im just using 1 text label, it works fine for my pretty low-end device

this worked and improved it quite a bit its now way less laggy and could maybe even be usable for something, ill keep working on it but thank you very much