How do I make viewports have the same lighting as in game?

I want to viewports to have the same lighting as in game, I’m making a module to make screenshots

The lighting is dark and doesn’t look good

I tried searching on the devforum for something like this but didn’t find anything

Here’s a snippet of the code

function SnapService:TakeScreenShot(camera: Camera) : ViewportFrame
	local viewport=Instance.new("ViewportFrame")
	viewport.CurrentCamera=camera

	local function removeScripts(dt)
		for _, v in pairs(dt:GetChildren()) do
			if v:IsA("BaseScript") then
				v:Destroy()
			else
				task.spawn(removeScripts, v)
			end
		end
	end

	local lighting=game:GetService('Lighting')

	local sunDir=lighting:GetSunDirection()

	viewport.LightDirection=(camera.CFrame:Inverse() * sunDir) *(lighting.Brightness*5)

	viewport.Ambient=lighting.Ambient

	for _, v in lighting:GetChildren() do
		v:Clone().Parent=viewport
	end

	for _, v in pairs(workspace:GetChildren()) do
		if v:IsA("Model") or v:IsA("Folder") then
			if v:IsA('Terrain') then continue end

			v.Archivable=true

			local cl=v:Clone()

			task.spawn(removeScripts, cl)

			cl.Parent=viewport
		elseif v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then continue end

			v.Archivable=true

			local cl=v:Clone()

			task.spawn(removeScripts, cl)

			cl.Parent=viewport
		end
	end

	return viewport
end

local SnapService = {}

type Camera = Instance
type ViewportFrame = Instance
type BasePart = Instance
type UnionOperation = Instance

local function removeScripts(instance: Instance)
	for _, child in instance:GetChildren() do
		if child:IsA("BaseScript") then
			child:Destroy()
		else
			task.spawn(removeScripts, child)
		end
	end
end

function SnapService:TakeScreenShot(camera: Camera): ViewportFrame
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera

	local lighting = game:GetService('Lighting')

	viewport.Ambient = lighting.Ambient
	viewport.OutdoorAmbient = lighting.OutdoorAmbient
	viewport.Brightness = lighting.Brightness
	viewport.ShadowSoftness = lighting.ShadowSoftness
	viewport.GeographicLatitude = lighting.GeographicLatitude

	local sunDirectionWorld = lighting:GetSunDirection()
	viewport.LightDirection = (camera.CFrame:Inverse() * sunDirectionWorld).Unit

	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") then
			lightObject:Clone().Parent = viewport
		end
	end

	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				continue
			end

			local clonedObject = v:Clone()
			task.spawn(removeScripts, clonedObject)
			clonedObject.Parent = viewport
		end
	end

	return viewport
end

return SnapService

Heres the ModuleScript! Make sure your script is in ReplicatedStorage.

viewport.OutdoorAmbient = lighting.OutdoorAmbient
viewport.Brightness = lighting.Brightness
viewport.ShadowSoftness = lighting.ShadowSoftness
viewport.GeographicLatitude = lighting.GeographicLatitude

These do no exist in viewports.
And the lighting is still scuffed

1 Like

Okay i removed things that are valid.

local SnapService = {}

type Camera = Instance
type ViewportFrame = Instance
type BasePart = Instance
type UnionOperation = Instance

local function removeScripts(instance: Instance)
	for _, child in instance:GetChildren() do
		if child:IsA("BaseScript") then
			child:Destroy()
		else
			task.spawn(removeScripts, child)
		end
	end
end

function SnapService:TakeScreenShot(camera: Camera): ViewportFrame
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera

	local lighting = game:GetService('Lighting')

