AutoExposure for the viewport

I’m trying to make an autoexposure feature for the viewport like real time games do, they also call it eye adaptation.
I think i am at a good point, but the script here runs in infinite loop:

import bpy
import bgl

def function(self, context):
	print("function")
	shading = context.area.spaces.active.shading.type
	if shading in ["MATERIAL" ,"RENDERED"]:
		# buffer for viewport info
		viewport = bgl.Buffer(bgl.GL_INT, 4)
		# store viewport info
		bgl.glGetIntegerv(bgl.GL_VIEWPORT, viewport)
		# width and height of the viewport
		width = int(viewport[2]/2)
		height = int(viewport[3]/2)

		bgl.glDisable(bgl.GL_DEPTH_TEST)
		# buffer for pixels
		buffer = bgl.Buffer(bgl.GL_BYTE, 3)
		# read viewport pixels
		bgl.glReadPixels(width, height, 1, 1, bgl.GL_RGB, bgl.GL_BYTE, buffer)
		avg = (buffer[0]+buffer[1]+buffer[2])/3
		mul = 0.5/avg
		bpy.context.scene.view_settings.exposure = mul


class VIEW3D_OT_TestDraw(bpy.types.Operator):
    bl_idname = "view3d.test_draw"
    bl_label = "Test Draw"

    def modal(self, context, event):
        print("modal")
        args = (self, context)
        bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
        bpy.types.SpaceView3D.draw_handler_add(function, args, 'WINDOW', 'PRE_VIEW')
        return {'CANCELLED'}

    def invoke(self, context, event):
        print("invoke")
        args = (self, context)
        self._handle = bpy.types.SpaceView3D.draw_handler_add(function, args, 'WINDOW', 'PRE_VIEW')
        return {'RUNNING_MODAL'}


def register():
    bpy.utils.register_class(VIEW3D_OT_TestDraw)


def unregister():
    bpy.utils.unregister_class(VIEW3D_OT_TestDraw)


if __name__ == "__main__":
    register()


Put this code in the Blender’s python text editor, run it, search for the “Test Draw” operator in the viewport and click it.
Then go in the rendered mode (in EEVEE) and look at the terminal output… it infinitely prints “function”, that means it is always calling the function “function(self, context)”.
That happens because at the end of the function i am changing the Filmic exposure, that changes the viewport pixels, that triggers the call of the function and so on… infinite loop.
I am making use of bpy.types.SpaceView3D.draw_handler_add to trigger the call to change the exposure of the scene, but when the exposure changes, i need to disable the draw handler for a moment because otherwise as you can see it triggers another call to the function.
Surely i’m using something wrongly, this is why i’m asking your help here.

3 Likes

Nice one, does this affect performance in any way due to the loop?

Would be cool to have a UI entry toggle and some settings somewhere.

I can’t seem to get it to work yet though. What are you using as the sensor?

I’m doing the UI stuff, and will release an addon with that feature, but yeah, it does affect performance, especially having multiple points to calculate the average luminance of the pixels.
If only there is a way we could disable and re-enable the draw handler after executing the change in the exposure of the scene, that would stop the infinite loop.

2 Likes

Maybe having a watchdog that detects a change in camera movement instead of a loop scene wide would be more performance safe?

Where would you put the controls? In the Render>Film setting?
bforartists_2019-04-24_09-07-38

I was opting for placing a button in the N panel of the viewport.
It is intended to work like in real time engines like Unreal Engine with Eye Adaptation that makes the exposure to change also in the viewport, not only for the camera.

I know of the feature, and enjoy it. It’s so believable.

Since it’s a post-process effect, I’d put it with the others (AO, Bloom, etc). Just a recommendation. UE has those settings organized in a similar way.

You are probably right… anyway :slight_smile:


Just finished writing the algorithms for the different modes of autoexposure :smiley: this is going to help a lot of people!

3 Likes

Just curious, would this Post Effect would work on render output?

I’m thinking about it, because it is possible, but how can the user know if the result is good if when you are rendering an animation the rendered image instantly gets saved. I need more experimenting in that area

I guess you would need to somehow remember the last frame exposure and FPS count on change. Like… UE has ticks to tell you how fast the exposure happens on render.

Is there a way, using the bgl module, to access viewport pixels RGB values before the color management correction?

It is working really well! Demos are coming but you can already test the addon here.
Here is how Blender does apply the color management and how the algorithm for the Auto Exposure works:


3 Likes

It is working smooth! Like Unreal Engine Eye Adaptation:

Auto%20Exposure

DEMO VIDEO

6 Likes

Beautiful! Bravo. How is it working on render now? I’ll have to give this a go! Good work.

Still only for the Eevee viewport, going to implement it also for Cycles viewport, then i will release it. Maybe in version 3.1 will add the render support, it’s a big task for an open addon, but fortunately with the addon updater the update will come automatically.

1 Like

Added support for Cycles viewport!
And here is a video showing how the addon works in Eevee:

7 Likes

It’s released!
DOWNLOAD: https://3d-wolf.com/products/camera.html

4 Likes

Congrats dude. You are a genious.

1 Like

Amazing dude! It’s really good