* New experimental tone mapper The new tone mapper proposed in this change replaces Uchimura (see below) and is composed of two distinct algorithms: - The first is the customizable compression curve from Timothy Lotte (from GDC 2015). The curve's default parameters were chosen to match the overall appearance (contrast) of the ACES curves used as our current default tone mapper. This was done to provide a good migration path to making this new solution the default in a future version of Filament. The compression curve can be customized via its contrast, shoulder, input middle gray, output middle gray, and maximum "HDR value" (scene referred). It is important to note that the compression is only applied to the luminance of the input scene referred value. - The second is a novel approach by Troy Sobotka called Exposure Value Invariant Luminance Scaling (or EVILS). This solution works on the largest chromatic channel of the input value as opposed to mapping all RGB channels as we do in our other tone mappers. The algorithm effectively scales all channel by a ratio that depends on both the compressed luminance from the first stage and the luminance of input value normalized by the largest channel. This new solution offers important benefits as it manages to preserve chromaticity for high input values. It is also designed to provide a desirable rolloff to white on overexposure. This means that even without gamut mapping, this solution avoids many hue skews (red to orange, blue to purple, etc.). The results provided by this solution are much more natural and make using other color grading tools much easier. * Use std::pow instead of pow * Fix Web builds
1036 lines
47 KiB
C++
1036 lines
47 KiB
C++
/*
|
|
* Copyright (C) 2018 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 <iostream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include <getopt/getopt.h>
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <filagui/ImGuiExtensions.h>
|
|
|
|
#include <utils/Path.h>
|
|
|
|
#include <filament/Camera.h>
|
|
#include <filament/Engine.h>
|
|
#include <filament/Exposure.h>
|
|
#include <filament/DebugRegistry.h>
|
|
#include <filament/IndirectLight.h>
|
|
#include <filament/IndexBuffer.h>
|
|
#include <filament/LightManager.h>
|
|
#include <filament/Material.h>
|
|
#include <filament/MaterialInstance.h>
|
|
#include <filament/Renderer.h>
|
|
#include <filament/RenderableManager.h>
|
|
#include <filament/Scene.h>
|
|
#include <filament/TransformManager.h>
|
|
#include <filament/View.h>
|
|
#include <filament/VertexBuffer.h>
|
|
|
|
#include <math/mat3.h>
|
|
#include <math/mat4.h>
|
|
#include <math/vec4.h>
|
|
#include <math/norm.h>
|
|
|
|
#include <filamentapp/Config.h>
|
|
#include <filamentapp/IBL.h>
|
|
#include <filamentapp/FilamentApp.h>
|
|
#include <filamentapp/MeshAssimp.h>
|
|
|
|
#include "material_sandbox.h"
|
|
|
|
using namespace filament::math;
|
|
using namespace filament;
|
|
using namespace filamat;
|
|
using namespace utils;
|
|
|
|
static std::vector<Path> g_filenames;
|
|
|
|
static Scene* g_scene = nullptr;
|
|
|
|
std::unique_ptr<MeshAssimp> g_meshSet;
|
|
static std::map<std::string, MaterialInstance*> g_meshMaterialInstances;
|
|
static SandboxParameters g_params;
|
|
static ColorGradingOptions g_lastColorGradingOptions;
|
|
static ColorGrading* g_colorGrading = nullptr;
|
|
static Config g_config;
|
|
static bool g_shadowPlane = false;
|
|
static bool g_singleMode = false;
|
|
static float g_rangePlot[1024 * 3];
|
|
static float g_curvePlot[1024 * 3];
|
|
|
|
const static ImVec2 verticalSliderSize(18.0f, 160.0f);
|
|
const static ImVec2 plotLinesSize(320.0f, 160.0f);
|
|
const static ImVec2 plotLinesWideSize(480.0f, 120.0f);
|
|
|
|
static void printUsage(char* name) {
|
|
std::string exec_name(Path(name).getName());
|
|
std::string usage(
|
|
"SAMPLE_MATERIAL showcases all material models\n"
|
|
"Usage:\n"
|
|
" SAMPLE_MATERIAL [options] <mesh files (.obj, .fbx)>\n"
|
|
"Options:\n"
|
|
" --help, -h\n"
|
|
" Prints this message\n\n"
|
|
" --api, -a\n"
|
|
" Specify the backend API: opengl (default), vulkan, or metal\n\n"
|
|
" --ibl=<path>, -i <path>\n"
|
|
" Applies an IBL\n\n"
|
|
" path can either be a directory containing IBL data files generated by cmgen,\n"
|
|
" or, a .hdr equiretangular image file\n\n"
|
|
" --split-view, -v\n"
|
|
" Splits the window into 4 views\n\n"
|
|
" --scale=[number], -s [number]\n"
|
|
" Applies uniform scale\n\n"
|
|
" --shadow-plane, -p\n"
|
|
" 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"
|
|
" --camera=<camera mode>, -c <camera mode>\n"
|
|
" Set the camera mode: orbit (default) or flight\n"
|
|
" Flight mode uses the following controls:\n"
|
|
" Click and drag the mouse to pan the camera\n"
|
|
" Use the scroll weel to adjust movement speed\n"
|
|
" W / S: forward / backward\n"
|
|
" A / D: left / right\n"
|
|
" E / Q: up / down\n"
|
|
);
|
|
const std::string from("SAMPLE_MATERIAL");
|
|
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
|
usage.replace(pos, from.length(), exec_name);
|
|
}
|
|
std::cout << usage;
|
|
}
|
|
|
|
static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
|
|
static constexpr const char* OPTSTR = "ha:vps:i:d:c:";
|
|
static const struct option OPTIONS[] = {
|
|
{ "help", no_argument, nullptr, 'h' },
|
|
{ "api", required_argument, nullptr, 'a' },
|
|
{ "ibl", required_argument, nullptr, 'i' },
|
|
{ "split-view", no_argument, nullptr, 'v' },
|
|
{ "scale", required_argument, nullptr, 's' },
|
|
{ "shadow-plane", no_argument, nullptr, 'p' },
|
|
{ "single", no_argument, nullptr, 'n' },
|
|
{ "dirt", required_argument, nullptr, 'd' },
|
|
{ "camera", required_argument, nullptr, 'c' },
|
|
{ nullptr, 0, nullptr, 0 } // termination of the option list
|
|
};
|
|
int opt;
|
|
int option_index = 0;
|
|
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
|
|
std::string arg(optarg ? optarg : "");
|
|
switch (opt) {
|
|
default:
|
|
case 'h':
|
|
printUsage(argv[0]);
|
|
exit(0);
|
|
case 'a':
|
|
if (arg == "opengl") {
|
|
config->backend = Engine::Backend::OPENGL;
|
|
} else if (arg == "vulkan") {
|
|
config->backend = Engine::Backend::VULKAN;
|
|
} else if (arg == "metal") {
|
|
config->backend = Engine::Backend::METAL;
|
|
} else {
|
|
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'." << std::endl;
|
|
}
|
|
break;
|
|
case 'c':
|
|
if (arg == "flight") {
|
|
config->cameraMode = camutils::Mode::FREE_FLIGHT;
|
|
} else if (arg == "orbit") {
|
|
config->cameraMode = camutils::Mode::ORBIT;
|
|
} else {
|
|
std::cerr << "Unrecognized camera mode. Must be 'flight'|'orbit'.\n";
|
|
}
|
|
break;
|
|
case 'i':
|
|
config->iblDirectory = arg;
|
|
break;
|
|
case 's':
|
|
try {
|
|
config->scale = std::stof(arg);
|
|
} catch (std::invalid_argument& e) {
|
|
// keep scale of 1.0
|
|
} catch (std::out_of_range& e) {
|
|
// keep scale of 1.0
|
|
}
|
|
break;
|
|
case 'v':
|
|
config->splitView = true;
|
|
break;
|
|
case 'p':
|
|
g_shadowPlane = true;
|
|
break;
|
|
case 'n':
|
|
g_singleMode = true;
|
|
break;
|
|
case 'd':
|
|
config->dirt = arg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return optind;
|
|
}
|
|
|
|
static void cleanup(Engine* engine, View*, Scene*) {
|
|
for (const auto& material : g_meshMaterialInstances) {
|
|
engine->destroy(material.second);
|
|
}
|
|
|
|
for (auto& i : g_params.materialInstance) {
|
|
engine->destroy(i);
|
|
}
|
|
|
|
for (auto& i : g_params.material) {
|
|
engine->destroy(i);
|
|
}
|
|
|
|
g_meshSet.reset(nullptr);
|
|
|
|
engine->destroy(g_params.light);
|
|
engine->destroy(g_params.spotLight);
|
|
engine->destroy(g_colorGrading);
|
|
|
|
EntityManager& em = EntityManager::get();
|
|
em.destroy(g_params.light);
|
|
em.destroy(g_params.spotLight);
|
|
}
|
|
|
|
static void setup(Engine* engine, View*, Scene* scene) {
|
|
g_scene = scene;
|
|
|
|
g_meshSet = std::make_unique<MeshAssimp>(*engine);
|
|
|
|
createInstances(g_params, *engine);
|
|
|
|
for (auto& filename : g_filenames) {
|
|
g_meshSet->addFromFile(filename, g_meshMaterialInstances);
|
|
}
|
|
|
|
auto& tcm = engine->getTransformManager();
|
|
auto ei = tcm.getInstance(g_meshSet->getRenderables()[0]);
|
|
tcm.setTransform(ei, mat4f{ mat3f(g_config.scale), float3(0.0f, 0.0f, -4.0f) } *
|
|
tcm.getWorldTransform(ei));
|
|
|
|
size_t count = 0;
|
|
auto& rcm = engine->getRenderableManager();
|
|
for (auto renderable : g_meshSet->getRenderables()) {
|
|
auto instance = rcm.getInstance(renderable);
|
|
if (!instance) continue;
|
|
|
|
rcm.setCastShadows(instance, g_params.castShadows);
|
|
rcm.setScreenSpaceContactShadows(instance, true);
|
|
|
|
if (!g_singleMode || count == 0) {
|
|
for (size_t i = 0; i < rcm.getPrimitiveCount(instance); i++) {
|
|
rcm.setMaterialInstanceAt(instance, i, g_params.materialInstance[MATERIAL_LIT]);
|
|
}
|
|
} else {
|
|
ei = tcm.getInstance(renderable);
|
|
tcm.setTransform(ei, mat4f{ mat3f(g_config.scale), float3(0.0f, 0.0f, -3.0f) } *
|
|
tcm.getWorldTransform(ei));
|
|
}
|
|
count++;
|
|
|
|
scene->addEntity(renderable);
|
|
}
|
|
|
|
scene->addEntity(g_params.light);
|
|
|
|
// Parent the spot light to the root renderable in the mesh.
|
|
tcm.create(g_params.spotLight, tcm.getInstance(g_meshSet->getRenderables()[0]));
|
|
g_params.spotLightPosition = float3{0.0, 1.0, 0.0f};
|
|
|
|
if (g_shadowPlane) {
|
|
EntityManager& em = EntityManager::get();
|
|
Material* shadowMaterial = Material::Builder()
|
|
.package(RESOURCES_GROUNDSHADOW_DATA, RESOURCES_GROUNDSHADOW_SIZE)
|
|
.build(*engine);
|
|
shadowMaterial->setDefaultParameter("strength", 0.7f);
|
|
|
|
const static uint32_t indices[] = {
|
|
0, 1, 2, 2, 3, 0
|
|
};
|
|
|
|
const static filament::math::float3 vertices[] = {
|
|
{ -10, 0, -10 },
|
|
{ -10, 0, 10 },
|
|
{ 10, 0, 10 },
|
|
{ 10, 0, -10 },
|
|
};
|
|
|
|
short4 tbn = filament::math::packSnorm16(
|
|
mat3f::packTangentFrame(
|
|
filament::math::mat3f{
|
|
float3{ 1.0f, 0.0f, 0.0f },
|
|
float3{ 0.0f, 0.0f, 1.0f },
|
|
float3{ 0.0f, 1.0f, 0.0f }
|
|
}
|
|
).xyzw);
|
|
|
|
const static filament::math::short4 normals[] { tbn, tbn, tbn, tbn };
|
|
|
|
VertexBuffer* vertexBuffer = VertexBuffer::Builder()
|
|
.vertexCount(4)
|
|
.bufferCount(2)
|
|
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
|
.attribute(VertexAttribute::TANGENTS, 1, VertexBuffer::AttributeType::SHORT4)
|
|
.normalized(VertexAttribute::TANGENTS)
|
|
.build(*engine);
|
|
|
|
vertexBuffer->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(
|
|
vertices, vertexBuffer->getVertexCount() * sizeof(vertices[0])));
|
|
vertexBuffer->setBufferAt(*engine, 1, VertexBuffer::BufferDescriptor(
|
|
normals, vertexBuffer->getVertexCount() * sizeof(normals[0])));
|
|
|
|
IndexBuffer* indexBuffer = IndexBuffer::Builder()
|
|
.indexCount(6)
|
|
.build(*engine);
|
|
|
|
indexBuffer->setBuffer(*engine, IndexBuffer::BufferDescriptor(
|
|
indices, indexBuffer->getIndexCount() * sizeof(uint32_t)));
|
|
|
|
Entity planeRenderable = em.create();
|
|
RenderableManager::Builder(1)
|
|
.boundingBox({{ 0, 0, 0 },
|
|
{ 10, 1e-4f, 10 }})
|
|
.material(0, shadowMaterial->getDefaultInstance())
|
|
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
|
|
vertexBuffer, indexBuffer, 0, 6)
|
|
.culling(false)
|
|
.receiveShadows(true)
|
|
.castShadows(false)
|
|
.build(*engine, planeRenderable);
|
|
|
|
scene->addEntity(planeRenderable);
|
|
|
|
tcm.setTransform(tcm.getInstance(planeRenderable),
|
|
filament::math::mat4f::translation(float3{ 0, -1, -4 }));
|
|
}
|
|
|
|
auto* ibl = FilamentApp::get().getIBL();
|
|
if (ibl) {
|
|
auto& params = g_params;
|
|
IndirectLight* const pIndirectLight = ibl->getIndirectLight();
|
|
params.lightDirection = IndirectLight::getDirectionEstimate(ibl->getSphericalHarmonics());
|
|
float4 c = pIndirectLight->getColorEstimate(ibl->getSphericalHarmonics(), params.lightDirection);
|
|
params.lightIntensity = c.w * pIndirectLight->getIntensity();
|
|
params.lightColor = c.rgb;
|
|
}
|
|
|
|
g_params.bloomOptions.dirt = FilamentApp::get().getDirtTexture();
|
|
}
|
|
|
|
static filament::MaterialInstance* updateInstances(
|
|
SandboxParameters& params, filament::Engine& engine) {
|
|
|
|
int material = params.currentMaterialModel;
|
|
if (material == MATERIAL_MODEL_LIT) {
|
|
if (params.currentBlending == BLENDING_TRANSPARENT) material = MATERIAL_TRANSPARENT;
|
|
if (params.currentBlending == BLENDING_FADE) material = MATERIAL_FADE;
|
|
if (params.ssr) {
|
|
if (params.currentBlending == BLENDING_THIN_REFRACTION) material = MATERIAL_THIN_SS_REFRACTION;
|
|
if (params.currentBlending == BLENDING_SOLID_REFRACTION) material = MATERIAL_SOLID_SS_REFRACTION;
|
|
} else {
|
|
if (params.currentBlending == BLENDING_THIN_REFRACTION) material = MATERIAL_THIN_REFRACTION;
|
|
if (params.currentBlending == BLENDING_SOLID_REFRACTION) material = MATERIAL_SOLID_REFRACTION;
|
|
}
|
|
}
|
|
|
|
bool hasRefraction = params.currentBlending == BLENDING_THIN_REFRACTION ||
|
|
params.currentBlending == BLENDING_SOLID_REFRACTION;
|
|
|
|
MaterialInstance* materialInstance = params.materialInstance[material];
|
|
materialInstance->setParameter("baseColor", RgbType::sRGB, params.color);
|
|
|
|
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH) {
|
|
math::float4 emissive(Color::toLinear(params.emissiveColor), params.emissiveExposureWeight);
|
|
emissive.rgb *= Exposure::luminance(params.emissiveEV);
|
|
materialInstance->setParameter("emissive", emissive);
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_LIT) {
|
|
materialInstance->setParameter("roughness", params.roughness);
|
|
materialInstance->setParameter("metallic", params.metallic);
|
|
if (!hasRefraction) {
|
|
materialInstance->setParameter("reflectance", params.reflectance);
|
|
}
|
|
materialInstance->setParameter("sheenColor", RgbType::sRGB, params.sheenColor);
|
|
materialInstance->setParameter("sheenRoughness", params.sheenRoughness);
|
|
materialInstance->setParameter("clearCoat", params.clearCoat);
|
|
materialInstance->setParameter("clearCoatRoughness", params.clearCoatRoughness);
|
|
materialInstance->setParameter("anisotropy", params.anisotropy);
|
|
|
|
if (params.currentBlending != BLENDING_OPAQUE) {
|
|
materialInstance->setParameter("alpha", params.alpha);
|
|
}
|
|
|
|
if (hasRefraction) {
|
|
math::float3 color = Color::toLinear(params.transmittanceColor);
|
|
materialInstance->setParameter("absorption",
|
|
Color::absorptionAtDistance(color, params.distance));
|
|
materialInstance->setParameter("ior", params.ior);
|
|
materialInstance->setParameter("transmission", params.transmission);
|
|
materialInstance->setParameter("thickness", params.thickness);
|
|
}
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_SPECGLOSS) {
|
|
materialInstance->setParameter("glossiness", params.glossiness);
|
|
materialInstance->setParameter("specularColor", params.specularColor);
|
|
materialInstance->setParameter("reflectance", params.reflectance);
|
|
materialInstance->setParameter("clearCoat", params.clearCoat);
|
|
materialInstance->setParameter("clearCoatRoughness", params.clearCoatRoughness);
|
|
materialInstance->setParameter("anisotropy", params.anisotropy);
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_SUBSURFACE) {
|
|
materialInstance->setParameter("roughness", params.roughness);
|
|
materialInstance->setParameter("metallic", params.metallic);
|
|
materialInstance->setParameter("reflectance", params.reflectance);
|
|
materialInstance->setParameter("thickness", params.thickness);
|
|
materialInstance->setParameter("subsurfacePower", params.subsurfacePower);
|
|
materialInstance->setParameter("subsurfaceColor", RgbType::sRGB, params.subsurfaceColor);
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_CLOTH) {
|
|
materialInstance->setParameter("roughness", params.roughness);
|
|
materialInstance->setParameter("sheenColor", RgbType::sRGB, params.sheenColor);
|
|
materialInstance->setParameter("subsurfaceColor", RgbType::sRGB, params.subsurfaceColor);
|
|
}
|
|
|
|
if (params.currentMaterialModel != MATERIAL_MODEL_UNLIT) {
|
|
materialInstance->setSpecularAntiAliasingVariance(params.specularAntiAliasingVariance);
|
|
materialInstance->setSpecularAntiAliasingThreshold(params.specularAntiAliasingThreshold);
|
|
}
|
|
|
|
return materialInstance;
|
|
}
|
|
|
|
static void computeRangePlot(SandboxParameters ¶meters) {
|
|
float4& ranges = parameters.colorGradingOptions.ranges;
|
|
ranges.y = clamp(ranges.y, ranges.x + 1e-5f, ranges.w - 1e-5f); // darks
|
|
ranges.z = clamp(ranges.z, ranges.x + 1e-5f, ranges.w - 1e-5f); // lights
|
|
|
|
for (size_t i = 0; i < 1024; i++) {
|
|
float x = i / 1024.0f;
|
|
float s = 1.0f - smoothstep(ranges.x, ranges.y, x);
|
|
float h = smoothstep(ranges.z, ranges.w, x);
|
|
g_rangePlot[i] = s;
|
|
g_rangePlot[1024 + i] = 1.0f - s - h;
|
|
g_rangePlot[2048 + i] = h;
|
|
}
|
|
}
|
|
|
|
static void rangePlotSeriesStart(int series) {
|
|
switch (series) {
|
|
case 0:
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.4f, 0.25f, 1.0f));
|
|
break;
|
|
case 1:
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.8f, 0.25f, 1.0f));
|
|
break;
|
|
case 2:
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.17f, 0.21f, 1.0f));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rangePlotSeriesEnd(int series) {
|
|
if (series < 3) {
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
|
|
static float getRangePlotValue(int series, void* data, int index) {
|
|
return ((float*) data)[series * 1024 + index];
|
|
}
|
|
|
|
inline float3 curves(float3 v, float3 shadowGamma, float3 midPoint, float3 highlightScale) {
|
|
float3 d = 1.0f / (pow(midPoint, shadowGamma - 1.0f));
|
|
float3 dark = pow(v, shadowGamma) * d;
|
|
float3 light = highlightScale * (v - midPoint) + midPoint;
|
|
return float3{
|
|
v.r <= midPoint.r ? dark.r : light.r,
|
|
v.g <= midPoint.g ? dark.g : light.g,
|
|
v.b <= midPoint.b ? dark.b : light.b,
|
|
};
|
|
}
|
|
|
|
static void computeCurvePlot(SandboxParameters ¶meters) {
|
|
ColorGradingOptions &colorGrading = parameters.colorGradingOptions;
|
|
for (size_t i = 0; i < 1024; i++) {
|
|
float3 x{i / 1024.0f * 2.0f};
|
|
float3 y = curves(x, colorGrading.gamma, colorGrading.midPoint, colorGrading.scale);
|
|
g_curvePlot[i] = y.r;
|
|
g_curvePlot[1024 + i] = y.g;
|
|
g_curvePlot[2048 + i] = y.b;
|
|
}
|
|
}
|
|
|
|
static void tooltipFloat(float value) {
|
|
if (ImGui::IsItemActive() || ImGui::IsItemHovered()) {
|
|
ImGui::SetTooltip("%.2f", value);
|
|
}
|
|
}
|
|
|
|
static void pushSliderColors(float hue) {
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4) ImColor::HSV(hue, 0.5f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4) ImColor::HSV(hue, 0.6f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4) ImColor::HSV(hue, 0.7f, 0.5f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4) ImColor::HSV(hue, 0.9f, 0.9f));
|
|
}
|
|
|
|
static void popSliderColors() { ImGui::PopStyleColor(4); }
|
|
|
|
static void gui(filament::Engine* engine, filament::View*) {
|
|
auto& params = g_params;
|
|
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f));
|
|
ImGui::Begin("Parameters");
|
|
{
|
|
if (ImGui::CollapsingHeader("Material", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
ImGui::Indent();
|
|
ImGui::Combo("Model", ¶ms.currentMaterialModel,
|
|
"Unlit\0Lit\0Subsurface\0Cloth\0Specular glossiness\0\0");
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_LIT) {
|
|
ImGui::Combo("Blending", ¶ms.currentBlending,
|
|
"Opaque\0Transparent\0Fade\0Thin refraction\0Solid refraction\0\0");
|
|
}
|
|
|
|
ImGui::ColorEdit3("Base color", ¶ms.color.r);
|
|
|
|
bool hasRefraction = params.currentBlending == BLENDING_THIN_REFRACTION ||
|
|
params.currentBlending == BLENDING_SOLID_REFRACTION;
|
|
|
|
if (params.currentMaterialModel > MATERIAL_MODEL_UNLIT) {
|
|
if (params.currentBlending == BLENDING_TRANSPARENT ||
|
|
params.currentBlending == BLENDING_FADE) {
|
|
ImGui::SliderFloat("Alpha", ¶ms.alpha, 0.0f, 1.0f);
|
|
}
|
|
|
|
if (params.currentMaterialModel != MATERIAL_MODEL_SPECGLOSS) {
|
|
ImGui::SliderFloat("Roughness", ¶ms.roughness, 0.0f, 1.0f);
|
|
} else {
|
|
ImGui::SliderFloat("Glossiness", ¶ms.glossiness, 0.0f, 1.0f);
|
|
ImGui::ColorEdit3("Specular color", ¶ms.specularColor.r);
|
|
}
|
|
|
|
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH &&
|
|
params.currentMaterialModel != MATERIAL_MODEL_SPECGLOSS) {
|
|
if (!hasRefraction) {
|
|
ImGui::SliderFloat("Metallic", ¶ms.metallic, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Reflectance", ¶ms.reflectance, 0.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
if (params.currentMaterialModel != MATERIAL_MODEL_CLOTH &&
|
|
params.currentMaterialModel != MATERIAL_MODEL_SUBSURFACE) {
|
|
ImGui::ColorEdit3("Sheen color", ¶ms.sheenColor.r);
|
|
ImGui::SliderFloat("Sheen roughness", ¶ms.sheenRoughness, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Clear coat", ¶ms.clearCoat, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Clear coat roughness", ¶ms.clearCoatRoughness, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Anisotropy", ¶ms.anisotropy, -1.0f, 1.0f);
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_SUBSURFACE) {
|
|
ImGui::SliderFloat("Thickness", ¶ms.thickness, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Subsurface power", ¶ms.subsurfacePower, 1.0f, 24.0f);
|
|
ImGui::ColorEdit3("Subsurface color", ¶ms.subsurfaceColor.r);
|
|
}
|
|
|
|
if (params.currentMaterialModel == MATERIAL_MODEL_CLOTH) {
|
|
ImGui::ColorEdit3("Sheen color", ¶ms.sheenColor.r);
|
|
ImGui::ColorEdit3("Subsurface color", ¶ms.subsurfaceColor.r);
|
|
}
|
|
|
|
if (hasRefraction) {
|
|
ImGui::SliderFloat("IOR", ¶ms.ior, 1.0f, 3.0f);
|
|
ImGui::SliderFloat("Transmission", ¶ms.transmission, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Thickness", ¶ms.thickness, 0.0f, 1.0f);
|
|
ImGui::ColorEdit3("Transmittance", ¶ms.transmittanceColor.r);
|
|
ImGui::SliderFloat("Distance", ¶ms.distance, 0.0f, 4.0f);
|
|
ImGui::Checkbox("Screen space refraction", ¶ms.ssr);
|
|
}
|
|
}
|
|
|
|
ImGui::ColorEdit3("Emissive color", ¶ms.emissiveColor.r);
|
|
ImGui::SliderFloat("Emissive EV", ¶ms.emissiveEV, -24.0f, 24.0f);
|
|
ImGui::SliderFloat("Exposure weight", ¶ms.emissiveExposureWeight, 0.0f, 1.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Shading AA")) {
|
|
ImGui::Indent();
|
|
ImGui::SliderFloat("Variance", ¶ms.specularAntiAliasingVariance, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Threshold", ¶ms.specularAntiAliasingThreshold, 0.0f, 1.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Object")) {
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Cast shadows##object", ¶ms.castShadows);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Camera")) {
|
|
ImGui::Indent();
|
|
ImGui::SliderFloat("Focal length", &FilamentApp::get().getCameraFocalLength(), 16.0f, 90.0f);
|
|
ImGui::SliderFloat("Aperture", ¶ms.cameraAperture, 1.0f, 32.0f);
|
|
ImGui::SliderFloat("Speed", ¶ms.cameraSpeed, 800.0f, 1.0f);
|
|
ImGui::SliderFloat("ISO", ¶ms.cameraISO, 25.0f, 6400.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Indirect Light")) {
|
|
ImGui::Indent();
|
|
ImGui::SliderFloat("IBL", ¶ms.iblIntensity, 0.0f, 50000.0f);
|
|
ImGui::SliderAngle("Rotation", ¶ms.iblRotation);
|
|
if (ImGui::CollapsingHeader("SSAO")) {
|
|
int quality = (int)params.ssaoOptions.quality;
|
|
int lowpass = (int)params.ssaoOptions.lowPassFilter;
|
|
bool upsampling = params.ssaoOptions.upsampling != View::QualityLevel::LOW;
|
|
DebugRegistry& debug = engine->getDebugRegistry();
|
|
ImGui::Checkbox("Enabled##ssao", ¶ms.ssaoOptions.enabled);
|
|
ImGui::SliderFloat("Radius", ¶ms.ssaoOptions.radius, 0.05f, 5.0f);
|
|
ImGui::SliderFloat("Bias", ¶ms.ssaoOptions.bias, 0.0f, 0.01f, "%.6f");
|
|
ImGui::SliderFloat("Min Horizon angle", ¶ms.ssaoOptions.minHorizonAngleRad, 0.0f, (float)M_PI_4, "%.6f");
|
|
ImGui::SliderFloat("Intensity", ¶ms.ssaoOptions.intensity, 0.0f, 4.0f);
|
|
ImGui::SliderFloat("Power", ¶ms.ssaoOptions.power, 0.0f, 4.0f);
|
|
ImGui::SliderInt("Quality", &quality, 0, 3);
|
|
ImGui::SliderInt("Low Pass", &lowpass, 0, 2);
|
|
ImGui::Checkbox("High quality upsampling", &upsampling);
|
|
params.ssaoOptions.upsampling = upsampling ? View::QualityLevel::HIGH : View::QualityLevel::LOW;
|
|
params.ssaoOptions.quality = (View::QualityLevel)quality;
|
|
params.ssaoOptions.lowPassFilter = (View::QualityLevel)lowpass;
|
|
if (ImGui::CollapsingHeader("Dominant Light Shadows")) {
|
|
int sampleCount = params.ssaoOptions.ssct.sampleCount;
|
|
int rayCount = params.ssaoOptions.ssct.rayCount;
|
|
ImGui::Checkbox("Enabled##dls", ¶ms.ssaoOptions.ssct.enabled);
|
|
ImGui::SliderFloat("Cone angle", ¶ms.ssaoOptions.ssct.lightConeRad, 0.0f, (float)M_PI_2);
|
|
ImGui::SliderFloat("Shadow Distance", ¶ms.ssaoOptions.ssct.shadowDistance, 0.0f, 10.0f);
|
|
ImGui::SliderFloat("Contact dist max", ¶ms.ssaoOptions.ssct.contactDistanceMax, 0.0f, 100.0f);
|
|
ImGui::SliderFloat("Intensity##dls", ¶ms.ssaoOptions.ssct.intensity, 0.0f, 10.0f);
|
|
ImGui::SliderFloat("Depth bias", ¶ms.ssaoOptions.ssct.depthBias, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Depth slope bias", ¶ms.ssaoOptions.ssct.depthSlopeBias, 0.0f, 1.0f);
|
|
ImGui::SliderInt("Sample Count", &sampleCount, 1, 32);
|
|
ImGui::SliderInt("Ray Count", &rayCount, 1, 8);
|
|
ImGuiExt::DirectionWidget("Direction##dls", params.ssaoOptions.ssct.lightDirection.v);
|
|
params.ssaoOptions.ssct.sampleCount = sampleCount;
|
|
params.ssaoOptions.ssct.rayCount = rayCount;
|
|
}
|
|
}
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Directional Light")) {
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Enabled##directionalLight", ¶ms.directionalLightEnabled);
|
|
ImGui::ColorEdit3("Color##directionalLight", ¶ms.lightColor.r);
|
|
ImGui::SliderFloat("Lux", ¶ms.lightIntensity, 0.0f, 150000.0f);
|
|
ImGui::SliderFloat("Sun size", ¶ms.sunAngularRadius, 0.1f, 10.0f);
|
|
ImGui::SliderFloat("Halo size", ¶ms.sunHaloSize, 1.01f, 40.0f);
|
|
ImGui::SliderFloat("Halo falloff", ¶ms.sunHaloFalloff, 0.0f, 2048.0f);
|
|
ImGuiExt::DirectionWidget("Direction", params.lightDirection.v);
|
|
if (ImGui::CollapsingHeader("Contact Shadows")) {
|
|
DebugRegistry& debug = engine->getDebugRegistry();
|
|
ImGui::Checkbox("Enabled##contactShadows", ¶ms.screenSpaceContactShadows);
|
|
ImGui::SliderInt("Steps", ¶ms.stepCount, 0, 255);
|
|
ImGui::SliderFloat("Distance", ¶ms.maxShadowDistance, 0.0f, 10.0f);
|
|
}
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Spot Light")) {
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Enabled##spotLight", ¶ms.spotLightEnabled);
|
|
ImGui::SliderFloat3("Position", ¶ms.spotLightPosition.x, -5.0f, 5.0f);
|
|
ImGui::ColorEdit3("Color##spotLight", ¶ms.spotLightColor.r);
|
|
ImGui::Checkbox("Cast shadows##spotLight", ¶ms.spotLightCastShadows);
|
|
ImGui::SliderFloat("Lumens", ¶ms.spotLightIntensity, 0.0, 1000000.f);
|
|
ImGui::SliderAngle("Cone angle", ¶ms.spotLightConeAngle, 0.0f, 90.0f);
|
|
ImGui::SliderFloat("Cone fade", ¶ms.spotLightConeFade, 0.0f, 1.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Fog")) {
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Enable Fog", ¶ms.fogOptions.enabled);
|
|
ImGui::SliderFloat("Start", ¶ms.fogOptions.distance, 0.0f, 100.0f);
|
|
ImGui::SliderFloat("Density", ¶ms.fogOptions.density, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Height", ¶ms.fogOptions.height, 0.0f, 100.0f);
|
|
ImGui::SliderFloat("Height Falloff", ¶ms.fogOptions.heightFalloff, 0.0f, 10.0f);
|
|
ImGui::SliderFloat("Scattering Start", ¶ms.fogOptions.inScatteringStart, 0.0f, 100.0f);
|
|
ImGui::SliderFloat("Scattering Size", ¶ms.fogOptions.inScatteringSize, 0.1f, 100.0f);
|
|
ImGui::Checkbox("Color from IBL", ¶ms.fogOptions.fogColorFromIbl);
|
|
ImGui::ColorPicker3("Color", params.fogOptions.color.v);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Post-processing")) {
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("MSAA 4x", ¶ms.msaa);
|
|
ImGui::Checkbox("TAA", ¶ms.taaOptions.enabled);
|
|
if (params.taaOptions.enabled) {
|
|
ImGui::Indent();
|
|
ImGui::SliderFloat("feedback", ¶ms.taaOptions.feedback, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("filter", ¶ms.taaOptions.filterWidth, 0.02f, 2.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::Checkbox("FXAA", ¶ms.fxaa);
|
|
ImGui::Checkbox("Bloom", ¶ms.bloomOptions.enabled);
|
|
if (params.bloomOptions.enabled) {
|
|
ImGui::Indent();
|
|
ImGui::SliderFloat("Strength", ¶ms.bloomOptions.strength, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Dirt", ¶ms.bloomOptions.dirtStrength, 0.0f, 1.0f);
|
|
ImGui::Unindent();
|
|
}
|
|
ImGui::Checkbox("Dithering", ¶ms.dithering);
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Color grading")) {
|
|
ColorGradingOptions& colorGrading = params.colorGradingOptions;
|
|
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Enabled##colorGrading", ¶ms.colorGrading);
|
|
ImGui::Combo("Tone-mapping", &colorGrading.toneMapping,
|
|
"Linear\0ACES (legacy)\0ACES\0Filmic\0EVILS\0Reinhard\0Display Range\0\0");
|
|
if (ImGui::CollapsingHeader("While balance")) {
|
|
ImGui::SliderInt("Temperature", &colorGrading.temperature, -100, 100);
|
|
ImGui::SliderInt("Tint", &colorGrading.tint, -100, 100);
|
|
}
|
|
if (ImGui::CollapsingHeader("Channel mixer")) {
|
|
pushSliderColors(0.0f / 7.0f);
|
|
ImGui::VSliderFloat("##outRed.r", verticalSliderSize, &colorGrading.outRed.r, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outRed.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outRed.g", verticalSliderSize, &colorGrading.outRed.g, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outRed.g);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outRed.b", verticalSliderSize, &colorGrading.outRed.b, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outRed.b);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
popSliderColors();
|
|
|
|
pushSliderColors(2.0f / 7.0f);
|
|
ImGui::VSliderFloat("##outGreen.r", verticalSliderSize, &colorGrading.outGreen.r, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outGreen.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outGreen.g", verticalSliderSize, &colorGrading.outGreen.g, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outGreen.g);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outGreen.b", verticalSliderSize, &colorGrading.outGreen.b, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outGreen.b);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
popSliderColors();
|
|
|
|
pushSliderColors(4.0f / 7.0f);
|
|
ImGui::VSliderFloat("##outBlue.r", verticalSliderSize, &colorGrading.outBlue.r, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outBlue.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outBlue.g", verticalSliderSize, &colorGrading.outBlue.g, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outBlue.g);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##outBlue.b", verticalSliderSize, &colorGrading.outBlue.b, -2.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.outBlue.b);
|
|
popSliderColors();
|
|
}
|
|
if (ImGui::CollapsingHeader("Tonal ranges")) {
|
|
ImGui::ColorEdit3("Shadows", &colorGrading.shadows.x);
|
|
ImGui::SliderFloat("Shadows weight", &colorGrading.shadows.w, -2.0f, 2.0f);
|
|
ImGui::ColorEdit3("Mid-tones", &colorGrading.midtones.x);
|
|
ImGui::SliderFloat("Mid-tones weight", &colorGrading.midtones.w, -2.0f, 2.0f);
|
|
ImGui::ColorEdit3("Highlights", &colorGrading.highlights.x);
|
|
ImGui::SliderFloat("Highlights weight", &colorGrading.highlights.w, -2.0f, 2.0f);
|
|
ImGui::SliderFloat4("Ranges", &colorGrading.ranges.x, 0.0f, 1.0f);
|
|
computeRangePlot(params);
|
|
ImGuiExt::PlotLinesSeries("", 3,
|
|
rangePlotSeriesStart, getRangePlotValue, rangePlotSeriesEnd,
|
|
g_rangePlot, 1024, 0, "", 0.0f, 1.0f, plotLinesWideSize);
|
|
}
|
|
if (ImGui::CollapsingHeader("Color decision list")) {
|
|
ImGui::SliderFloat3("Slope", &colorGrading.slope.x, 0.0f, 2.0f);
|
|
ImGui::SliderFloat3("Offset", &colorGrading.offset.x, -0.5f, 0.5f);
|
|
ImGui::SliderFloat3("Power", &colorGrading.power.x, 0.0f, 2.0f);
|
|
}
|
|
if (ImGui::CollapsingHeader("Adjustments")) {
|
|
ImGui::SliderFloat("Contrast", &colorGrading.contrast, 0.0f, 2.0f);
|
|
ImGui::SliderFloat("Vibrance", &colorGrading.vibrance, 0.0f, 2.0f);
|
|
ImGui::SliderFloat("Saturation", &colorGrading.saturation, 0.0f, 2.0f);
|
|
}
|
|
if (ImGui::CollapsingHeader("Curves")) {
|
|
ImGui::Checkbox("Linked curves", &colorGrading.linkedCurves);
|
|
|
|
computeCurvePlot(params);
|
|
|
|
if (!colorGrading.linkedCurves) {
|
|
pushSliderColors(0.0f / 7.0f);
|
|
ImGui::VSliderFloat("##curveGamma.r", verticalSliderSize, &colorGrading.gamma.r, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.gamma.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveMid.r", verticalSliderSize, &colorGrading.midPoint.r, 0.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.midPoint.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveScale.r", verticalSliderSize, &colorGrading.scale.r, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.scale.r);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
popSliderColors();
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.0f, 0.7f, 0.8f));
|
|
ImGui::PlotLines("", g_curvePlot, 1024, 0, "Red", 0.0f, 2.0f, plotLinesSize);
|
|
ImGui::PopStyleColor();
|
|
|
|
pushSliderColors(2.0f / 7.0f);
|
|
ImGui::VSliderFloat("##curveGamma.g", verticalSliderSize, &colorGrading.gamma.g, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.gamma.g);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveMid.g", verticalSliderSize, &colorGrading.midPoint.g, 0.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.midPoint.g);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveScale.g", verticalSliderSize, &colorGrading.scale.g, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.scale.g);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
popSliderColors();
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.3f, 0.7f, 0.8f));
|
|
ImGui::PlotLines("", g_curvePlot + 1024, 1024, 0, "Green", 0.0f, 2.0f, plotLinesSize);
|
|
ImGui::PopStyleColor();
|
|
|
|
pushSliderColors(4.0f / 7.0f);
|
|
ImGui::VSliderFloat("##curveGamma.b", verticalSliderSize, &colorGrading.gamma.b, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.gamma.b);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveMid.b", verticalSliderSize, &colorGrading.midPoint.b, 0.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.midPoint.b);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveScale.b", verticalSliderSize, &colorGrading.scale.b, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.scale.b);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
popSliderColors();
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.6f, 0.7f, 0.8f));
|
|
ImGui::PlotLines("", g_curvePlot + 2048, 1024, 0, "Blue", 0.0f, 2.0f, plotLinesSize);
|
|
ImGui::PopStyleColor();
|
|
} else {
|
|
ImGui::VSliderFloat("##curveGamma", verticalSliderSize, &colorGrading.gamma.r, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.gamma.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveMid", verticalSliderSize, &colorGrading.midPoint.r, 0.0f, 2.0f, "");
|
|
tooltipFloat(colorGrading.midPoint.r);
|
|
ImGui::SameLine();
|
|
ImGui::VSliderFloat("##curveScale", verticalSliderSize, &colorGrading.scale.r, 0.0f, 4.0f, "");
|
|
tooltipFloat(colorGrading.scale.r);
|
|
ImGui::SameLine(0.0f, 18.0f);
|
|
|
|
colorGrading.gamma = float3{colorGrading.gamma.r};
|
|
colorGrading.midPoint = float3{colorGrading.midPoint.r};
|
|
colorGrading.scale = float3{colorGrading.scale.r};
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotLines, (ImVec4) ImColor::HSV(0.17f, 0.21f, 0.9f));
|
|
ImGui::PlotLines("", g_curvePlot, 1024, 0, "RGB", 0.0f, 2.0f, plotLinesSize);
|
|
ImGui::PopStyleColor();
|
|
}
|
|
}
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
if (ImGui::CollapsingHeader("Debug")) {
|
|
DebugRegistry& debug = engine->getDebugRegistry();
|
|
ImGui::Indent();
|
|
ImGui::Checkbox("Camera at origin",
|
|
debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
|
|
ImGui::Checkbox("Stable Shadow Map", ¶ms.stableShadowMap);
|
|
ImGui::Checkbox("Light Far uses shadow casters",
|
|
debug.getPropertyAddress<bool>("d.shadowmap.far_uses_shadowcasters"));
|
|
ImGui::Checkbox("Focus shadow casters",
|
|
debug.getPropertyAddress<bool>("d.shadowmap.focus_shadowcasters"));
|
|
ImGui::Checkbox("Show checker board",
|
|
debug.getPropertyAddress<bool>("d.shadowmap.checkerboard"));
|
|
|
|
ImGui::SliderFloat("Normal bias", ¶ms.normalBias, 0.0f, 4.0f);
|
|
ImGui::SliderFloat("Constant bias", ¶ms.constantBias, 0.0f, 1.0f);
|
|
ImGui::SliderFloat("Polygon Offset Scale", ¶ms.polygonOffsetSlope, 0.0f, 10.0f);
|
|
ImGui::SliderFloat("Polygon Offset Constant", ¶ms.polygonOffsetConstant, 0.0f, 10.0f);
|
|
|
|
bool* lispsm;
|
|
if (debug.getPropertyAddress<bool>("d.shadowmap.lispsm", &lispsm)) {
|
|
ImGui::Checkbox("Enable LiSPSM", lispsm);
|
|
if (*lispsm) {
|
|
ImGui::SliderFloat("dzn",
|
|
debug.getPropertyAddress<float>("d.shadowmap.dzn"), 0.0f, 1.0f);
|
|
ImGui::SliderFloat("dzf",
|
|
debug.getPropertyAddress<float>("d.shadowmap.dzf"),-1.0f, 0.0f);
|
|
}
|
|
}
|
|
ImGui::Unindent();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
|
|
MaterialInstance* materialInstance = updateInstances(params, *engine);
|
|
|
|
auto& rcm = engine->getRenderableManager();
|
|
size_t count = 0;
|
|
for (auto renderable : g_meshSet->getRenderables()) {
|
|
auto instance = rcm.getInstance(renderable);
|
|
if (!instance) continue;
|
|
if (!g_singleMode || count == 0) {
|
|
for (size_t i = 0; i < rcm.getPrimitiveCount(instance); i++) {
|
|
rcm.setMaterialInstanceAt(instance, i, materialInstance);
|
|
}
|
|
}
|
|
count++;
|
|
rcm.setCastShadows(instance, params.castShadows);
|
|
}
|
|
|
|
if (params.directionalLightEnabled && !params.hasDirectionalLight) {
|
|
g_scene->addEntity(params.light);
|
|
params.hasDirectionalLight = true;
|
|
} else if (!params.directionalLightEnabled && params.hasDirectionalLight) {
|
|
g_scene->remove(params.light);
|
|
params.hasDirectionalLight = false;
|
|
}
|
|
|
|
auto* ibl = FilamentApp::get().getIBL();
|
|
if (ibl) {
|
|
ibl->getIndirectLight()->setIntensity(params.iblIntensity);
|
|
ibl->getIndirectLight()->setRotation(
|
|
mat3f::rotation(params.iblRotation, float3{ 0, 1, 0 }));
|
|
}
|
|
|
|
auto& lcm = engine->getLightManager();
|
|
auto lightInstance = lcm.getInstance(params.light);
|
|
lcm.setColor(lightInstance, params.lightColor);
|
|
lcm.setIntensity(lightInstance, params.lightIntensity);
|
|
lcm.setDirection(lightInstance, params.lightDirection);
|
|
lcm.setSunAngularRadius(lightInstance, params.sunAngularRadius);
|
|
lcm.setSunHaloSize(lightInstance, params.sunHaloSize);
|
|
lcm.setSunHaloFalloff(lightInstance, params.sunHaloFalloff);
|
|
|
|
LightManager::ShadowOptions options = lcm.getShadowOptions(lightInstance);
|
|
options.stable = params.stableShadowMap;
|
|
options.normalBias = params.normalBias;
|
|
options.constantBias = params.constantBias;
|
|
options.polygonOffsetConstant = params.polygonOffsetConstant;
|
|
options.polygonOffsetSlope = params.polygonOffsetSlope;
|
|
options.screenSpaceContactShadows = params.screenSpaceContactShadows;
|
|
options.stepCount = params.stepCount;
|
|
options.maxShadowDistance = params.maxShadowDistance;
|
|
lcm.setShadowOptions(lightInstance, options);
|
|
|
|
if (params.spotLightEnabled && !params.hasSpotLight) {
|
|
g_scene->addEntity(params.spotLight);
|
|
params.hasSpotLight = true;
|
|
} else if (!params.spotLightEnabled && params.hasSpotLight) {
|
|
g_scene->remove(params.spotLight);
|
|
params.hasSpotLight = false;
|
|
}
|
|
auto spotLightInstance = lcm.getInstance(params.spotLight);
|
|
auto& tcm = engine->getTransformManager();
|
|
tcm.setTransform(tcm.getInstance(params.spotLight),
|
|
mat4f::translation(params.spotLightPosition));
|
|
lcm.setColor(spotLightInstance, params.spotLightColor);
|
|
lcm.setShadowCaster(spotLightInstance, params.spotLightCastShadows);
|
|
lcm.setIntensity(spotLightInstance, params.spotLightIntensity);
|
|
lcm.setSpotLightCone(spotLightInstance, params.spotLightConeAngle * params.spotLightConeFade,
|
|
params.spotLightConeAngle);
|
|
}
|
|
|
|
static void preRender(filament::Engine* engine, filament::View* view, filament::Scene*,
|
|
filament::Renderer* renderer) {
|
|
view->setAntiAliasing(g_params.fxaa ? View::AntiAliasing::FXAA : View::AntiAliasing::NONE);
|
|
view->setDithering(g_params.dithering ? View::Dithering::TEMPORAL : View::Dithering::NONE);
|
|
view->setBloomOptions(g_params.bloomOptions);
|
|
view->setFogOptions(g_params.fogOptions);
|
|
view->setTemporalAntiAliasingOptions(g_params.taaOptions);
|
|
view->setSampleCount((uint8_t) (g_params.msaa ? 4 : 1));
|
|
view->setAmbientOcclusionOptions(g_params.ssaoOptions);
|
|
|
|
if (g_params.colorGrading) {
|
|
if (g_params.colorGradingOptions != g_lastColorGradingOptions) {
|
|
ColorGradingOptions &options = g_params.colorGradingOptions;
|
|
ColorGrading *colorGrading = ColorGrading::Builder()
|
|
.whiteBalance(options.temperature / 100.0f, options.tint / 100.0f)
|
|
.channelMixer(options.outRed, options.outGreen, options.outBlue)
|
|
.shadowsMidtonesHighlights(
|
|
Color::toLinear(options.shadows),
|
|
Color::toLinear(options.midtones),
|
|
Color::toLinear(options.highlights),
|
|
options.ranges
|
|
)
|
|
.slopeOffsetPower(options.slope, options.offset, options.power)
|
|
.contrast(options.contrast)
|
|
.vibrance(options.vibrance)
|
|
.saturation(options.saturation)
|
|
.curves(options.gamma, options.midPoint, options.scale)
|
|
.toneMapping(static_cast<ColorGrading::ToneMapping>(options.toneMapping))
|
|
.build(*engine);
|
|
|
|
if (g_colorGrading) {
|
|
engine->destroy(g_colorGrading);
|
|
}
|
|
|
|
g_colorGrading = colorGrading;
|
|
g_lastColorGradingOptions = options;
|
|
}
|
|
view->setColorGrading(g_colorGrading);
|
|
} else {
|
|
view->setColorGrading(nullptr);
|
|
}
|
|
|
|
// Without an IBL, we must clear the swapchain to black before each frame.
|
|
renderer->setClearOptions({
|
|
.clearColor = { 0.0f, 0.0f, 0.0f, 1.0f },
|
|
.clear = !FilamentApp::get().getIBL() });
|
|
|
|
Camera& camera = view->getCamera();
|
|
camera.setExposure(g_params.cameraAperture, 1.0f / g_params.cameraSpeed, g_params.cameraISO);
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
int option_index = handleCommandLineArgments(argc, argv, &g_config);
|
|
int num_args = argc - option_index;
|
|
if (num_args < 1) {
|
|
printUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
for (int i = option_index; i < argc; i++) {
|
|
utils::Path filename = argv[i];
|
|
if (!filename.exists()) {
|
|
std::cerr << "file " << argv[i] << " not found!" << std::endl;
|
|
return 1;
|
|
}
|
|
g_filenames.push_back(filename);
|
|
}
|
|
|
|
g_params.bloomOptions.enabled = true;
|
|
|
|
g_config.title = "Material Sandbox";
|
|
FilamentApp& filamentApp = FilamentApp::get();
|
|
filamentApp.run(g_config, setup, cleanup, gui, preRender);
|
|
return 0;
|
|
}
|