new generatePrefilterMipmap.a library
This commit is contained in:
committed by
Mathias Agopian
parent
27f7b198a6
commit
4e50e64888
@@ -786,6 +786,7 @@ add_subdirectory(${LIBRARIES}/filabridge)
|
||||
add_subdirectory(${LIBRARIES}/filaflat)
|
||||
add_subdirectory(${LIBRARIES}/filagui)
|
||||
add_subdirectory(${LIBRARIES}/filameshio)
|
||||
add_subdirectory(${LIBRARIES}/generatePrefilterMipmap)
|
||||
add_subdirectory(${LIBRARIES}/gltfio)
|
||||
add_subdirectory(${LIBRARIES}/ibl)
|
||||
add_subdirectory(${LIBRARIES}/iblprefilter)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(filament C ASM)
|
||||
|
||||
set(TARGET filament-generatePrefilterMipmap)
|
||||
set(PUBLIC_HDR_DIR include)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# ==================================================================================================
|
||||
# Sources and headers
|
||||
# ==================================================================================================
|
||||
set(PUBLIC_HDRS
|
||||
include/${TARGET}/generatePrefilterMipmap.h
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
src/generatePrefilterMipmap.cpp
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
# Includes & target definition
|
||||
# ==================================================================================================
|
||||
# specify where our headers are
|
||||
include_directories(${PUBLIC_HDR_DIR})
|
||||
include_directories(src)
|
||||
|
||||
# we're building a library
|
||||
add_library(${TARGET} STATIC ${PRIVATE_HDRS} ${PUBLIC_HDRS} ${SRCS})
|
||||
|
||||
# specify where the public headers of this library are
|
||||
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
|
||||
|
||||
# ==================================================================================================
|
||||
# Dependencies
|
||||
# ==================================================================================================
|
||||
|
||||
target_link_libraries(${TARGET} PUBLIC math)
|
||||
target_link_libraries(${TARGET} PUBLIC utils)
|
||||
target_link_libraries(${TARGET} PUBLIC ibl)
|
||||
target_link_libraries(${TARGET} PRIVATE filament)
|
||||
|
||||
# ==================================================================================================
|
||||
# Compiler flags
|
||||
# ==================================================================================================
|
||||
if (MSVC)
|
||||
set(OPTIMIZATION_FLAGS
|
||||
/fp:fast
|
||||
)
|
||||
elseif(WEBGL)
|
||||
# Avoid strict-vtable-pointers here, it is broken in WebAssembly.
|
||||
set(OPTIMIZATION_FLAGS -fvisibility-inlines-hidden)
|
||||
else()
|
||||
set(OPTIMIZATION_FLAGS
|
||||
-ffast-math
|
||||
-fno-finite-math-only
|
||||
-ffp-contract=fast
|
||||
# TODO: aggressive vectorization is currently broken on Android
|
||||
# -fslp-vectorize-aggressive
|
||||
-fvisibility-inlines-hidden
|
||||
-fstrict-vtable-pointers
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(${TARGET} PRIVATE
|
||||
${FILAMENT_WARNINGS}
|
||||
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
|
||||
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
set(INSTALL_TYPE ARCHIVE)
|
||||
install(TARGETS ${TARGET} ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/${TARGET} DESTINATION include)
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
namespace backend {
|
||||
class PixelBufferDescriptor;
|
||||
}
|
||||
|
||||
class Engine;
|
||||
class Texture;
|
||||
|
||||
struct UTILS_PUBLIC FaceOffsets {
|
||||
using size_type = size_t;
|
||||
union {
|
||||
struct {
|
||||
size_type px; //!< +x face offset in bytes
|
||||
size_type nx; //!< -x face offset in bytes
|
||||
size_type py; //!< +y face offset in bytes
|
||||
size_type ny; //!< -y face offset in bytes
|
||||
size_type pz; //!< +z face offset in bytes
|
||||
size_type nz; //!< -z face offset in bytes
|
||||
};
|
||||
size_type offsets[6];
|
||||
};
|
||||
size_type operator[](size_t const n) const noexcept { return offsets[n]; }
|
||||
size_type& operator[](size_t const n) { return offsets[n]; }
|
||||
FaceOffsets() noexcept = default;
|
||||
explicit FaceOffsets(size_type const faceSize) noexcept {
|
||||
px = faceSize * 0;
|
||||
nx = faceSize * 1;
|
||||
py = faceSize * 2;
|
||||
ny = faceSize * 3;
|
||||
pz = faceSize * 4;
|
||||
nz = faceSize * 5;
|
||||
}
|
||||
FaceOffsets(const FaceOffsets& rhs) noexcept {
|
||||
px = rhs.px;
|
||||
nx = rhs.nx;
|
||||
py = rhs.py;
|
||||
ny = rhs.ny;
|
||||
pz = rhs.pz;
|
||||
nz = rhs.nz;
|
||||
}
|
||||
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
|
||||
px = rhs.px;
|
||||
nx = rhs.nx;
|
||||
py = rhs.py;
|
||||
ny = rhs.ny;
|
||||
pz = rhs.pz;
|
||||
nz = rhs.nz;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for environment 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] = {};
|
||||
};
|
||||
|
||||
UTILS_PUBLIC
|
||||
void generatePrefilterMipmap(Texture* UTILS_NONNULL texture, Engine& engine,
|
||||
backend::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
|
||||
PrefilterOptions const* UTILS_NULLABLE options = nullptr);
|
||||
|
||||
|
||||
} // namespace filament
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "filament-generatePrefilterMipmap/generatePrefilterMipmap.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Texture.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <ibl/Cubemap.h>
|
||||
#include <ibl/CubemapIBL.h>
|
||||
#include <ibl/CubemapUtils.h>
|
||||
#include <ibl/Image.h>
|
||||
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace utils;
|
||||
|
||||
void generatePrefilterMipmap(Texture* const texture, Engine& engine,
|
||||
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
|
||||
PrefilterOptions const* options) {
|
||||
using namespace ibl;
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
const size_t size = texture->getWidth();
|
||||
const size_t stride = buffer.stride ? buffer.stride : size;
|
||||
|
||||
/* validate input data */
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
buffer.format == PixelDataFormat::RGB || buffer.format == PixelDataFormat::RGBA)
|
||||
<< "input data format must be RGB or RGBA";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(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";
|
||||
|
||||
/* validate texture */
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!(size & (size - 1)))
|
||||
<< "input data cubemap dimensions must be a power-of-two";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!Texture::isTextureFormatCompressed(texture->getFormat()))
|
||||
<< "reflections texture cannot be compressed";
|
||||
|
||||
PrefilterOptions const defaultOptions;
|
||||
options = options ? options : &defaultOptions;
|
||||
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
|
||||
auto generateMipmaps = [](JobSystem& js,
|
||||
FixedCapacityVector<Cubemap>& levels, FixedCapacityVector<Image>& 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
|
||||
*/
|
||||
|
||||
size_t bytesPerPixel = 0;
|
||||
|
||||
switch (buffer.format) {
|
||||
case PixelDataFormat::RGB:
|
||||
bytesPerPixel = 3;
|
||||
break;
|
||||
case PixelDataFormat::RGBA:
|
||||
bytesPerPixel = 4;
|
||||
break;
|
||||
default:
|
||||
// this cannot happen due to the checks above
|
||||
break;
|
||||
}
|
||||
|
||||
switch (buffer.type) { // NOLINT
|
||||
case PixelDataType::FLOAT:
|
||||
bytesPerPixel *= 4;
|
||||
break;
|
||||
case PixelDataType::HALF:
|
||||
bytesPerPixel *= 2;
|
||||
break;
|
||||
default:
|
||||
// this cannot happen due to the checks above
|
||||
break;
|
||||
}
|
||||
assert_invariant(bytesPerPixel);
|
||||
|
||||
Image temp;
|
||||
Cubemap cml = CubemapUtils::create(temp, size);
|
||||
for (size_t j = 0; j < 6; j++) {
|
||||
Cubemap::Face const face = Cubemap::Face(j);
|
||||
Image const& image = cml.getImageForFace(face);
|
||||
for (size_t y = 0; y < size; y++) {
|
||||
Cubemap::Texel* out = static_cast<Cubemap::Texel*>(image.getPixelRef(0, y));
|
||||
if (buffer.type == PixelDataType::FLOAT) {
|
||||
float3 const* src = pointermath::add((float3 const*)buffer.buffer, faceOffsets[j]);
|
||||
src = pointermath::add(src, y * stride * bytesPerPixel);
|
||||
for (size_t x = 0; x < size; x++, out++) {
|
||||
Cubemap::writeAt(out, *src);
|
||||
src = pointermath::add(src, bytesPerPixel);
|
||||
}
|
||||
} else if (buffer.type == PixelDataType::HALF) {
|
||||
half3 const* src = pointermath::add((half3 const*)buffer.buffer, faceOffsets[j]);
|
||||
src = pointermath::add(src, y * stride * bytesPerPixel);
|
||||
for (size_t x = 0; x < size; x++, out++) {
|
||||
Cubemap::writeAt(out, *src);
|
||||
src = pointermath::add(src, bytesPerPixel);
|
||||
}
|
||||
} else if (buffer.type == PixelDataType::UINT_10F_11F_11F_REV) {
|
||||
// this doesn't depend on buffer.format
|
||||
uint32_t const* src = reinterpret_cast<uint32_t const*>(
|
||||
static_cast<char const*>(buffer.buffer)
|
||||
+ faceOffsets[j]) + y * stride;
|
||||
for (size_t x = 0; x < size; x++, out++, src++) {
|
||||
using fp10 = fp<0, 5, 5>;
|
||||
using fp11 = fp<0, 5, 6>;
|
||||
fp11 const r{ uint16_t( *src & 0x7FFu) };
|
||||
fp11 const g{ uint16_t((*src >> 11u) & 0x7FFu) };
|
||||
fp10 const b{ uint16_t((*src >> 22u) & 0x3FFu) };
|
||||
Cubemap::Texel const texel{ fp11::tof(r), fp11::tof(g), fp10::tof(b) };
|
||||
Cubemap::writeAt(out, texel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the mipmap chain
|
||||
*/
|
||||
|
||||
auto images = FixedCapacityVector<Image>::with_capacity(texture->getLevels());
|
||||
auto levels = FixedCapacityVector<Cubemap>::with_capacity(texture->getLevels());
|
||||
|
||||
images.push_back(std::move(temp));
|
||||
levels.push_back(std::move(cml));
|
||||
|
||||
const float3 mirror = options->mirror ? float3{ -1, 1, 1 } : float3{ 1, 1, 1 };
|
||||
|
||||
// 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 = ctz(size);
|
||||
size_t const numSamples = options->sampleCount;
|
||||
const size_t numLevels = baseExp + 1;
|
||||
for (ssize_t i = (ssize_t)baseExp; i >= 0; --i) {
|
||||
const size_t dim = 1U << i;
|
||||
const size_t level = baseExp - i;
|
||||
const float lod = saturate(float(level) / float(numLevels - 1));
|
||||
const float linearRoughness = lod * lod;
|
||||
|
||||
Image image;
|
||||
Cubemap dst = CubemapUtils::create(image, dim);
|
||||
CubemapIBL::roughnessFilter(js, dst, { levels.begin(), uint32_t(levels.size()) },
|
||||
linearRoughness, numSamples, mirror, true);
|
||||
|
||||
PixelBufferDescriptor const pbd(image.getData(), image.getSize(),
|
||||
PixelBufferDescriptor::PixelDataFormat::RGB,
|
||||
PixelBufferDescriptor::PixelDataType::FLOAT, 1, 0, 0, image.getStride());
|
||||
|
||||
uintptr_t const base = uintptr_t(image.getData());
|
||||
for (size_t j = 0; j < 6; j++) {
|
||||
Image const& faceImage = dst.getImageForFace(Cubemap::Face(j));
|
||||
auto offset = uintptr_t(faceImage.getData()) - base;
|
||||
texture->setImage(engine, level, 0, 0, j, dim, dim, 1,
|
||||
PixelBufferDescriptor::make((char*) image.getData() + offset,
|
||||
dim * dim * 3 * sizeof(float),
|
||||
PixelBufferDescriptor::PixelDataFormat::RGB,
|
||||
PixelBufferDescriptor::PixelDataType::FLOAT, 1,
|
||||
0, 0, uint32_t(image.getStride()),
|
||||
[j, &image](void const*, size_t) {
|
||||
// free the buffer only when processing the last image
|
||||
if (j == 5) {
|
||||
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 filament
|
||||
Reference in New Issue
Block a user