A new depth of field (DoF) post-process effect
This commit is contained in:
committed by
Mathias Agopian
parent
64609cc4aa
commit
4e873e8007
@@ -35,6 +35,7 @@ A new header is inserted each time a *tag* is created.
|
||||
light's intensity in candela.
|
||||
- Fixed an issue where some `ShadowOptions` were not being respected when passed to
|
||||
`LightManager::Builder`.
|
||||
- Added a Depth of Field post-processing effect
|
||||
|
||||
## v1.5.2
|
||||
|
||||
|
||||
@@ -252,3 +252,10 @@ Java_com_google_android_filament_View_nSetBlendMode(JNIEnv *, jclass , jlong nat
|
||||
View* view = (View*) nativeView;
|
||||
view->setBlendMode((View::BlendMode)blendMode);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetDepthOfFieldOptions(JNIEnv *, jclass ,
|
||||
jlong nativeView, jfloat focusDistance, jfloat blurScale, jboolean enabled) {
|
||||
View* view = (View*) nativeView;
|
||||
view->setDepthOfFieldOptions({ .focusDistance = focusDistance, .blurScale = blurScale, .enabled = (bool)enabled });
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class View {
|
||||
private FogOptions mFogOptions;
|
||||
private RenderTarget mRenderTarget;
|
||||
private BlendMode mBlendMode;
|
||||
private DepthOfFieldOptions mDepthOfFieldOptions;
|
||||
|
||||
/**
|
||||
* Generic Quality Level
|
||||
@@ -310,6 +311,23 @@ public class View {
|
||||
public boolean enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to control Depth of Field (DoF) effect in the scene
|
||||
*
|
||||
* @see View#setDepthOfFieldOptions
|
||||
*/
|
||||
public static class DepthOfFieldOptions {
|
||||
|
||||
/** focus distance in world units */
|
||||
public float focusDistance = 10.0f;
|
||||
|
||||
/** scale factor controlling the amount of blur (values other than 1.0 are not physically correct)*/
|
||||
public float blurScale = 1.0f;
|
||||
|
||||
/** enable or disable Depth of field effect */
|
||||
public boolean enabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure used to set the color precision for the rendering of a <code>View</code>.
|
||||
*
|
||||
@@ -977,6 +995,32 @@ public class View {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets Depth of Field options.
|
||||
*
|
||||
* @param options Options for depth of field effect.
|
||||
* @see #getDepthOfFieldOptions
|
||||
*/
|
||||
public void setDepthOfFieldOptions(@NonNull DepthOfFieldOptions options) {
|
||||
mDepthOfFieldOptions = options;
|
||||
nSetDepthOfFieldOptions(getNativeObject(), options.focusDistance, options.blurScale, options.enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Depth of Field options
|
||||
*
|
||||
* @return Depth of Field options currently set.
|
||||
* @see #setDepthOfFieldOptions
|
||||
*/
|
||||
@NonNull
|
||||
public DepthOfFieldOptions getDepthOfFieldOptions() {
|
||||
if (mDepthOfFieldOptions == null) {
|
||||
mDepthOfFieldOptions = new DepthOfFieldOptions();
|
||||
}
|
||||
return mDepthOfFieldOptions;
|
||||
}
|
||||
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed View");
|
||||
@@ -1016,4 +1060,5 @@ public class View {
|
||||
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled);
|
||||
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, boolean enabled);
|
||||
private static native void nSetBlendMode(long nativeView, int blendMode);
|
||||
private static native void nSetDepthOfFieldOptions(long nativeView, float focusDistance, float blurScale, boolean enabled);
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ set(PRIVATE_HDRS
|
||||
|
||||
set(MATERIAL_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/dof.mat
|
||||
src/materials/dofBlur.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/blitMedium.mat
|
||||
src/materials/blitHigh.mat
|
||||
@@ -226,6 +228,18 @@ add_custom_command(
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/dof.filamat"
|
||||
DEPENDS src/materials/dofUtils.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/dofBlur.filamat"
|
||||
DEPENDS src/materials/dofUtils.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${MATERIAL_BINS}
|
||||
|
||||
@@ -165,6 +165,15 @@ public:
|
||||
bool enabled = false; //!< enable or disable fog
|
||||
};
|
||||
|
||||
/**
|
||||
* Options to control Depth of Field (DoF) effect in the scene
|
||||
*/
|
||||
struct DepthOfFieldOptions {
|
||||
float focusDistance = 10.0f; //!< focus distance in world units
|
||||
float blurScale = 1.0f; //!< a scale factor for the amount of blur
|
||||
bool enabled = false; //!< enable or disable Depth of field effect
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure used to set the precision of the color buffer and related quality settings.
|
||||
*
|
||||
@@ -485,6 +494,13 @@ public:
|
||||
*/
|
||||
void setFogOptions(FogOptions options) noexcept;
|
||||
|
||||
/**
|
||||
* Enables or disables Depth of Field. Disabled by default.
|
||||
*
|
||||
* @param options options
|
||||
*/
|
||||
void setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept;
|
||||
|
||||
/**
|
||||
* Queries the bloom options.
|
||||
*
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <filament/Exposure.h>
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
@@ -60,7 +61,7 @@ void UTILS_NOINLINE FCamera::setProjection(double fov, double aspect, double nea
|
||||
|
||||
void FCamera::setLensProjection(double focalLength, double aspect, double near, double far) noexcept {
|
||||
// a 35mm camera has a 36x24mm wide frame size
|
||||
double theta = 2.0 * std::atan(24.0 / (2.0 * focalLength));
|
||||
double theta = 2.0 * std::atan(SENSOR_SIZE * 1000.0f / (2.0 * focalLength));
|
||||
theta *= 180.0 / math::F_PI;
|
||||
FCamera::setProjection(theta, aspect, near, far, Fov::VERTICAL);
|
||||
}
|
||||
@@ -234,6 +235,34 @@ Frustum FCamera::getFrustum(mat4 const& projection, mat4f const& viewMatrix) noe
|
||||
return Frustum(mat4f{ projection * viewMatrix });
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
CameraInfo::CameraInfo(FCamera const& camera) noexcept {
|
||||
projection = mat4f{ camera.getProjectionMatrix() };
|
||||
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
|
||||
model = camera.getModelMatrix();
|
||||
view = camera.getViewMatrix();
|
||||
zn = camera.getNear();
|
||||
zf = camera.getCullingFar();
|
||||
ev100 = Exposure::ev100(camera);
|
||||
f = (FCamera::SENSOR_SIZE * (float)projection[1][1]) * 0.5f;
|
||||
A = f / camera.getAperture();
|
||||
}
|
||||
|
||||
CameraInfo::CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept {
|
||||
const mat4f modelMatrix{ worldOriginCamera * camera.getModelMatrix() };
|
||||
projection = mat4f{ camera.getProjectionMatrix() };
|
||||
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
|
||||
model = modelMatrix;
|
||||
view = FCamera::getViewMatrix(model);
|
||||
zn = camera.getNear();
|
||||
zf = camera.getCullingFar();
|
||||
ev100 = Exposure::ev100(camera);
|
||||
f = (FCamera::SENSOR_SIZE * (float)projection[1][1]) * 0.5f;
|
||||
A = f / camera.getAperture();
|
||||
worldOffset = camera.getPosition();
|
||||
worldOrigin = worldOriginCamera;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ void PostProcessManager::init() noexcept {
|
||||
mBlit[2] = PostProcessMaterial(mEngine, MATERIALS_BLITHIGH_DATA, MATERIALS_BLITHIGH_SIZE);
|
||||
mTonemapping = PostProcessMaterial(mEngine, MATERIALS_TONEMAPPING_DATA, MATERIALS_TONEMAPPING_SIZE);
|
||||
mFxaa = PostProcessMaterial(mEngine, MATERIALS_FXAA_DATA, MATERIALS_FXAA_SIZE);
|
||||
mDoFBlur = PostProcessMaterial(mEngine, MATERIALS_DOFBLUR_DATA, MATERIALS_DOFBLUR_SIZE);
|
||||
mDoF = PostProcessMaterial(mEngine, MATERIALS_DOF_DATA, MATERIALS_DOF_SIZE);
|
||||
|
||||
// UBO storage size.
|
||||
// The effective kernel size is (kMaxPositiveKernelSize - 1) * 4 + 1.
|
||||
@@ -157,6 +159,8 @@ void PostProcessManager::terminate(DriverApi& driver) noexcept {
|
||||
mBlit[2].terminate(engine);
|
||||
mTonemapping.terminate(engine);
|
||||
mFxaa.terminate(engine);
|
||||
mDoFBlur.terminate(engine);
|
||||
mDoF.terminate(engine);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -316,6 +320,148 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
|
||||
return ppFXAA.getData().output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
const View::DepthOfFieldOptions& dofOptions,
|
||||
const CameraInfo& cameraInfo) noexcept {
|
||||
|
||||
FEngine& engine = mEngine;
|
||||
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
|
||||
|
||||
FrameGraphId<FrameGraphTexture> depth = fg.getBlackboard().get<FrameGraphTexture>("structure");
|
||||
assert(depth.isValid());
|
||||
|
||||
const size_t sampleCount = 25; // (keep in sync with dofUtils.fs)
|
||||
const float focusDistance = std::max(cameraInfo.zn, dofOptions.focusDistance);
|
||||
auto const& desc = fg.getDescriptor<FrameGraphTexture>(input);
|
||||
const float Kc = (cameraInfo.A * cameraInfo.f) / (focusDistance - cameraInfo.f);
|
||||
const float Ks = ((float)desc.height / sampleCount) / FCamera::SENSOR_SIZE;
|
||||
const float2 cocParams{
|
||||
// we use 1/zn instead of (zf - zn) / (zf * zn), because in reality we're using
|
||||
// a projection with an infinite far plane
|
||||
(dofOptions.blurScale * Ks * Kc) * focusDistance / cameraInfo.zn,
|
||||
(dofOptions.blurScale * Ks * Kc) * (1.0f - focusDistance / cameraInfo.zn)
|
||||
};
|
||||
|
||||
struct PostProcessDoFBlur {
|
||||
FrameGraphId<FrameGraphTexture> color;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> vertical;
|
||||
FrameGraphId<FrameGraphTexture> diagonal;
|
||||
FrameGraphRenderTargetHandle rt;
|
||||
};
|
||||
|
||||
auto& ppDoFBlur = fg.addPass<PostProcessDoFBlur>("dofblur",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto const& inputDesc = fg.getDescriptor(input);
|
||||
data.color = builder.sample(input);
|
||||
data.depth = builder.sample(depth);
|
||||
data.vertical = builder.createTexture("dof vertical output", {
|
||||
.width = inputDesc.width,
|
||||
.height = inputDesc.height,
|
||||
.format = inputDesc.format
|
||||
});
|
||||
data.vertical = builder.write(data.vertical);
|
||||
data.diagonal = builder.createTexture("dof diagonal output", {
|
||||
.width = inputDesc.width,
|
||||
.height = inputDesc.height,
|
||||
.format = inputDesc.format
|
||||
});
|
||||
data.diagonal = builder.write(data.diagonal);
|
||||
data.rt = builder.createRenderTarget("DoF Target", {
|
||||
.attachments = {
|
||||
{ data.vertical, data.diagonal }, {}, {}
|
||||
}
|
||||
});
|
||||
},
|
||||
[=](FrameGraphPassResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
auto const& desc = resources.getDescriptor(data.color);
|
||||
auto const& color = resources.getTexture(data.color);
|
||||
auto const& depth = resources.getTexture(data.depth);
|
||||
auto const& out = resources.get(data.rt);
|
||||
|
||||
PostProcessMaterial& material = mDoFBlur;
|
||||
FMaterialInstance* const mi = material.getMaterialInstance();
|
||||
mi->setParameter("color", color, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("depth", depth, {
|
||||
.filterMin = SamplerMinFilter::NEAREST
|
||||
});
|
||||
mi->setParameter("resolution", float4{
|
||||
desc.width, desc.height, 1.0f / desc.width, 1.0f / desc.height });
|
||||
mi->setParameter("cocParams", cocParams);
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState());
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive);
|
||||
driver.endRenderPass();
|
||||
});
|
||||
|
||||
struct PostProcessDoF {
|
||||
FrameGraphId<FrameGraphTexture> vertical;
|
||||
FrameGraphId<FrameGraphTexture> diagonal;
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> output;
|
||||
FrameGraphRenderTargetHandle rt;
|
||||
};
|
||||
|
||||
auto& ppDoF = fg.addPass<PostProcessDoF>("dof",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto const& inputDesc = fg.getDescriptor(input);
|
||||
data.vertical = builder.sample(ppDoFBlur.getData().vertical);
|
||||
data.diagonal = builder.sample(ppDoFBlur.getData().diagonal);
|
||||
data.depth = builder.sample(depth);
|
||||
data.output = builder.createTexture("dof output", {
|
||||
.width = inputDesc.width,
|
||||
.height = inputDesc.height,
|
||||
.format = inputDesc.format
|
||||
});
|
||||
data.output = builder.write(data.output);
|
||||
data.rt = builder.createRenderTarget("DoF Target", {
|
||||
.attachments = { data.output }
|
||||
});
|
||||
},
|
||||
[=](FrameGraphPassResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
auto const& desc = resources.getDescriptor(data.vertical);
|
||||
auto const& vertical = resources.getTexture(data.vertical);
|
||||
auto const& diagonal = resources.getTexture(data.diagonal);
|
||||
auto const& depth = resources.getTexture(data.depth);
|
||||
auto const& out = resources.get(data.rt);
|
||||
|
||||
PostProcessMaterial& material = mDoF;
|
||||
FMaterialInstance* const mi = material.getMaterialInstance();
|
||||
mi->setParameter("buffer0", vertical, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("buffer1", diagonal, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("depth", depth, {
|
||||
.filterMin = SamplerMinFilter::NEAREST
|
||||
});
|
||||
mi->setParameter("resolution", float4{
|
||||
desc.width, desc.height, 1.0f / desc.width, 1.0f / desc.height });
|
||||
mi->setParameter("cocParams", cocParams);
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState());
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive);
|
||||
driver.endRenderPass();
|
||||
});
|
||||
|
||||
return ppDoF.getData().output;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor outDesc) noexcept {
|
||||
|
||||
@@ -770,7 +916,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
FrameGraphId<FrameGraphTexture> output, uint8_t dstLevel,
|
||||
bool reinhard, size_t kernelWidth, float sigmaRatio) noexcept {
|
||||
|
||||
const float sigma = (kernelWidth + 1) / sigmaRatio;
|
||||
const float sigma = (kernelWidth + 1.0f) / sigmaRatio;
|
||||
|
||||
Handle<HwRenderPrimitive> fullScreenRenderPrimitive = mEngine.getFullScreenRenderPrimitive();
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ public:
|
||||
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
|
||||
bool translucent) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> dof(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
const View::DepthOfFieldOptions& dofOptions,
|
||||
const details::CameraInfo& cameraInfo) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> opaqueBlit(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor outDesc) noexcept;
|
||||
|
||||
@@ -134,6 +139,8 @@ private:
|
||||
PostProcessMaterial mBlit[3];
|
||||
PostProcessMaterial mTonemapping;
|
||||
PostProcessMaterial mFxaa;
|
||||
PostProcessMaterial mDoFBlur;
|
||||
PostProcessMaterial mDoF;
|
||||
|
||||
backend::Handle<backend::HwTexture> mDummyOneTexture;
|
||||
backend::Handle<backend::HwTexture> mDummyZeroTexture;
|
||||
|
||||
@@ -204,14 +204,17 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
uint8_t msaa = view.getSampleCount();
|
||||
float2 scale = view.updateScale(mFrameInfoManager.getLastFrameInfo());
|
||||
const View::QualityLevel upscalingQuality = view.getDynamicResolutionOptions().quality;
|
||||
auto bloomOptions = view.getBloomOptions();
|
||||
auto dofOptions = view.getDepthOfFieldOptions();
|
||||
auto aoOptions = view.getAmbientOcclusionOptions();
|
||||
if (!hasPostProcess) {
|
||||
// dynamic scaling and FXAA are part of the post-process phase and can't happen if
|
||||
// it's disabled.
|
||||
fxaa = false;
|
||||
dithering = false;
|
||||
scale = 1.0f;
|
||||
// disable all effects that are part of post-processing
|
||||
msaa = 1;
|
||||
dofOptions.enabled = false;
|
||||
bloomOptions.enabled = false;
|
||||
dithering = false;
|
||||
fxaa = false;
|
||||
scale = 1.0f;
|
||||
}
|
||||
|
||||
const bool scaled = any(notEqual(scale, float2(1.0f)));
|
||||
@@ -382,9 +385,12 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
TextureFormat::RGBA8 : getLdrFormat(translucent); // e.g. RGB8 or RGBA8
|
||||
|
||||
if (hasPostProcess) {
|
||||
if (dofOptions.enabled) {
|
||||
input = ppm.dof(fg, input, dofOptions, cameraInfo);
|
||||
}
|
||||
if (toneMapping) {
|
||||
input = ppm.toneMapping(fg, input, ldrFormat, translucent, fxaa, scale,
|
||||
view.getBloomOptions(), dithering);
|
||||
bloomOptions, dithering);
|
||||
}
|
||||
if (fxaa) {
|
||||
input = ppm.fxaa(fg, input, ldrFormat, !toneMapping || translucent);
|
||||
|
||||
@@ -80,16 +80,9 @@ void ShadowMap::render(DriverApi& driver, Handle<HwRenderTarget> rt,
|
||||
params.viewport = viewport;
|
||||
|
||||
FCamera const& camera = getCamera();
|
||||
details::CameraInfo cameraInfo = {
|
||||
.projection = mat4f{ camera.getProjectionMatrix() },
|
||||
.cullingProjection = mat4f{ camera.getCullingProjectionMatrix() },
|
||||
.model = camera.getModelMatrix(),
|
||||
.view = camera.getViewMatrix(),
|
||||
.zn = camera.getNear(),
|
||||
.zf = camera.getCullingFar(),
|
||||
};
|
||||
pass.setCamera(cameraInfo);
|
||||
details::CameraInfo cameraInfo(camera);
|
||||
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
|
||||
view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), range);
|
||||
|
||||
@@ -391,32 +391,10 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are
|
||||
}
|
||||
|
||||
// Note: for debugging (i.e. visualize what the camera / objects are doing, using
|
||||
// the viewing camera), we can set worldOriginCamera to identity when mViewingCamera
|
||||
// is set: e.g.
|
||||
// worldOriginCamera = mViewingCamera ? mat4f{} : worldOriginScene
|
||||
// the viewing camera), we can set worldOriginScene to identity when mViewingCamera
|
||||
// is set
|
||||
mViewingCameraInfo = CameraInfo(*camera, worldOriginScene);
|
||||
|
||||
const mat4f worldOriginCamera = worldOriginScene;
|
||||
const mat4f model{ worldOriginCamera * camera->getModelMatrix() };
|
||||
mViewingCameraInfo = CameraInfo{
|
||||
// projection with infinite z-far
|
||||
.projection = mat4f{ camera->getProjectionMatrix() },
|
||||
// projection used for culling, with finite z-far
|
||||
.cullingProjection = mat4f{ camera->getCullingProjectionMatrix() },
|
||||
// camera model matrix -- apply the world origin to it
|
||||
.model = model,
|
||||
// camera view matrix
|
||||
.view = FCamera::getViewMatrix(model),
|
||||
// near plane
|
||||
.zn = camera->getNear(),
|
||||
// far plane
|
||||
.zf = camera->getCullingFar(),
|
||||
// exposure
|
||||
.ev100 = Exposure::ev100(*camera),
|
||||
// world offset to allow users to determine the API-level camera position
|
||||
.worldOffset = camera->getPosition(),
|
||||
// world origin transform, use only for debugging
|
||||
.worldOrigin = worldOriginCamera
|
||||
};
|
||||
mCullingFrustum = FCamera::getFrustum(
|
||||
mCullingCamera->getCullingProjectionMatrix(),
|
||||
FCamera::getViewMatrix(worldOriginScene * mCullingCamera->getModelMatrix()));
|
||||
@@ -431,7 +409,7 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are
|
||||
* Light culling: runs in parallel with Renderable culling (below)
|
||||
*/
|
||||
|
||||
auto prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
|
||||
auto *prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
|
||||
[&frustum = mCullingFrustum, &engine, scene](JobSystem& js, JobSystem::Job*) {
|
||||
FView::prepareVisibleLights(
|
||||
engine.getLightManager(), js, frustum, scene->getLightData());
|
||||
@@ -981,6 +959,10 @@ void View::setFogOptions(View::FogOptions options) noexcept {
|
||||
upcast(this)->setFogOptions(options);
|
||||
}
|
||||
|
||||
void View::setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept {
|
||||
upcast(this)->setDepthOfFieldOptions(options);
|
||||
}
|
||||
|
||||
View::BloomOptions View::getBloomOptions() const noexcept {
|
||||
return upcast(this)->getBloomOptions();
|
||||
}
|
||||
@@ -993,5 +975,4 @@ View::BlendMode View::getBlendMode() const noexcept {
|
||||
return upcast(this)->getBlendMode();
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -39,6 +39,8 @@ class FEngine;
|
||||
*/
|
||||
class FCamera : public Camera {
|
||||
public:
|
||||
// a 35mm camera has a 36x24mm wide frame size
|
||||
static constexpr const float SENSOR_SIZE = 0.024f; // 24mm
|
||||
|
||||
FCamera(FEngine& engine, utils::Entity e);
|
||||
|
||||
@@ -169,14 +171,20 @@ private:
|
||||
};
|
||||
|
||||
struct CameraInfo {
|
||||
math::mat4f projection;
|
||||
math::mat4f cullingProjection;
|
||||
math::mat4f model;
|
||||
math::mat4f view;
|
||||
float zn;
|
||||
float zf;
|
||||
float ev100 = 0.0f;
|
||||
math::float3 worldOffset;
|
||||
CameraInfo() noexcept = default;
|
||||
explicit CameraInfo(FCamera const& camera) noexcept;
|
||||
CameraInfo(FCamera const& camera, const math::mat4f& worldOriginCamera) noexcept;
|
||||
|
||||
math::mat4f projection; // projection matrix for drawing (infinite zfar)
|
||||
math::mat4f cullingProjection; // projection matrix for culling
|
||||
math::mat4f model; // camera model matrix
|
||||
math::mat4f view; // camera view matrix
|
||||
float zn{}; // distance (positive) to the near plane
|
||||
float zf{}; // distance (positive) to the far plane
|
||||
float ev100{}; // exposure
|
||||
float f{}; // focal length (in m)
|
||||
float A{}; // aperture diameter (in m)
|
||||
math::float3 worldOffset{}; // world offset, API-level camera position
|
||||
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
|
||||
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
|
||||
|
||||
|
||||
@@ -270,6 +270,10 @@ public:
|
||||
mBloomOptions = options;
|
||||
}
|
||||
|
||||
BloomOptions getBloomOptions() const noexcept {
|
||||
return mBloomOptions;
|
||||
}
|
||||
|
||||
void setFogOptions(FogOptions options) noexcept {
|
||||
options.distance = std::max(0.0f, options.distance);
|
||||
options.maximumOpacity = math::clamp(options.maximumOpacity, 0.0f, 1.0f);
|
||||
@@ -280,8 +284,14 @@ public:
|
||||
mFogOptions = options;
|
||||
}
|
||||
|
||||
BloomOptions getBloomOptions() const noexcept {
|
||||
return mBloomOptions;
|
||||
void setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept {
|
||||
options.focusDistance = std::max(0.0f, options.focusDistance);
|
||||
options.blurScale = std::max(0.0f, options.blurScale);
|
||||
mDepthOfFieldOptions = options;
|
||||
}
|
||||
|
||||
DepthOfFieldOptions getDepthOfFieldOptions() const noexcept {
|
||||
return mDepthOfFieldOptions;
|
||||
}
|
||||
|
||||
void setBlendMode(BlendMode blendMode) noexcept {
|
||||
@@ -378,6 +388,7 @@ private:
|
||||
AmbientOcclusionOptions mAmbientOcclusionOptions{};
|
||||
BloomOptions mBloomOptions;
|
||||
FogOptions mFogOptions;
|
||||
DepthOfFieldOptions mDepthOfFieldOptions;
|
||||
BlendMode mBlendMode = BlendMode::OPAQUE;
|
||||
|
||||
DynamicResolutionOptions mDynamicResolution;
|
||||
|
||||
63
filament/src/materials/dof.mat
Normal file
63
filament/src/materials/dof.mat
Normal file
@@ -0,0 +1,63 @@
|
||||
material {
|
||||
name : DepthOfField,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : buffer0,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : buffer1,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : depth,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : cocParams
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
postProcess.vertex.xy = postProcess.normalizedUV;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#include "dofUtils.fs"
|
||||
|
||||
const vec2 kDirVertical= unitvec(PI / 2.0 + BOKEH_ROTATION_ANGLE);
|
||||
const vec2 kDirDiamond = unitvec(3.0 * PI / 4.0 + BOKEH_ROTATION_ANGLE);
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy;
|
||||
|
||||
float depth = textureLod(materialParams_depth, uv, 0.0).r;
|
||||
float coc = getCOC(depth, materialParams.cocParams);
|
||||
|
||||
vec4 box = blurTexture(materialParams_buffer0, uv, kDirVertical, depth, coc);
|
||||
vec4 diamond = blurTexture(materialParams_buffer1, uv, kDirDiamond, depth, coc);
|
||||
vec4 dof = min(box, diamond);
|
||||
|
||||
postProcess.color = dof;
|
||||
}
|
||||
|
||||
}
|
||||
75
filament/src/materials/dofBlur.mat
Normal file
75
filament/src/materials/dofBlur.mat
Normal file
@@ -0,0 +1,75 @@
|
||||
material {
|
||||
name : DepthOfFieldBlur,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : color,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : depth,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : cocParams
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
postProcess.vertex.xy = postProcess.normalizedUV;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
/*
|
||||
* Separable octogonal blur
|
||||
*
|
||||
* This uses a lot of ideas from
|
||||
* "Hexagonal Bokeh Blur Revisited" by Colin Barré-Brisebois
|
||||
* (https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/)
|
||||
*
|
||||
* "Efficiently Simulating the Bokeh of Polygonal Apertures in a Post-Process Depth of Field Shader"
|
||||
* by L. McIntosh, B. E. Riecke and S. DiPaola
|
||||
*/
|
||||
|
||||
layout(location = 1) out vec4 fragColor1;
|
||||
|
||||
#include "dofUtils.fs"
|
||||
|
||||
const vec2 kDirHorizontal = unitvec(0.0 + BOKEH_ROTATION_ANGLE);
|
||||
const vec2 kDirDiagonal = unitvec(PI / 4.0 + BOKEH_ROTATION_ANGLE);
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy;
|
||||
|
||||
float depth = textureLod(materialParams_depth, uv, 0.0).r;
|
||||
float coc = getCOC(depth, materialParams.cocParams);
|
||||
|
||||
vec4 horizontal = blurTexture(materialParams_color, uv, kDirHorizontal, depth, coc);
|
||||
vec4 diagonal = blurTexture(materialParams_color, uv, kDirDiagonal, depth, coc);
|
||||
|
||||
// Output to MRTs
|
||||
postProcess.color = horizontal;
|
||||
#if defined(TARGET_MOBILE)
|
||||
diagonal = clamp(diagonal, 0.0, MEDIUMP_FLT_MAX);
|
||||
#endif
|
||||
fragColor1 = diagonal;
|
||||
}
|
||||
|
||||
}
|
||||
75
filament/src/materials/dofUtils.fs
Normal file
75
filament/src/materials/dofUtils.fs
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
/*
|
||||
* DoF blur
|
||||
*
|
||||
* This uses a lot of ideas from
|
||||
* "Bokeh depth of field in a single pass" by Dennis Gustafsson
|
||||
* (http://blog.tuxedolabs.com/2018/05/04/bokeh-depth-of-field-in-single-pass.html)
|
||||
*
|
||||
* needs:
|
||||
* materialParams_depth : depth texture
|
||||
* materialParams.cocParams : coc scale and bias
|
||||
* materialParams.resolution : screen resolution
|
||||
*/
|
||||
|
||||
// Filter sample count, prefer odd values
|
||||
// (keep in sync with PostProcessManager.cpp:dof())
|
||||
const float SAMPLE_COUNT = 25.0;
|
||||
|
||||
// A value > 1.0 allows a larger blur w/ dithering
|
||||
const float BLUR_SCALE = 1.0;
|
||||
|
||||
// This is here just for aesthetic reasons
|
||||
const float BOKEH_ROTATION_ANGLE = PI / 6.0;
|
||||
|
||||
#define unitvec(angle) vec2(cos(angle), sin(angle))
|
||||
|
||||
// random number between 0 and 1, using interleaved gradient noise
|
||||
float random(const highp vec2 w) {
|
||||
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
|
||||
return fract(m.z * fract(dot(w, m.xy)));
|
||||
}
|
||||
|
||||
float getCOC(float depth, vec2 cocParams) {
|
||||
float CoC = abs(depth * cocParams.x + cocParams.y);
|
||||
return saturate(CoC);
|
||||
}
|
||||
|
||||
void tap(inout vec4 finalColor, inout float blurAmount, float radius,
|
||||
highp vec2 uv, sampler2D colorBuffer, float centerDepth, float centerCoc) {
|
||||
float depth = textureLod(materialParams_depth, uv, 0.0).r;
|
||||
float coc = getCOC(depth, materialParams.cocParams);
|
||||
vec4 color = textureLod(colorBuffer, uv, 0.0);
|
||||
|
||||
// prevent blurry background to bleed onto sharp foreground
|
||||
if (depth > centerDepth) {
|
||||
coc = clamp(coc, 0.0, centerCoc * 2.0);
|
||||
}
|
||||
|
||||
float m = step(radius * BLUR_SCALE, coc * (SAMPLE_COUNT * BLUR_SCALE));
|
||||
finalColor += mix(finalColor * (1.0 / blurAmount), color, m);
|
||||
blurAmount += 1.0;
|
||||
}
|
||||
|
||||
vec4 blurTexture(sampler2D colorBuffer, highp vec2 uv, highp vec2 direction, float centerDepth, float centerCoc) {
|
||||
float blurAmount = 1.0;
|
||||
vec4 finalColor = textureLod(colorBuffer, uv, 0.0);
|
||||
|
||||
highp vec2 unit = materialParams.resolution.zw;
|
||||
direction *= unit * BLUR_SCALE;
|
||||
|
||||
float noise = 0.0;
|
||||
if (BLUR_SCALE != 1.0) {
|
||||
// we span 2 samples because the first sample is always fixed (no noise added)
|
||||
noise = (random(gl_FragCoord.xy) - 0.5) * 2.0;
|
||||
uv += direction * noise;
|
||||
}
|
||||
|
||||
vec4 tc = uv.xyxy;
|
||||
for (float radius = 1.0 ; radius < (SAMPLE_COUNT * 0.5); radius += 1.0) {
|
||||
tc += vec4(direction, -direction);
|
||||
tap(finalColor, blurAmount, radius + noise, tc.xy, colorBuffer, centerDepth, centerCoc);
|
||||
tap(finalColor, blurAmount, radius - noise, tc.zw, colorBuffer, centerDepth, centerCoc);
|
||||
}
|
||||
return finalColor * (1.0 / blurAmount);
|
||||
}
|
||||
@@ -94,7 +94,7 @@ fragment {
|
||||
}
|
||||
|
||||
// random number between 0 and 1, using interleaved gradient noise
|
||||
float random(vec2 w) {
|
||||
float random(const highp vec2 w) {
|
||||
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
|
||||
return fract(m.z * fract(dot(w, m.xy)));
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ struct App {
|
||||
sRGBColor backgroundColor = { 0.0f };
|
||||
} viewOptions;
|
||||
|
||||
View::DepthOfFieldOptions dofOptions;
|
||||
|
||||
struct Scene {
|
||||
Entity groundPlane;
|
||||
VertexBuffer* groundVertexBuffer;
|
||||
@@ -396,10 +398,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera")) {
|
||||
ImGui::SliderFloat("Focal length", &FilamentApp::get().getCameraFocalLength(), 16.0f, 90.0f);
|
||||
ImGui::SliderFloat("Focal length (mm)", &FilamentApp::get().getCameraFocalLength(), 16.0f, 90.0f);
|
||||
ImGui::SliderFloat("Aperture", &app.viewOptions.cameraAperture, 1.0f, 32.0f);
|
||||
ImGui::SliderFloat("Speed", &app.viewOptions.cameraSpeed, 800.0f, 1.0f);
|
||||
ImGui::SliderFloat("Speed (1/s)", &app.viewOptions.cameraSpeed, 1000.0f, 1.0f);
|
||||
ImGui::SliderFloat("ISO", &app.viewOptions.cameraISO, 25.0f, 6400.0f);
|
||||
ImGui::Checkbox("DoF", &app.dofOptions.enabled);
|
||||
ImGui::SliderFloat("Focus distance", &app.dofOptions.focusDistance, 0.0f, 30.0f);
|
||||
ImGui::SliderFloat("Blur scale", &app.dofOptions.blurScale, 0.1f, 10.0f);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -451,6 +456,8 @@ int main(int argc, char** argv) {
|
||||
1.0f / app.viewOptions.cameraSpeed,
|
||||
app.viewOptions.cameraISO);
|
||||
|
||||
view->setDepthOfFieldOptions(app.dofOptions);
|
||||
|
||||
app.scene.groundMaterial->setDefaultParameter(
|
||||
"strength", app.viewOptions.groundShadowStrength);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user