Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan: Separate near/far dof_blur_amount no longer available (unlike 3.x) #62473

Open
TokisanGames opened this issue Jun 27, 2022 · 11 comments · May be fixed by #62501
Open

Vulkan: Separate near/far dof_blur_amount no longer available (unlike 3.x) #62473

TokisanGames opened this issue Jun 27, 2022 · 11 comments · May be fixed by #62501

Comments

@TokisanGames
Copy link
Contributor

Godot version

4.0.alpha10

System information

Win 10/64 NVIDIA GeForce GTX 1060/PCIe/SSE2

Issue description

Godot 4 has one setting for dof_blur_amount.
Godot 3 has two, one for near and one for far.
Since Godot isn't calculating real DOF, the near and far planes may need to be different values, so both should be provided.

Example 1:

I've written a physical camera for Out of the Ashes that calculates the real depth of field ranges based upon aperture and focal length of my lens. Then I position the DOF blur plane distances according to the math, set the transition value to the same as the distance, and simulate the amount of blur based upon math, a lot of experimentation, and my photographic experience.

Here you can see that the near plane has a strength of 0.12 and the far plane 0.062. Near distance 3.561, Far distance 4.619.

This is not possible in Godot 4, but should be.

image

Example 2:
At https://dofsimulator.net/en/ you can play with camera lenses and position of objects and see how the focus ranges are positioned. This demonstrates why having only one blur setting is physically incorrect.

Here is a 90mm lens at f/4, with the subject 20m away. You can see the background is nearly in focus in the sample image. This is because, as you see on the bottom, the far focus plane is very close to the infinity focus mark, where anything after that distance is in focus. You can see the hyperfocal distance is 34.91m, meaning if the subject is there or farther, the background will be perfectly in focus. However, that does NOT mean everything in front of the subject is also in focus! Imagine if someone's head or hand steps in front of the camera within 5m. Is it also going to be as nearly in focus as the background? No, it's going to be a very blurry mess.

image

Steps to reproduce

Built into the engine.

Minimal reproduction project

No response

@clayjohn
Copy link
Member

clayjohn commented Jun 28, 2022

Sounds good to me. I will be implementing more physical attributes for the camera later this week and will keep this in mind.

If you are happy sharing your code for calculating blur amounts from aperture and focal length, it would be a big help.

@Calinou
Copy link
Member

Calinou commented Jun 28, 2022

Separate amounts for near/far DOF blur was removed in the 4.0 DOF implementation for performance reasons, as it allows performing both DOFs in a single pass. This is also why #50723 in 3.x was reverted.

Tweaking the transition distances can help make it look like you're using different DOF amounts for both, although it's not the same thing.

If you can find a way to have separate amounts for near/far DOF blur without incurring a performance penalty, it would be very welcome 🙂

@TokisanGames
Copy link
Contributor Author

TokisanGames commented Jun 28, 2022

@Calinou It may be faster, but it's wrong. Transition amounts are not going to cut it. There's no way to do what I described above accurately, and have it work in all cases using only transition amounts. You could have a faster, mirrored option for those who do not care about being physically correct, and two for others.

Also a better blur would be good. The current 3.x implementation gets ugly and unusable very fast.

@clayjohn What do you have in mind to do?

@clayjohn
Copy link
Member

We can just pass in a two values, one for blur near and one for blur far:

float get_blur_size(float depth) {
if (params.blur_near_active && depth < params.blur_near_begin) {
return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative
}
if (params.blur_far_active && depth > params.blur_far_begin) {
return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP;
}
return 0.0;
}

@Calinou
Copy link
Member

Calinou commented Jun 28, 2022

@clayjohn I'm looking into it, and I see a bunch of other references to params.blur_size throughout the shader, which don't use get_blur_size(). Can these also be made to use near/blur size as needed?

Also, can raster DOF (used in the mobile renderer) be modified to support separate near/blur DOF sizes?

@clayjohn
Copy link
Member

@clayjohn I'm looking into it, and I see a bunch of other references to params.blur_size throughout the shader, which don't use get_blur_size(). Can these also be made to use near/blur size as needed?

