From 4764eefafc50327c9d0b5a76a159f09e6daa2fe4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 27 Sep 2018 17:24:44 -0700 Subject: [PATCH] improve cubemap bilinear sampling We now handle all face corners correctly and follow the OpenGL recommandation of averaging the corners of all 3 adjacent faces. --- tools/cmgen/src/Cubemap.cpp | 85 ++++++++++++++++++++++++-------- tools/cmgen/src/CubemapUtils.cpp | 12 ++--- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/tools/cmgen/src/Cubemap.cpp b/tools/cmgen/src/Cubemap.cpp index 273e2c7e79..96031b2327 100644 --- a/tools/cmgen/src/Cubemap.cpp +++ b/tools/cmgen/src/Cubemap.cpp @@ -96,37 +96,80 @@ Cubemap::Address Cubemap::getAddressFor(const double3& r) { */ void Cubemap::makeSeamless() { size_t dim = getDimensions(); - auto stitch = [ & ](void* dst, size_t incDst, void const* src, ssize_t incSrc) { - for (size_t i = 0; i < dim; ++i) { - *(Texel*)dst = *(Texel*)src; - dst = ((uint8_t*)dst + incDst); - src = ((uint8_t*)src + incSrc); - } - }; + size_t D = dim; + // here we assume that all faces share the same underlying image const size_t bpr = getImageForFace(Face::NX).getBytesPerRow(); const size_t bpp = getImageForFace(Face::NX).getBytesPerPixel(); - stitch(getImageForFace(Face::NX).getPixelRef(0, dim), bpp, - getImageForFace(Face::NY).getPixelRef(0, dim - 1), -bpr); + auto getTexel = [](Image& image, ssize_t x, ssize_t y) -> Texel* { + return (Texel*)((uint8_t*)image.getData() + x * image.getBytesPerPixel() + y * image.getBytesPerRow()); + }; - stitch(getImageForFace(Face::PY).getPixelRef(dim, 0), bpr, - getImageForFace(Face::PX).getPixelRef(dim - 1, 0), -bpp); + auto stitch = [ & ]( + Face faceDst, ssize_t xdst, ssize_t ydst, size_t incDst, + Face faceSrc, size_t xsrc, size_t ysrc, ssize_t incSrc) { + Image& imageDst = getImageForFace(faceDst); + Image& imageSrc = getImageForFace(faceSrc); + Texel* dst = getTexel(imageDst, xdst, ydst); + Texel* src = getTexel(imageSrc, xsrc, ysrc); + for (size_t i = 0; i < dim; ++i) { + *dst = *src; + dst = (Texel*)((uint8_t*)dst + incDst); + src = (Texel*)((uint8_t*)src + incSrc); + } + }; - stitch(getImageForFace(Face::PX).getPixelRef(0, dim), bpp, - getImageForFace(Face::NY).getPixelRef(dim - 1, 0), bpr); + auto corners = [ & ](Face face) { + size_t L = D - 1; + Image& image = getImageForFace(face); + *getTexel(image, -1, -1) = (*getTexel(image, 0, 0) + *getTexel(image, -1, 0) + *getTexel(image, 0, -1)) / 3; + *getTexel(image, L+1, -1) = (*getTexel(image, L, 0) + *getTexel(image, L+1, 0) + *getTexel(image, 0, -1)) / 3; + *getTexel(image, -1, L+1) = (*getTexel(image, 0, L) + *getTexel(image, -1, 0) + *getTexel(image, 0, L+1)) / 3; + *getTexel(image, L+1,L+1) = (*getTexel(image, L, L) + *getTexel(image, L+1, 0) + *getTexel(image, 0, L+1)) / 3; + }; - stitch(getImageForFace(Face::NY).getPixelRef(dim, 0), bpr, - getImageForFace(Face::PX).getPixelRef(0, dim - 1), bpp); + // +Y / Top + stitch( Face::PY, -1, 0, bpr, Face::NX, 0, 0, bpp); // left + stitch( Face::PY, 0, -1, bpp, Face::NZ, D-1, 0, -bpp); // top + stitch( Face::PY, D, 0, bpr, Face::PX, D-1, 0, -bpp); // right + stitch( Face::PY, 0, D, bpp, Face::PZ, 0, 0, bpp); // bottom + corners(Face::PY); - stitch(getImageForFace(Face::NZ).getPixelRef(0, dim), bpp, - getImageForFace(Face::NY).getPixelRef(dim - 1, dim - 1), -bpp); + // -X / Left + stitch( Face::NX, -1, 0, bpr, Face::NZ, D-1, 0, bpr); // left + stitch( Face::NX, 0, -1, bpp, Face::PY, 0, 0, bpr); // top + stitch( Face::NX, D, 0, bpr, Face::PZ, 0, 0, bpr); // right + stitch( Face::NX, 0, D, bpp, Face::NY, 0, D-1, -bpr); // bottom + corners(Face::NX); - stitch(getImageForFace(Face::NZ).getPixelRef(dim, 0), bpr, - getImageForFace(Face::NX).getPixelRef(0, 0), bpr); + // +Z / Front + stitch( Face::PZ, -1, 0, bpr, Face::NX, D-1, 0, bpr); // left + stitch( Face::PZ, 0, -1, bpp, Face::PY, 0, D-1, bpp); // top + stitch( Face::PZ, D, 0, bpr, Face::PX, 0, 0, bpr); // right + stitch( Face::PZ, 0, D, bpp, Face::NY, 0, 0, bpp); // bottom + corners(Face::PZ); - stitch(getImageForFace(Face::NY).getPixelRef(0, dim), bpp, - getImageForFace(Face::NZ).getPixelRef(dim - 1, dim - 1), -bpp); + // +X / Right + stitch( Face::PX, -1, 0, bpr, Face::PZ, D-1, 0, bpr); // left + stitch( Face::PX, 0, -1, bpp, Face::PY, D-1, D-1, -bpr); // top + stitch( Face::PX, D, 0, bpr, Face::NZ, 0, 0, bpr); // right + stitch( Face::PX, 0, D, bpp, Face::NY, D-1, 0, bpr); // bottom + corners(Face::PX); + + // -Z / Back + stitch( Face::NZ, -1, 0, bpr, Face::PX, D-1, 0, bpr); // left + stitch( Face::NZ, 0, -1, bpp, Face::PY, D-1, 0, -bpp); // top + stitch( Face::NZ, D, 0, bpr, Face::NX, 0, 0, bpr); // right + stitch( Face::NZ, 0, D, bpp, Face::NY, D-1, 0, -bpp); // bottom + corners(Face::NZ); + + // -Y / Bottom + stitch( Face::NY, -1, 0, bpr, Face::NX, D-1, D-1, -bpp); // left + stitch( Face::NY, 0, -1, bpp, Face::PZ, 0, D-1, bpp); // top + stitch( Face::NY, D, 0, bpr, Face::PX, 0, D-1, bpp); // right + stitch( Face::NY, 0, D, bpp, Face::NZ, D-1, D-1, -bpp); // bottom + corners(Face::NY); } Cubemap::Texel Cubemap::filterAt(const Image& image, double x, double y) { diff --git a/tools/cmgen/src/CubemapUtils.cpp b/tools/cmgen/src/CubemapUtils.cpp index 8a413438ff..416436a01d 100644 --- a/tools/cmgen/src/CubemapUtils.cpp +++ b/tools/cmgen/src/CubemapUtils.cpp @@ -167,7 +167,7 @@ void CubemapUtils::downsampleCubemapLevelBoxFilter(Cubemap& dst, const Cubemap& // ------------------------------------------------------------------------------------------------ void CubemapUtils::setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image) { - size_t dim = cm.getDimensions(); + size_t dim = cm.getDimensions() + 2; // 2 extra per image, for seemlessness size_t x = 0; size_t y = 0; switch (face) { @@ -191,7 +191,7 @@ void CubemapUtils::setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image break; } Image subImage; - subImage.subset(image, x, y, dim, dim); + subImage.subset(image, x + 1, y + 1, dim, dim); cm.setImageForFace(face, subImage); } @@ -205,16 +205,16 @@ void CubemapUtils::setAllFacesFromCross(Cubemap& cm, const Image& image) { } Image CubemapUtils::createCubemapImage(size_t dim, bool horizontal) { - size_t width = 4 * dim; - size_t height = 3 * dim; + size_t width = 4 * (dim + 2); + size_t height = 3 * (dim + 2); if (!horizontal) { std::swap(width, height); } // always allocate an extra column and row, to allow the cubemap to be "seamless" - size_t bpr = (width + 1) * sizeof(Cubemap::Texel); + size_t bpr = width * sizeof(Cubemap::Texel); bpr = (bpr + 31) & ~31; - size_t bufSize = bpr * (height + 1); + size_t bufSize = bpr * height; std::unique_ptr data(new uint8_t[bufSize]); memset(data.get(), 0, bufSize); Image image(std::move(data), width, height, bpr, sizeof(Cubemap::Texel));