Hey all. Today I want to show off something that I’ve been wanting to create for years, which is a 3D game engine with textures that is fully rendered on to a GUI without the use of a ViewportFrame!
This is my fast and yet pretty looking Raycast Renderer entirely made within a GUI.
This engine works a lot like older 3D games in the early 90s (such as Wolfenstein 3D and DOOM). This engine uses raycasting to simulate a 3D environment within a 2D top-down map.
To cut it short, a 2D Raycast Renderer is the projection of a 2D map into a “third” dimension. From the Player’s position, a bunch of rays are cast with incrementing change of angle (based on the FOV). The ray travels along the 2D map and returns the distance to the first “wall” it hits. For each ray, a thin rectangle is drawn, with height being determined by the returned raycast distance.
Here’s a list of advanced features that the engine currently supports:
Textured walls
Fake second level walls
Sprites/objects
Skyboxes
Basic lighting and shading
Here’s a list of planned updates and features:
Rework of drawing textured columns to improve both look and framerate. The current method is very messy and hacky.
Interactable switches
Guns and items
Animated textures and sprites
(Maybe) Allow rendering walls through other transparent walls (such as windows or fences)
Better player collisions (will allow your character to slide along walls when ran into)
NPCs or enemies
Major optimisation of code
(Maybe) Add curved/round walls
Removal of slight distortion (most noticeable with higher FOV)
Here’s a simple open sourced version of the renderer without the advanced graphics and textures:
I’d love to hear your thoughts and feedback on this renderer. Thank you!
Augh I love it @Ethanthegrand14!!!
You inspired me into this real time ray tracing game and I’m so glad I’m working to make it better and better with you.
I absolutely love that you’ve taken this idea and pushed it to it’s limits and beyond with textures and a skymap, HOW DOES ANY OF THAT EVEN WORK!?!?
That said, you’re missing a huge component that frankly is such a simple addition, and removes the fish eye effect seen here:
Before committing fully to my 3D ray tracer I worked on a 2D one like this too! I wrote this bit here which removes the curviture of hard edges.
Result obviously being the result of the raycast. CharacterAngle is the angle the character is currently turned toward. Dist, the output will then be turned into a value that can represent the length of each row. LookAngle is the specific angle of the row the loop is currently on.
With that bit here’s what the sharp edges on mine look like:
Well I’m not going to go too far into how texture rending works, because that is very complicated and would take forever to explain. Not even I fully understand how a bit of works.
Basically for each column, you want to compare the center of the object that your ray hits, with the position of where your ray has landed. And with that distance between the two positions, you can show a portion of the texture on each column that is shifted depending on the distance between those two positions.
And for the skybox, it is simply a very large image that shifts horizontally depending on the orientation of the player.
And to push more limits, I am also working on optimization and fake multilevel objects and buildings that are basically 2 or 3 maps stacked on top of each-other.
Maybe with the help of your optimization skills, we can fix that
But because I am raycasting 3 times more than usual, the FPS drops quite a bit. Sooo that’s a work in progress for now.
Ok. I’ve actually attempted this multiple times, but I just simply don’t know what the CharacterAngle and LookAngle fully represent. Do they represent a CFrame axis value? do they represent a vector3 axis value?
Basically I just don’t know what type of angle I should be using here.
Also if it wasn’t for @GreekForge, this textured beauty wouldn’t be here. He helped explain some of the math for rending textures which brought us here!
Oh uhm, actually it’s in the form of -PI to PI. So -3.14159 to 3.14159.
However because of the cyclical nature of pi I didn’t even bother to put those limits on it, so my angle starts at 0, when you hold D it is added to, and when you hold A it is subtracted from.
The amount that it changes by is a very small number to coinside with the small range of pi.
For a more comprehensive guide on how I did this, here’s all of the relevent code that goes into my calculations:
local CharacterAngle = 0
function GetFovFromIndex(Index)
local x1 = Index
local x2 = Settings.Resolution
local y1
local y2 = Settings.FOV
local a = x1 * y2
local y1 = a / x2
return y1
end
game:GetService("RunService").RenderStepped:Connect(function(Delta)
for i = 1, Settings.Resolution do
local Pixel = -- get current row
local FovPart = GetFovFromIndex(i)
local LookAngle = CharacterAngle + math.rad(FovPart - Settings.FOV/2)
local Direction = Vector3.new(math.cos(LookAngle), 0, math.sin(LookAngle))
local RayParams = RaycastParams.new()
RayParams.FilterDescendantsInstances = {Character}
local Result = workspace:Raycast(Origin, Direction * Settings.RenderDistance, RayParams)
if Result then
local Dist = (Character.Position - Result.Position).Magnitude
Dist = Dist * math.cos(CharacterAngle - LookAngle)
Pixel.Size = UDim2.new(Width, 0, 1 / (Dist*0.1), 0)
end
end
end)
also yeah you can totally convert degrees, which is what roblox’s vector3 uses, into radians, which is what pi uses.
If you want to convert a degree number to a radians number all you have to do it say math.rad(MyDegrees) and if you want to convert a number in radians to degrees simply do the reverse: math.deg(MyRadians)