Alright, I’m totally ready to make this open source, and I would love to hear critique. If you can find even a single line that could be causing a fraction of lag please tell me!
In this reply I will be demonstrating the use of each script and documenting how they’re written and perform.
This image depicts everything there is with this, and the Settings
and FPS
parts are entirely optional, you can use them to find the settings you want, then delete them.
The GuiRemover
script just get’s rid of all default roblox UI, this can also be removed if you really want.
The Background
Frame itself really just serves to be a black background, but again this can also be removed and will not affect the pixelation at all. Really you can think of it as a filter on the screen, as long as you still have that Main
Frame, it will function.
Getting into the Main
Frame!
There are two scripts, one is a regular local script, and the other is a module script used for creating and saving your screen settings.
The ModuleScript
‘FrameCreator
’ has the following properties and methods:
Properties: [Property Datatype] FrameCreator.Property (Limits)
-
[Integer] FrameCreator.Resolution (Current Limits: 1 to 200)
The Resolution of the image to be rendered, it is in the form of X by X pixel resolution, X being the number given. It is read only! There is a method to change it, use that!
-
[Number] FrameCreator.RenderDist
The length that each raycast for each pixel will travel before stopping and defaulting to sky. It is read only! There is a method to change it, use that!
-
[Integer] FrameCreator.PaxelSize (Unfortunate Limits: 1 to 10)
The number of pixels that will exist in each paxel, this number is automatically calculated currently, it will set itself to the highest number between it’s limits that is a modulo of the Resolution. It is read only and updates automatically!
-
[Number] FrameCreator.Center
The exact center of the Main
Frame, used to generate the PixelToWorldspace
that provides the direction of the raycast. It is read only and updates automatically!
-
[Boolean] FrameCreator.Materialization
Dictates whether or not to perform the color calculations to generate persistent materials onto the projected parts, Materials are currently performed by a random number generator whos seed is determined by a calculation based on the position and orientation of the part and raycast position. The system is very good, but will take a lot of number playing to make all of the materials look good. This property is Read and Write and will update as soon as it’s changed without needing to recreate the frame.
-
[Boolean] FrameCreator.Shading
Dictates whether or not to calculate the proper shading for each pixel based on it’s cross product of the angle of the normal and the direction toward the sun, it takes close to no additional processing power, and adds a lot of detail. This property is Read and Write and will update as soon as it’s changed without needing to recreate the frame.
-
[Boolean] FrameCreator.Shadows
Dictates whether or not to cast an additional ray for each pixel that hits a part to test if it is blocking the sun, in which case a shadow will be drawn by making that pixel darker. This takes a considerable amount of processing power, but less than the power it takes to calculate each ray regardless, I’m unsure why. This property is Read and Write and will update as soon as it’s changed without needing to recreate the frame.
-
[Boolean] FrameCreator.LineByLine
Dictates whether or not to partition the pixel calculations between Stepped
loop intervals. It will, instead of calculating each frame and then drawing it all at once, calculate 10 lines of the frame 10 I have found to be the best number in this case for performance at a time and then display them in order until reaching the bottom and restarting at the top. This allows for higher Game FPS at the expense of the FPS of the Main
Frame. In short, it will allow you to not crash regardless of what you make the settings. This property is Read and Write and will update as soon as it’s changed without needing to recreate the frame.
-
[Boolean] FrameCreator.Skybox
Dictates whether or not to calculate a gradient color to represent the sky when a raycast did not hit a part, similarly to Shading, it takes very little processing power, and adds good detail. I am considering also making the sun visible, and maybe clouds as well. This property is Read and Write and will update as soon as it’s changed without needing to recreate the frame.
Methods: [Return Datatype] FrameCreator.MethodName([Parameter Datatype])
-
[Table of ColorSequenceKeypoint] FrameCreator.CreateKeyPoints(Table of Color3)
When given a table of 1 to 10 Color3 values it will return a table of ColorSequenceKeypoint values that will allow you to create a ColorSequence value of distinct sharp pixels, hard edges and no gradient. It’s used to calculate the color for each paxel.
-
[Sync Void] FrameCreator.SetResolution([Integer]Res, [Number]Render)
When given a resolution or Res, as well as an optional render distance or Render, it will set the Resolution and RenderDist accordingly as well as recalculate the PaxelSize and Center and then it will delete the current frame and re create it with our new settings. This method is also how a first frame is initialized. Additionally, it’s synchronous; I did this so that you could be certain the code beneath calling this method won’t run until the frame is ready.
-
[BaseGui Frame] FrameCreator.GetPaxel([Integer]X, [Integer]Y)
When given an X in the paxel space, not pixel, and a Y assuming these numbers are within the bounds of the frame it will return the BaseGui Frame Object of that paxel, this can then be used later to set each pixel color.
-
[Async Void] FrameCreator.SetPaxelColors([BaseGui Frame]Paxel, [ColorSequence]ColorSeq)
When given a paxel or Frame in the form of a BaseGui Frame Object obtained via the GetPaxel() method as well as a ColorSequence or ColorSeq obtained via the CreateKeyPoints() method this method will set the color of the paxle’s UIGradient object to ColorSeq. This is used to set the color of each paxel and each pixel individually. It is not synchronous, code will not wait for it to finish to continue.
Alright! That pretty much covers everything there is worth knowing about the FrameCreator module.
The only thing left to go over is the RayCaster script which does all of the heavy lifting and calculations for each pixel on the screen as well as handles what to do with all of the settings given in FrameCreator.
Really it’s not as formatted as FrameCreator is and frankly not worth me going over is as much detail, but there are some major point I want to explain in case people are confused.
The Material Persistence Calculations:
local MaterialColorOffset = {
{Enum.Material.Brick, -10, 0, 10, nil, function(Position)
return Position.X * Position.Y * Position.Z
end},
{Enum.Material.Grass, -10, 0, 0.01, nil, function(Position)
return Position.X + Position.Y + Position.Z
end},
{Enum.Material.Cobblestone, -10, 0, 1, {31, 16, 6}},
{Enum.Material.Pebble, -20, 0, 10, nil, function(Position)
return Position.X * Position.Y * Position.Z
end},
{Enum.Material.Concrete, -5, 0, 5, nil, function(Position)
return Position.X * Position.Y * Position.Z
end},
{Enum.Material.Metal, -3, 0, 3, {-30, -30, -30}, function(Position)
return Position.X + Position.Y + Position.Z
end},
{Enum.Material.Neon, 0, 0, 1, {81, 81, 81}, function(Position)
return 0
end},
{Enum.Material.Sand, -10, 0, 0.1, nil, function(Position)
return math.sin(Position.X + Position.Z) * 5
end},
{Enum.Material.WoodPlanks, -20, 0, 20, nil, function(Position)
return Position.X
end},
}
This lookup table defines how each material will behave and be calculated. It isn’t a dictionary so it must be hard to understand, allow me to explain.
The MaterialColorOffset table contains tables of the following format to describe the calculation behavior of each material:
{
Material,
Lowest Brightness Offset,
Highest Brightness Offset,
Brightness Offset Step Size,
{
Red Offset,
Green Offset,
Blue Offset
},
Material Seed Calculation()
}
-
The first value given is the Material that this data refers to.
-
The second value is the Lowest Brightness Offset. The Brightness offset is how materials are performed, using math to calculate particular areas to shade in order to recreate each material. This value represents the lowest it will randomly generate to subtract from the brightness.
-
The third value is the Highest Brightness Offset. This value represents the highest it will randomly generate to subtract from the brightness of each pixel.
-
The fourth value is the Brightness Offset Step Size. This is essentially in what steps can the random number between Lowest Brightness Offset and Highest Brightness Offset be calculated, assume that your lowest is -10, and your highest is 0, if your step size is 1 then you will get 10 possible random numbers, -10 through 0; However, if your step size were 5 then you would only have 3 possible random numbers, -10, -5, and 0. These numbers are in the RGB 0 to 255 space.
-
The fifth value is that Color Offset. This is represented by a table of 3 numbers all of which are in the RGB 0 to 255 space. They represent the R, G, and B offset respectively, they will be added to the final color to tint the material, as some roblox materials change the color of the part slightly.
-
The sixth and final value is the Material Seed Calculation. This is a function that will receive a parameter in the form of a Vector3 position value. This position value is calculated with the following code:
Raycast.Instance.CFrame:inverse() * CFrame.new(Raycast.Position)
‘Racycast’ being the current cast for each pixel. Using the calculation, the material shading is able to be persistent with both position and orientation of any part.
That is all folks!
To anyone that actually read all of that, uhm great now you’re 100% equipped to work on this with me!
To everyone else, the majority of you, I hope that my use of bold and italics will help you skim through it when you need specific information.
Thanks all, and I hope we can make this efficient enough to put into any project!