Add a new tone mapping option using a LUT
This is now the default, and the tone mapper used is now the same on desktop and mobile. In the future this will allow us to implement many features such as color grading at no cpu cost.
This commit is contained in:
committed by
Mathias Agopian
parent
0f6a06a2e1
commit
62a591bc73
@@ -84,6 +84,7 @@ set(SRCS
|
||||
src/SwapChain.cpp
|
||||
src/Stream.cpp
|
||||
src/Texture.cpp
|
||||
src/Tonemapper.cpp
|
||||
src/UniformBuffer.cpp
|
||||
src/View.cpp
|
||||
src/Viewport.cpp
|
||||
@@ -129,6 +130,7 @@ set(PRIVATE_HDRS
|
||||
src/details/Stream.h
|
||||
src/details/SwapChain.h
|
||||
src/details/Texture.h
|
||||
src/details/Tonemapper.h
|
||||
src/details/VertexBuffer.h
|
||||
src/details/View.h
|
||||
src/FilamentAPI-impl.h
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "details/Material.h"
|
||||
#include "details/MaterialInstance.h"
|
||||
#include "details/Texture.h"
|
||||
#include "details/Tonemapper.h"
|
||||
#include "generated/resources/materials.h"
|
||||
|
||||
#include <private/filament/SibGenerator.h>
|
||||
@@ -145,6 +146,8 @@ void PostProcessManager::init() noexcept {
|
||||
*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));
|
||||
|
||||
mTonemapper = new Tonemapper(mEngine);
|
||||
}
|
||||
|
||||
void PostProcessManager::terminate(DriverApi& driver) noexcept {
|
||||
@@ -165,6 +168,7 @@ void PostProcessManager::terminate(DriverApi& driver) noexcept {
|
||||
mFxaa.terminate(engine);
|
||||
mDoFBlur.terminate(engine);
|
||||
mDoF.terminate(engine);
|
||||
mTonemapper->terminate(engine);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -235,6 +239,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::toneMapping(FrameGraph& fg,
|
||||
data.dirt.isValid() ? resources.getTexture(data.dirt) : getOneTexture();
|
||||
|
||||
FMaterialInstance* mi = mTonemapping.getMaterialInstance();
|
||||
mi->setParameter("lut", mTonemapper->getHwHandle(), {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("colorBuffer", colorTexture, { /* shader uses texelFetch */ });
|
||||
mi->setParameter("bloomBuffer", bloomTexture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
@@ -280,6 +288,10 @@ void PostProcessManager::toneMappingSubpass(DriverApi& driver,
|
||||
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
|
||||
|
||||
FMaterialInstance* mi = mTonemappingWithSubpass.getMaterialInstance();
|
||||
mi->setParameter("lut", mTonemapper->getHwHandle(), {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("dithering", dithering);
|
||||
mi->setParameter("fxaa", fxaa);
|
||||
mi->commit(driver);
|
||||
|
||||
@@ -28,11 +28,12 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FEngine;
|
||||
class FMaterial;
|
||||
class FMaterialInstance;
|
||||
class FEngine;
|
||||
class FView;
|
||||
class RenderPass;
|
||||
class Tonemapper;
|
||||
struct CameraInfo;
|
||||
|
||||
class PostProcessManager {
|
||||
@@ -148,6 +149,9 @@ private:
|
||||
backend::Handle<backend::HwTexture> mDummyZeroTexture;
|
||||
|
||||
size_t mSeparableGaussianBlurKernelStorageSize = 0;
|
||||
|
||||
// eventually this will be a separate object
|
||||
Tonemapper* mTonemapper = nullptr;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
111
filament/src/Tonemapper.cpp
Normal file
111
filament/src/Tonemapper.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "details/Tonemapper.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <filament/Color.h>
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace math;
|
||||
using namespace backend;
|
||||
|
||||
static constexpr size_t LUT_DIMENSION = 32u;
|
||||
|
||||
|
||||
Tonemapper::Tonemapper(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// create the texture, we use RGBA16F because RGB16F is not supported everywhere
|
||||
mLutHandle = driver.createTexture(SamplerType::SAMPLER_3D, 1,
|
||||
TextureFormat::RGBA16F, 0,
|
||||
LUT_DIMENSION, LUT_DIMENSION, LUT_DIMENSION, TextureUsage::DEFAULT);
|
||||
|
||||
// it's okay to allocate the data in the command stream because it's only 256 KiB
|
||||
constexpr size_t lutSize = LUT_DIMENSION * LUT_DIMENSION * LUT_DIMENSION;
|
||||
constexpr size_t elementSize = sizeof(half4);
|
||||
void* data = malloc(lutSize * elementSize);
|
||||
PixelBufferDescriptor lutData(data,
|
||||
lutSize * elementSize, PixelDataFormat::RGBA, PixelDataType::HALF,
|
||||
[](void* buffer, size_t, void*) {
|
||||
free(buffer);
|
||||
}
|
||||
);
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
half4* p = (half4*)data;
|
||||
for (size_t b = 0; b < LUT_DIMENSION; b++) {
|
||||
for (size_t g = 0; g < LUT_DIMENSION; g++) {
|
||||
for (size_t r = 0; r < LUT_DIMENSION; r++) {
|
||||
float3 v = float3{ r, g, b } * (1.0 / (LUT_DIMENSION - 1u));
|
||||
v.x = lutToLinear(v.x);
|
||||
v.y = lutToLinear(v.y);
|
||||
v.z = lutToLinear(v.z);
|
||||
v = tonemap_ACES(v);
|
||||
v = Color::toSRGB(v);
|
||||
*p++ = half4{ v, 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
std::chrono::duration<float, std::milli> duration = std::chrono::steady_clock::now() - now;
|
||||
slog.d << "LUT generation time: " << duration.count() << " ms" << io::endl;
|
||||
|
||||
driver.update3DImage(mLutHandle, 0,
|
||||
0, 0, 0,
|
||||
LUT_DIMENSION, LUT_DIMENSION, LUT_DIMENSION, std::move(lutData));
|
||||
}
|
||||
|
||||
Tonemapper::~Tonemapper() noexcept = default;
|
||||
|
||||
void Tonemapper::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyTexture(mLutHandle);
|
||||
}
|
||||
|
||||
float3 Tonemapper::tonemap_ACES(float3 x) noexcept {
|
||||
// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
|
||||
const float a = 2.51f;
|
||||
const float b = 0.03f;
|
||||
const float c = 2.43f;
|
||||
const float d = 0.59f;
|
||||
const float e = 0.14f;
|
||||
return (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
}
|
||||
|
||||
float Tonemapper::lutToLinear(float x) noexcept {
|
||||
// Alexa LogC EI 1000 curve
|
||||
const float ia = 1.0f / 5.555556f;
|
||||
const float b = 0.047996f;
|
||||
const float ic = 1.0f / 0.244161f;
|
||||
const float d = 0.386036f;
|
||||
return (std::pow(10.0f, (x - d) * ic) - b) * ia;
|
||||
}
|
||||
|
||||
float Tonemapper::linearToLut(float x) noexcept {
|
||||
// Alexa LogC EI 1000 curve
|
||||
const float a = 5.555556f;
|
||||
const float b = 0.047996f;
|
||||
const float c = 0.244161f;
|
||||
const float d = 0.386036f;
|
||||
return c * std::log10(a * x + b) + d;
|
||||
}
|
||||
|
||||
} //namespace filament
|
||||
48
filament/src/details/Tonemapper.h
Normal file
48
filament/src/details/Tonemapper.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DETAILS_TONEMAPPER_H
|
||||
#define TNT_FILAMENT_DETAILS_TONEMAPPER_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FEngine;
|
||||
|
||||
class Tonemapper {
|
||||
public:
|
||||
Tonemapper(FEngine& engine);
|
||||
|
||||
~Tonemapper() noexcept;
|
||||
|
||||
// frees driver resources, object becomes invalid
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
backend::TextureHandle getHwHandle() const noexcept { return mLutHandle; }
|
||||
|
||||
private:
|
||||
static math::float3 tonemap_ACES(math::float3 x) noexcept;
|
||||
static float lutToLinear(float x) noexcept;
|
||||
static float linearToLut(float x) noexcept;
|
||||
|
||||
backend::TextureHandle mLutHandle;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif //TNT_FILAMENT_DETAILS_TONEMAPPER_H
|
||||
@@ -4,7 +4,12 @@ material {
|
||||
{
|
||||
type : sampler2d,
|
||||
name : colorBuffer,
|
||||
precision: high
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : sampler3d,
|
||||
name : lut,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
@@ -64,7 +69,7 @@ fragment {
|
||||
if (materialParams.bloom.x > 0.0) {
|
||||
color.rgb = bloom(color.rgb);
|
||||
}
|
||||
color.rgb = tonemap(color.rgb);
|
||||
color.rgb = tonemap(materialParams_lut, color.rgb);
|
||||
color.rgb = OECF(color.rgb);
|
||||
if (materialParams.fxaa > 0) {
|
||||
color.a = luminance(color.rgb);
|
||||
@@ -75,7 +80,7 @@ fragment {
|
||||
if (materialParams.bloom.x > 0.0) {
|
||||
color.rgb = bloom(color.rgb);
|
||||
}
|
||||
color.rgb = tonemap(color.rgb);
|
||||
color.rgb = tonemap(materialParams_lut, color.rgb);
|
||||
color.rgb = OECF(color.rgb);
|
||||
color.rgb *= color.a + FLT_EPS;
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
material {
|
||||
name : tonemap,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler3d,
|
||||
name : lut,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : dithering
|
||||
@@ -8,10 +13,6 @@ material {
|
||||
{
|
||||
type : int,
|
||||
name : fxaa
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : bloom
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
@@ -50,7 +51,7 @@ fragment {
|
||||
vec4 resolve() {
|
||||
#if POST_PROCESS_OPAQUE
|
||||
vec4 color = vec4(resolveFragment(ivec2(getUV())), 1.0);
|
||||
color.rgb = tonemap(color.rgb);
|
||||
color.rgb = tonemap(materialParams_lut, color.rgb);
|
||||
color.rgb = OECF(color.rgb);
|
||||
if (materialParams.fxaa > 0) {
|
||||
color.a = luminance(color.rgb);
|
||||
@@ -58,7 +59,7 @@ fragment {
|
||||
#else
|
||||
vec4 color = resolveAlphaFragment(ivec2(getUV()));
|
||||
color.rgb /= color.a + FLT_EPS;
|
||||
color.rgb = tonemap(color.rgb);
|
||||
color.rgb = tonemap(materialParams_lut, color.rgb);
|
||||
color.rgb = OECF(color.rgb);
|
||||
color.rgb *= color.a + FLT_EPS;
|
||||
#endif
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define CONVERSION_FUNCTION_sRGB 1
|
||||
#define CONVERSION_FUNCTION_sRGB_FAST 2
|
||||
|
||||
#if TONE_MAPPING_OPERATOR <= TONE_MAPPING_FILMIC_ALU
|
||||
#if TONE_MAPPING_OPERATOR < TONE_MAPPING_LINEAR
|
||||
#define CONVERSION_FUNCTION CONVERSION_FUNCTION_LINEAR
|
||||
#else
|
||||
#ifdef TARGET_MOBILE
|
||||
|
||||
@@ -3,22 +3,27 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Operators for LDR output
|
||||
|
||||
// Operators with built-in sRGB
|
||||
#define TONE_MAPPING_UNREAL 0
|
||||
#define TONE_MAPPING_FILMIC_ALU 1
|
||||
#define TONE_MAPPING_LINEAR 2 // Operators with built-in sRGB go above
|
||||
#define TONE_MAPPING_REINHARD 3
|
||||
#define TONE_MAPPING_ACES 4
|
||||
#define TONE_MAPPING_LUT 2
|
||||
|
||||
// Operators without built-in sRGB
|
||||
#define TONE_MAPPING_LINEAR 3
|
||||
#define TONE_MAPPING_REINHARD 4
|
||||
#define TONE_MAPPING_ACES 5
|
||||
|
||||
// Operators for HDR output
|
||||
#define TONE_MAPPING_ACES_REC2020_1K 5
|
||||
#define TONE_MAPPING_ACES_REC2020_1K 6
|
||||
|
||||
// Debug operators
|
||||
#define TONE_MAPPING_DISPLAY_RANGE 9
|
||||
|
||||
#ifdef TARGET_MOBILE
|
||||
#define TONE_MAPPING_OPERATOR TONE_MAPPING_UNREAL
|
||||
#define TONE_MAPPING_OPERATOR TONE_MAPPING_LUT
|
||||
#else
|
||||
#define TONE_MAPPING_OPERATOR TONE_MAPPING_ACES
|
||||
#define TONE_MAPPING_OPERATOR TONE_MAPPING_LUT
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -59,6 +64,16 @@ vec3 Tonemap_ACES(const vec3 x) {
|
||||
return (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
}
|
||||
|
||||
vec3 Tonemap_LUT(mediump sampler3D lut, const vec3 x) {
|
||||
// Alexa LogC EI 1000
|
||||
const float a = 5.555556;
|
||||
const float b = 0.047996;
|
||||
const float c = 0.244161 / log2(10.0);
|
||||
const float d = 0.386036;
|
||||
vec3 logc = c * log2(a * x + b) + d;
|
||||
return textureLod(lut, logc, 0.0).rgb;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tone-mapping operators for HDR output
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -145,7 +160,7 @@ vec3 Tonemap_DisplayRange(const vec3 x) {
|
||||
* pre-exposed. Our HDR to LDR tone mapping operators are designed to tone-map
|
||||
* the range [0..~8] to [0..1].
|
||||
*/
|
||||
vec3 tonemap(const vec3 x) {
|
||||
vec3 tonemap(mediump sampler3D lut, const vec3 x) {
|
||||
#if TONE_MAPPING_OPERATOR == TONE_MAPPING_UNREAL
|
||||
return Tonemap_Unreal(x);
|
||||
#elif TONE_MAPPING_OPERATOR == TONE_MAPPING_FILMIC_ALU
|
||||
@@ -160,6 +175,8 @@ vec3 tonemap(const vec3 x) {
|
||||
return Tonemap_ACES_Rec2020_1k(x);
|
||||
#elif TONE_MAPPING_OPERATOR == TONE_MAPPING_DISPLAY_RANGE
|
||||
return Tonemap_DisplayRange(x);
|
||||
#elif TONE_MAPPING_OPERATOR == TONE_MAPPING_LUT
|
||||
return Tonemap_LUT(lut, x);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user