Creating an outline/edge shader

I might be able to find it in the coreScripts, I’ll check it out when I can!

–[[
local function GetEdges(part)
local edges = {}
local corners = GetCorners(part)
for _, Vector in pairs(Vertices) do
table.insert(corners, part.CFrame * CFrame.new(part.Size.X/2 * Vector[1], part.Size.Y/2 * Vector[2], part.Size.Z/2 * Vector[3]).Position)
end
for _, edge in pairs(EdgeConnections) do
table.insert(edges, {corners[edge[1]], corners[edge[2]]})
end
return edges
end

local parts = {}

for _, obj in pairs(script.Parent:GetChildren()) do
if obj:IsA(“BasePart”) then
table.insert(parts, obj)
end
end

local function GetNormal(part, normalId)
if normalId == Enum.NormalId.Top then
return part.CFrame.UpVector
elseif normalId == Enum.NormalId.Bottom then
return -part.CFrame.UpVector
elseif normalId == Enum.NormalId.Right then
return part.CFrame.RightVector
elseif normalId == Enum.NormalId.Left then
return -part.CFrame.RightVector
elseif normalId == Enum.NormalId.Front then
return part.CFrame.LookVector
elseif normalId == Enum.NormalId.Back then
return -part.CFrame.LookVector
end
end

local function GetFacePos(part, normalId)
local facepos = part.CFrame * Vector3.FromNormalId(normalId)
if normalId == Enum.NormalId.Top or normalId == Enum.NormalId.Bottom then
return facepos.Y
elseif normalId == Enum.NormalId.Left or normalId == Enum.NormalId.Right then
return facepos.X
elseif normalId == Enum.NormalId.Front or normalId == Enum.NormalId.Back then
return facepos.Z
end
end

for _, part1 in pairs(parts) do
local randomColor1 = BrickColor.random()
for _, part2 in pairs(parts) do
if part1 ~= part2 then
local randomColor2 = BrickColor.random()
for _, face in pairs(faces) do
local part1Normal = GetNormal(part1, face)
local part2Normal = GetNormal(part2, face)
local part1Facepos = GetFacePos(part1, face)
local part2Facepos = GetFacePos(part2, face)
if part1Facepos == part2Facepos then
local surfaceGUI1 = Instance.new(“SurfaceGui”)
surfaceGUI1.Parent = part1
surfaceGUI1.Face = face

 local surfaceGUI2 = Instance.new("SurfaceGui")
 surfaceGUI2.Parent = part2
 surfaceGUI2.Face = face
 
 local frame1 = Instance.new("Frame")
 frame1.Parent = surfaceGUI1
 frame1.Size = UDim2.new(1,0,1,0)
 frame1.BackgroundColor = randomColor2
 
 local frame2 = Instance.new("Frame")
 frame2.Parent = surfaceGUI2
 frame2.Size = UDim2.new(1,0,1,0)
 frame2.BackgroundColor = randomColor2
end

end
end
end
end
]]

Not sure I get what this is, could you elaborate please?

I am the architec to the solution which is the solution whcih is the solution to your hence mentioned poroblem you stated earlier along the space time continuem.

I ended up trying something just kind of as a proof of concept:

It’s obviously not great (and its a bit laggy) but the way I did it is by essentially raycasting for each of these “pixels” on the screen out from the camera. Then I tracked the normals of all the raycasts, and compared each pixels detected normal to the ones around it. If the normal is different, it must mean that the surface is not flat and bends at that point. If so, it makes that pixel black (all other pixels are transparent so that you can see the actual game)

The theory is kinda there. I have to do some more testing to see what will bottleneck it (I assume it’s the raycasts, but I’ll make sure)

Another problem that I found is on things like stairs. Because the normals of the raycasts between two pixels when you face a staircase is the same, no outline is drawn. This could probably be fixed by also keeping track of the pixels distance from the camera (aka depth buffer).

The idea is hopefully to get a higher fidelity on the raycasts (I really hope that the GUI updating is the actual bottleneck…) by replacing this GUI method with something else.

1 Like

I think I’ve figured it out.

Take a look at stravant’s GapFill plugin.

Gonna try and reverse engineer this tomorrow.

1 Like

This is cool, very nice seeing that you made something this cool this quickly. I believe that I saw something like this before, can’t find the post, but remember the people working on it had been dealing with a lot of performance issues because the amount of ray casting; it was a while ago and their have been a lot of updates since then, but you’ll still be limited in resolution and how much you can do with it. Still cool tho. BTW, if your able to make this more detailed, consider making lines further away thicker or skinnier; Manifold Garden did this, thought it was pretty cool.

looks interesting, hopefully you find something useful in it.

Still working on it, but I found the actual code that does the edge finding.

It only finds the CLOSEST edge, and the way it does it is like this: you feed it a ray cast, and it gets the position and normal of the raycast result (which represents a plane in 3d space). It then explores outward on the plane with raycasts until it goes past the edge, then (I think) it moves down relative to the surface of the part we were hitting, and raycasts back towards the origin to get a position and normal of the next face (which is another plane in 3d space). It then calculates the ray that represents the line of intersection between the two planes we have, and then explores along that edge with raycasts (somehow, I don’t quite understand that right now.)

I actually got some kind of outlining going, there are still some bugs though.

If you’re ok with manually writing out geometry info, maybe you could look into wireframe handle adornments?

1 Like

That sounds complicated af, but I think I get it. How many ray casts are you doing in total tho?

A lot. I actually pivoted a bit. He has code to get the edges of every single thing in roblox except for meshparts and unions, which use the other method. I’m now trying to use that information to figure out if edges coincide.

It may take a few seconds due to a large amount of comparisons but I’m okay hiding the screen for the player until its done.

Might also be better to do it in studio before you publish if all you need is edge data.

You could save up time by doing custom thick raycasting, which is basically sweeping a convex mesh in a direction and checking if it hits something. Easier to compute and provides the necessary amount of information to see if you really need to test it further. Not sure if you are up to do the incredible complicated challenge of writing a mesh sweeper, but it is a pretty cool challenge.

Yea, I might make it a plugin so you can bake it. The problem I’m running into is still just getting rid of overlapping edges being outlined.

Interesting…I will definitely look into that.

1 Like

I think the final part of the puzzle is combing my current edge thing that I derived from stravant and integrating it with the flush surfaces code

How’s it goin? Are you having any luck?

I have exams rn, will work on it later

With the new exciting update EditableImage and EditableMesh we can likely create a render that can create an edge detection shader. Shaders are already being made: link

1 Like