Yes. DoF first generates a radius and stores it in a texture, then during the main pass the radius used is limited by the one that was stored in the texture. That way you can do far and near in a single pass and have different amounts of blur for each. In the second pass blur_size takes on a new meaning, it is more like maximum blur radius (which is why circle mode just outright replaces it). We can just do max(near_blur, far_blur).

Also, can raster DOF (used in the mobile renderer) be modified to support separate near/blur DOF sizes?

Yes, it's the same as the compute version.

@Calinou Calinou added this to the 4.0 milestone Jun 28, 2022
@Calinou Calinou changed the title Separate near/far dof_blur_amount Vulkan: Separate near/far dof_blur_amount no longer available (unlike 3.x) Jun 28, 2022
@clayjohn
Copy link
Member

I was just working on this in #63751

While doing that, I realized that the design of the new DoF is such that blur_amount actually specifies maximum blur amount, while blur transition is used to scale the blur. For example, If your near blur is set at 1 metre, your transition is 1 metre, and your amount is 0.1. to cut near blur in half without affecting far blur, you can just set your transition to 2.

Alternatively, you can use the physically-based formula by setting transition to -1. The physically based formula version uses a realistic curve based on the focus distance (desmos graph for reference) and fixes the maximum blur amount to 0.5.

@TokisanGames
Copy link
Contributor Author

TokisanGames commented Aug 18, 2022

Doubling the near transition plane could work to reduce, but halving won't work to increase. It will be noticeable if the transition area is too short relative to the amount of blur. I'd rather just set the transition amount correctly since I'm constantly adjusting the parameters as the player moves around.

There is a physically defined area of focus, with exact near and far distances around the focus point. I've estimated the near transition distance is the near plane distance. I've estimated the far transition distance as the same as the far plane distance, unless the camera is focusing at or beyond the hyperfocal distance, in which case it is disabled.

I'll test the -1 setting.

Also now that dof scale is fixed, I'm testing #62501 again. Indeed I've found near and far planes affect each other inversely. As one gets stronger, the other gets weaker.

Edit: Or do you mean, rather than fix the engine to provide near and far planes, you can hack the current system by jacking the blur amount, and using extended values for transition as a proxy?

@clayjohn
Copy link
Member

I'll test the -1 setting.

So far it is implemented in #63751 but it hasn't been merged yet

Also now that dof scale is fixed, I'm testing #62501 again. Indeed I've found near and far planes affect each other inversely. As one gets stronger, the other gets weaker.

Yes, I'm not sure that that PR is the way to go. In order to do DoF efficiently the near and far blurs are done simultaneously in the same pass. It's great for efficiency, but it has the side affect of connecting the blur amounts. With a greater number of samples (i.e. on ultra quality) the impact shouldn't be that bad, but it'll be too slow.

I think properly separating the blur amounts will require deeper changes to how blur is calculated.

Edit: Or do you mean, rather than fix the engine to provide near and far planes, you can hack the current system by jacking the blur amount, and using extended values for transition as a proxy?

I was suggesting a workaround in case the physical camera settings don't work for your use-case. But yeah, that's essentially what I am suggesting.

@TokisanGames
Copy link
Contributor Author

FYI, Have you seen this post from Tuxedo Labs?

https://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html

@clayjohn
Copy link
Member

FYI, Have you seen this post from Tuxedo Labs?

https://tuxedolabs.blogspot.com/2018/05/bokeh-depth-of-field-in-single-pass.html

I haven't but looking at it now, it uses very similar code to what we have for box and hexagon bokeh, notable differences are:

  1. He adds average color when rejecting samples, we use the source color of a pixel (we should try average color and see if it improves the look)
  2. He increases radius by RADIUS_SCALE/ radius (so each step gets bigger by an increasing amount) while we increase radius in fixed steps which increase until reaching RADIUS_SCALE

I'll bet some of what he does could be used to improve the quality of our current Bokeh, so thanks for sharing!

As far as this issue is concerned though, his approach also has a single RADIUS_SCALE that gets applied to near blur and far blur. So it'll run into the same issue that we have in that case.

@clayjohn clayjohn modified the milestones: 4.0, 4.x Feb 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants