How can I replicate GetSunDirection() function with a part?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

I’m attempting to create a lens flare that will work with a part in the workspace rather than the in-game sun,

  1. What is the issue? Include screenshots / videos if possible!

The issue is that with my code, the flare ends up somewhere that isn’t where the part is, so I’m wondering if anyone knows what type of math GetSunDirection() does to get the vector3 of the sun since my math does not seem to work.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I’ve looked at different resources on the API about GetSunDirection and also how to get an angle from subtracting vector 3’s
This source and This source Helped me understand and edit my scripts, however it’s still not working as intended.

For reference
This is a freemodel script that I’ve edited.

Here's the original version:
local camera=workspace.CurrentCamera
local host=script.Parent
local lFlares={}
local collision

for q,w in pairs(host:GetChildren()) do
  if w.Name:sub(1,9)=="LensFlare" then
    lFlares[w]=tonumber(w.Name:match("LensFlare(%-?.+)"))
  end
end

function findFlareCoord(cordFrame,sunPos)
  local setCoord=cordFrame:pointToObjectSpace(sunPos)
  local fov=math.atan(math.rad(camera.FieldOfView))
  local z=-setCoord.z
  if z>0 then
    local x=(setCoord.x/fov/1.2)/(z+1)*(host.AbsoluteSize.y/host.AbsoluteSize.x)
    local y=(setCoord.y/fov/1.2)/(z+1)*-1
    return x,y,true
  else
    return 0,0,false
  end
end

local stepped = game:GetService("RunService").Stepped

while true do
  local x,y,z=findFlareCoord(camera.CoordinateFrame,camera.CoordinateFrame.p+game.Lighting:GetSunDirection()*5)

    dist = (camera.CoordinateFrame.lookVector - game.Lighting:GetSunDirection()).magnitude * .1
    local sunCast = Ray.new(camera.CoordinateFrame.p, game.Lighting:GetSunDirection() * 333)
    local hitPart, hitPos = workspace:FindPartOnRayWithIgnoreList(sunCast,{game.Players.LocalPlayer.Character, camera})
    collision = hitPart and true or false

if z and (game.Lighting:GetMinutesAfterMidnight()>335 and game.Lighting:GetMinutesAfterMidnight()<1105) and collision==false then
    for lFlare,pos in pairs(lFlares) do
      lFlare:TweenPosition(UDim2.new(0.5 + x*pos,-lFlare.AbsoluteSize.x/2,0.5+y*pos,-lFlare.AbsoluteSize.y/2),"Out","Quad",0.01,true)
      lFlare.Visible=true
    end
    local shineTrans=(0.2-(math.abs(x+y)*0.5))
    if shineTrans>=0.2 then
      shineTrans=0.2
    elseif shineTrans<=0 then
      shineTrans=0
    end
    --host.ShineOverlay.Transparency=1-shineTrans
    --print(x)
  else
    for lFlare in pairs(lFlares) do lFlare.Visible=false end
    --host.ShineOverlay.Transparency=1
  end
  stepped:wait()
end

And here’s the script after my alterations:

local camera=workspace.CurrentCamera
local host=script.Parent
local lFlares={}
local collision

local Sun = game.Workspace.System.Star.Star


local function GetSunDir()
	local SUnposs = Sun.Position
	local OG = Vector3.new(0,0,0)

	local vector = SUnposs - OG
	--print(DIR)
	
	if vector.magnitude < .001 then
		vector = Vector3.new(0, 0, -1) -- default direction
	else
		vector = vector.unit
	end
	
	local DIR = vector
	
	return DIR
end

for q,w in pairs(host:GetChildren()) do
  if w.Name:sub(1,9)=="LensFlare" then
    lFlares[w]=tonumber(w.Name:match("LensFlare(%-?.+)"))
  end
end

function findFlareCoord(cordFrame,sunPos)
  local setCoord=cordFrame:pointToObjectSpace(sunPos)
  local fov=math.atan(math.rad(camera.FieldOfView))
  local z=-setCoord.z
  if z>0 then
    local x=(setCoord.x/fov/1.2)/(z+1)*(host.AbsoluteSize.y/host.AbsoluteSize.x)
    local y=(setCoord.y/fov/1.2)/(z+1)*-1
    return x,y,true
  else
    return 0,0,false
  end
end

local stepped = game:GetService("RunService").Stepped

while true do
	local x,y,z=findFlareCoord(camera.CoordinateFrame,camera.CoordinateFrame.p+GetSunDir()*5)

	dist = (camera.CoordinateFrame.lookVector - GetSunDir()).magnitude * .1
	local sunCast = Ray.new(camera.CoordinateFrame.p, GetSunDir() * 333)
    local hitPart, hitPos = workspace:FindPartOnRayWithIgnoreList(sunCast,{game.Players.LocalPlayer.Character, camera})
    collision = hitPart and true or false

	if z and collision==false then --  (game.Lighting:GetMinutesAfterMidnight()>335 and game.Lighting:GetMinutesAfterMidnight()<1105
    for lFlare,pos in pairs(lFlares) do
      lFlare:TweenPosition(UDim2.new(0.5 + x*pos,-lFlare.AbsoluteSize.x/2,0.5+y*pos,-lFlare.AbsoluteSize.y/2),"Out","Quad",0.01,true)
      lFlare.Visible=true
    end
    local shineTrans=(0.2-(math.abs(x+y)*0.5))
    if shineTrans>=0.2 then
      shineTrans=0.2
    elseif shineTrans<=0 then
      shineTrans=0
    end
    --host.ShineOverlay.Transparency=1-shineTrans
    --print(x)
  else
    for lFlare in pairs(lFlares) do lFlare.Visible=false end
    --host.ShineOverlay.Transparency=1
  end
  stepped:wait()
end

The GetSunDir() function I created subtracts vectors but, I think I’m doing something wrong here. Let me know if you see anything!

(part.Position - camera.CFrame.Position).Unit

2 Likes

I tried replacing this but to my surprise it actually does not show the lens flare at all, and I even commented out

if vector.magnitude < .001 then
		vector = Vector3.new(0, 0, 0) -- default direction
else
		vector = vector.unit
end

To make sure that wasn’t interfering

Try without the Unit.

1 Like

then the lens flare might be showing up behind the camera, i always mix up whether its b - a or a - b, try multiplying the unit vector by -1

1 Like

Have you considered using Camera:WorldToScreenPoint to do your 3D-to-2D calcuations, instead of manually doing it? That would replace the need for both the GetSunDir or the findFlareCoord functions.

tried this and it didn’t change anything

Also tried this and it does not seem to be the issue. Not sure why changing it to the camera’s pos made everything not work

Used this for GetSunDir however the flare remains on the top f the screen as if it’s just above the camera at all times

ok i actually looked at your code and it looks like you are just trying to position a 2d gui in 3d space… in which case all you have to do is

local a = game.Workspace.CurrentCamera:WorldToViewportPoint(game.Workspace.Part.Position)
gui.Position = UDim2.new(0, a.x, 0, a.y)

or you could just use a billboardgui and set AlwaysOnTop to true so it doesnt clip into stuff and use raycasts to see if its obstructed or not and hide it if it is