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