1061 lines
48 KiB
C++
1061 lines
48 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 "common/arguments.h"
|
|
|
|
#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_USAGE"
|
|
" --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);
|
|
}
|
|
const std::string apiUsage("API_USAGE");
|
|
for (size_t pos = usage.find(apiUsage); pos != std::string::npos; pos = usage.find(apiUsage, pos)) {
|
|
usage.replace(pos, apiUsage.length(), samples::getBackendAPIArgumentsUsage());
|
|
}
|
|
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':
|
|
config->backend = samples::parseArgumentsForBackend(arg);
|
|
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();
|
|
// If we loaded an equirectangular IBL, we don't have spherical harmonics. In that case,
|
|
// simply skip the estimates.
|
|
if (ibl->hasSphericalHarmonics()) {
|
|
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", ¶ms.cameraFocalLength, 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::SliderFloat("Near", ¶ms.cameraNear, 0.01f, 1.0f);
|
|
ImGui::SliderFloat("Far", ¶ms.cameraFar, 1.0f, 10000.0f);
|
|
ImGui::Unindent();
|
|
|
|
FilamentApp::get().setCameraFocalLength(params.cameraFocalLength);
|
|
FilamentApp::get().setCameraNearFar(params.cameraNear, params.cameraFar);
|
|
}
|
|
|
|
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::SliderFloat("Bilateral Threshold", ¶ms.ssaoOptions.bilateralThreshold, 0.0f, 0.5f);
|
|
ImGui::Checkbox("Bent Normals", ¶ms.ssaoOptions.bentNormals);
|
|
ImGui::Checkbox("High quality upsampling", &upsampling);
|
|
// Can be used to debug SSAO
|
|
// ImGui::SliderInt("SampleCount",
|
|
// debug.getPropertyAddress<int>("d.ssao.sampleCount"), 1, 128);
|
|
// ImGui::SliderInt("spiralTurns",
|
|
// debug.getPropertyAddress<int>("d.ssao.spiralTurns"), 1,
|
|
// *debug.getPropertyAddress<int>("d.ssao.sampleCount"));
|
|
// ImGui::SliderInt("kernelSize",
|
|
// debug.getPropertyAddress<int>("d.ssao.kernelSize"), 1, 23);
|
|
// ImGui::SliderFloat("stddev",
|
|
// debug.getPropertyAddress<float>("d.ssao.stddev"), 0.0f, 8.0f);
|
|
|
|
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")) {
|
|
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\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);
|
|
|
|
ImGui::Checkbox("Enable LiSPSM", ¶ms.lispsm);
|
|
if (params.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.lispsm = params.lispsm;
|
|
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);
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
view->setSampleCount((uint8_t) (g_params.msaa ? 4 : 1));
|
|
#pragma clang diagnostic pop
|
|
view->setAmbientOcclusionOptions(g_params.ssaoOptions);
|
|
|
|
if (g_params.colorGrading) {
|
|
if (g_params.colorGradingOptions != g_lastColorGradingOptions) {
|
|
ColorGradingOptions &options = g_params.colorGradingOptions;
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
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);
|
|
#pragma clang diagnostic pop
|
|
|
|
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;
|
|
}
|