Feedback

What's your question?

By: [ Editor ] Asked from United Kingdom

Simulating defocus in a theoretical lens

Hi all,

I'm trying to figure out some maths at the moment, and was hoping someone might know what happens inside the theoretical camera lens...

What I'm trying to get a function for is, for a specified focal length/aperture, focused to a specified distance away, what sized area on the image plane will a point at a different specified distance be.

I'm planning on making a Defocus node where you set the defocus based on camera, lens and location information rather than just a pixel size.

I need to do a bit more checking on this, but this is what I understand so far:

The f/stop is defined as a ratio between the physical aperture and the focal length of the lens. So the actual aperture opening is the equivalent of:

aperture diameter = focal length x f-stop aperture distance (from image plane) = focal length

My question is this:

In this simplified model of the lens, where is the theoretical refraction point? There has to be some plane where the light rays are bent to ensure that they converge on the image plane. Is this fixed down, or can it vary between lenses? If it can vary between lenses, does this mean that different makes of lenses at the same focal length and aperture can have different sized bokeh?

I'm hoping that it will turn out that a lens can be simulated by bending the rays at the actual aperture plane, but it may well be a little more complex than that.

Thanks for any advice on this!

Edit: Solution

Thanks to Matt T for help on this one. I've put my Shake node below, for anyone who's interested.

This node allows you to specify the size and resolution of the sensor/film back. I didn't have the specs of the 35mm film back size to hand - I made it default to 24mmx18mm, which is close enough for now. You can also specify from a drop-down list what your units for focus and object distance are (all other units - sensor size, focal length - are assumed to be in mm)

CameraDefocus.h:

image CameraDefocus(image in = 0, float focalLength = 50, float fStop = 2.8, string distanceUnits = "feet", float focusDistance = 25.0, float objectDistance = 10.0, int sensorPixelsX = GetDefaultWidth(), int sensorPixelsY = GetDefaultHeight(), float sensorWidth = 24, float sensorHeight = 18.0)
{
Defocus1 = Defocus(in, defocusDiameter / pixelWidth, defocusDiameter / pixelHeight,
"rgba", 100, "circle", 0.95, 1,
    float pixelWidth = sensorWidth / sensorPixelsX,
    float pixelHeight = sensorHeight / sensorPixelsY,
    float distanceMultiplier = distanceUnits=="meters"?1000:distanceUnits=="feet"?304.8:distanceUnits=="inches"?25.4:1,
    float focusDistanceMM = focusDistance * distanceMultiplier,
    float objectDistanceMM = objectDistance * distanceMultiplier,
    float m = ((focusDistanceMM * focalLength) / (focusDistanceMM - focalLength)) / focusDistanceMM,
    float defocusDiameter = ((focalLength * m) / fStop) * (fabs(objectDistanceMM - focusDistanceMM) / objectDistanceMM)
    );

    return Defocus1;
}

CameraDefocusUI.h:

nuiPushMenu("Tools");
nuiPushToolBox("Filter");
    nuiToolBoxItem("@CameraDefocus",CameraDefocus());
nuiPopToolBox();
nuiPopMenu();

nuiDefSlider("CameraDefocus.focalLength",0,500,1);
nuiDefSlider("CameraDefocus.fStop",0,50,0.1);
nuxDefMultiChoice("CameraDefocus.distanceUnits", "meters|millimeters|feet|inches");
nuiDefSlider("CameraDefocus.focusDistance",0,1000,-1);
nuiDefSlider("CameraDefocus.objectDistance",0,1000,-1);
nuxDefSlider("CameraDefocus.sensorPixelsX",0,GetDefaultWidth(),1);
nuxDefSlider("CameraDefocus.sensorPixelsY",0,GetDefaultHeight(),1);
nuxDefSlider("CameraDefocus.sensorWidth", 0, 50, 0.1);
nuxDefSlider("CameraDefocus.sensorHeight", 0, 50, 0.1);
  • 4

matt t [ Editor ]

Hi Hugh,

