From 5bf3cdd5927918e423639d02151193d57fc4d4bb Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 28 Mar 2019 11:59:21 +0100 Subject: [PATCH] Working on cubemap seams --- libraries/image/src/image/CubeMap.cpp | 200 +++++++++++++++++++++----- libraries/image/src/image/CubeMap.h | 46 +++++- libraries/image/src/image/Image.cpp | 28 ++-- libraries/image/src/image/Image.h | 6 +- 4 files changed, 221 insertions(+), 59 deletions(-) diff --git a/libraries/image/src/image/CubeMap.cpp b/libraries/image/src/image/CubeMap.cpp index acd8d6fb85..852852e0a1 100644 --- a/libraries/image/src/image/CubeMap.cpp +++ b/libraries/image/src/image/CubeMap.cpp @@ -15,6 +15,7 @@ #include #include "RandomAndNoise.h" +#include "Image.h" #ifndef M_PI #define M_PI 3.14159265359 @@ -22,14 +23,160 @@ using namespace image; -CubeMap::CubeMap(int width, int height, int mipCount) : - _width(width), _height(height) { +static const glm::vec3 FACE_NORMALS[24] = { + // POSITIVE X + glm::vec3(1.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 1.0f, -1.0f), + glm::vec3(1.0f, -1.0f, 1.0f), + glm::vec3(1.0f, -1.0f, -1.0f), + // NEGATIVE X + glm::vec3(-1.0f, 1.0f, -1.0f), + glm::vec3(-1.0f, 1.0f, 1.0f), + glm::vec3(-1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, 1.0f), + // POSITIVE Y + glm::vec3(-1.0f, 1.0f, -1.0f), + glm::vec3(1.0f, 1.0f, -1.0f), + glm::vec3(-1.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 1.0f, 1.0f), + // NEGATIVE Y + glm::vec3(-1.0f, -1.0f, 1.0f), + glm::vec3(1.0f, -1.0f, 1.0f), + glm::vec3(-1.0f, -1.0f, -1.0f), + glm::vec3(1.0f, -1.0f, -1.0f), + // POSITIVE Z + glm::vec3(-1.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 1.0f, 1.0f), + glm::vec3(-1.0f, -1.0f, 1.0f), + glm::vec3(1.0f, -1.0f, 1.0f), + // NEGATIVE Z + glm::vec3(1.0f, 1.0f, -1.0f), + glm::vec3(-1.0f, 1.0f, -1.0f), + glm::vec3(1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, -1.0f) +}; + +CubeMap::CubeMap(int width, int height, int mipCount) { + reset(width, height, mipCount); +} + +CubeMap::CubeMap(gpu::TexturePointer texture, const std::atomic& abortProcessing) { + reset(texture->getWidth(), texture->getHeight(), texture->getNumMips()); + + const auto srcTextureFormat = texture->getTexelFormat(); + + for (gpu::uint16 mipLevel = 0; mipLevel < texture->getNumMips(); ++mipLevel) { + auto mipDims = texture->evalMipDimensions(mipLevel); + auto destLineStride = getFaceLineStride(mipLevel); + + for (face = 0; face < 6; face++) { + auto sourcePixels = texture->accessStoredMipFace(mipLevel, face)->data(); + auto destPixels = editFace(mipLevel, face); + + convertToFloat(sourcePixels, mipDims.x, mipDims.y, sizeof(uint32)*mipDims.x, srcTextureFormat, destPixels, destLineStride); + if (abortProcessing.load()) { + return; + } + } + + // Now copy edge rows and columns from neighbouring faces to fix + // seam filtering issues + seamColumnAndRow(mipLevel, gpu::Texture::CUBE_FACE_TOP_POS_Y, mipDims.x, gpu::Texture::CUBE_FACE_RIGHT_POS_X, -1, -1); + seamColumnAndRow(mipLevel, gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y, mipDims.x, gpu::Texture::CUBE_FACE_RIGHT_POS_X, mipDims.y, 1); + seamColumnAndColumn(mipLevel, gpu::Texture::CUBE_FACE_FRONT_NEG_Z, 0, gpu::Texture::CUBE_FACE_RIGHT_POS_X, mipDims.x, 1); + seamColumnAndColumn(mipLevel, gpu::Texture::CUBE_FACE_BACK_POS_Z, mipDims.x, gpu::Texture::CUBE_FACE_RIGHT_POS_X, -1, 1); + + seamRowAndRow(mipLevel, gpu::Texture::CUBE_FACE_BACK_POS_Z, -1, gpu::Texture::CUBE_FACE_TOP_POS_Y, mipDims.y, 1); + seamRowAndRow(mipLevel, gpu::Texture::CUBE_FACE_BACK_POS_Z, mipDims.y, gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y, -1, 1); + seamColumnAndColumn(mipLevel, gpu::Texture::CUBE_FACE_BACK_POS_Z, -1, gpu::Texture::CUBE_FACE_LEFT_NEG_X, mipDims.x, 1); + + seamRowAndRow(mipLevel, gpu::Texture::CUBE_FACE_TOP_POS_Y, -1, gpu::Texture::CUBE_FACE_FRONT_NEG_Z, -1, -1); + seamColumnAndRow(mipLevel, gpu::Texture::CUBE_FACE_TOP_POS_Y, -1, gpu::Texture::CUBE_FACE_LEFT_NEG_X, -1, 1); + + seamColumnAndColumn(mipLevel, gpu::Texture::CUBE_FACE_LEFT_NEG_X, -1, gpu::Texture::CUBE_FACE_FRONT_NEG_Z, mipDims.x, 1); + seamColumnAndRow(mipLevel, gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y, -1, gpu::Texture::CUBE_FACE_LEFT_NEG_X, mipDims.y, -1); + + seamRowAndRow(mipLevel, gpu::Texture::CUBE_FACE_FRONT_NEG_Z, mipDims.y, gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y, mipDims.y, -1); + + // Duplicate corner pixels + for (face = 0; face < 6; face++) { + auto& pixels = _mips[mipLevel][face]; + + pixels[0] = pixels[1]; + pixels[mipDims.x+1] = pixels[mipDims.x]; + pixels[(mipDims.y+1)*(mipDims.x+2)] = pixels[(mipDims.y+1)*(mipDims.x+2)+1]; + pixels[(mipDims.y+2)*(mipDims.x+2)-1] = pixels[(mipDims.y+2)*(mipDims.x+2)-2]; + } + } +} + +inline static std::pair getSrcAndDst(int dim, int value) { + int src; + int dst; + + if (value < 0) { + src = 1; + dst = 0; + } else if (value >= dim) { + src = dim; + dst = dim+1; + } + return std::make_pair(src, dst); +} + +void CubeMap::seamColumnAndColumn(gpu::uint16 mipLevel, int face0, int col0, int face1, int col1, int inc) { + auto mipDims = getMipDimensions(mipLevel); + auto coords0 = getSrcAndDst(mipDims.x, col0); + auto coords1 = getSrcAndDst(mipDims.x, col1); + + copyColumnToColumn(mipLevel, face0, coords0.first, face1, coords1.second, inc); + copyColumnToColumn(mipLevel, face1, coords1.first, face0, coords0.second, inc); +} + +void CubeMap::seamColumnAndRow(gpu::uint16 mipLevel, int face0, int col0, int face1, int row1, int inc) { + auto mipDims = getMipDimensions(mipLevel); + auto coords0 = getSrcAndDst(mipDims.x, col0); + auto coords1 = getSrcAndDst(mipDims.y, row1); + + copyColumnToRow(mipLevel, face0, coords0.first, face1, coords1.second, inc); + copyRowToColumn(mipLevel, face1, coords1.first, face0, coords0.second, inc); +} + +void CubeMap::seamRowAndRow(gpu::uint16 mipLevel, int face0, int row0, int face1, int row1, int inc) { + auto mipDims = getMipDimensions(mipLevel); + auto coords0 = getSrcAndDst(mipDims.y, row0); + auto coords1 = getSrcAndDst(mipDims.y, row1); + + copyRowToRow(mipLevel, face0, coords0.first, face1, coords1.second, inc); + copyRowToRow(mipLevel, face1, coords1.first, face0, coords0.second, inc); +} + +void CubeMap::copyColumnToColumn(gpu::uint16 mipLevel, int srcFace, int srcCol, int dstFace, int dstCol, int dstInc) { + +} + +void CubeMap::copyRowToRow(gpu::uint16 mipLevel, int srcFace, int srcRow, int dstFace, int dstRow, int dstInc) { + +} + +void CubeMap::copyColumnToRow(gpu::uint16 mipLevel, int srcFace, int srcCol, int dstFace, int dstRow, int dstInc) { + +} + +void CubeMap::copyRowToColumn(gpu::uint16 mipLevel, int srcFace, int srcRow, int dstFace, int dstCol, int dstInc) { + +} + +void CubeMap::reset(int width, int height, int mipCount) { assert(mipCount >0 && _width > 0 && _height > 0); + _width = width; + _height = height; _mips.resize(mipCount); for (auto mipLevel = 0; mipLevel < mipCount; mipLevel++) { - auto mipWidth = std::max(1, width >> mipLevel); - auto mipHeight = std::max(1, height >> mipLevel); - auto mipPixelCount = mipWidth * mipHeight; + auto mipDimensions = getMipDimensions(mipLevel); + // Add extra pixels on edges to perform edge seam fixup (we will duplicate pixels from + // neighbouring faces) + auto mipPixelCount = (mipDimensions.x+2) * (mipDimensions.y+2); for (auto& face : _mips[mipLevel]) { face.resize(mipPixelCount); @@ -37,6 +184,14 @@ CubeMap::CubeMap(int width, int height, int mipCount) : } } +glm::vec4* CubeMap::editFace(gpu::uint16 mipLevel, int face) { + return _mips[mipLevel][face].data() + 3 + _width; +} + +const glm::vec4* CubeMap::getFace(gpu::uint16 mipLevel, int face) const; +size_t CubeMap::getFaceLineStride(gpu::uint16 mipLevel) const; + + glm::vec4 CubeMap::fetchLod(const glm::vec3& dir, float lod) const { // TODO return glm::vec4(0.0f); @@ -155,40 +310,7 @@ void CubeMap::convolveForGGX(CubeMap& output, const std::atomic& abortProc } void CubeMap::convolveMipFaceForGGX(const GGXSamples& samples, CubeMap& output, gpu::uint16 mipLevel, int face, const std::atomic& abortProcessing) const { - static const glm::vec3 NORMALS[24] = { - // POSITIVE X - glm::vec3(1.0f, 1.0f, 1.0f), - glm::vec3(1.0f, 1.0f, -1.0f), - glm::vec3(1.0f, -1.0f, 1.0f), - glm::vec3(1.0f, -1.0f, -1.0f), - // NEGATIVE X - glm::vec3(-1.0f, 1.0f, -1.0f), - glm::vec3(-1.0f, 1.0f, 1.0f), - glm::vec3(-1.0f, -1.0f, -1.0f), - glm::vec3(-1.0f, -1.0f, 1.0f), - // POSITIVE Y - glm::vec3(-1.0f, 1.0f, -1.0f), - glm::vec3(1.0f, 1.0f, -1.0f), - glm::vec3(-1.0f, 1.0f, 1.0f), - glm::vec3(1.0f, 1.0f, 1.0f), - // NEGATIVE Y - glm::vec3(-1.0f, -1.0f, 1.0f), - glm::vec3(1.0f, -1.0f, 1.0f), - glm::vec3(-1.0f, -1.0f, -1.0f), - glm::vec3(1.0f, -1.0f, -1.0f), - // POSITIVE Z - glm::vec3(-1.0f, 1.0f, 1.0f), - glm::vec3(1.0f, 1.0f, 1.0f), - glm::vec3(-1.0f, -1.0f, 1.0f), - glm::vec3(1.0f, -1.0f, 1.0f), - // NEGATIVE Z - glm::vec3(1.0f, 1.0f, -1.0f), - glm::vec3(-1.0f, 1.0f, -1.0f), - glm::vec3(1.0f, -1.0f, -1.0f), - glm::vec3(-1.0f, -1.0f, -1.0f) - }; - - const glm::vec3* faceNormals = NORMALS + face * 4; + const glm::vec3* faceNormals = FACE_NORMALS + face * 4; const glm::vec3 deltaXNormalLo = faceNormals[1] - faceNormals[0]; const glm::vec3 deltaXNormalHi = faceNormals[3] - faceNormals[2]; auto& outputFace = output._mips[mipLevel][face]; diff --git a/libraries/image/src/image/CubeMap.h b/libraries/image/src/image/CubeMap.h index 231db7d76f..578cb65af5 100644 --- a/libraries/image/src/image/CubeMap.h +++ b/libraries/image/src/image/CubeMap.h @@ -12,7 +12,7 @@ #ifndef hifi_image_CubeMap_h #define hifi_image_CubeMap_h -#include +#include #include #include #include @@ -22,15 +22,34 @@ namespace image { class CubeMap { public: - - using Face = std::vector; - using Faces = std::array; - + CubeMap(int width, int height, int mipCount); + CubeMap(gpu::TexturePointer texture, const std::atomic& abortProcessing = false); + + void reset(int width, int height, int mipCount); gpu::uint16 getMipCount() const { return (gpu::uint16)_mips.size(); } - Faces& editMip(gpu::uint16 mipLevel) { return _mips[mipLevel]; } - const Faces& getMip(gpu::uint16 mipLevel) const { return _mips[mipLevel]; } + int getMipWidth(gpu::uint16 mipLevel) const { + return std::max(1, _width >> mipLevel); + } + int getMipHeight(gpu::uint16 mipLevel) const { + return std::max(1, _height >> mipLevel); + } + gpu::Vec2i getMipDimensions(gpu::uint16 mipLevel) const { + return gpu::Vec2i(getMipWidth(mipLevel), getMipHeight(mipLevel)); + } + + glm::vec4* editFace(gpu::uint16 mipLevel, int face) { + return _mips[mipLevel][face].data() + getFaceLineStride(mipLevel) + 1; + } + + const glm::vec4* getFace(gpu::uint16 mipLevel, int face) const { + return _mips[mipLevel][face].data() + getFaceLineStride(mipLevel) + 1; + } + + size_t getFaceLineStride(gpu::uint16 mipLevel) const { + return getMipWidth(mipLevel)+2; + } void convolveForGGX(CubeMap& output, const std::atomic& abortProcessing) const; glm::vec4 fetchLod(const glm::vec3& dir, float lod) const; @@ -39,6 +58,9 @@ namespace image { struct GGXSamples; + using Face = std::vector; + using Faces = std::array; + int _width; int _height; std::vector _mips; @@ -46,6 +68,16 @@ namespace image { static void generateGGXSamples(GGXSamples& data, float roughness, const int resolution); void convolveMipFaceForGGX(const GGXSamples& samples, CubeMap& output, gpu::uint16 mipLevel, int face, const std::atomic& abortProcessing) const; glm::vec4 computeConvolution(const glm::vec3& normal, const GGXSamples& samples) const; + + void seamColumnAndColumn(gpu::uint16 mipLevel, int face0, int col0, int face1, int col1, int inc); + void seamColumnAndRow(gpu::uint16 mipLevel, int face0, int col0, int face1, int row1, int inc); + void seamRowAndRow(gpu::uint16 mipLevel, int face0, int row0, int face1, int row1, int inc); + + void copyColumnToColumn(gpu::uint16 mipLevel, int srcFace, int srcCol, int dstFace, int dstCol, int dstInc); + void copyColumnToRow(gpu::uint16 mipLevel, int srcFace, int srcCol, int dstFace, int dstRow, int dstInc); + void copyRowToRow(gpu::uint16 mipLevel, int srcFace, int srcRow, int dstFace, int dstRow, int dstInc); + void copyRowToColumn(gpu::uint16 mipLevel, int srcFace, int srcRow, int dstFace, int dstCol, int dstInc); + }; } diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 7131871937..3d4dfa8c40 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -521,14 +521,15 @@ public: } }; -void convertToFloat(const unsigned char* source, int width, int height, int lineStride, gpu::Element sourceFormat, std::vector& output) { - std::vector::iterator outputIt; +void image::convertToFloat(const unsigned char* source, int width, int height, int srcLineByteStride, gpu::Element sourceFormat, + glm::vec4* output, int outputLinePixelStride) { + glm::vec4* outputIt; auto unpackFunc = getUnpackingFunction(sourceFormat); - output.resize(width * height); - outputIt = output.begin(); + outputLinePixelStride -= width; + outputIt = output; for (auto lineNb = 0; lineNb < height; lineNb++) { - const uint32* srcPixelIt = reinterpret_cast(source + lineNb * lineStride); + const uint32* srcPixelIt = reinterpret_cast(source + lineNb * srcLineByteStride); const uint32* srcPixelEnd = srcPixelIt + width; while (srcPixelIt < srcPixelEnd) { @@ -536,17 +537,19 @@ void convertToFloat(const unsigned char* source, int width, int height, int line ++srcPixelIt; ++outputIt; } + outputIt += outputLinePixelStride; } - assert(outputIt == output.end()); } -void convertFromFloat(unsigned char* output, int width, int height, int lineStride, gpu::Element outputFormat, const std::vector& source) { - std::vector::const_iterator sourceIt; +void image::convertFromFloat(unsigned char* output, int width, int height, int outputLineByteStride, gpu::Element outputFormat, + const glm::vec4* source, int srcLinePixelStride) { + const glm::vec4* sourceIt; auto packFunc = getPackingFunction(outputFormat); - sourceIt = source.begin(); + srcLinePixelStride -= width; + sourceIt = source; for (auto lineNb = 0; lineNb < height; lineNb++) { - uint32* outPixelIt = reinterpret_cast(output + lineNb * lineStride); + uint32* outPixelIt = reinterpret_cast(output + lineNb * outputLineByteStride); uint32* outPixelEnd = outPixelIt + width; while (outPixelIt < outPixelEnd) { @@ -554,8 +557,8 @@ void convertFromFloat(unsigned char* output, int width, int height, int lineStri ++outPixelIt; ++sourceIt; } + sourceIt += srcLinePixelStride; } - assert(sourceIt == source.end()); } void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target, const std::atomic& abortProcessing, int face) { @@ -594,7 +597,8 @@ void generateHDRMips(gpu::Texture* texture, QImage&& image, BackendTarget target return; } - convertToFloat(localCopy.bits(), width, height, localCopy.bytesPerLine(), HDR_FORMAT, data); + data.resize(width * height); + convertToFloat(localCopy.bits(), width, height, localCopy.bytesPerLine(), HDR_FORMAT, data.data(), width); // We're done with the localCopy, free up the memory to avoid bloating the heap localCopy = QImage(); // QImage doesn't have a clear function, so override it with an empty one. diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 9c27b0cf3c..df113c9eff 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -27,7 +27,11 @@ extern const QImage::Format QIMAGE_HDRFORMAT; std::function getHDRPackingFunction(); std::function getHDRUnpackingFunction(); - +void convertToFloat(const unsigned char* source, int width, int height, int srcLineByteStride, gpu::Element sourceFormat, + glm::vec4* output, int outputLinePixelStride); +void convertFromFloat(unsigned char* output, int width, int height, int outputLineByteStride, gpu::Element outputFormat, + const glm::vec4* source, int srcLinePixelStride); + namespace TextureUsage { enum Type {