Culling System hiding blocks that should have clearly unobstructed traces

  1. What do you want to achieve?
    Culling algorythm, hiding blocks that the player cannot see.

  2. What is the issue?
    Traces are hiding blocks that should be visible and unobstructed.

  3. What solutions have you tried so far?
    Implementing rigorous debugging, including showing the traces.

Place (can be opened in Studio): Culling - Roblox

Create a model, name it “CULL”, fill it with parts. (ie house with parent model named CULL)
This script is a local script, insert it into StarterCharacterScripts.

local rayOrigin = Vector3.new(0,0,0)
local rayDirection = Vector3.new(0,-100,0)
local raycastParams = RaycastParams.new()
local PL:Model = script.Parent
local C:Camera = workspace.CurrentCamera
local H:Part = PL:WaitForChild("Head")
local CULL = workspace:WaitForChild("CULL")
local DBG_Col_Fast = Color3.new(1,1,1)
local DBG_Col_NoRay = Color3.new(0,0,0)
local DBG_Col_CT = Color3.new(1,0,1)
local DBG_Col_CT_OB = Color3.new(1,1,0)
local Debris:Debris = game:GetService("Debris")
local DBG_SZ = Vector3.new(1,1,1)
task.wait(1) -- Short delay to wait for model & player to be loaded
local TBC = CULL:GetChildren() -- Get all children of the model to be culled.
local function Check_Aggressive(PP:Vector3,v:BasePart)
	local cf:CFrame = v.CFrame
	local szd = v.Size/2
	local szx:number = szd.X;local szy:number = szd.Y;local szz:number = szd.Z;
	do
		--[[
		PPP 7
		NPP 2
		PNP 4
		PPN 6
		NNP 8
		PNN 5
		NPN 1
		NNN 3
		]]
		local R = workspace:Raycast(PP,(cf*CFrame.new(-szx,szy,-szz).Position)-PP,raycastParams)
		if (R ~= nil) and (R.Instance == v) then
			v.Transparency = 0
			--v.Color = DBG_Col_CT
		else
			R = workspace:Raycast(PP,(cf*CFrame.new(-szx,szy,szx).Position)-PP,raycastParams)
			if (R ~= nil) and (R.Instance == v) then
				v.Transparency = 0
				--v.Color = DBG_Col_CT
			else
				R = workspace:Raycast(PP,(cf*CFrame.new(-szd).Position)-PP,raycastParams)
				if (R ~= nil) and (R.Instance == v) then
					v.Transparency = 0
					--v.Color = DBG_Col_CT
				else
					R = workspace:Raycast(PP,(cf*CFrame.new(szx,-szy,szz).Position)-PP,raycastParams)
					if (R ~= nil) and (R.Instance == v) then
						v.Transparency = 0
						--v.Color = DBG_Col_CT
					else
						R = workspace:Raycast(PP,(cf*CFrame.new(szx,-szy,-szz).Position)-PP,raycastParams)
						if (R ~= nil) and (R.Instance == v) then
							v.Transparency = 0
							--v.Color = DBG_Col_CT
						else
							R = workspace:Raycast(PP,(cf*CFrame.new(szx,szy,-szz).Position)-PP,raycastParams)
							if (R ~= nil) and (R.Instance == v) then
								v.Transparency = 0
								--v.Color = DBG_Col_CT
							else
								R = workspace:Raycast(PP,(cf*CFrame.new(szd).Position)-PP,raycastParams)
								if (R ~= nil) and (R.Instance == v) then
									v.Transparency = 0
									--v.Color = DBG_Col_CT
								else
									R = workspace:Raycast(PP,(cf*CFrame.new(-szx,-szy,szz).Position)-PP,raycastParams)
									if (R ~= nil) and (R.Instance == v) then
										v.Transparency = 0
										--v.Color = DBG_Col_CT
									else
										v.Transparency = 1
										--v.Color = DBG_Col_CT_OB
									end
								end
							end
						end
					end
				end
			end
		end
	end
end
local PP:Vector3;
local OP:OverlapParams;
do
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.RespectCanCollide = true
	raycastParams.IgnoreWater = true
	task.wait(1)
	raycastParams.FilterDescendantsInstances = {PL}
end
--[[do
	OP = OverlapParams.new()
	OP.FilterType = Enum.RaycastFilterType.Exclude
	OP.FilterDescendantsInstances = {PL}
	OP.MaxParts = 25
	OP.BruteForceAllSlow = false
	OP.RespectCanCollide = true
end]]
local function FastCull(PP,v)
	--[[local VP = v.Position
	OP.FilterDescendantsInstances = {PL,v}
	local GPIP = workspace:GetPartBoundsInRadius(VP,v.Size.Magnitude,OP)
	if (GPIP ~= nil) and (#GPIP > 0) then
		table.insert(GPIP,PL)
		raycastParams.FilterDescendantsInstances = GPIP
	else
		raycastParams.FilterDescendantsInstances = {PL}
	end]]
	local r:RaycastResult = workspace:Raycast(PP,(v.Position-PP),raycastParams)
	if (r ~= nil) then
		local i = r.Instance
		if (i ~= v) then
			return v
		else
			v.Transparency = 0
			--v.Color = DBG_Col_Fast
		end
	--[[else
		task.wait()
		warn("NORAY")
		v.Color = DBG_Col_NoRay
		return v]]
	end
end
local CLIP_Z:number = 2000
while (CULL ~= nil) and (C ~= nil) do
	task.wait(.1)
	local PP:Vector3;
	local CF:CFrame = C.CFrame
	local CFP:Vector3 = CF.Position
	local CullFourthPass = {}
	do
		local CullFirstPass = {}
		for k,v:BasePart in pairs(TBC) do
			if ((CFP-v.Position).Magnitude <= CLIP_Z) then
				table.insert(CullFirstPass,v)
			else
				v.Transparency = 1
			end
		end
		local CullSecondPass = {}
		PP = H.Position
		for k,v:BasePart in pairs(CullFirstPass) do
			local Ret:BasePart? = FastCull(PP,v)
			if (Ret ~= nil) then
				table.insert(CullSecondPass,Ret)
			end
		end
		task.wait()
		local CullThirdPass = {}
		do
			local CP = CFP-(CF.LookVector*100)
			PP = Vector3.new(math.floor(CP.X*10)/10,math.floor(CP.Y*10)/10,math.floor(CP.Z*10)/10)
		end
		for k,v:BasePart in pairs(CullSecondPass) do
			local Ret:BasePart? = FastCull(PP,v)
			if (Ret ~= nil) then
				table.insert(CullThirdPass,Ret)
			end
		end
		task.wait()
		do
			local CP = CFP-(CF.LookVector*10)
			PP = Vector3.new(math.floor(CP.X*10)/10,math.floor(CP.Y*10)/10,math.floor(CP.Z*10)/10)
		end
		for k,v:BasePart in pairs(CullThirdPass) do
			local Ret:BasePart? = FastCull(PP,v)
			if (Ret ~= nil) then
				table.insert(CullFourthPass,Ret)
			end
		end
	end
	task.wait()
	local CullFinalPass = {}
	do
		local CP:Vector3 = CFP
		PP = Vector3.new(math.floor(CP.X*10)/10,math.floor(CP.Y*10)/10,math.floor(CP.Z*10)/10)
	end
	for k,v in pairs(CullFourthPass) do
		local Ret:BasePart? = FastCull(PP,v)
		if (Ret ~= nil) then
			task.delay(k/10000,Check_Aggressive,PP,Ret)
		end
	end
end