First, I’d like to disclose that this is a fork of @atfdaj 's auto exposure, refined, fixed and optimized. The logic is entirely designed by them, using Editable Images and CaptureService.
api
EditableImage | Documentation - Roblox Creator Hub
CaptureService | Documentation - Roblox Creator Hub
How Does This Resource Work?
As stated previously, @atfdaj designed most of the inner workings of the script, and I refined, optimized, and fixed the code.
For every Heartbeat
, a screenshot is taken via CaptureService and converted into an EditableImage. In a parallel thread, this image is processed and the HSV Value of every ‘pixel’ is analyzed and averaged into a value. From hereon, the appropriate ExposureCompensation
is calculated. The new Exposure is then applied in a RenderStepped (intentionally separate from the calculation; this part has less overhead, and the rest of the script could be modified to work at a more comfortable frequency) via a clamped lerp, which works according to the adjustTime
variable.
CODE/model
Please make sure that the localscript is parented to an actor.
SCRIPT
-- // Developed by @atfdaj, @AborayStudios, and @A_Mp5
--[[
https://devforum.roblox.com/t/free-performant-auto-exposure-adaptation-of-atfdajs/3000200
]]
print(script.Parent:IsA("Actor") and "" or "⚠️ Make sure Auto Exposure is parented to an actor!")
local captureService = game:FindFirstChildOfClass("CaptureService")
local assetService = game:FindFirstChildOfClass("AssetService")
local lighting = game:FindFirstChildOfClass("Lighting")
local runService = game:FindFirstChildOfClass("RunService")
local screen
local adjustTime = 3 -- Amount of time before camera is fully adjusted
local calibrationConstant = 6.5 -- A higher value makes exposure darker
local resolution = 50 -- Numbers too far from 50 may cause instability/errors;
local targetExposureValue = 10
local updatedExposure = 0
local function log2(x)
return math.log(x) / math.log(2)
end
local function lerp(start, goal, alpha)
alpha = math.clamp(alpha,0,1)
return start + (goal - start) * alpha
end
local function calculateNewExposure(brightness)
local luminance = brightness * 100
local ev = log2(luminance) + calibrationConstant
local compensation = targetExposureValue - ev
return compensation
end
local function getAverageBrightness()
local brightnessValues = {}
local totalBrightness = 0
for x = 1, screen.Size.X, resolution do
for y = 1, screen.Size.Y, resolution do
local pixel = screen:ReadPixels(Vector2.new(x, y), Vector2.one)
if pixel then
local color = Color3.new(pixel[1], pixel[2], pixel[3])
local _, _, value = color:ToHSV()
totalBrightness += value
table.insert(brightnessValues, value)
end
end
end
return totalBrightness / #brightnessValues
end
local function updateScreen(deltaTime)
captureService:CaptureScreenshot(function(capture)
screen = assetService:CreateEditableImageAsync(capture)
task.desynchronize()
local averageBrightness = getAverageBrightness()
updatedExposure = calculateNewExposure(averageBrightness)
task.synchronize()
screen:Destroy()
end)
end
local function updateExposure(deltaTime)
lighting.ExposureCompensation = lerp(lighting.ExposureCompensation,updatedExposure, adjustTime * deltaTime)
end
runService.Heartbeat:Connect(updateScreen)
runService.RenderStepped:Connect(updateExposure)
Demo place (COPYLOCKED, requires FFlagEditableImageEnabled = True
)
Examples & Video
Known Bugs
- Adjustment may appear to spike when closer to interpolation goal rather than linearly transition throughout the entire time
- The usage of CaptureService unintentionally makes the exposure adjust according to any UI elements, including the pause menu. (Possibly an upside if you take a look at flashbang effects, for instance.)
- Editable image API fails on weird aspect ratios.
- Possible memory leaks from the nature of the services used and their recency.
- As of 2024/06/01, the script may inexplicably stop working after a few playtests in Studio. This is a Roblox issue.
- As of 2024/06/01, the script does not work in production-game and studio without the Beta or FFlag enabled.
- In-Studio, make sure the EditableImage beta is enabled. & restart
- Ingame, using a custom bootstrapper like BloxStrap or manually editing the FFlag json, set
FFlagEditableImageEnabled
toTrue
. (This goes without saying: only players with the fflag enabled playing your game can see the effect, otherwise console is filled with warnings. Bound to change when it’s out of beta.) - Encountering one of the previously mentioned bugs (2024/06/01) in studio, reopen the place file.
Performance???
FPS remains relatively unaffected on the spectrum of devices. The script is easily modifiable to be infrequent instead of framely.
Extremely low-end device:
~2 - 9 frame reduction
(1 - 5ms on microprofiler)
Mid-High end device:
~0 - 3 frame reduction
(< 0.1ms on microprofiler)
Conclusion
Will you use this resource?
- Yes
- No (respond why)
- In the future
- Maybe
0 voters