cleaning up the SH generation

This commit is contained in:
Sam Cake 2015-05-13 16:00:05 -07:00
parent 8324268ec8
commit 238d3751c5
4 changed files with 233 additions and 223 deletions

View file

@ -53,7 +53,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
skybox.getAmbientSH(); skybox.getIrradianceSH();
static gpu::PipelinePointer thePipeline; static gpu::PipelinePointer thePipeline;
static gpu::BufferPointer theBuffer; static gpu::BufferPointer theBuffer;
@ -121,51 +121,51 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
} }
glm::vec3 sRGBToLinear(glm::vec3& color) {
const float GAMMA_CORRECTION = 2.2f;
return glm::pow(color, glm::vec3(GAMMA_CORRECTION));
}
void sphericalHarmonicsAdd(float * result, int order, glm::vec3 linearTosRGB(glm::vec3& color) {
const float * inputA, const float * inputB) const float GAMMA_CORRECTION_INV = 1.0f / 2.2f;
{ return glm::pow(color, glm::vec3(GAMMA_CORRECTION_INV));
}
// Originial code for the Spherical Harmonics taken from "Sun and Black Cat- Igor Dykhta (igor dykhta email) © 2007-2014 "
void sphericalHarmonicsAdd(float * result, int order, const float * inputA, const float * inputB) {
const int numCoeff = order * order; const int numCoeff = order * order;
for(int i=0; i < numCoeff; i++) for(int i=0; i < numCoeff; i++) {
{
result[i] = inputA[i] + inputB[i]; result[i] = inputA[i] + inputB[i];
} }
} }
void sphericalHarmonicsScale(float * result, int order, void sphericalHarmonicsScale(float * result, int order, const float * input, float scale) {
const float * input, float scale)
{
const int numCoeff = order * order; const int numCoeff = order * order;
for(int i=0; i < numCoeff; i++) for(int i=0; i < numCoeff; i++) {
{
result[i] = input[i] * scale; result[i] = input[i] * scale;
} }
} }
void sphericalHarmonicsEvaluateDirection(float * result, int order, void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm::vec3 & dir) {
const glm::vec3 & dir)
{
// calculate coefficients for first 3 bands of spherical harmonics // calculate coefficients for first 3 bands of spherical harmonics
double p_0_0 = 0.282094791773878140; double P_0_0 = 0.282094791773878140;
double p_1_0 = 0.488602511902919920 * dir.z; double P_1_0 = 0.488602511902919920 * dir.z;
double p_1_1 = -0.488602511902919920; double P_1_1 = -0.488602511902919920;
double p_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050; double P_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050;
double p_2_1 = -1.092548430592079200 * dir.z; double P_2_1 = -1.092548430592079200 * dir.z;
double p_2_2 = 0.546274215296039590; double P_2_2 = 0.546274215296039590;
result[0] = p_0_0; result[0] = P_0_0;
result[1] = p_1_1 * dir.y; result[1] = P_1_1 * dir.y;
result[2] = p_1_0; result[2] = P_1_0;
result[3] = p_1_1 * dir.x; result[3] = P_1_1 * dir.x;
result[4] = p_2_2 * (dir.x * dir.y + dir.y * dir.x); result[4] = P_2_2 * (dir.x * dir.y + dir.y * dir.x);
result[5] = p_2_1 * dir.y; result[5] = P_2_1 * dir.y;
result[6] = p_2_0; result[6] = P_2_0;
result[7] = p_2_1 * dir.x; result[7] = P_2_1 * dir.x;
result[8] = p_2_2 * (dir.x * dir.x - dir.y * dir.y); result[8] = P_2_2 * (dir.x * dir.x - dir.y * dir.y);
} }
void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
std::vector<glm::vec3> & output, const uint order)
{
const uint sqOrder = order*order; const uint sqOrder = order*order;
// allocate memory for calculations // allocate memory for calculations
@ -174,45 +174,34 @@ void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture,
std::vector<float> resultG(sqOrder); std::vector<float> resultG(sqOrder);
std::vector<float> resultB(sqOrder); std::vector<float> resultB(sqOrder);
// variables that describe current face of cube texture int width, height;
//std::unique_ptr data;
GLint width, height;
GLint internalFormat;
GLint numComponents;
// initialize values // initialize values
float fWt = 0.0f; float fWt = 0.0f;
for(uint i=0; i < sqOrder; i++) for(uint i=0; i < sqOrder; i++) {
{ output[i] = glm::vec3(0.0f);
output[i].x = 0; resultR[i] = 0.0f;
output[i].y = 0;
output[i].z = 0;
resultR[i] = 0;
resultG[i] = 0; resultG[i] = 0;
resultB[i] = 0; resultB[i] = 0;
} }
std::vector<float> shBuff(sqOrder); std::vector<float> shBuff(sqOrder);
std::vector<float> shBuffB(sqOrder); std::vector<float> shBuffB(sqOrder);
// bind current texture
// glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture->texture());
// for each face of cube texture
for(int face=0; face < 6; face++)
{
// get width and height // get width and height
// glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_WIDTH, &width);
// glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_HEIGHT, &height);
width = height = cubeTexture.getWidth(); width = height = cubeTexture.getWidth();
if(width != height) if(width != height) {
{
return; return;
} }
const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());
numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount(); // for each face of cube texture
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
auto numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount();
auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData(); auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData();
if (data == nullptr) {
continue;
}
// step between two texels for range [0, 1] // step between two texels for range [0, 1]
float invWidth = 1.0f / float(width); float invWidth = 1.0f / float(width);
@ -221,54 +210,57 @@ void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture,
// step between two texels for range [-1, 1] // step between two texels for range [-1, 1]
float invWidthBy2 = 2.0f / float(width); float invWidthBy2 = 2.0f / float(width);
for(int y=0; y < width; y++) for(int y=0; y < width; y++) {
{
// texture coordinate V in range [-1 to 1] // texture coordinate V in range [-1 to 1]
const float fV = negativeBound + float(y) * invWidthBy2; const float fV = negativeBound + float(y) * invWidthBy2;
for(int x=0; x < width; x++) for(int x=0; x < width; x++) {
{
// texture coordinate U in range [-1 to 1] // texture coordinate U in range [-1 to 1]
const float fU = negativeBound + float(x) * invWidthBy2; const float fU = negativeBound + float(x) * invWidthBy2;
// determine direction from center of cube texture to current texel // determine direction from center of cube texture to current texel
glm::vec3 dir; glm::vec3 dir;
switch(face) switch(face) {
{ case gpu::Texture::CUBE_FACE_RIGHT_POS_X: {
case gpu::Texture::CUBE_FACE_RIGHT_POS_X:
dir.x = 1.0f; dir.x = 1.0f;
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth); dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth);
dir = -dir; dir = -dir;
break; break;
case gpu::Texture::CUBE_FACE_LEFT_NEG_X: }
case gpu::Texture::CUBE_FACE_LEFT_NEG_X: {
dir.x = -1.0f; dir.x = -1.0f;
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth); dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth);
dir = -dir; dir = -dir;
break; break;
case gpu::Texture::CUBE_FACE_TOP_POS_Y: }
case gpu::Texture::CUBE_FACE_TOP_POS_Y: {
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
dir.y = 1.0f; dir.y = 1.0f;
dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth); dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth);
dir = -dir; dir = -dir;
break; break;
case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: }
case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: {
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
dir.y = - 1.0f; dir.y = - 1.0f;
dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth); dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth);
dir = -dir; dir = -dir;
break; break;
case gpu::Texture::CUBE_FACE_BACK_POS_Z: }
case gpu::Texture::CUBE_FACE_BACK_POS_Z: {
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
dir.z = 1.0f; dir.z = 1.0f;
break; break;
case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: }
case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: {
dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth); dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth);
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
dir.z = - 1.0f; dir.z = - 1.0f;
break; break;
}
default: default:
return; return;
} }
@ -286,12 +278,14 @@ void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture,
// index of texel in texture // index of texel in texture
uint pixOffsetIndex = (x + y * width) * numComponents; uint pixOffsetIndex = (x + y * width) * numComponents;
// get color from texture and map to range [0, 1] // get color from texture and map to range [0, 1]
glm::vec3 clr( glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
float(data[pixOffsetIndex]) / 255, float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
float(data[pixOffsetIndex+1]) / 255, float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);
float(data[pixOffsetIndex+2]) / 255
); // Gamma correct
clr = sRGBToLinear(clr);
// scale color and add to previously accumulated coefficients // scale color and add to previously accumulated coefficients
sphericalHarmonicsScale(shBuffB.data(), order, sphericalHarmonicsScale(shBuffB.data(), order,
@ -317,54 +311,31 @@ void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture,
sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj); sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj);
// save result // save result
for(uint i=0; i < sqOrder; i++) for(uint i=0; i < sqOrder; i++) {
{ // gamma Correct
output[i].r = resultR[i]; // output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i]));
output[i].g = resultG[i]; output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]);
output[i].b = resultB[i];
} }
} }
/*
glm::vec3 sphericalHarmonicsFromTexture(glm::vec3 & N, std::vector & coef)
{
return
// constant term, lowest frequency ////// const SphericalHarmonics& Skybox::getIrradianceSH() const {
C4 * coef[0] +
// axis aligned terms ///////////////////
2.0 * C2 * coef[1] * N.y +
2.0 * C2 * coef[2] * N.z +
2.0 * C2 * coef[3] * N.x +
// band 2 terms /////////////////////////
2.0 * C1 * coef[4] * N.x * N.y +
2.0 * C1 * coef[5] * N.y * N.z +
C3 * coef[6] * N.z * N.z - C5 * coef[6] +
2.0 * C1 * coef[7] * N.x * N.z +
C1 * coef[8] * (N.x * N.x - N.y * N.y);
}
*/
const SphericalHarmonics& Skybox::getAmbientSH() const {
if (!_isSHValid) { if (!_isSHValid) {
if (_cubemap && _cubemap->isDefined()) { if (_cubemap && _cubemap->isDefined()) {
std::vector< glm::vec3 > coefs;
std::vector< glm::vec3 > coefs(10, glm::vec3(0.0f));
sphericalHarmonicsFromTexture(*_cubemap, coefs, 3); sphericalHarmonicsFromTexture(*_cubemap, coefs, 3);
_ambientSH.L00 = coefs[0]; _irradianceSH.L00 = coefs[0];
_ambientSH.L1m1 = coefs[1]; _irradianceSH.L1m1 = coefs[1];
_ambientSH.L10 = coefs[2]; _irradianceSH.L10 = coefs[2];
_ambientSH.L11 = coefs[3]; _irradianceSH.L11 = coefs[3];
_ambientSH.L2m2 = coefs[4]; _irradianceSH.L2m2 = coefs[4];
_ambientSH.L2m1 = coefs[5]; _irradianceSH.L2m1 = coefs[5];
_ambientSH.L20 = coefs[6]; _irradianceSH.L20 = coefs[6];
_ambientSH.L21 = coefs[7]; _irradianceSH.L21 = coefs[7];
_ambientSH.L22 = coefs[8]; _irradianceSH.L22 = coefs[8];
_isSHValid = true; _isSHValid = true;
} }
} }
return _ambientSH; return _irradianceSH;
} }