-- --- Lighting Properties ---
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient -- Fallback to Ambient if OutdoorAmbient is nil (though it shouldn't be)

	-- LightDirection: Correctly set as a normalized vector relative to the camera.
	local sunDirectionWorld = lighting:GetSunDirection()
	viewport.LightDirection = (camera.CFrame:Inverse() * sunDirectionWorld).Unit

-- --- Clone Lighting Objects ---
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") then
			lightObject:Clone().Parent = viewport
		end
	end

-- --- Clone Workspace Objects ---
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				continue
			end

			local clonedObject = v:Clone()
			task.spawn(removeScripts, clonedObject)
			clonedObject.Parent = viewport
		end
	end

	return viewport
end

return SnapService

REMOVED:

viewport.OutdoorAmbient = lighting.OutdoorAmbient
viewport.Brightness = lighting.Brightness
viewport.ShadowSoftness = lighting.ShadowSoftness
viewport.GeographicLatitude = lighting.GeographicLatitude

Sorry for that! I thought viewport.[LIGHTING PROPERTY] exists😅

Reply to me if theres any issues!.

It’s still scuffed


There’s no shadows and very poor lighting.

Can i check your Lighting properties? by using screenshot

1 Like
local SnapService = {}

type Camera = Instance
type ViewportFrame = Instance
type BasePart = Instance
type UnionOperation = Instance

local function removeScripts(instance: Instance)
	for _, child in instance:GetChildren() do
		if child:IsA("BaseScript") then
			child:Destroy()
		else
			task.spawn(removeScripts, child)
		end
	end
end

local function enableShadowsRecursively(instance: Instance)
	-- Enable shadows for all parts
	if instance:IsA("BasePart") then
		instance.CastShadow = true
	end
	
	for _, child in instance:GetChildren() do
		enableShadowsRecursively(child)
	end
end

function SnapService:TakeScreenShot(camera: Camera): ViewportFrame
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera
	
	local lighting = game:GetService('Lighting')

	-- --- Enhanced Lighting Properties ---
	-- Ambient lighting
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient
	
	-- Light direction: Use the actual sun direction without transformation
	-- ViewportFrame expects world-space direction, not camera-relative
	viewport.LightDirection = lighting:GetSunDirection()
	
	-- Light color - this is crucial for proper lighting
	viewport.LightColor = lighting.SunAngularSize > 0 and lighting.SunSize or Color3.fromRGB(255, 255, 255)

	-- --- Clone Lighting Objects ---
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") then
			local clonedLight = lightObject:Clone()
			clonedLight.Parent = viewport
		end
	end

	-- --- Clone Workspace Objects with Shadow Support ---
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				continue
			end

			local clonedObject = v:Clone()
			task.spawn(removeScripts, clonedObject)
			
			-- Enable shadows for all parts in the cloned object
			enableShadowsRecursively(clonedObject)
			
			clonedObject.Parent = viewport
		end
	end
	
	-- Additional viewport properties for better rendering
	viewport.ImageTransparency = 0
	viewport.BackgroundTransparency = 1
	
	-- Force refresh the viewport
	task.wait()
	
	return viewport
end

-- Alternative method with more lighting control
function SnapService:TakeScreenShotEnhanced(camera: Camera, customLightDirection: Vector3?): ViewportFrame
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera
	
	local lighting = game:GetService('Lighting')

	-- --- More Comprehensive Lighting Setup ---
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient
	
	-- Use custom light direction if provided, otherwise use sun direction
	if customLightDirection then
		viewport.LightDirection = customLightDirection.Unit
	else
		viewport.LightDirection = lighting:GetSunDirection()
	end
	
	-- Set a bright white light color for better visibility
	viewport.LightColor = Color3.fromRGB(255, 255, 255)

	-- --- Clone All Lighting Effects ---
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") or
		   lightObject:IsA("BlurEffect") or
		   lightObject:IsA("BloomEffect") then
			local clonedLight = lightObject:Clone()
			clonedLight.Parent = viewport
		end
	end

	-- --- Enhanced Object Cloning ---
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				continue
			end

			local clonedObject = v:Clone()
			task.spawn(removeScripts, clonedObject)
			
			-- Enhanced shadow and lighting setup
			enableShadowsRecursively(clonedObject)
			
			clonedObject.Parent = viewport
		end
	end
	
	-- Viewport rendering properties
	viewport.ImageTransparency = 0
	viewport.BackgroundTransparency = 1
	
	-- Additional properties that can help with rendering quality
	if viewport:FindFirstChild("ImageLabel") then
		viewport.ImageLabel.ImageTransparency = 0
	end
	
	return viewport
end

return SnapService
  1. Created TakeScreenShotEnhanced() that allows custom light direction and has more comprehensive lighting setup
function SnapService:TakeScreenShotEnhanced(camera: Camera, customLightDirection: Vector3?): ViewportFrame
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera
	
	local lighting = game:GetService('Lighting')

	-- --- More Comprehensive Lighting Setup ---
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient
	
	-- Use custom light direction if provided, otherwise use sun direction
	if customLightDirection then
		viewport.LightDirection = customLightDirection.Unit
	else
		viewport.LightDirection = lighting:GetSunDirection()
	end
	
	-- Set a bright white light color for better visibility
	viewport.LightColor = Color3.fromRGB(255, 255, 255)

	-- --- Clone All Lighting Effects ---
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") or
		   lightObject:IsA("BlurEffect") or
		   lightObject:IsA("BloomEffect") then
			local clonedLight = lightObject:Clone()
			clonedLight.Parent = viewport
		end
	end

	-- --- Enhanced Object Cloning ---
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				continue
			end

			local clonedObject = v:Clone()
			task.spawn(removeScripts, clonedObject)
			
			-- Enhanced shadow and lighting setup
			enableShadowsRecursively(clonedObject)
			
			clonedObject.Parent = viewport
		end
	end
	
	-- Viewport rendering properties
	viewport.ImageTransparency = 0
	viewport.BackgroundTransparency = 1
	
	-- Additional properties that can help with rendering quality
	if viewport:FindFirstChild("ImageLabel") then
		viewport.ImageLabel.ImageTransparency = 0
	end
	
	return viewport
end
  1. Shadow Supported: Added enableShadowsRecursively() function that ensures all BaseParts have CastShadow = true.

  2. Light Color: Added proper light color setup with bright white light for better visibility.

  3. Force Refresh: Added a small wait to ensure the viewport renders properly

If theres any issues, please add some details to make it understandable

Or Copy this one with prints and warns!:

--// THIS SHOULD BE THE OUTPUT!
📸 Starting screenshot capture...
Camera position: 10, 50, 10
💡 Setting up lighting...
Ambient color set to: 135, 135, 135
✨ Cloned 3 lighting effects
🌍 Cloning workspace objects...
Enabled shadows for 15/20 parts in PlayerModel
📦 Cloned 8 objects, skipped 3
✅ Screenshot capture complete!

HERES THE FULL-SCRIPT!:

local SnapService = {}

type Camera = Instance
type ViewportFrame = Instance
type BasePart = Instance
type UnionOperation = Instance

local function removeScripts(instance: Instance)
	for _, child in instance:GetChildren() do
		if child:IsA("BaseScript") then
			print("Removing script:", child:GetFullName())
			child:Destroy()
		else
			task.spawn(removeScripts, child)
		end
	end
end

local function enableShadowsRecursively(instance: Instance)
	local shadowsEnabled = 0
	local totalParts = 0
	
	local function enableForInstance(inst: Instance)
		-- Enable shadows for all parts
		if inst:IsA("BasePart") then
			totalParts += 1
			if inst.CastShadow ~= true then
				inst.CastShadow = true
				shadowsEnabled += 1
			end
		end
		
		for _, child in inst:GetChildren() do
			enableForInstance(child)
		end
	end
	
	enableForInstance(instance)
	
	if totalParts > 0 then
		print(string.format("Enabled shadows for %d/%d parts in %s", shadowsEnabled, totalParts, instance.Name))
	end
end

function SnapService:TakeScreenShot(camera: Camera): ViewportFrame
	print("📸 Starting screenshot capture...")
	
	if not camera then
		warn("❌ Camera is nil!")
		return nil
	end
	
	print("Camera position:", camera.CFrame.Position)
	print("Camera looking at:", camera.CFrame.LookVector)
	
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera
	
	local lighting = game:GetService('Lighting')
	print("💡 Setting up lighting...")

	-- --- Enhanced Lighting Properties ---
	-- Ambient lighting
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient
	print("Ambient color set to:", viewport.Ambient)
	
	-- Light direction: Use the actual sun direction without transformation
	-- ViewportFrame expects world-space direction, not camera-relative
	local sunDirection = lighting:GetSunDirection()
	viewport.LightDirection = sunDirection
	print("Sun direction:", sunDirection)
	
	-- Light color - this is crucial for proper lighting
	viewport.LightColor = lighting.SunAngularSize > 0 and lighting.SunSize or Color3.fromRGB(255, 255, 255)
	print("Light color:", viewport.LightColor)

	-- --- Clone Lighting Objects ---
	local lightingEffectsCloned = 0
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") then
			local clonedLight = lightObject:Clone()
			clonedLight.Parent = viewport
			lightingEffectsCloned += 1
			print("Cloned lighting effect:", lightObject.ClassName)
		end
	end
	print(string.format("✨ Cloned %d lighting effects", lightingEffectsCloned))

	-- --- Clone Workspace Objects with Shadow Support ---
	print("🌍 Cloning workspace objects...")
	local objectsCloned = 0
	local objectsSkipped = 0
	
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			warn("Making object archivable:", v.Name)
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				print("Skipping Terrain")
				objectsSkipped += 1
				continue
			end

			local success, clonedObject = pcall(function()
				return v:Clone()
			end)
			
			if not success then
				warn("Failed to clone object:", v.Name, "Error:", clonedObject)
				objectsSkipped += 1
				continue
			end
			
			print("Cloning object:", v.Name, "Type:", v.ClassName)
			task.spawn(removeScripts, clonedObject)
			
			-- Enable shadows for all parts in the cloned object
			enableShadowsRecursively(clonedObject)
			
			clonedObject.Parent = viewport
			objectsCloned += 1
		else
			objectsSkipped += 1
		end
	end
	
	print(string.format("📦 Cloned %d objects, skipped %d", objectsCloned, objectsSkipped))
	
	-- Additional viewport properties for better rendering
	viewport.ImageTransparency = 0
	viewport.BackgroundTransparency = 1
	print("🎨 Viewport properties configured")
	
	-- Force refresh the viewport
	print("⏳ Waiting for viewport to render...")
	task.wait()
	
	print("✅ Screenshot capture complete!")
	return viewport
end

-- Alternative method with more lighting control
function SnapService:TakeScreenShotEnhanced(camera: Camera, customLightDirection: Vector3?): ViewportFrame
	print("🚀 Starting enhanced screenshot capture...")
	
	if not camera then
		warn("❌ Camera is nil!")
		return nil
	end
	
	print("Camera CFrame:", camera.CFrame)
	if customLightDirection then
		print("Using custom light direction:", customLightDirection)
	end
	
	local viewport = Instance.new("ViewportFrame")
	viewport.CurrentCamera = camera
	
	local lighting = game:GetService('Lighting')
	print("💡 Enhanced lighting setup...")

	-- --- More Comprehensive Lighting Setup ---
	viewport.Ambient = lighting.OutdoorAmbient or lighting.Ambient
	print("Ambient lighting:", viewport.Ambient)
	
	-- Use custom light direction if provided, otherwise use sun direction
	if customLightDirection then
		viewport.LightDirection = customLightDirection.Unit
		print("Custom light direction applied:", viewport.LightDirection)
	else
		viewport.LightDirection = lighting:GetSunDirection()
		print("Sun direction applied:", viewport.LightDirection)
	end
	
	-- Set a bright white light color for better visibility
	viewport.LightColor = Color3.fromRGB(255, 255, 255)
	print("Light color set to white")

	-- --- Clone All Lighting Effects ---
	local enhancedEffectsCloned = 0
	for _, lightObject in lighting:GetChildren() do
		if lightObject:IsA("Atmosphere") or
		   lightObject:IsA("Bloom") or
		   lightObject:IsA("ColorCorrection") or
		   lightObject:IsA("DepthOfField") or
		   lightObject:IsA("Fog") or
		   lightObject:IsA("SunRays") or
		   lightObject:IsA("Sky") or
		   lightObject:IsA("BlurEffect") or
		   lightObject:IsA("BloomEffect") then
			local clonedLight = lightObject:Clone()
			clonedLight.Parent = viewport
			enhancedEffectsCloned += 1
			print("Enhanced lighting effect cloned:", lightObject.ClassName)
		end
	end
	print(string.format("✨ Enhanced cloning: %d lighting effects", enhancedEffectsCloned))

	-- --- Enhanced Object Cloning ---
	print("🌍 Enhanced workspace cloning...")
	local enhancedObjectsCloned = 0
	local enhancedObjectsFailed = 0
	
	for _, v in pairs(workspace:GetChildren()) do
		if not v.Archivable then
			warn("Setting archivable for:", v.Name)
			v.Archivable = true
		end

		if v:IsA("Model") or v:IsA("Folder") or v:IsA("BasePart") or v:IsA("UnionOperation") then
			if v:IsA('Terrain') then
				print("Skipping Terrain in enhanced mode")
				continue
			end

			local success, result = pcall(function()
				local clonedObject = v:Clone()
				task.spawn(removeScripts, clonedObject)
				
				-- Enhanced shadow and lighting setup
				enableShadowsRecursively(clonedObject)
				
				clonedObject.Parent = viewport
				return clonedObject
			end)
			
			if success then
				print("Enhanced clone successful:", v.Name)
				enhancedObjectsCloned += 1
			else
				warn("Enhanced clone failed for:", v.Name, "Error:", result)
				enhancedObjectsFailed += 1
			end
		end
	end
	
	print(string.format("📦 Enhanced cloning complete: %d successful, %d failed", enhancedObjectsCloned, enhancedObjectsFailed))
	
	-- Viewport rendering properties
	viewport.ImageTransparency = 0
	viewport.BackgroundTransparency = 1
	print("🎨 Enhanced viewport properties configured")
	
	-- Additional properties that can help with rendering quality
	if viewport:FindFirstChild("ImageLabel") then
		viewport.ImageLabel.ImageTransparency = 0
		print("ImageLabel transparency set")
	end
	
	print("⏳ Enhanced rendering wait...")
	task.wait()
	
	print("✅ Enhanced screenshot capture complete!")
	return viewport
end

return SnapService

And screenshot the output or on Developer Console by pressing F9 on your keybroad to open!.

Same results.


Here’s the output ig

This is impossible, viewports will always look ugly so low end devices can keep up.

And this feels like chat-gpt, might be wrong though, but why would you add emojis.

1 Like
viewport.LightDirection = -lighting:GetSunDirection()

should probably make it slightly better

That’s what I’m thinking.
If i would use chatgpt it would do the same thing as he did

1 Like

Why do you want to use viewports anyway they cant rly look good.

1 Like

If there are any alternatives that’s fine to me

Depends on what you want to achieve, so tell me please.

I’m trying to achieve screenshot quality if possible.

Then you could just clone everything in the workspace and move it far away

if you want to datastore it becomes a little more complex though but i can help witht that