/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "material_sandbox.h" using namespace filament::math; using namespace filament; using namespace filamat; using namespace utils; static std::vector g_filenames; static Scene* g_scene = nullptr; std::unique_ptr g_meshSet; static std::map 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] \n" "Options:\n" " --help, -h\n" " Prints this message\n\n" "API_USAGE" " --ibl=, -i \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=, -c \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*) { g_meshSet.reset(nullptr); 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); } 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(*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("d.ssao.sampleCount"), 1, 128); // ImGui::SliderInt("spiralTurns", // debug.getPropertyAddress("d.ssao.spiralTurns"), 1, // *debug.getPropertyAddress("d.ssao.sampleCount")); // ImGui::SliderInt("kernelSize", // debug.getPropertyAddress("d.ssao.kernelSize"), 1, 23); // ImGui::SliderFloat("stddev", // debug.getPropertyAddress("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("d.view.camera_at_origin")); ImGui::Checkbox("Stable Shadow Map", ¶ms.stableShadowMap); ImGui::Checkbox("Light Far uses shadow casters", debug.getPropertyAddress("d.shadowmap.far_uses_shadowcasters")); ImGui::Checkbox("Focus shadow casters", debug.getPropertyAddress("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("d.shadowmap.dzn"), 0.0f, 1.0f); ImGui::SliderFloat("dzf", debug.getPropertyAddress("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(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; }