Compare commits

...

7 Commits

Author SHA1 Message Date
Powei Feng
ce4199b5f7 Testing rendering to quad 2024-08-27 13:47:43 -07:00
Mathias Agopian
1c2ffc9ed4 new screenshot debug option in gltf_viewer
screenshots are saved in the current directory as 
"screenshotXX.ppm" with XX increasing after each
screenshot.
2024-08-20 22:41:49 -07:00
Sungun Park
574518ea74 Cleanup color pass for multiview (#8062) 2024-08-20 21:44:17 +00:00
Ben Doherty
d5a274fa25 Add better glShaderSource fix for IMG devices (#8061) 2024-08-20 12:28:13 -07:00
Mathias Agopian
3351db1178 improve FrameSkipper when disregarding skipping
It is legal for the app to draw a frame even when FrameSkipper 
detects that a frame should be skipped. In that case we used to
overwrite the most recent fence, but this wasn't ideal. We now
proceed as if the fence had signaled, i.e. we destroy it and
move all the fences up, creating a new one at the end of the 
delayed array.
2024-08-20 12:07:59 -07:00
Mathias Agopian
b70aa43727 depth clamp cannot work with VSM
This is because the shadowmap doesn't store depth values so it doesn't 
work to "flatten" all casters behind de camera to the near plane.
The bulk of shadowing works but the filtering/edges don't always.

Disable depth-clamping when VSM is enabled.
2024-08-20 12:04:01 -07:00
Sungun Park
1f9a11802d Minor tweak (#8053)
Move the common part outside the loop.
2024-08-20 17:13:55 +00:00
18 changed files with 357 additions and 290 deletions

View File

@@ -541,9 +541,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
bugs->delay_fbo_destruction = true;
// PowerVR seems to have no problem with this (which is good for us)
bugs->allow_read_only_ancillary_feedback_loop = true;
// PowerVR doesn't respect lengths passed to glShaderSource, so concatenate them into a
// single string.
bugs->concatenate_shader_strings = true;
} else if (strstr(renderer, "Apple")) {
// Apple GPU
} else if (strstr(renderer, "Tegra") ||

View File

@@ -316,12 +316,6 @@ public:
// bugs or performance issues.
bool force_feature_level0;
// Some drivers don't respect the length argument of glShaderSource() and (apparently)
// require each shader source string to be null-terminated. This works around the issue by
// concatenating the strings into a single null-terminated string before passing it to
// glShaderSource().
bool concatenate_shader_strings;
} bugs = {};
// state getters -- as needed.
@@ -568,9 +562,6 @@ private:
{ bugs.force_feature_level0,
"force_feature_level0",
""},
{ bugs.concatenate_shader_strings,
"concatenate_shader_strings",
""},
}};
// this is chosen to minimize code size

View File

