* Generalize luminance scaling in color grading Previously the luminance scaling step was tied to a specific tone mapping operator, called "EVILS" in the API. This was however somewhat misleading as EVILS really is a color handling system that's independent of what we call tone mapping. This change splits the luminance scaling step from the tone mapping step, allowing the use of luminance scaling (the LICH part of EVILS) with any tone mapping operator. As a result, the operator known as EVILS is currently mapped to the linear operator and renamed to "RESERVED" until it is replaced by a proper — configurable — operator. * Update release notes * Fix web build
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\0Reserved\0Reinhard\0Display Range\0\0");
|
|
ImGui::Checkbox("Luminance scaling", &colorGrading.luminanceScaling);
|
|
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::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))
|
|
.luminanceScaling(options.luminanceScaling)
|
|
.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;
|
|
}
|