From 56e9d8eda5e4c5d65091de6ce5c7be4bb9e72fdc Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 20 Jun 2019 18:53:27 -0700 Subject: [PATCH] Utility to prefilter an environment at runtime This adds a utility function on IndirectLight populate the reflection map from an environment at runtime. This performs some processing similar to cmgen, albeit at a reduced quality. --- .../.idea/codeStyles/codeStyleConfig.xml | 2 +- android/filament-android/CMakeLists.txt | 5 + .../src/main/cpp/IndirectLight.cpp | 3 + .../filament-android/src/main/cpp/Texture.cpp | 42 ++++ .../android/filament/IndirectLight.java | 5 +- .../com/google/android/filament/Texture.java | 37 ++++ filament/CMakeLists.txt | 1 + .../backend/include/backend/DriverEnums.h | 5 + .../include/backend/PixelBufferDescriptor.h | 2 + filament/include/filament/IndirectLight.h | 1 + filament/include/filament/Texture.h | 50 +++++ filament/src/IndirectLight.cpp | 3 + filament/src/Texture.cpp | 184 ++++++++++++++++++ filament/src/details/IndirectLight.h | 1 + filament/src/details/Texture.h | 5 + libs/ibl/CMakeLists.txt | 2 +- libs/ibl/include/ibl/Cubemap.h | 13 +- libs/ibl/include/ibl/CubemapIBL.h | 7 +- libs/ibl/include/ibl/Image.h | 6 + libs/ibl/include/ibl/utilities.h | 10 +- libs/ibl/src/CubemapIBL.cpp | 6 +- libs/ibl/src/CubemapUtils.cpp | 8 +- samples/app/IBL.cpp | 17 +- samples/app/IBL.h | 9 + 24 files changed, 396 insertions(+), 28 deletions(-) diff --git a/android/filament-android/.idea/codeStyles/codeStyleConfig.xml b/android/filament-android/.idea/codeStyles/codeStyleConfig.xml index c79f34ced8..a55e7a179b 100644 --- a/android/filament-android/.idea/codeStyles/codeStyleConfig.xml +++ b/android/filament-android/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index 234ab1ffc9..ef51ced31a 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -14,6 +14,10 @@ add_library(utils STATIC IMPORTED) set_target_properties(utils PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a) +add_library(ibl STATIC IMPORTED) +set_target_properties(ibl PROPERTIES IMPORTED_LOCATION + ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl.a) + add_library(filaflat STATIC IMPORTED) set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a) @@ -86,6 +90,7 @@ target_link_libraries(filament-jni filaflat filabridge geometry + ibl utils log GLESv3 diff --git a/android/filament-android/src/main/cpp/IndirectLight.cpp b/android/filament-android/src/main/cpp/IndirectLight.cpp index e0767b563f..8eaea5ee91 100644 --- a/android/filament-android/src/main/cpp/IndirectLight.cpp +++ b/android/filament-android/src/main/cpp/IndirectLight.cpp @@ -17,6 +17,9 @@ #include #include +#include +#include +#include using namespace filament; diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp index 94c3f7d94d..29e8f87c22 100644 --- a/android/filament-android/src/main/cpp/Texture.cpp +++ b/android/filament-android/src/main/cpp/Texture.cpp @@ -329,6 +329,48 @@ Java_com_google_android_filament_Texture_nIsStreamValidForTexture(JNIEnv*, jclas return (jboolean) (texture->getTarget() == SamplerType::SAMPLER_EXTERNAL); } +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_Texture_nGeneratePrefilterMipmap(JNIEnv *env, jclass, + jlong nativeTexture, jlong nativeEngine, jint width, jint height, + jobject storage, jint remaining, jint left, + jint top, jint type, jint alignment, jint stride, jint format, + jintArray faceOffsetsInBytes_, jobject handler, jobject runnable, jint sampleCount, + jboolean mirror) { + + Texture *texture = (Texture *) nativeTexture; + Engine *engine = (Engine *) nativeEngine; + + jint *faceOffsetsInBytes = env->GetIntArrayElements(faceOffsetsInBytes_, NULL); + Texture::FaceOffsets faceOffsets; + std::copy_n(faceOffsetsInBytes, 6, faceOffsets.offsets); + env->ReleaseIntArrayElements(faceOffsetsInBytes_, faceOffsetsInBytes, JNI_ABORT); + + stride = stride ? stride : width; + size_t sizeInBytes = 6 * + Texture::computeTextureDataSize((Texture::Format) format, (Texture::Type) type, + (size_t) stride, (size_t) height, (size_t) alignment); + + AutoBuffer nioBuffer(env, storage, 0); + if (sizeInBytes > (remaining << nioBuffer.getShift())) { + // BufferOverflowException + return -1; + } + + void *buffer = nioBuffer.getData(); + auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer)); + + Texture::PixelBufferDescriptor desc(buffer, sizeInBytes, (backend::PixelDataFormat) format, + (backend::PixelDataType) type, (uint8_t) alignment, (uint32_t)0, (uint32_t)0, + (uint32_t) stride, &JniBufferCallback::invoke, callback); + + Texture::PrefilterOptions options; + options.sampleCount = sampleCount; + options.mirror = mirror; + texture->generatePrefilterMipmap(*engine, std::move(desc), faceOffsets, &options); + + return 0; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // ANDROID SPECIFIC BITS //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/android/filament-android/src/main/java/com/google/android/filament/IndirectLight.java b/android/filament-android/src/main/java/com/google/android/filament/IndirectLight.java index 31fffcb5e7..26b37803c9 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/IndirectLight.java +++ b/android/filament-android/src/main/java/com/google/android/filament/IndirectLight.java @@ -92,7 +92,7 @@ public class IndirectLight { } @NonNull - public Builder rotation(@NonNull @Size(min = 9) float rotation[]) { + public Builder rotation(@NonNull @Size(min = 9) float[] rotation) { nRotation(mNativeBuilder, rotation[0], rotation[1], rotation[2], rotation[3], rotation[4], rotation[5], @@ -132,7 +132,7 @@ public class IndirectLight { return nGetIntensity(getNativeObject()); } - public void setRotation(@NonNull @Size(min = 9) float rotation[]) { + public void setRotation(@NonNull @Size(min = 9) float[] rotation) { nSetRotation(getNativeObject(), rotation[0], rotation[1], rotation[2], rotation[3], rotation[4], rotation[5], @@ -164,5 +164,4 @@ public class IndirectLight { private static native void nSetIntensity(long nativeIndirectLight, float intensity); private static native float nGetIntensity(long nativeIndirectLight); private static native void nSetRotation(long nativeIndirectLight, float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8); - } diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java index 832fce94f9..75112ed38a 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java @@ -287,6 +287,11 @@ public class Texture { } } + public static class PrefilterOptions { + public int sampleCount = 8; + public boolean mirror = true; + } + public static boolean isTextureFormatSupported(@NonNull Engine engine, @NonNull InternalFormat format) { return nIsTextureFormatSupported(engine.getNativeObject(), format.ordinal()); @@ -490,6 +495,33 @@ public class Texture { nGenerateMipmaps(getNativeObject(), engine.getNativeObject()); } + public void generatePrefilterMipmap(@NonNull Engine engine, + @NonNull PixelBufferDescriptor buffer, @NonNull @Size(min = 6) int[] faceOffsetsInBytes, + PrefilterOptions options) { + + int width = getWidth(0); + int height= getHeight(0); + int sampleCount = 8; + boolean mirror = true; + if (options != null) { + sampleCount = options.sampleCount; + mirror = options.mirror; + } + + int result = nGeneratePrefilterMipmap(getNativeObject(), engine.getNativeObject(), + width, height, + buffer.storage, buffer.storage.remaining(), + buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment, + buffer.stride, buffer.format.ordinal(), faceOffsetsInBytes, + buffer.handler, buffer.callback, + sampleCount, mirror); + + if (result < 0) { + throw new BufferOverflowException(); + } + } + + @UsedByReflection("TextureHelper.java") long getNativeObject() { if (mNativeObject == 0) { @@ -554,4 +586,9 @@ public class Texture { private static native void nGenerateMipmaps(long nativeTexture, long nativeEngine); private static native boolean nIsStreamValidForTexture(long nativeTexture, long nativeStream); + + private static native int nGeneratePrefilterMipmap(long nativeIndirectLight, long nativeEngine, + int width, int height, Buffer storage, int remaining, int left, int top, + int type, int alignment, int stride, int format, int[] faceOffsetsInBytes, + Object handler, Runnable callback, int sampleCount, boolean mirror); } diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 78ed45546f..c0f5348712 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -274,6 +274,7 @@ target_link_libraries(${TARGET} PUBLIC geometry) # TODO: remove this dependency target_link_libraries(${TARGET} PUBLIC filaflat) target_link_libraries(${TARGET} PUBLIC filabridge) target_link_libraries(${TARGET} PUBLIC image_headers) +target_link_libraries(${TARGET} PUBLIC ibl) # Android, iOS, and WebGL do not use bluegl. if(NOT IOS AND NOT ANDROID AND NOT WEBGL) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 4851fa4300..f0f5eda506 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -511,6 +511,11 @@ inline constexpr TextureUsage operator^(TextureUsage lhs, TextureUsage rhs) noex return TextureUsage(uint8_t(lhs) ^ uint8_t(rhs)); } +//! returns whether this format a compressed format +static constexpr bool isCompressedFormat(TextureFormat format) noexcept { + return format >= TextureFormat::EAC_R11; +} + //! returns whether this format is an ETC2 compressed format static constexpr bool isETC2Compression(TextureFormat format) noexcept { return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8; diff --git a/filament/backend/include/backend/PixelBufferDescriptor.h b/filament/backend/include/backend/PixelBufferDescriptor.h index 342bc63edc..be73d2ccf5 100644 --- a/filament/backend/include/backend/PixelBufferDescriptor.h +++ b/filament/backend/include/backend/PixelBufferDescriptor.h @@ -36,6 +36,8 @@ public: using PixelDataFormat = backend::PixelDataFormat; using PixelDataType = backend::PixelDataType; + PixelBufferDescriptor() = default; + PixelBufferDescriptor(void const* buffer, size_t size, PixelDataFormat format, PixelDataType type, uint8_t alignment = 1, uint32_t left = 0, uint32_t top = 0, uint32_t stride = 0, diff --git a/filament/include/filament/IndirectLight.h b/filament/include/filament/IndirectLight.h index e4e03bfad0..c5c493892d 100644 --- a/filament/include/filament/IndirectLight.h +++ b/filament/include/filament/IndirectLight.h @@ -92,6 +92,7 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI { struct BuilderDetails; public: + //! Use Builder to construct an IndirectLight object instance class Builder : public BuilderBase { friend struct BuilderDetails; diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h index 2b8aff8330..513b82792f 100644 --- a/filament/include/filament/Texture.h +++ b/filament/include/filament/Texture.h @@ -85,6 +85,20 @@ public: static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride, size_t height, size_t alignment) noexcept; + + /** + * Options for enviornment prefiltering into reflection map + * + * @see generatePrefilterMipmap() + */ + struct UTILS_PUBLIC PrefilterOptions { + uint16_t sampleCount = 8; //!< sample count used for filtering + bool mirror = true; //!< whether the environment must be mirrored + private: + UTILS_UNUSED uintptr_t reserved[3] = {}; + }; + + //! Use Builder to construct a Texture object instance class Builder : public BuilderBase { friend struct BuilderDetails; @@ -353,6 +367,42 @@ public: * @attention This Texture instance must NOT use Sampler::SAMPLER_CUBEMAP or it has no effect */ void generateMipmaps(Engine& engine) const noexcept; + + /** + * Creates a reflection map from an environment map. + * + * This is a utility function that replaces calls to Texture::setImage(). + * The provided environment map is processed and all mipmap levels are populated. The + * processing is similar to the offline tool `cmgen` as a lower quality setting. + * + * This function is intended to be used when the environment cannot be processed offline, + * for instance if it's generated at runtime. + * + * The source data must obey to some constraints: + * - the data type must be PixelDataFormat::RGB + * - the data format must be one of + * - PixelDataType::FLOAT + * - PixelDataType::HALF + * + * The current texture must be a cubemap + * + * The reflections cubemap's internal format cannot be a compressed format. + * + * The reflections cubemap's dimension must be a power-of-two. + * + * @warning This operation is computationally intensive, especially with large environments. + * + * @param engine Reference to the filament::Engine to associate this IndirectLight with. + * @param buffer Client-side buffer containing the images to set. + * @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets + * are specified in the following order: +x, -x, +y, -y, +z, -z + * + * @exception utils::PreConditionPanic if the source data constraints are not respected. + * + */ + void generatePrefilterMipmap(Engine& engine, + PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* options = nullptr); }; } // namespace filament diff --git a/filament/src/IndirectLight.cpp b/filament/src/IndirectLight.cpp index ad25ab20be..3e288eadd7 100644 --- a/filament/src/IndirectLight.cpp +++ b/filament/src/IndirectLight.cpp @@ -23,6 +23,9 @@ #include +#include +#include + #define IBL_INTEGRATION_PREFILTERED_CUBEMAP 0 #define IBL_INTEGRATION_IMPORTANCE_SAMPLING 1 #define IBL_INTEGRATION IBL_INTEGRATION_PREFILTERED_CUBEMAP diff --git a/filament/src/Texture.cpp b/filament/src/Texture.cpp index 973bd4630b..1984259c3e 100644 --- a/filament/src/Texture.cpp +++ b/filament/src/Texture.cpp @@ -21,6 +21,11 @@ #include "FilamentAPI-impl.h" +#include +#include +#include +#include + #include namespace filament { @@ -333,6 +338,180 @@ size_t FTexture::getFormatSize(InternalFormat format) noexcept { } } + +// this is a hack to be able to create a std::function<> with a non-copyable closure +template +auto make_copyable_function(F&& f) { + using dF = std::decay_t; + auto spf = std::make_shared(std::forward(f)); + return [spf](auto&& ... args) -> decltype(auto) { + return (*spf)(decltype(args)(args)...); + }; +} + +void FTexture::generatePrefilterMipmap(FEngine& engine, + PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* options) { + using namespace ibl; + using namespace backend; + using namespace math; + + const size_t size = getWidth(); + const size_t stride = buffer.stride ? buffer.stride : size; + + /* validate input data */ + + if (!ASSERT_PRECONDITION_NON_FATAL(buffer.format == PixelDataFormat::RGB, + "input data format must be RGB")) { + return; + } + + if (!ASSERT_PRECONDITION_NON_FATAL( + buffer.type == PixelDataType::FLOAT || + buffer.type == PixelDataType::HALF || + buffer.type == PixelDataType::UINT_10F_11F_11F_REV, + "input data type must be FLOAT, HALF or UINT_10F_11F_11F_REV")) { + return; + } + + /* validate texture */ + + if (!ASSERT_PRECONDITION_NON_FATAL(!(size & (size-1)), + "input data cubemap dimensions must be a power-of-two")) { + return; + } + + if (!ASSERT_PRECONDITION_NON_FATAL(!isCompressed(), + "reflections texture cannot be compressed")) { + return; + } + + + PrefilterOptions defaultOptions; + options = options ? options : &defaultOptions; + + utils::JobSystem& js = engine.getJobSystem(); + FEngine::DriverApi& driver = engine.getDriverApi(); + + auto generateMipmaps = [](utils::JobSystem& js, + std::vector& levels, std::vector& images) { + Image temp; + const Cubemap& base(levels[0]); + size_t dim = base.getDimensions(); + size_t mipLevel = 0; + while (dim > 1) { + dim >>= 1u; + Cubemap dst = CubemapUtils::create(temp, dim); + const Cubemap& src(levels[mipLevel++]); + CubemapUtils::downsampleCubemapLevelBoxFilter(js, dst, src); + dst.makeSeamless(); + images.push_back(std::move(temp)); + levels.push_back(std::move(dst)); + } + }; + + + /* + * Create a Cubemap data structure + */ + + Image temp; + Cubemap cml = CubemapUtils::create(temp, size); + for (size_t j = 0; j < 6; j++) { + Cubemap::Face face = (Cubemap::Face)j; + Image const& image = cml.getImageForFace(face); + for (size_t y = 0; y < size; y++) { + Cubemap::Texel* out = (Cubemap::Texel*)image.getPixelRef(0, y); + if (buffer.type == PixelDataType::FLOAT) { + float3 const* src = reinterpret_cast( + static_cast(buffer.buffer) + faceOffsets[j]) + y * stride; + for (size_t x = 0; x < size; x++, out++, src++) { + Cubemap::writeAt(out, *src); + } + } else if (buffer.type == PixelDataType::HALF) { + half3 const* src = reinterpret_cast( + static_cast(buffer.buffer) + faceOffsets[j]) + y * stride; + for (size_t x = 0; x < size; x++, out++, src++) { + Cubemap::writeAt(out, *src); + } + } else if (buffer.type == PixelDataType::UINT_10F_11F_11F_REV) { + uint32_t const* src = reinterpret_cast( + static_cast(buffer.buffer) + faceOffsets[j]) + y * stride; + for (size_t x = 0; x < size; x++, out++, src++) { + using fp10 = math::fp<0, 5, 5>; + using fp11 = math::fp<0, 5, 6>; + fp11 r{ uint16_t( *src & 0x7FFu) }; + fp11 g{ uint16_t((*src >> 11u) & 0x7FFu) }; + fp10 b{ uint16_t((*src >> 22u) & 0x3FFu) }; + Cubemap::Texel texel{ fp11::tof(r), fp11::tof(g), fp10::tof(b) }; + Cubemap::writeAt(out, texel); + } + } + } + } + + /* + * Create the mipmap chain + */ + + std::vector images; + std::vector levels; + images.reserve(getLevels()); + levels.reserve(getLevels()); + + images.push_back(std::move(temp)); + levels.push_back(std::move(cml)); + + if (options->mirror) { + Image mirrorImage; + Cubemap cubemap(CubemapUtils::create(mirrorImage, size)); + CubemapUtils::mirrorCubemap(js, cubemap, levels[0]); + std::swap(levels[0], cubemap); + std::swap(images[0], mirrorImage); + } + + // make the cubemap seamless + levels[0].makeSeamless(); + + // Now generate all the mipmap levels + generateMipmaps(js, levels, images); + + // Finally generate each pre-filtered mipmap level + const size_t baseExp = utils::ctz(size); + size_t numSamples = options->sampleCount; + const size_t numLevels = baseExp + 1; + for (ssize_t i = baseExp; i >= 0; --i) { + const size_t dim = 1U << i; + const size_t level = baseExp - i; + const float lod = saturate(level / (numLevels - 1.0)); + const float linearRoughness = lod * lod; + + Image image; + Cubemap dst = CubemapUtils::create(image, dim); + CubemapIBL::roughnessFilter(js, dst, levels, linearRoughness, numSamples); + + Texture::PixelBufferDescriptor pbd(image.getData(), image.getSize(), + Texture::PixelBufferDescriptor::PixelDataFormat::RGB, + Texture::PixelBufferDescriptor::PixelDataType::FLOAT, 1, 0, 0, image.getStride()); + + uintptr_t base = uintptr_t(image.getData()); + backend::FaceOffsets offsets{}; + for (size_t j = 0; j < 6; j++) { + Image const& faceImage = dst.getImageForFace((Cubemap::Face)j); + offsets[j] = uintptr_t(faceImage.getData()) - base; + } + // upload all 6 faces into the texture + driver.updateCubeImage(mHandle, level, std::move(pbd), offsets); + + // enqueue a commands that holds the image data until it's executed + driver.queueCommand(make_copyable_function([data = image.detach()]() {})); + } + + // no need to call the user callback because buffer is a reference and it'll be destroyed + // by the caller (without being move()d here). +} + + } // namespace details // ------------------------------------------------------------------------------------------------ @@ -405,4 +584,9 @@ size_t Texture::computeTextureDataSize(Texture::Format format, Texture::Type typ return FTexture::computeTextureDataSize(format, type, stride, height, alignment); } +void Texture::generatePrefilterMipmap(Engine& engine, Texture::PixelBufferDescriptor&& buffer, + const Texture::FaceOffsets& faceOffsets, PrefilterOptions const* options) { + upcast(this)->generatePrefilterMipmap(upcast(engine), std::move(buffer), faceOffsets, options); +} + } // namespace filament diff --git a/filament/src/details/IndirectLight.h b/filament/src/details/IndirectLight.h index 0761c91658..a3c8666c31 100644 --- a/filament/src/details/IndirectLight.h +++ b/filament/src/details/IndirectLight.h @@ -22,6 +22,7 @@ #include #include +#include #include diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index db64b17676..774918bd5a 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -60,6 +60,10 @@ public: void setImage(FEngine& engine, size_t level, PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const noexcept; + void generatePrefilterMipmap(FEngine& engine, + PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* options); + void setExternalImage(FEngine& engine, void* image) noexcept; void setExternalStream(FEngine& engine, FStream* stream) noexcept; @@ -68,6 +72,7 @@ public: void setSampleCount(size_t sampleCount) noexcept { mSampleCount = uint8_t(sampleCount); } size_t getSampleCount() const noexcept { return mSampleCount; } bool isMultisample() const noexcept { return mSampleCount > 1; } + bool isCompressed() const noexcept { return backend::isCompressedFormat(mFormat); } bool isCubemap() const noexcept { return mTarget == Sampler::SAMPLER_CUBEMAP; } diff --git a/libs/ibl/CMakeLists.txt b/libs/ibl/CMakeLists.txt index c2c1c2dd9d..32deb9e0cd 100644 --- a/libs/ibl/CMakeLists.txt +++ b/libs/ibl/CMakeLists.txt @@ -44,7 +44,7 @@ target_link_libraries(${TARGET} math utils) if (MSVC OR CLANG_CL) target_compile_options(${TARGET} PRIVATE /fp:fast) else() - target_compile_options(${TARGET} PRIVATE -ffast-math) + target_compile_options(${TARGET} PRIVATE -ffast-math $<$:-fPIC>) endif() diff --git a/libs/ibl/include/ibl/Cubemap.h b/libs/ibl/include/ibl/Cubemap.h index e34181a23a..2e49ff9ec9 100644 --- a/libs/ibl/include/ibl/Cubemap.h +++ b/libs/ibl/include/ibl/Cubemap.h @@ -27,7 +27,6 @@ #include - namespace filament { namespace ibl { @@ -60,12 +59,12 @@ public: enum class Face : uint8_t { - NX = 0, // left +----+ - PX, // right | PY | - NY, // bottom +----+----+----+----+ - PY, // top | NX | PZ | PX | NZ | - NZ, // back +----+----+----+----+ - PZ // front | NY | + PX = 0, // left +----+ + NX, // right | PY | + PY, // bottom +----+----+----+----+ + NY, // top | NX | PZ | PX | NZ | + PZ, // back +----+----+----+----+ + NZ // front | NY | // +----+ }; diff --git a/libs/ibl/include/ibl/CubemapIBL.h b/libs/ibl/include/ibl/CubemapIBL.h index c6954a24d5..4dbc06c14f 100644 --- a/libs/ibl/include/ibl/CubemapIBL.h +++ b/libs/ibl/include/ibl/CubemapIBL.h @@ -17,9 +17,6 @@ #ifndef IBL_CUBEMAPIBL_H #define IBL_CUBEMAPIBL_H -namespace utils { -class JobSystem; -} // namespace utils #include #include @@ -27,6 +24,10 @@ class JobSystem; #include #include +namespace utils { +class JobSystem; +} // namespace utils + namespace filament { namespace ibl { diff --git a/libs/ibl/include/ibl/Image.h b/libs/ibl/include/ibl/Image.h index f61b753cb2..5771392696 100644 --- a/libs/ibl/include/ibl/Image.h +++ b/libs/ibl/include/ibl/Image.h @@ -41,6 +41,8 @@ public: size_t getWidth() const { return mWidth; } + size_t getStride() const { return mBpr / getBytesPerPixel(); } + size_t getHeight() const { return mHeight; } size_t getBytesPerRow() const { return mBpr; } @@ -49,8 +51,12 @@ public: void* getData() const { return mData; } + size_t getSize() const { return mBpr * mHeight; } + void* getPixelRef(size_t x, size_t y) const; + std::unique_ptr detach() { return std::move(mOwnedData); } + private: size_t mBpr = 0; size_t mWidth = 0; diff --git a/libs/ibl/include/ibl/utilities.h b/libs/ibl/include/ibl/utilities.h index c69e3b3876..29e005603c 100644 --- a/libs/ibl/include/ibl/utilities.h +++ b/libs/ibl/include/ibl/utilities.h @@ -44,11 +44,11 @@ inline bool isPOT(size_t x) { inline filament::math::double2 hammersley(uint32_t i, float iN) { constexpr float tof = 0.5f / 0x80000000U; uint32_t bits = i; - bits = (bits << 16) | (bits >> 16); - bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); - bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); - bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); - bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return { i * iN, bits * tof }; } diff --git a/libs/ibl/src/CubemapIBL.cpp b/libs/ibl/src/CubemapIBL.cpp index 960fea4fcd..b54f1037c2 100644 --- a/libs/ibl/src/CubemapIBL.cpp +++ b/libs/ibl/src/CubemapIBL.cpp @@ -300,8 +300,8 @@ void CubemapIBL::roughnessFilter(JobSystem& js, Cubemap& dst, const std::vector< if (linearRoughness == 0) { CubemapUtils::process(dst, js, [&] (CubemapUtils::EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) { - size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; if (updater) { + size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; updater(0, (float)p / (dim * 6)); } const Cubemap& cm = levels[0]; @@ -395,8 +395,8 @@ void CubemapIBL::roughnessFilter(JobSystem& js, Cubemap& dst, const std::vector< [&](CubemapUtils::EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) { - size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; if (updater) { + size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; updater(0, (float)p / (dim * 6)); } @@ -563,8 +563,8 @@ void CubemapIBL::diffuseIrradiance(JobSystem& js, Cubemap& dst, const std::vecto [&](CubemapUtils::EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) { - size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; if (updater) { + size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1; updater(0, (float)p / (dim * 6)); } diff --git a/libs/ibl/src/CubemapUtils.cpp b/libs/ibl/src/CubemapUtils.cpp index bf0f4cb85f..6955364288 100644 --- a/libs/ibl/src/CubemapUtils.cpp +++ b/libs/ibl/src/CubemapUtils.cpp @@ -241,7 +241,7 @@ void CubemapUtils::downsampleCubemapLevelBoxFilter(JobSystem& js, Cubemap& dst, // ------------------------------------------------------------------------------------------------ void CubemapUtils::setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image) { - size_t dim = cm.getDimensions() + 2; // 2 extra per image, for seemlessness + size_t dim = cm.getDimensions() + 2; // 2 extra per image, for seamlessness size_t x = 0; size_t y = 0; switch (face) { @@ -322,12 +322,12 @@ void CubemapUtils::mirrorCubemap(JobSystem& js, Cubemap& dst, const Cubemap& src void CubemapUtils::generateUVGrid(JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY) { Cubemap::Texel const colors[6] = { - { 1, 0, 0 }, // -X / l - red { 1, 1, 1 }, // +X / r - white - { 0, 1, 0 }, // -Y / b - green + { 1, 0, 0 }, // -X / l - red { 0, 0, 1 }, // +Y / t - blue - { 1, 0, 1 }, // -Z / bk - magenta + { 0, 1, 0 }, // -Y / b - green { 1, 1, 0 }, // +z / fr - yellow + { 1, 0, 1 }, // -Z / bk - magenta }; const float uvGridHDRIntensity = 5.0f; size_t gridSizeX = cml.getDimensions() / gridFrequencyX; diff --git a/samples/app/IBL.cpp b/samples/app/IBL.cpp index d515376885..af8fae2f71 100644 --- a/samples/app/IBL.cpp +++ b/samples/app/IBL.cpp @@ -121,6 +121,7 @@ bool IBL::loadFromDirectory(const utils::Path& path) { // Read mip-mapped cubemap const std::string prefix = "m"; if (!loadCubemapLevel(&mTexture, path, 0, prefix + "0_")) return false; + size_t numLevels = mTexture->getLevels(); for (size_t i = 1; isetImage(mEngine, level, std::move(buffer), offsets); + return true; +} + +bool IBL::loadCubemapLevel( + filament::Texture** texture, + Texture::PixelBufferDescriptor* outBuffer, + Texture::FaceOffsets* outOffsets, + const utils::Path& path, size_t level, std::string const& levelPrefix) const { static const char* faceSuffix[6] = { "px", "nx", "py", "ny", "pz", "nz" }; size_t size = 0; @@ -223,7 +237,8 @@ bool IBL::loadCubemapLevel(filament::Texture** texture, const utils::Path& path, if (!success) return false; - (*texture)->setImage(mEngine, level, std::move(buffer), offsets); + *outBuffer = std::move(buffer); + *outOffsets = offsets; return true; } diff --git a/samples/app/IBL.h b/samples/app/IBL.h index 080fb168eb..0b27e283aa 100644 --- a/samples/app/IBL.h +++ b/samples/app/IBL.h @@ -17,6 +17,8 @@ #ifndef TNT_FILAMENT_SAMPLE_IBL_H #define TNT_FILAMENT_SAMPLE_IBL_H +#include + #include #include @@ -56,6 +58,13 @@ private: bool loadCubemapLevel(filament::Texture** texture, const utils::Path& path, size_t level = 0, std::string const& levelPrefix = "") const; + + bool loadCubemapLevel(filament::Texture** texture, + filament::Texture::PixelBufferDescriptor* outBuffer, + filament::Texture::FaceOffsets* outOffsets, + const utils::Path& path, + size_t level = 0, std::string const& levelPrefix = "") const; + filament::Engine& mEngine; filament::math::float3 mBands[9] = {};