Working on cubemap seams

This commit is contained in:
Olivier Prat 2019-03-28 11:59:21 +01:00
parent 4a2323f3c2
commit 5bf3cdd592
4 changed files with 221 additions and 59 deletions

View file

@ -15,6 +15,7 @@
#include <tbb/blocked_range2d.h>
#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<bool>& 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<int,int> 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<bool>& abortProc
}
void CubeMap::convolveMipFaceForGGX(const GGXSamples& samples, CubeMap& output, gpu::uint16 mipLevel, int face, const std::atomic<bool>& 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];

View file

@ -12,7 +12,7 @@
#ifndef hifi_image_CubeMap_h
#define hifi_image_CubeMap_h
#include <gpu/Forward.h>
#include <gpu/Texture.h>
#include <glm/vec4.hpp>
#include <vector>
#include <array>
@ -22,15 +22,34 @@ namespace image {
class CubeMap {
public:
using Face = std::vector<glm::vec4>;
using Faces = std::array<Face, 6>;
CubeMap(int width, int height, int mipCount);
CubeMap(gpu::TexturePointer texture, const std::atomic<bool>& 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<bool>& abortProcessing) const;
glm::vec4 fetchLod(const glm::vec3& dir, float lod) const;
@ -39,6 +58,9 @@ namespace image {
struct GGXSamples;
using Face = std::vector<glm::vec4>;
using Faces = std::array<Face, 6>;
int _width;
int _height;
std::vector<Faces> _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<bool>& 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);
};
}

View file

@ -521,14 +521,15 @@ public:
}
};
void convertToFloat(const unsigned char* source, int width, int height, int lineStride, gpu::Element sourceFormat, std::vector<glm::vec4>& output) {
std::vector<glm::vec4>::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<const uint32*>(source + lineNb * lineStride);
const uint32* srcPixelIt = reinterpret_cast<const uint32*>(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<glm::vec4>& source) {
std::vector<glm::vec4>::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<uint32*>(output + lineNb * lineStride);
uint32* outPixelIt = reinterpret_cast<uint32*>(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<bool>& 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.

View file

@ -27,7 +27,11 @@ extern const QImage::Format QIMAGE_HDRFORMAT;
std::function<gpu::uint32(const glm::vec3&)> getHDRPackingFunction();
std::function<glm::vec3(gpu::uint32)> 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 {