I wrote a macro for Shake to do what you are asking a few years back using a modified 'Defocus' node. I agree with Andrew that you can treat any lens in this case as a simple thin lens. There are however slight variations in the maths depending on what sort of imaging you are using. ie the maths for macro work is different to telephoto work. However I found the differences are pretty negligable in practice so a general formula is fine.

A good reference site http://www.dofmaster.com/equations.html

General notes on the macro below.

1) All measurements in meters except focal length in mm 2) This is currently setup for 2k scans (2048x1556) of 35mm film. 3) I have arguably assumed the coc (circle of confusion) to be the width of a pixel at this resolution. Therefore this is an editable parameter. Any thoughts on this assumption would be welcome. 4) I have calculated the actual diameter of the blur spot as a ratio to coc so it should be flexible for all image formats. This means as I used a pixel to define my coc then the ratio equates to size in pixels of defocus. 5) I would love to get a definitive coc value for this macro by testing with real cameras and lenses, until then it it best to try the default value. 6) Disclaimer - I am not a programmer or a writer of code and this was created using Shake's internal macro maker. So apologies if the code is shabby:-). This has however been used on many top live action and cg movies over the last couple of years.

image CameraFocus(
image In=0,
float f_stop=4,
float focal_length=35,
float FocusDistance=5,
float ObjectDistance=5,
const char *channels="rgba",
const char *shape="circle",
float boostPoint=0.95,
float superWhite=1,
)
{
Defocus1 = Defocus(In, blur_spot_diam<CoC? 1 : blur_spot_diam/CoC, 
    xPixels, channels, 100, shape, boostPoint, superWhite, float CoC = 0.01215, 
    float f_stop = f_stop, float focal_length = focal_length, 
    float Hyperfocal = H/1000, float NearDistance = Dn/1000, 
    float FarDistance = Df/1000, float FocusDistance = FocusDistance, 
    float ObjectDistance = ObjectDistance, float H = (f*f)/(N*CoC), 
    float f = focal_length, float s = FocusDistance*1000, float Dn = (H*s)/(H+s), 
    float Df = (H*s)/(H-s), float N = f_stop, float blur_spot_diam = ((f*m)/N)*((fabs(o-s))/o), 
    float m = ((s*f)/(s-f))/s, float o = ObjectDistance*1000);

return Defocus1;

}

and the UI

nuiPushMenu("Tools");
nuiPushToolBox("User");
    nuiToolBoxItem(",CameraFocus());
nuiPopToolBox();
nuiPopMenu();

nuiDefSlider("CameraFocus.f_stop",0,64,0.1);
nuiDefSlider("CameraFocus.focal_length",0,500,1);
nuiDefSlider("CameraFocus.FocusDistance",0,1000,-1);
nuiDefSlider("CameraFocus.ObjectDistance",0,1000,-1);
nuiDefControlAlias("CameraFocus.shape","shape");
nuiDefSlider("CameraFocus.boostPoint",0,1,0);
nuiDefSlider("CameraFocus.superWhite",1,20,0);

Hopefully this will get you in the ballpark even if you are not working in Shake. I can deconstruct it a bit if you need it simplified. If you are using shake and would like the ZCameraFocus macro that uses a Z channel to create depth of field then give me a shout.

Cheers,

Matt

NN comments
hugh_gid
-

Very cool stuff, Matt! Thanks for posting that. I am, I think, going to want to try to understand what you’re doing there rather than just using it straight-off, but it’s good to see! I was (am) going to give the user controls for sensor size and resolution (so that they can still defocus an oversized image as if it were 2k 35mm)

I’m not sure I can give you more than one up vote, but if I could, I would!

matt t
-

Hugh, the main key to understanding it for me was that all equations you find are for systems in focus! Therefore I created a variable called ‘blurspotdiam’ which is the size of the coc circle of a system that is not in focus. The rest comes from using the known equations and a bit of pythagoras. Good luck!

hugh_gid
-

I’ve just edited my question with my own node that I’ve put together for this. It’s a little more extensive than yours – allowing different units for focus and object distance, and allowing you to specify the sensor size/resolution.

Thanks for the help!

or Cancel