View file

@ -36,14 +36,14 @@ public:
const gpu::TexturePointer& getCubemap() const { return _cubemap; } const gpu::TexturePointer& getCubemap() const { return _cubemap; }
void clearCubemap(); void clearCubemap();
const SphericalHarmonics& getAmbientSH() const; const SphericalHarmonics& getIrradianceSH() const;
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox); static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
protected: protected:
gpu::TexturePointer _cubemap; gpu::TexturePointer _cubemap;
mutable SphericalHarmonics _ambientSH; mutable SphericalHarmonics _irradianceSH;
mutable bool _isSHValid = false; mutable bool _isSHValid = false;
Color _color{1.0f, 1.0f, 1.0f}; Color _color{1.0f, 1.0f, 1.0f};

View file

@ -293,7 +293,7 @@ void DeferredLightingEffect::render() {
if (locations->ambientSphere >= 0) { if (locations->ambientSphere >= 0) {
model::SphericalHarmonics sh; model::SphericalHarmonics sh;
if (useSkyboxCubemap) { if (useSkyboxCubemap) {
sh = _skybox->getAmbientSH(); sh = _skybox->getIrradianceSH();
} else { } else {
sh = globalLight->getAmbientSphere(); sh = globalLight->getAmbientSphere();
} }

View file

@ -603,6 +603,45 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
faces.push_back(image.copy(QRect(3 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false)); faces.push_back(image.copy(QRect(3 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
// Front = -Z // Front = -Z
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false)); faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
} else if ((_height / 4) == (_width / 3)) {
int faceWidth = _height / 4;
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
//
// <-------WIDTH-------->
// ^ +------+------+------+
// | | | | |
// | | | +Y | |
// | | | | |
// H +------+------+------+
// E | | | |
// I | -X | -Z | +X |
// G | | | |
// H +------+------+------+
// T | | | |
// | | | -Y | |
// | | | | |
// | +------+------+------+
// | | | | |
// | | | +Z! | | <+Z is upside down!
// | | | | |
// V +------+------+------+
//
// FaceWidth = width / 3 = height / 4
// Right = +X
faces.push_back(image.copy(QRect(2 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
// Left = -X
faces.push_back(image.copy(QRect(0 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
// Top = +Y
faces.push_back(image.copy(QRect(1 * faceWidth, 0, faceWidth, faceWidth)).mirrored(false, true));
// Bottom = -Y
faces.push_back(image.copy(QRect(1 * faceWidth, 2 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
// Back = +Z
faces.push_back(image.copy(QRect(1 * faceWidth, 3 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
// Front = -Z
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
} }
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {