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.
This commit is contained in:
Mathias Agopian
2018-09-27 17:24:44 -07:00
committed by Mathias Agopian
parent 86b8a97389
commit 4764eefafc
2 changed files with 70 additions and 27 deletions

View File

@@ -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) {

View File

@@ -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<uint8_t[]> data(new uint8_t[bufSize]);
memset(data.get(), 0, bufSize);
Image image(std::move(data), width, height, bpr, sizeof(Cubemap::Texel));