Bloom can now have a dirt texture applied

This commit is contained in:
Mathias Agopian
2020-02-13 17:29:57 -08:00
committed by Mathias Agopian
parent a4e4021950
commit 8bbf0ce9ef
14 changed files with 170 additions and 45 deletions

View File

@@ -249,10 +249,14 @@ Java_com_google_android_filament_View_nSetAmbientOcclusionOptions(JNIEnv*, jclas
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetBloomOptions(JNIEnv*, jclass,
jlong nativeView, jfloat strength, jint resolution, jfloat anamorphism, jint levels,
jlong nativeView, jlong nativeTexture,
jfloat dirtStrength, jfloat strength, jint resolution, jfloat anamorphism, jint levels,
jint blendMode, jboolean threshold, jboolean enabled) {
View* view = (View*) nativeView;
Texture* dirt = (Texture*) nativeTexture;
View::BloomOptions options = {
.dirt = dirt,
.dirtStrength = dirtStrength,
.strength = strength,
.resolution = (uint32_t)resolution,
.anamorphism = anamorphism,

View File

@@ -175,6 +175,9 @@ public class View {
* anamorphism: Bloom's aspect ratio (x/y), for artistic purposes.
* threshold: When enabled, a threshold at 1.0 is applied on the source image, this is
* useful for artistic reasons.
* dirt: A dirt/scratch/smudges texture (that can be RGB), which gets added to the
* bloom effect. Smudges are visible where bloom occurs.
* dirtStrength: Strength of the dirt texture.
*
* @see setBloomOptions
*/
@@ -185,6 +188,17 @@ public class View {
INTERPOLATE
}
/**
* User provided dirt texture
*/
@Nullable
public Texture dirt = null;
/**
* strength of the dirt texture
*/
public float dirtStrength = 0.2f;
/**
* Strength of the bloom effect, between 0.0 and 1.0
*/
@@ -802,7 +816,8 @@ public class View {
*/
public void setBloomOptions(@NonNull BloomOptions options) {
mBloomOptions = options;
nSetBloomOptions(getNativeObject(), options.strength, options.resolution,
nSetBloomOptions(getNativeObject(), options.dirt.getNativeObject(),
options.dirtStrength, options.strength, options.resolution,
options.anamorphism, options.levels, options.blendingMode.ordinal(),
options.threshold, options.enabled);
}
@@ -862,5 +877,5 @@ public class View {
private static native void nSetAmbientOcclusion(long nativeView, int ordinal);
private static native int nGetAmbientOcclusion(long nativeView);
private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity);
private static native void nSetBloomOptions(long nativeView, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled);
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);
}

View File

@@ -34,6 +34,7 @@ class Camera;
class MaterialInstance;
class RenderTarget;
class Scene;
class Texture;
class Viewport;
/**
@@ -140,19 +141,24 @@ public:
* anamorphism: Bloom's aspect ratio (x/y), for artistic purposes.
* threshold: When enabled, a threshold at 1.0 is applied on the source image, this is
* useful for artistic reasons.
* dirt: A dirt/scratch/smudges texture (that can be RGB), which gets added to the
* bloom effect. Smudges are visible where bloom occurs.
* dirtStrength: Strength of the dirt texture.
*/
struct BloomOptions {
enum class BlendMode : uint8_t {
ADD, //!< Bloom is modulated by the strength parameter and added to the scene
INTERPOLATE //!< Bloom is interpolated with the scene using the strength parameter
};
float strength = 0.10f; //!< Between 0.0 and 1.0
uint32_t resolution = 360; //!< Resolution of minor axis (2^levels to 4096)
float anamorphism = 1.0f; //!< Bloom x/y aspect-ratio (1/32 to 32)
uint8_t levels = 6; //!< number of blur levels (3 to 12)
BlendMode blendMode = BlendMode::ADD; //!< How the bloom effect is applied
bool threshold = true; //!< Whether to threshold the source
bool enabled = false; //!< enable or disable bloom
Texture* dirt = nullptr; //!< User provided dirt texture
float dirtStrength = 0.2f; //!< strength of the dirt texture
float strength = 0.10f; //!< Between 0.0 and 1.0
uint32_t resolution = 360; //!< Resolution of minor axis (2^levels to 4096)
float anamorphism = 1.0f; //!< Bloom x/y aspect-ratio (1/32 to 32)
uint8_t levels = 6; //!< number of blur levels (3 to 12)
BlendMode blendMode = BlendMode::ADD; //!< How the bloom effect is applied
bool threshold = true; //!< Whether to threshold the source
bool enabled = false; //!< enable or disable bloom
};
/**

View File

@@ -133,8 +133,11 @@ void PostProcessManager::init() noexcept {
mSeparableGaussianBlurKernelStorageSize = mSeparableGaussianBlur.getMaterial()->reflect("kernel")->size;
DriverApi& driver = mEngine.getDriverApi();
mNoSSAOTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::R8, 0, 1, 1, 1, TextureUsage::DEFAULT);
mDummyOneTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 0, 1, 1, 1, TextureUsage::DEFAULT);
mDummyZeroTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 0, 1, 1, 1, TextureUsage::DEFAULT);
mNoiseTexture = driver.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGB16F, 0, 16, 16, 1, TextureUsage::DEFAULT);
@@ -164,17 +167,19 @@ void PostProcessManager::init() noexcept {
}
driver.update2DImage(mNoiseTexture, 0, 0, 0, 16, 16, std::move(noiseData));
PixelBufferDescriptor data(driver.allocate(1), 1, PixelDataFormat::R, PixelDataType::UBYTE);
auto p = static_cast<uint8_t *>(data.buffer);
*p = 0xFFu;
driver.update2DImage(mNoSSAOTexture, 0, 0, 0, 1, 1, std::move(data));
PixelBufferDescriptor dataOne(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
PixelBufferDescriptor dataZero(driver.allocate(4), 4, PixelDataFormat::RGBA, PixelDataType::UBYTE);
*static_cast<uint32_t *>(dataOne.buffer) = 0xFFFFFFFF;
*static_cast<uint32_t *>(dataZero.buffer) = 0;
driver.update2DImage(mDummyOneTexture, 0, 0, 0, 1, 1, std::move(dataOne));
driver.update2DImage(mDummyZeroTexture, 0, 0, 0, 1, 1, std::move(dataZero));
}
void PostProcessManager::terminate(DriverApi& driver) noexcept {
driver.destroyTexture(mNoSSAOTexture);
driver.destroyTexture(mNoiseTexture);
FEngine& engine = mEngine;
driver.destroyTexture(mDummyOneTexture);
driver.destroyTexture(mDummyZeroTexture);
driver.destroyTexture(mNoiseTexture);
mSSAO.terminate(engine);
mMipmapDepth.terminate(engine);
mBilateralBlur.terminate(engine);
@@ -200,15 +205,26 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::toneMapping(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
FrameGraphId<FrameGraphTexture> bloom;
FrameGraphId<FrameGraphTexture> dirt;
FrameGraphRenderTargetHandle rt;
};
FrameGraphId<FrameGraphTexture> bloomBlur;
FrameGraphId<FrameGraphTexture> bloomDirt;
float bloom = 0.0f;
if (bloomOptions.enabled) {
bloom = clamp(bloomOptions.strength, 0.0f, 1.0f);
bloomBlur = bloomPass(fg, input, TextureFormat::R11F_G11F_B10F, bloomOptions, scale);
if (bloomOptions.dirt) {
FTexture* fdirt = upcast(bloomOptions.dirt);
FrameGraphTexture frameGraphTexture { .texture = fdirt->getHwHandle() };
bloomDirt = fg.import("dirt", {
.width = (uint32_t)fdirt->getWidth(0u),
.height = (uint32_t)fdirt->getHeight(0u),
.format = fdirt->getFormat()
}, frameGraphTexture);
}
}
auto& ppToneMapping = fg.addPass<PostProcessToneMapping>("tonemapping",
@@ -222,33 +238,49 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::toneMapping(FrameGraph& fg,
});
data.rt = builder.createRenderTarget(data.output);
if (!bloomBlur.isValid()) {
// we need a dummy texture
bloomBlur = builder.createTexture("dummy", {});
if (bloomBlur.isValid()) {
data.bloom = builder.sample(bloomBlur);
}
if (bloomDirt.isValid()) {
data.dirt = builder.sample(bloomDirt);
}
data.bloom = builder.sample(bloomBlur);
},
[=](FrameGraphPassResources const& resources,
PostProcessToneMapping const& data, DriverApi& driver) {
auto const& colorTexture = resources.getTexture(data.input);
auto const& bloomTexture = resources.getTexture(data.bloom);
FMaterialInstance* pInstance = mTonemapping.getMaterialInstance();
pInstance->setParameter("colorBuffer", colorTexture, { /* shader uses texelFetch */ });
pInstance->setParameter("bloomBuffer", bloomTexture, {
Handle<HwTexture> colorTexture = resources.getTexture(data.input);
Handle<HwTexture> bloomTexture =
data.bloom.isValid() ? resources.getTexture(data.bloom) : getZeroTexture();
Handle<HwTexture> dirtTexture =
data.dirt.isValid() ? resources.getTexture(data.dirt) : getOneTexture();
FMaterialInstance* mi = mTonemapping.getMaterialInstance();
mi->setParameter("colorBuffer", colorTexture, { /* shader uses texelFetch */ });
mi->setParameter("bloomBuffer", bloomTexture, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR /* always read base level in shader */
});
mi->setParameter("dirtBuffer", dirtTexture, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
float2 bloomParameter{ bloom / float(bloomOptions.levels), 1.0f };
float4 bloomParameter{
bloom / float(bloomOptions.levels),
1.0f,
(bloomOptions.enabled && bloomOptions.dirt) ? bloomOptions.dirtStrength : 0.0f,
0.0f
};
if (bloomOptions.blendMode == View::BloomOptions::BlendMode::INTERPOLATE) {
bloomParameter.y = 1.0f - bloomParameter.x;
}
pInstance->setParameter("dithering", dithering);
pInstance->setParameter("bloom", bloomParameter);
pInstance->setParameter("fxaa", fxaa);
pInstance->commit(driver);
mi->setParameter("dithering", dithering);
mi->setParameter("bloom", bloomParameter);
mi->setParameter("fxaa", fxaa);
mi->commit(driver);
const uint8_t variant = uint8_t(translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
@@ -256,12 +288,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::toneMapping(FrameGraph& fg,
PipelineState pipeline{
.program = mTonemapping.getMaterial()->getProgram(variant),
.rasterState = mTonemapping.getMaterial()->getRasterState(),
.scissor = pInstance->getScissor()
.scissor = mi->getScissor()
};
auto const& target = resources.getRenderTarget(data.rt);
driver.beginRenderPass(target.target, target.params);
pInstance->use(driver);
mi->use(driver);
driver.draw(pipeline, fullScreenRenderPrimitive);
driver.endRenderPass();
});

View File

@@ -77,9 +77,8 @@ public:
FrameGraphId<FrameGraphTexture> output, uint8_t dstLevel,
bool reinhard, size_t kernelWidth, float sigma = 6.0f) noexcept;
backend::Handle<backend::HwTexture> getNoSSAOTexture() const {
return mNoSSAOTexture;
}
backend::Handle<backend::HwTexture> getOneTexture() const { return mDummyOneTexture; }
backend::Handle<backend::HwTexture> getZeroTexture() const { return mDummyZeroTexture; }
private:
details::FEngine& mEngine;
@@ -134,7 +133,8 @@ private:
PostProcessMaterial mTonemapping;
PostProcessMaterial mFxaa;
backend::Handle<backend::HwTexture> mNoSSAOTexture;
backend::Handle<backend::HwTexture> mDummyOneTexture;
backend::Handle<backend::HwTexture> mDummyZeroTexture;
backend::Handle<backend::HwTexture> mNoiseTexture;
size_t mSeparableGaussianBlurKernelStorageSize = 0;

View File

@@ -344,7 +344,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
[&ppm, &js, &view, jobFroxelize]
(FrameGraphPassResources const& resources, auto const& data, DriverApi& driver) {
view.prepareSSAO(data.ssao.isValid() ? resources.getTexture(data.ssao)
: ppm.getNoSSAOTexture());
: ppm.getOneTexture());
view.commitUniforms(driver);
if (jobFroxelize) {
auto sync = jobFroxelize;

View File

@@ -245,6 +245,7 @@ public:
}
void setBloomOptions(BloomOptions options) noexcept {
options.dirtStrength = math::saturate(options.dirtStrength);
options.levels = math::clamp(options.levels, uint8_t(3), uint8_t(12));
mBloomOptions = options;
}

View File

@@ -11,6 +11,11 @@ material {
name : bloomBuffer,
precision: medium
},
{
type : sampler2d,
name : dirtBuffer,
precision: medium
},
{
type : int,
name : dithering
@@ -20,7 +25,7 @@ material {
name : fxaa
},
{
type : float2,
type : float4,
name : bloom
}
],

View File

@@ -154,6 +154,14 @@ public:
*/
void enableSSAO(bool b) { mEnableSsao = b; }
/**
* Enables Bloom.
* Defaults to true.
*/
void enableBloom(bool bloom) {
mBloomOptions.enabled = bloom;
}
/**
* Adjusts the intensity of the IBL.
* See also filament::IndirectLight::setIntensity().

View File

@@ -24,6 +24,7 @@
struct Config {
std::string title;
std::string iblDirectory;
std::string dirt;
float scale = 1.0f;
bool splitView = false;
filament::Engine::Backend backend = filament::Engine::Backend::OPENGL;

View File

@@ -46,6 +46,8 @@
#include "Cube.h"
#include "NativeWindowHelper.h"
#include <stb_image.h>
#include "generated/resources/resources.h"
using namespace filament;
@@ -137,6 +139,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
window->mDepthView->getView()->setClearColor({0, 0, 0, 1});
}
loadDirt(config);
loadIBL(config);
if (mIBL != nullptr) {
mIBL->getSkybox()->setLayerMask(0x7, 0x4);
@@ -452,6 +455,37 @@ void FilamentApp::loadIBL(const Config& config) {
}
}
void FilamentApp::loadDirt(const Config& config) {
if (!config.dirt.empty()) {
Path dirtPath(config.dirt);
if (!dirtPath.exists()) {
std::cerr << "The specified dirt file does not exist: " << dirtPath << std::endl;
return;
}
if (!dirtPath.isFile()) {
std::cerr << "The specified dirt path is not a file: " << dirtPath << std::endl;
return;
}
int w, h, n;
unsigned char* data = stbi_load(dirtPath.getAbsolutePath().c_str(), &w, &h, &n, 3);
assert(n == 3);
mDirt = Texture::Builder()
.width(w)
.height(h)
.format(Texture::InternalFormat::RGB8)
.build(*mEngine);
mDirt->setImage(*mEngine, 0, { data, size_t(w * h * 3),
Texture::Format::RGB, Texture::Type::UBYTE,
(Texture::PixelBufferDescriptor::Callback)&stbi_image_free });
}
}
void FilamentApp::initSDL() {
ASSERT_POSTCONDITION(SDL_Init(SDL_INIT_EVENTS) == 0, "SDL_Init Failure");
}

View File

@@ -76,6 +76,7 @@ public:
filament::Material const* getDefaultMaterial() const noexcept { return mDefaultMaterial; }
filament::Material const* getTransparentMaterial() const noexcept { return mTransparentMaterial; }
IBL* getIBL() const noexcept { return mIBL.get(); }
filament::Texture* getDirtTexture() const noexcept { return mDirt; }
filament::View* getGuiView() const noexcept;
void close() { mClosed = true; }
@@ -199,10 +200,12 @@ private:
void initSDL();
void loadIBL(const Config& config);
void loadDirt(const Config& config);
filament::Engine* mEngine = nullptr;
filament::Scene* mScene = nullptr;
std::unique_ptr<IBL> mIBL;
filament::Texture* mDirt = nullptr;
bool mClosed = false;
uint64_t mTime = 0;

View File

@@ -88,6 +88,8 @@ static void printUsage(char* name) {
" Enable shadow plane\n\n"
" --single\n"
" Only apply the edited material to the first renderable in the scene\n\n"
" --dirt\n"
" Specify a dirt texture\n\n"
);
const std::string from("SAMPLE_MATERIAL");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
@@ -97,7 +99,7 @@ static void printUsage(char* name) {
}
static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
static constexpr const char* OPTSTR = "ha:vps:i:";
static constexpr const char* OPTSTR = "ha:vps:i:d:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
@@ -106,6 +108,7 @@ static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
{ "scale", required_argument, nullptr, 's' },
{ "shadow-plane", no_argument, nullptr, 'p' },
{ "single", no_argument, nullptr, 'n' },
{ "dirt", required_argument, nullptr, 'd' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
@@ -149,6 +152,9 @@ static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
case 'n':
g_singleMode = true;
break;
case 'd':
config->dirt = arg;
break;
}
}
@@ -289,6 +295,8 @@ static void setup(Engine* engine, View*, Scene* scene) {
params.lightIntensity = c.w * pIndirectLight->getIntensity();
params.lightColor = c.rgb;
}
g_params.bloomOptions.dirt = FilamentApp::get().getDirtTexture();
}
static void gui(filament::Engine* engine, filament::View*) {
@@ -401,6 +409,7 @@ static void gui(filament::Engine* engine, filament::View*) {
ImGui::Checkbox("bloom", &params.bloomOptions.enabled);
if (params.bloomOptions.enabled) {
ImGui::SliderFloat("strength", &params.bloomOptions.strength, 0.0f, 1.0f);
ImGui::SliderFloat("dirt", &params.bloomOptions.dirtStrength, 0.0f, 1.0f);
}
ImGui::Checkbox("dithering", &params.dithering);
ImGui::Unindent();
@@ -520,6 +529,5 @@ int main(int argc, char* argv[]) {
g_config.title = "Material Sandbox";
FilamentApp& filamentApp = FilamentApp::get();
filamentApp.run(g_config, setup, cleanup, gui, preRender);
return 0;
}

View File

@@ -2,8 +2,16 @@
// Bloom
//------------------------------------------------------------------------------
vec3 bloom(const vec3 color) {
vec3 bloom(vec3 color) {
highp vec2 uv = variable_vertex.xy;
vec3 blurred = textureLod(materialParams_bloomBuffer, uv, 0.0).rgb;
return blurred * materialParams.bloom.x + color * materialParams.bloom.y;
color = blurred * materialParams.bloom.x + color * materialParams.bloom.y;
if (materialParams.bloom.z > 0.0) {
float dirtIntensity = materialParams.bloom.z;
vec3 dirt = textureLod(materialParams_dirtBuffer, uv, 0.0).rgb;
color += blurred * dirt * dirtIntensity;
}
return color;
}