@@ -597,40 +597,30 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
version = "#version 310 es\n";
}
const std::array<const char*, 5> sources = {
version.data(),
prolog.data(),
specializationConstantString.c_str(),
packingFunctions.data(),
body.data()
std::array<std::string_view, 5> sources = {
version,
prolog,
specializationConstantString,
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
const std::array<GLint, 5> lengths = {
(GLint)version.length(),
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
// drivers instead interpret zero as a sentinel for a null-terminated string.
auto partitionPoint = std::stable_partition(
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
size_t count = std::distance(sources.begin(), partitionPoint);
std::array<const char*, 5> shaderStrings;
std::array<GLint, 5> lengths;
for (size_t i = 0; i < count; i++) {
shaderStrings[i] = sources[i].data();
lengths[i] = sources[i].size();
}
GLuint const shaderId = glCreateShader(glShaderType);
if (UTILS_UNLIKELY(context.bugs.concatenate_shader_strings)) {
size_t totalSize = 0;
for (size_t i = 0; i < sources.size(); i++) {
totalSize += lengths[i];
}
std::string concatenatedShaderSource;
concatenatedShaderSource.reserve(totalSize);
for (size_t i = 0; i < sources.size(); i++) {
concatenatedShaderSource.append(sources[i], lengths[i]);
}
const GLchar* ptr = concatenatedShaderSource.c_str();
GLint length = concatenatedShaderSource.length();
glShaderSource(shaderId, 1, &ptr, &length);
} else {
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
}
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
glCompileShader(shaderId);

View File

@@ -16,16 +16,22 @@
#include "FrameSkipper.h"
#include <utils/Log.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <algorithm>
#include <stddef.h>
namespace filament {
using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(latency - 1) {
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
@@ -41,31 +47,32 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
auto& fences = mDelayedFences;
auto fence = fences.front();
if (fence) {
auto status = driver.getFenceStatus(fence);
if (status == FenceStatus::TIMEOUT_EXPIRED) {
// Sync not ready, skip frame
if (fences.front()) {
// Do we have a latency old fence?
auto status = driver.getFenceStatus(fences.front());
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
// The fence hasn't signaled yet, skip this frame
return false;
}
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
driver.destroyFence(fence);
}
// shift all fences down by 1
std::move(fences.begin() + 1, fences.end(), fences.begin());
fences.back() = {};
return true;
}
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
// If the user produced a new frame despite the fact that the previous one wasn't finished
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
// a fence that might be here already)
auto& fence = mDelayedFences[mLast];
if (fence) {
driver.destroyFence(fence);
auto& fences = mDelayedFences;
size_t const last = mLast;
// pop the oldest fence and advance the other ones
if (fences.front()) {
driver.destroyFence(fences.front());
}
fence = driver.createFence();
std::move(fences.begin() + 1, fences.end(), fences.begin());
// add a new fence to the end
assert_invariant(!fences[last]);
fences[last] = driver.createFence();
}
} // namespace filament

View File

@@ -22,6 +22,9 @@
#include <array>
#include <stddef.h>
#include <stdint.h>
namespace filament {
/*
@@ -60,7 +63,7 @@ public:
private:
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
mutable Container mDelayedFences{};
size_t mLast;
uint8_t const mLast;
};
} // namespace filament

View File

@@ -34,6 +34,8 @@
#include <math/mat4.h>
#include <utils/Log.h>
namespace filament {
using namespace backend;
@@ -120,6 +122,8 @@ void PerViewUniforms::prepareViewport(
s.resolution = { physical.zw, 1.0f / physical.zw };
s.logicalViewportScale = physical.zw / logical.zw;
s.logicalViewportOffset = -logical.xy / logical.zw;
utils::slog.e << "resolution=" << s.resolution << " logical=" <<
s.logicalViewportScale << " " << s.logicalViewportOffset << utils::io::endl;
}
void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept {

View File

@@ -2455,18 +2455,24 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
const float temporalNoise = mUniformDistribution(mEngine.getRandomEngine());
auto yy = float4{
(float)vp.left / input.width,
(float)vp.bottom / input.height,
(float)vp.width / input.width,
(float)vp.height / input.height
};
mi->setParameter("dithering", colorGradingConfig.dithering);
mi->setParameter("bloom", bloomParameters);
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("fxaa", colorGradingConfig.fxaa);
mi->setParameter("temporalNoise", temporalNoise);
mi->setParameter("viewport", float4{
(float)vp.left / input.width,
(float)vp.bottom / input.height,
(float)vp.width / input.width,
(float)vp.height / input.height
});
mi->setParameter("viewport", yy);
utils::slog.e <<"yy------------yy in=" <<
input.width << "x" << input.height << " viewport=" << yy << utils::io::endl;
const uint8_t variant = uint8_t(colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
@@ -2508,6 +2514,17 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
auto xx = float4{
(float)vp.left / inDesc.width,
(float)vp.bottom / inDesc.height,
(float)vp.width / inDesc.width,
(float)vp.height / inDesc.height
};
utils::slog.e <<"xx-----------xx" <<
"in=" << inDesc.width <<"x" << inDesc.height << " vp=" << vp << " viewport=" << xx << utils::io::endl;
mi->setParameter("viewport", float4{
(float)vp.left / inDesc.width,
(float)vp.bottom / inDesc.height,

View File

@@ -75,7 +75,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
uint8_t layerCount = 1;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
@@ -172,9 +171,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
if (view.hasStereo() && engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
layerCount = engine.getConfig().stereoscopicEyeCount;
}
/*
* There is a bit of magic happening here regarding the viewport used.
@@ -196,7 +192,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.layerCount = layerCount,
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
blackboard["depth"] = data.depth;
},

View File

@@ -371,7 +371,13 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
if (view.isFrontFaceWindingInverted()) {
renderPassFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
}
if (mIsDepthClampSupported && engine.debug.shadowmap.depth_clamp) {
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
@@ -650,9 +656,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
cameraInfo.zf = -nearFarPlanes[i + 1];
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
bool const canUseDepthClamp =
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
auto shaderParameters = shadowMap.updateDirectional(engine,
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
mIsDepthClampSupported && engine.debug.shadowmap.depth_clamp);
canUseDepthClamp);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();

View File

@@ -597,6 +597,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
bool hasColorGrading = hasPostProcess;
bool hasDithering = view.getDithering() == Dithering::TEMPORAL;
bool hasFXAA = view.getAntiAliasing() == AntiAliasing::FXAA;
// bool hasFXAA = false;//view.getAntiAliasing() == AntiAliasing::FXAA;
float2 scale = view.updateScale(engine, mFrameInfoManager.getLastFrameInfo(), mFrameRateOptions, mDisplayInfo);
auto msaaOptions = view.getMultiSampleAntiAliasingOptions();
auto dsrOptions = view.getDynamicResolutionOptions();
@@ -610,6 +611,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
auto guardBandOptions = view.getGuardBandOptions();
const bool isRenderingMultiview = view.hasStereo() &&
engine.getConfig().stereoscopicType == backend::StereoscopicType::MULTIVIEW;
utils::slog.e <<"hasFXAA=" <<hasFXAA << " view=" << &view << utils::io::endl;
// FIXME: This is to override some settings that are not supported for multiview at the moment.
// Remove this when all features are supported.
if (isRenderingMultiview) {
@@ -703,6 +705,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
// guardBandOptions.enabled = !noBufferPadding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -719,7 +722,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// the SwapChain, and we might want to keep it this way.
auto round = [](uint32_t x) {
constexpr uint32_t rounding = 16u;
constexpr uint32_t rounding = 256u;
return (x + (rounding - 1u)) & ~(rounding - 1u);
};
@@ -727,6 +730,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const float width = float(round(svp.width )) + 2.0f * guardBand;
const float height = float(round(svp.height)) + 2.0f * guardBand;
utils::slog.e << "new dim=" << width <<"x" << height <<
" old dim=" << svp.width << "x" << svp.height << utils::io::endl;
// scale the field-of-view up, so it covers exactly the extra pixels
const float3 clipSpaceScaling{
float(svp.width) / width,
@@ -744,9 +750,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
1.0f - clipSpaceScaling.y - 2.0f * guardBand / height
};
utils::slog.e <<"clipspace scaling=" << clipSpaceScaling << utils::io::endl;
utils::slog.e <<"clipspace translation=" << clipSpaceTranslation << utils::io::endl;
mat4f ts = mat4f::scaling(clipSpaceScaling);
ts[3].xy = -clipSpaceTranslation;
utils::slog.e <<"mat=" << ts << utils::io::endl;
// update the camera projection
cameraInfo.projection = highPrecisionMultiply(ts, cameraInfo.projection);
@@ -759,6 +770,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
svp.height = uint32_t(height);
xvp.left = int32_t(guardBand);
xvp.bottom = int32_t(guardBand);
utils::slog.e <<"xvp.left=" << int32_t(guardBand) << utils::io::endl;
}
view.prepare(engine, driver, rootArenaScope, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
@@ -870,6 +882,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const TextureFormat hdrFormat = getHdrFormat(view, needsAlphaChannel);
utils::slog.e <<"padding=" << !noBufferPadding << " svp=" << svp << utils::io::endl;
utils::slog.e <<"padding=" << !noBufferPadding << " xvp=" << xvp << utils::io::endl;
// the clearFlags and clearColor specified below will only apply when rendering into the
// temporary color buffer. In particular, they won't apply when rendering into the main
// swapchain (imported render target above)
@@ -1074,6 +1089,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.format = config.hdrFormat
};
utils::slog.e <<"color buffer output=" << colorBufferDesc.width <<"x" << colorBufferDesc.height
<< utils::io::endl;
// Set the depth to the number of layers if we're rendering multiview.
if (isRenderingMultiview) {
colorBufferDesc.depth = engine.getConfig().stereoscopicEyeCount;
@@ -1227,11 +1245,13 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
bloomOptions, vignetteOptions);
// the padded buffer is resolved now
xvp.left = xvp.bottom = 0;
utils::slog.e <<"color grading is resolved" << utils::io::endl;
svp = xvp;
}
}
if (hasFXAA) {
utils::slog.e <<"fxaa=" << xvp << utils::io::endl;
input = ppm.fxaa(fg, input, xvp, colorGradingConfig.ldrFormat,
!hasColorGrading || needsAlphaChannel);
// the padded buffer is resolved now
@@ -1245,6 +1265,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.width = viewport.width, .height = viewport.height,
.format = colorGradingConfig.ldrFormat }, SamplerMagFilter::LINEAR);
xvp.left = xvp.bottom = 0;
utils::slog.e <<"scaled" << utils::io::endl;
svp = xvp;
}
}
@@ -1277,6 +1298,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const bool outputIsSwapChain =
(input == colorPassOutput) && (viewRenderTarget == mRenderTargetHandle);
if (mightNeedFinalBlit) {
utils::slog.e <<"inside might need final blit xvp!=svp=" << (xvp != svp) <<
" xvp=" << xvp << " svp=" << svp <<
utils::io::endl;
if (blendModeTranslucent ||
xvp != svp ||
(outputIsSwapChain &&
@@ -1285,6 +1309,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
hasScreenSpaceRefraction ||
ssReflectionsOptions.enabled))) {
assert_invariant(!scaled);
utils::slog.e <<"doing final blit " << svp << " " << xvp << utils::io::endl;
input = ppm.blit(fg, blendModeTranslucent, input, xvp, {
.width = vp.width, .height = vp.height,
.format = colorGradingConfig.ldrFormat },
@@ -1314,6 +1339,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
fg.forwardResource(fgViewRenderTarget, input);
utils::slog.e <<"present view=" << &view << " scaled=" << scaled << " mightNeedFinalBlit=" <<
mightNeedFinalBlit <<
utils::io::endl;
fg.present(fgViewRenderTarget);
fg.compile();

View File

@@ -190,10 +190,10 @@ void RenderPassNode::resolve() noexcept {
minHeight = std::min(minHeight, h);
maxHeight = std::max(maxHeight, h);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
}
// additionally, clear implies discardStart
rt.backend.params.flags.discardStart |= (
rt.descriptor.clearFlags & rt.targetBufferFlags);
assert_invariant(minWidth == maxWidth);
assert_invariant(minHeight == maxHeight);

View File

@@ -224,6 +224,9 @@ public:
*/
static void exportSettings(const Settings& settings, const char* filename);
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine);
Options getOptions() const { return mOptions; }
bool isRunning() const { return mIsRunning; }
size_t currentTest() const { return mCurrentTest; }

View File

@@ -56,7 +56,7 @@ static void convertRGBAtoRGB(void* buffer, uint32_t width, uint32_t height) {
}
}
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
void AutomationEngine::exportScreenshot(View* view, Renderer* renderer, std::string filename,
bool autoclose, AutomationEngine* automationEngine) {
const Viewport& vp = view->getViewport();
const size_t byteCount = vp.width * vp.height * 4;
@@ -244,7 +244,8 @@ void AutomationEngine::tick(Engine* engine, const ViewerContent& content, float
}
if (mOptions.exportScreenshots) {
exportScreenshot(content.view, content.renderer, prefix + ".ppm", isLastTest, this);
AutomationEngine::exportScreenshot(
content.view, content.renderer, prefix + ".ppm", isLastTest, this);
}
if (isLastTest) {

View File

@@ -562,6 +562,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, Settings* out)
}
void applySettings(Engine* engine, const ViewSettings& settings, View* dest) {
utils::slog.e << "antialiasing=" << (int) settings.antiAliasing << " view=" << dest
<< utils::io::endl;
dest->setAntiAliasing(settings.antiAliasing);
dest->setTemporalAntiAliasingOptions(settings.taa);
dest->setMultiSampleAntiAliasingOptions(settings.msaa);

View File

@@ -276,7 +276,7 @@ if (NOT ANDROID)
target_link_libraries(hellopbr PRIVATE filameshio suzanne-resources)
target_link_libraries(image_viewer PRIVATE viewer imageio)
target_link_libraries(multiple_windows PRIVATE filameshio suzanne-resources)
target_link_libraries(rendertarget PRIVATE filameshio suzanne-resources)
target_link_libraries(rendertarget PRIVATE gltf-demo-resources filameshio suzanne-resources gltfio viewer)
target_link_libraries(sample_cloth PRIVATE filameshio)
target_link_libraries(sample_normal_map PRIVATE filameshio)
target_link_libraries(suzanne PRIVATE filameshio suzanne-resources)

View File

@@ -61,8 +61,10 @@
#include <algorithm>
#include <array>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include "generated/resources/gltf_demo.h"
@@ -137,6 +139,8 @@ struct App {
AutomationSpec* automationSpec = nullptr;
AutomationEngine* automationEngine = nullptr;
bool screenshot = false;
uint8_t screenshotSeq = 0;
};
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
@@ -877,10 +881,15 @@ int main(int argc, char** argv) {
if (ImGui::CollapsingHeader("Debug")) {
auto& debug = engine->getDebugRegistry();
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
if (engine->getBackend() == Engine::Backend::METAL) {
if (ImGui::Button("Capture frame")) {
bool* captureFrame =
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
}
}
if (ImGui::Button("Screenshot")) {
app.screenshot = true;
}
ImGui::Checkbox("Disable buffer padding",
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
@@ -1138,6 +1147,14 @@ int main(int argc, char** argv) {
};
auto postRender = [&app](Engine* engine, View* view, Scene*, Renderer* renderer) {
if (app.screenshot) {
std::ostringstream stringStream;
stringStream << "screenshot" << std::setfill('0') << std::setw(2) << +app.screenshotSeq;
AutomationEngine::exportScreenshot(
view, renderer, stringStream.str() + ".ppm", false, app.automationEngine);
++app.screenshotSeq;
app.screenshot = false;
}
if (app.automationEngine->shouldClose()) {
FilamentApp::get().close();
return;

View File

@@ -12,6 +12,8 @@ material {
uv0
],
shadingModel : unlit,
vertexDomain: device,
// vertexDomainDeviceJittered: true,
}
fragment {
@@ -20,17 +22,11 @@ fragment {
if (gl_FrontFacing) {
// Make a lookup into the mirror texture.
vec2 uv = getResolution().zw * gl_FragCoord.xy;
// vec2 uv = getResolution().zw * gl_FragCoord.xy;
vec2 uv = getNormalizedViewportCoord().xy;
material.baseColor.rgb = texture(materialParams_albedo, uv).rgb;
// Add black borders to the mirror.
vec2 st = getUV0();
float minDist0 = min(st.x, st.y);
float minDist1 = min(1.0 - st.x, 1.0 - st.y);
float minDist = min(minDist0, minDist1);
material.baseColor.rgb *= smoothstep(0.0, 0.1, minDist);
} else {
material.baseColor = vec4(1, 0, 0, 1);
}
material.baseColor.a = 1.0;
}
}

View File

@@ -28,10 +28,17 @@
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h>
#include <utils/NameComponentManager.h>
#include <utils/EntityManager.h>
#include <imgui.h>
#include <filagui/ImGuiExtensions.h>
#include <viewer/ViewerGui.h>
#include <filameshio/MeshReader.h>
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
@@ -40,11 +47,16 @@
#include <iostream>
#include "generated/resources/resources.h"
#include "generated/resources/monkey.h"
//#include "generated/resources/monkey.h"
#include "generated/resources/gltf_demo.h"
using namespace filament;
using namespace filamesh;
using namespace filament::math;
using namespace filament::viewer;
using namespace filament::gltfio;
#include <utils/Log.h>
struct Vertex {
float3 position;
@@ -55,8 +67,6 @@ struct App {
utils::Entity lightEntity;
Material* meshMaterial;
MaterialInstance* meshMatInstance;
MeshReader::Mesh monkeyMesh;
utils::Entity reflectedMonkey;
mat4f transform;
Texture* offscreenColorTexture = nullptr;
@@ -66,12 +76,6 @@ struct App {
Scene* offscreenScene = nullptr;
Camera* offscreenCamera = nullptr;
enum class ReflectionMode {
RENDERABLES,
CAMERA,
};
ReflectionMode mode = ReflectionMode::CAMERA;
Config config;
utils::Entity quadEntity;
@@ -80,46 +84,22 @@ struct App {
Material* quadMaterial = nullptr;
MaterialInstance* quadMatInstance = nullptr;
float3 quadCenter;
float3 quadNormal;
MaterialProvider* materials;
AssetLoader* assetLoader;
ResourceLoader* resourceLoader;
FilamentAsset* asset = nullptr;
utils::NameComponentManager* names;
gltfio::TextureProvider* stbDecoder = nullptr;
gltfio::TextureProvider* ktxDecoder = nullptr;
Engine* engine;
ViewerGui* viewer;
utils::Entity rootTransformEntity;
};
static mat4f reflectionMatrix(float4 plane) {
mat4f m;
m[0][0] = -2 * plane.x * plane.x + 1;
m[0][1] = -2 * plane.x * plane.y;
m[0][2] = -2 * plane.x * plane.z;
m[0][3] = -2 * plane.x * plane.w;
m[1][0] = -2 * plane.x * plane.y;
m[1][1] = -2 * plane.y * plane.y + 1;
m[1][2] = -2 * plane.y * plane.z;
m[1][3] = -2 * plane.y * plane.w;
m[2][0] = -2 * plane.z * plane.x;
m[2][1] = -2 * plane.z * plane.y;
m[2][2] = -2 * plane.z * plane.z + 1;
m[2][3] = -2 * plane.z * plane.w;
m[3][0] = 0;
m[3][1] = 0;
m[3][2] = 0;
m[3][3] = 1;
return transpose(m);
}
static void setReflectionMode(App& app, App::ReflectionMode mode) {
switch (mode) {
case App::ReflectionMode::RENDERABLES:
app.offscreenScene->addEntity(app.reflectedMonkey);
app.offscreenScene->remove(app.monkeyMesh.renderable);
app.offscreenView->setFrontFaceWindingInverted(false);
break;
case App::ReflectionMode::CAMERA:
app.offscreenScene->addEntity(app.monkeyMesh.renderable);
app.offscreenScene->remove(app.reflectedMonkey);
app.offscreenView->setFrontFaceWindingInverted(true);
break;
}
app.mode = mode;
}
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
@@ -172,14 +152,6 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
}
break;
case 'm':
if (arg == "camera") {
app->mode = App::ReflectionMode::CAMERA;
} else if (arg == "renderables") {
app->mode = App::ReflectionMode::RENDERABLES;
} else {
std::cerr << "Unrecognized mode. Must be 'camera'|'renderables'.\n";
exit(1);
}
break;
}
}
@@ -189,126 +161,182 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
int main(int argc, char** argv) {
App app{};
app.config.title = "rendertarget";
app.config.iblDirectory = FilamentApp::getRootAssetsPath() + DEFAULT_IBL;
handleCommandLineArguments(argc, argv, &app);
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
auto gui = [&app](Engine*, View*) {
app.viewer->updateUserInterface();
FilamentApp::get().setSidebarWidth(app.viewer->getSidebarWidth());
};
auto loadResources = [&app] () {
// Load external textures and buffers.
std::string const gltfPath;
ResourceConfiguration configuration = {};
configuration.engine = app.engine;
configuration.gltfPath = gltfPath.c_str();
configuration.normalizeSkinningWeights = true;
app.resourceLoader = new gltfio::ResourceLoader(configuration);
app.stbDecoder = createStbProvider(app.engine);
app.ktxDecoder = createKtx2Provider(app.engine);
app.resourceLoader->addTextureProvider("image/png", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder);
if (!app.resourceLoader->loadResources(app.asset)) {
std::cerr << "Unable to start loading resources" << std::endl;
exit(1);
}
app.asset->getInstance()->recomputeBoundingBoxes();
app.asset->releaseSourceData();
// Enable stencil writes on all material instances.
auto instance = app.asset->getInstance();
const size_t matInstanceCount = instance->getMaterialInstanceCount();
MaterialInstance* const* const instances = instance->getMaterialInstances();
for (int mi = 0; mi < matInstanceCount; mi++) {
instances[mi]->setStencilWrite(true);
instances[mi]->setStencilOpDepthStencilPass(MaterialInstance::StencilOperation::INCR);
}
auto ibl = FilamentApp::get().getIBL();
if (ibl) {
app.viewer->setIndirectLight(ibl->getIndirectLight(), ibl->getSphericalHarmonics());
app.viewer->getSettings().view.fogSettings.fogColorTexture = ibl->getFogTexture();
}
};
auto setup = [&app, loadResources](Engine* engine, View* view, Scene* scene) {
app.engine = engine;
auto& em = utils::EntityManager::get();
auto& tcm = engine->getTransformManager();
auto& rcm = engine->getRenderableManager();
auto& em = utils::EntityManager::get();
auto vp = view->getViewport();
// Offscreen view
{
// Instantiate offscreen render target.
app.offscreenView = engine->createView();
app.offscreenScene = engine->createScene();
app.offscreenView->setScene(app.offscreenScene);
// Instantiate offscreen render target.
app.offscreenView = engine->createView();
app.offscreenScene = engine->createScene();
app.offscreenView->setScene(app.offscreenScene);
app.offscreenView->setPostProcessingEnabled(false);
app.offscreenColorTexture = Texture::Builder()
.width(vp.width).height(vp.height).levels(1)
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
.format(Texture::InternalFormat::RGBA8).build(*engine);
app.offscreenDepthTexture = Texture::Builder()
.width(vp.width).height(vp.height).levels(1)
.usage(Texture::Usage::DEPTH_ATTACHMENT)
.format(Texture::InternalFormat::DEPTH24).build(*engine);
app.offscreenRenderTarget = RenderTarget::Builder()
.texture(RenderTarget::AttachmentPoint::COLOR, app.offscreenColorTexture)
.texture(RenderTarget::AttachmentPoint::DEPTH, app.offscreenDepthTexture)
.build(*engine);
app.offscreenView->setRenderTarget(app.offscreenRenderTarget);
app.offscreenView->setViewport({0, 0, vp.width, vp.height});
app.offscreenCamera = engine->createCamera(em.create());
app.offscreenView->setCamera(app.offscreenCamera);
FilamentApp::get().addOffscreenView(app.offscreenView);
app.viewer = new ViewerGui(engine, app.offscreenScene, app.offscreenView, 410);
app.viewer->setUiCallback([&app, scene, view, engine]() {
if (ImGui::CollapsingHeader("Debug")) {
auto& debug = engine->getDebugRegistry();
ImGui::Checkbox("Disable buffer padding",
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
ImGui::Checkbox("Disable sub-passes",
debug.getPropertyAddress<bool>("d.renderer.disable_subpasses"));
}
});
Viewport vp = view->getViewport();
vp.width = vp.width - app.viewer->getSidebarWidth();
// Position and orient the mirror in an interesting way.
float3 c = app.quadCenter = {-2, 0, -5};
float3 n = app.quadNormal = normalize(float3 {1, 0, 2});
float3 u = normalize(cross(app.quadNormal, float3(0, 1, 0)));
float3 v = cross(n, u);
u = 1.5 * u;
v = 1.5 * v;
static Vertex kQuadVertices[4] = { {{}, {1, 0}}, {{}, {0, 0}}, {{}, {1, 1}}, {{}, {0, 1}} };
kQuadVertices[0].position = c - u - v;
kQuadVertices[1].position = c + u - v;
kQuadVertices[2].position = c - u + v;
kQuadVertices[3].position = c + u + v;
app.offscreenColorTexture =
Texture::Builder()
.width(vp.width)
.height(vp.height)
.levels(1)
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
.format(Texture::InternalFormat::RGBA8)
.build(*engine);
app.offscreenDepthTexture = Texture::Builder()
.width(vp.width)
.height(vp.height)
.levels(1)
.usage(Texture::Usage::DEPTH_ATTACHMENT)
.format(Texture::InternalFormat::DEPTH32F)
.build(*engine);
app.offscreenRenderTarget = RenderTarget::Builder()
.texture(RenderTarget::AttachmentPoint::COLOR,
app.offscreenColorTexture)
.texture(RenderTarget::AttachmentPoint::DEPTH,
app.offscreenDepthTexture)
.build(*engine);
app.offscreenView->setRenderTarget(app.offscreenRenderTarget);
app.offscreenView->setViewport({0, 0, vp.width, vp.height});
app.offscreenCamera = engine->createCamera(em.create());
app.offscreenView->setCamera(app.offscreenCamera);
FilamentApp::get().addOffscreenView(app.offscreenView);
}
// Create quad vertex buffer.
static_assert(sizeof(Vertex) == 20, "Strange vertex size.");
app.quadVb = VertexBuffer::Builder()
.vertexCount(4)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, 20)
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 12, 20)
.build(*engine);
app.quadVb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(kQuadVertices, 80, nullptr));
// damaged helmet gltf
{
app.names = new utils::NameComponentManager(em);
app.materials = createJitShaderProvider(engine, true);
app.assetLoader = AssetLoader::create({engine, app.materials, app.names});
// Create quad index buffer.
static constexpr uint16_t kQuadIndices[6] = { 0, 1, 2, 3, 2, 1 };
app.quadIb = IndexBuffer::Builder()
.indexCount(6)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.quadIb->setBuffer(*engine, IndexBuffer::BufferDescriptor(kQuadIndices, 12, nullptr));
app.asset = app.assetLoader->createAsset(GLTF_DEMO_DAMAGEDHELMET_DATA,
GLTF_DEMO_DAMAGEDHELMET_SIZE);
// Create quad material and renderable.
app.quadMaterial = Material::Builder()
.package(RESOURCES_MIRROR_DATA, RESOURCES_MIRROR_SIZE)
.build(*engine);
app.quadMatInstance = app.quadMaterial->createInstance();
TextureSampler sampler(TextureSampler::MinFilter::LINEAR, TextureSampler::MagFilter::LINEAR);
app.quadMatInstance->setParameter("albedo", app.offscreenColorTexture, sampler);
app.quadEntity = em.create();
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, app.quadMatInstance)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.quadVb, app.quadIb, 0, 6)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.quadEntity);
scene->addEntity(app.quadEntity);
loadResources();
// Instantiate mesh material.
app.meshMaterial = Material::Builder()
.package(RESOURCES_AIDEFAULTMAT_DATA, RESOURCES_AIDEFAULTMAT_SIZE).build(*engine);
auto mi = app.meshMatInstance = app.meshMaterial->createInstance();
mi->setParameter("baseColor", RgbType::LINEAR, {0.8, 1.0, 1.0});
mi->setParameter("metallic", 0.0f);
mi->setParameter("roughness", 0.4f);
mi->setParameter("reflectance", 0.5f);
app.offscreenScene->addEntities(app.asset->getLightEntities(),
app.asset->getLightEntityCount());
static constexpr int kNumAvailable = 128;
utils::Entity renderables[kNumAvailable];
gltfio::FilamentAsset::SceneMask mask;
mask.set(0);
while (size_t numWritten = app.asset->popRenderables(renderables, kNumAvailable)) {
app.asset->addEntitiesToScene(*app.offscreenScene, renderables, numWritten, mask);
}
// Add monkey into the scene.
app.monkeyMesh = MeshReader::loadMeshFromBuffer(engine, MONKEY_SUZANNE_DATA, nullptr, nullptr, mi);
auto ti = tcm.getInstance(app.monkeyMesh.renderable);
app.transform = mat4f{ mat3f(1), float3(0, 0, -4) } * tcm.getWorldTransform(ti);
rcm.setCastShadows(rcm.getInstance(app.monkeyMesh.renderable), false);
scene->addEntity(app.monkeyMesh.renderable);
app.rootTransformEntity = engine->getEntityManager().create();
tcm.create(app.rootTransformEntity);
TransformManager::Instance const& root = tcm.getInstance(app.rootTransformEntity);
tcm.setParent(tcm.getInstance(app.asset->getRoot()), root);
tcm.setTransform(root, mat4::translation(float3(0, 0, -4)));
}
// Create a reflected monkey, which is used only for App::ReflectionMode::RENDERABLES.
app.reflectedMonkey = em.create();
RenderableManager::Builder(1)
.boundingBox({{ -2, -2, -2 }, { 2, 2, 2 }})
.material(0, mi)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.monkeyMesh.vertexBuffer, app.monkeyMesh.indexBuffer)
.receiveShadows(true)
.castShadows(false)
.build(*engine, app.reflectedMonkey);
setReflectionMode(app, app.mode);
// full-screen quad
{
static Vertex kQuadVertices[4] = {{{1, 1, 0}, {1, 1}}, {{1, -1, 0}, {1, 0}},
{{-1, -1, 0}, {0, 0}}, {{-1, 1, 0}, {0, 1}}};
// Create quad vertex buffer.
static_assert(sizeof(Vertex) == 20, "Strange vertex size.");
app.quadVb = VertexBuffer::Builder()
.vertexCount(4)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT3, 0, 20)
.attribute(VertexAttribute::UV0, 0,
VertexBuffer::AttributeType::FLOAT2, 12, 20)
.build(*engine);
app.quadVb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(kQuadVertices, 80, nullptr));
// Add light source to both scenes.
// NOTE: this is slightly wrong when the reflection mode is RENDERABLES.
app.lightEntity = em.create();
LightManager::Builder(LightManager::Type::SUN)
.color(Color::toLinear<ACCURATE>(sRGBColor(0.98f, 0.92f, 0.89f)))
.intensity(110000)
.direction({ 0.7, -1, -0.8 })
.sunAngularRadius(1.9f)
.castShadows(false)
.build(*engine, app.lightEntity);
scene->addEntity(app.lightEntity);
app.offscreenScene->addEntity(app.lightEntity);
// Create quad index buffer.
static constexpr uint16_t kQuadIndices[6] = {0, 2, 1, 2, 0, 3};
app.quadIb = IndexBuffer::Builder()
.indexCount(6)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.quadIb->setBuffer(*engine,
IndexBuffer::BufferDescriptor(kQuadIndices, 12, nullptr));
// Create quad material and renderable.
// NOTE: this material is VertexDomain=device
app.quadMaterial = Material::Builder()
.package(RESOURCES_MIRROR_DATA, RESOURCES_MIRROR_SIZE)
.build(*engine);
app.quadMatInstance = app.quadMaterial->createInstance();
TextureSampler sampler(TextureSampler::MinFilter::LINEAR,
TextureSampler::MagFilter::LINEAR);
app.quadMatInstance->setParameter("albedo", app.offscreenColorTexture, sampler);
app.quadEntity = em.create();
RenderableManager::Builder(1)
.boundingBox({{-1, -1, -1}, {1, 1, 1}})
.material(0, app.quadMatInstance)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.quadVb,
app.quadIb, 0, 6)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.quadEntity);
scene->addEntity(app.quadEntity);
}
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
@@ -318,14 +346,10 @@ int main(int argc, char** argv) {
engine->destroyCameraComponent(camera);
em.destroy(camera);
engine->destroy(app.reflectedMonkey);
engine->destroy(app.lightEntity);
engine->destroy(app.quadEntity);
engine->destroy(app.meshMatInstance);
engine->destroy(app.meshMaterial);
engine->destroy(app.monkeyMesh.renderable);
engine->destroy(app.monkeyMesh.vertexBuffer);
engine->destroy(app.monkeyMesh.indexBuffer);
engine->destroy(app.offscreenColorTexture);
engine->destroy(app.offscreenDepthTexture);
engine->destroy(app.offscreenRenderTarget);
@@ -337,43 +361,22 @@ int main(int argc, char** argv) {
engine->destroy(app.quadMaterial);
};
auto preRender = [&app](Engine*, View*, Scene*, Renderer* renderer) {
auto preRender = [&app](Engine* engine, View* view, Scene*, Renderer* renderer) {
renderer->setClearOptions({.clearColor = {0.1,0.2,0.4,1.0}, .clear = true});
};
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
auto& tcm = engine->getTransformManager();
utils::slog.e <<"offscreen view=" << app.offscreenView << " primary view=" <<
view << utils::io::endl;
// Animate the monkey by spinning and sliding back and forth along Z.
auto ti = tcm.getInstance(app.monkeyMesh.renderable);
mat4f xlate = mat4f::translation(float3(0, 0, 0.5 + sin(now)));
mat4f xform = app.transform * xlate * mat4f::rotation(now, float3{ 0, 1, 0 });
tcm.setTransform(ti, xform);
// Generate a reflection matrix from the plane equation Ax + By + Cz + D = 0.
const float3 planeNormal = app.quadNormal;
const float4 planeEquation(planeNormal, -dot(planeNormal, app.quadCenter));
const mat4f reflection = reflectionMatrix(planeEquation);
// Apply the reflection matrix to either the renderable or the camera, depending on mode.
Camera const& camera = view->getCamera();
const auto model = camera.getModelMatrix();
const auto renderingProjection = camera.getProjectionMatrix();
const auto cullingProjection = camera.getCullingProjectionMatrix();
app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection,
camera.getNear(), camera.getCullingFar());
switch (app.mode) {
case App::ReflectionMode::RENDERABLES:
tcm.setTransform(tcm.getInstance(app.reflectedMonkey), reflection * xform);
app.offscreenCamera->setModelMatrix(model);
break;
case App::ReflectionMode::CAMERA:
app.offscreenCamera->setModelMatrix(reflection * model);
break;
}
});
const auto model = camera.getModelMatrix();
app.offscreenCamera->setModelMatrix(model);
};
FilamentApp::get().run(app.config, setup, cleanup, FilamentApp::ImGuiCallback(), preRender);
FilamentApp::get().run(app.config, setup, cleanup, gui, preRender);
return 0;
}