From 536fff4502c4d440a8a9e1ecb95c0377422266b9 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Tue, 12 May 2015 23:11:52 -0700 Subject: [PATCH 1/3] Trying to create the SH from the skybox cubemap --- interface/src/Application.cpp | 2 +- libraries/gpu/src/gpu/Texture.h | 2 +- libraries/model/src/model/Light.cpp | 3 + libraries/model/src/model/Light.h | 1 + libraries/model/src/model/Skybox.cpp | 252 +++++++++++++++++++++++++++ libraries/model/src/model/Skybox.h | 7 + 6 files changed, 265 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 01e094e786..a33b4898b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3357,7 +3357,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs auto skyStage = DependencyManager::get()->getSkyStage(); DependencyManager::get()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity()); DependencyManager::get()->setGlobalAtmosphere(skyStage->getAtmosphere()); - // NOt yet DependencyManager::get()->setGlobalSkybox(skybox); + DependencyManager::get()->setGlobalSkybox(skybox); PROFILE_RANGE("DeferredLighting"); PerformanceTimer perfTimer("lighting"); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e4f66c94e6..5d094ddbb4 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -127,7 +127,7 @@ public: CUBE_FACE_LEFT_NEG_X, CUBE_FACE_TOP_POS_Y, CUBE_FACE_BOTTOM_NEG_Y, - CUBE_FACE_BACK_POS_X, + CUBE_FACE_BACK_POS_Z, CUBE_FACE_FRONT_NEG_Z, NUM_CUBE_FACES, // Not a valid vace index diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 6460e0b316..9a2af11664 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -99,3 +99,6 @@ void Light::setShowContour(float show) { } editSchema()._control.w = show; } + + + diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index 18d8cc5001..abcb06181f 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -180,6 +180,7 @@ public: } } }; +typedef std::shared_ptr< SphericalHarmonics > SHPointer; class Light { public: diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 1a54ebd955..7ec15eb008 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -49,6 +49,9 @@ void Skybox::clearCubemap() { void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) { if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + + skybox.getAmbientSH(); + static gpu::PipelinePointer thePipeline; static gpu::BufferPointer theBuffer; static gpu::Stream::FormatPointer theFormat; @@ -113,3 +116,252 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(skybox.getColor(),1.0f), 0.f, 0); } } + + + +void sphericalHarmonicsAdd(float * result, int order, + const float * inputA, const float * inputB) +{ + const int numCoeff = order * order; + for(int i=0; i < numCoeff; i++) + { + result[i] = inputA[i] + inputB[i]; + } +} + +void sphericalHarmonicsScale(float * result, int order, + const float * input, float scale) +{ + const int numCoeff = order * order; + for(int i=0; i < numCoeff; i++) + { + result[i] = input[i] * scale; + } +} + +void sphericalHarmonicsEvaluateDirection(float * result, int order, + const glm::vec3 & dir) +{ + // calculate coefficients for first 3 bands of spherical harmonics + double p_0_0 = 0.282094791773878140; + double p_1_0 = 0.488602511902919920 * dir.z; + double p_1_1 = -0.488602511902919920; + double p_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050; + double p_2_1 = -1.092548430592079200 * dir.z; + double p_2_2 = 0.546274215296039590; + result[0] = p_0_0; + result[1] = p_1_1 * dir.y; + result[2] = p_1_0; + result[3] = p_1_1 * dir.x; + result[4] = p_2_2 * (dir.x * dir.y + dir.y * dir.x); + result[5] = p_2_1 * dir.y; + result[6] = p_2_0; + result[7] = p_2_1 * dir.x; + result[8] = p_2_2 * (dir.x * dir.x - dir.y * dir.y); +} + +void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, + std::vector & output, const uint order) +{ + const uint sqOrder = order*order; + + // allocate memory for calculations + output.resize(sqOrder); + std::vector resultR(sqOrder); + std::vector resultG(sqOrder); + std::vector resultB(sqOrder); + + // variables that describe current face of cube texture + //std::unique_ptr data; + GLint width, height; + GLint internalFormat; + GLint numComponents; + + // initialize values + float fWt = 0.0f; + for(uint i=0; i < sqOrder; i++) + { + output[i].x = 0; + output[i].y = 0; + output[i].z = 0; + resultR[i] = 0; + resultG[i] = 0; + resultB[i] = 0; + } + std::vector shBuff(sqOrder); + std::vector 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 + // glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_WIDTH, &width); + // glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_HEIGHT, &height); + + width = height = cubeTexture.getWidth(); + if(width != height) + { + return; + } + + + numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount(); + + auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData(); + + // step between two texels for range [0, 1] + float invWidth = 1.0f / float(width); + // initial negative bound for range [-1, 1] + float negativeBound = -1.0f + invWidth; + // step between two texels for range [-1, 1] + float invWidthBy2 = 2.0f / float(width); + + for(int y=0; y < width; y++) + { + // texture coordinate V in range [-1 to 1] + const float fV = negativeBound + float(y) * invWidthBy2; + + for(int x=0; x < width; x++) + { + // texture coordinate U in range [-1 to 1] + const float fU = negativeBound + float(x) * invWidthBy2; + + // determine direction from center of cube texture to current texel + glm::vec3 dir; + switch(face) + { + case gpu::Texture::CUBE_FACE_RIGHT_POS_X: + dir.x = 1.0f; + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth); + dir = -dir; + break; + case gpu::Texture::CUBE_FACE_LEFT_NEG_X: + dir.x = -1.0f; + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth); + dir = -dir; + break; + case gpu::Texture::CUBE_FACE_TOP_POS_Y: + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f; + dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth); + dir = -dir; + break; + case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = - 1.0f; + dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir = -dir; + break; + case gpu::Texture::CUBE_FACE_BACK_POS_Z: + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = 1.0f; + break; + case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: + dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = - 1.0f; + break; + default: + return; + } + + // normalize direction + dir = glm::normalize(dir); + + // scale factor depending on distance from center of the face + const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) * + sqrtf(1.0f + fU*fU + fV*fV)); + fWt += fDiffSolid; + + // calculate coefficients of spherical harmonics for current direction + sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir); + + // index of texel in texture + uint pixOffsetIndex = (x + y * width) * numComponents; + // get color from texture and map to range [0, 1] + glm::vec3 clr( + float(data[pixOffsetIndex]) / 255, + float(data[pixOffsetIndex+1]) / 255, + float(data[pixOffsetIndex+2]) / 255 + ); + + // scale color and add to previously accumulated coefficients + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.r * fDiffSolid); + sphericalHarmonicsAdd(resultR.data(), order, + resultR.data(), shBuffB.data()); + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.g * fDiffSolid); + sphericalHarmonicsAdd(resultG.data(), order, + resultG.data(), shBuffB.data()); + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.b * fDiffSolid); + sphericalHarmonicsAdd(resultB.data(), order, + resultB.data(), shBuffB.data()); + } + } + } + + // final scale for coefficients + const float fNormProj = (4.0f * glm::pi()) / fWt; + sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj); + sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj); + sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj); + + // save result + for(uint i=0; i < sqOrder; i++) + { + output[i].r = resultR[i]; + output[i].g = resultG[i]; + output[i].b = resultB[i]; + } +} +/* +glm::vec3 sphericalHarmonicsFromTexture(glm::vec3 & N, std::vector & coef) +{ + return + + // constant term, lowest frequency ////// + 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 (_cubemap && _cubemap->isDefined()) { + + std::vector< glm::vec3 > coefs(10, glm::vec3(0.0f)); + sphericalHarmonicsFromTexture(*_cubemap, coefs, 2); + + _ambientSH.L00 = coefs[0]; + _ambientSH.L1m1 = coefs[1]; + _ambientSH.L10 = coefs[2]; + _ambientSH.L11 = coefs[3]; + _ambientSH.L2m2 = coefs[4]; + _ambientSH.L2m1 = coefs[5]; + _ambientSH.L20 = coefs[6]; + _ambientSH.L21 = coefs[7]; + _ambientSH.L22 = coefs[8]; + + _isSHValid = true; + } + } + return _ambientSH; +} diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index b739032f39..c66cbfb627 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -13,6 +13,8 @@ #include "gpu/Texture.h" +#include "Light.h" + class ViewFrustum; //class Transform; namespace gpu { class Batch; } @@ -34,11 +36,16 @@ public: const gpu::TexturePointer& getCubemap() const { return _cubemap; } void clearCubemap(); + const SphericalHarmonics& getAmbientSH() const; + static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox); protected: gpu::TexturePointer _cubemap; + mutable SphericalHarmonics _ambientSH; + mutable bool _isSHValid = false; + Color _color{1.0f, 1.0f, 1.0f}; }; typedef std::shared_ptr< Skybox > SkyboxPointer; From 65e83df214b2996723f2572fb6ce98eca22d4e41 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 13 May 2015 11:08:18 -0700 Subject: [PATCH 2/3] Generating the SH from the cube map! --- libraries/gpu/src/gpu/Texture.cpp | 2 +- libraries/model/src/model/Skybox.cpp | 5 ++++- libraries/model/src/model/Skybox.slf | 3 ++- libraries/render-utils/src/DeferredGlobalLight.slh | 4 ++-- libraries/render-utils/src/DeferredLightingEffect.cpp | 7 ++++++- libraries/render-utils/src/TextureCache.cpp | 3 ++- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 07bb73a375..542a47c915 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -57,7 +57,7 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const { PixelsPointer mipFace = getMipFace(level, face); - if (mipFace) { + if (mipFace && (_type != TEX_CUBE)) { mipFace->_isGPULoaded = true; mipFace->_sysmem.resize(0); } diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index 7ec15eb008..f9d2905864 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -39,6 +39,9 @@ void Skybox::setColor(const Color& color) { } void Skybox::setCubemap(const gpu::TexturePointer& cubemap) { + if (_isSHValid && (cubemap != _cubemap)) { + _isSHValid = false; + } _cubemap = cubemap; } @@ -348,7 +351,7 @@ const SphericalHarmonics& Skybox::getAmbientSH() const { if (_cubemap && _cubemap->isDefined()) { std::vector< glm::vec3 > coefs(10, glm::vec3(0.0f)); - sphericalHarmonicsFromTexture(*_cubemap, coefs, 2); + sphericalHarmonicsFromTexture(*_cubemap, coefs, 3); _ambientSH.L00 = coefs[0]; _ambientSH.L1m1 = coefs[1]; diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf index 5739cfda34..abb30bf09c 100755 --- a/libraries/model/src/model/Skybox.slf +++ b/libraries/model/src/model/Skybox.slf @@ -20,5 +20,6 @@ varying vec3 color; void main(void) { vec3 coord = normalize(normal); vec4 texel = textureCube(cubeMap, coord); - gl_FragData[0] = vec4(texel.xyz * color, 0.0); + vec3 pixel = pow(texel.xyz * color, vec3(1.0/2.2)); // manual Gamma correction + gl_FragData[0] = vec4(pixel, 0.0); } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index d676a9c6b7..2f312e42d8 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -111,8 +111,8 @@ vec3 evalSkyboxGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec4 fragEyeVector = invViewMat * vec4(-position, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - vec3 color = diffuse.rgb * evalSkyboxLight(fragNormal, 0.75).xyz * getLightAmbientIntensity(light); - + vec3 color = diffuse.rgb * evalSphericalLight(ambientSphere, fragNormal).xyz * getLightAmbientIntensity(light); + vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); color += vec3(diffuse + shading.rgb) * shading.w * shadowAttenuation * getLightColor(light) * getLightIntensity(light); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 9b0c5647ed..d9a4aa0378 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -291,7 +291,12 @@ void DeferredLightingEffect::render() { auto globalLight = _allocatedLights[_globalLights.front()]; if (locations->ambientSphere >= 0) { - auto sh = globalLight->getAmbientSphere(); + model::SphericalHarmonics sh; + if (useSkyboxCubemap) { + sh = _skybox->getAmbientSH(); + } else { + sh = globalLight->getAmbientSphere(); + } for (int i =0; i setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i)); } diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index c1c8ec030f..a6c932b4d8 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -514,7 +514,8 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo if ((_width > 0) && (_height > 0)) { - bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = !(_type == CUBE_TEXTURE); //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); From 238d3751c5ef88bac35c7a79ec3894850c450f82 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 13 May 2015 16:00:05 -0700 Subject: [PATCH 3/3] cleaning up the SH generation --- libraries/model/src/model/Skybox.cpp | 411 ++++++++---------- libraries/model/src/model/Skybox.h | 4 +- .../src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/TextureCache.cpp | 39 ++ 4 files changed, 233 insertions(+), 223 deletions(-) diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp index f9d2905864..ab11966188 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/model/src/model/Skybox.cpp @@ -53,7 +53,7 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - skybox.getAmbientSH(); + skybox.getIrradianceSH(); static gpu::PipelinePointer thePipeline; static gpu::BufferPointer theBuffer; @@ -121,250 +121,221 @@ 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, - const float * inputA, const float * inputB) -{ +glm::vec3 linearTosRGB(glm::vec3& color) { + 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; - for(int i=0; i < numCoeff; i++) - { + for(int i=0; i < numCoeff; i++) { result[i] = inputA[i] + inputB[i]; } } -void sphericalHarmonicsScale(float * result, int order, - const float * input, float scale) -{ +void sphericalHarmonicsScale(float * result, int order, const float * input, float scale) { const int numCoeff = order * order; - for(int i=0; i < numCoeff; i++) - { + for(int i=0; i < numCoeff; i++) { result[i] = input[i] * scale; } } -void sphericalHarmonicsEvaluateDirection(float * result, int order, - const glm::vec3 & dir) -{ +void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm::vec3 & dir) { // calculate coefficients for first 3 bands of spherical harmonics - double p_0_0 = 0.282094791773878140; - double p_1_0 = 0.488602511902919920 * dir.z; - double p_1_1 = -0.488602511902919920; - double p_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050; - double p_2_1 = -1.092548430592079200 * dir.z; - double p_2_2 = 0.546274215296039590; - result[0] = p_0_0; - result[1] = p_1_1 * dir.y; - result[2] = p_1_0; - result[3] = p_1_1 * dir.x; - result[4] = p_2_2 * (dir.x * dir.y + dir.y * dir.x); - result[5] = p_2_1 * dir.y; - result[6] = p_2_0; - result[7] = p_2_1 * dir.x; - result[8] = p_2_2 * (dir.x * dir.x - dir.y * dir.y); + double P_0_0 = 0.282094791773878140; + double P_1_0 = 0.488602511902919920 * dir.z; + double P_1_1 = -0.488602511902919920; + double P_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050; + double P_2_1 = -1.092548430592079200 * dir.z; + double P_2_2 = 0.546274215296039590; + result[0] = P_0_0; + result[1] = P_1_1 * dir.y; + result[2] = P_1_0; + result[3] = P_1_1 * dir.x; + result[4] = P_2_2 * (dir.x * dir.y + dir.y * dir.x); + result[5] = P_2_1 * dir.y; + result[6] = P_2_0; + result[7] = P_2_1 * dir.x; + result[8] = P_2_2 * (dir.x * dir.x - dir.y * dir.y); } -void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, - std::vector & output, const uint order) -{ - const uint sqOrder = order*order; +void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector & output, const uint order) { + const uint sqOrder = order*order; - // allocate memory for calculations - output.resize(sqOrder); - std::vector resultR(sqOrder); - std::vector resultG(sqOrder); - std::vector resultB(sqOrder); + // allocate memory for calculations + output.resize(sqOrder); + std::vector resultR(sqOrder); + std::vector resultG(sqOrder); + std::vector resultB(sqOrder); - // variables that describe current face of cube texture - //std::unique_ptr data; - GLint width, height; - GLint internalFormat; - GLint numComponents; - - // initialize values - float fWt = 0.0f; - for(uint i=0; i < sqOrder; i++) - { - output[i].x = 0; - output[i].y = 0; - output[i].z = 0; - resultR[i] = 0; - resultG[i] = 0; - resultB[i] = 0; - } - std::vector shBuff(sqOrder); - std::vector 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 - // glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_WIDTH, &width); - // glGetTexLevelParameteriv(cubeSides[face], 0, GL_TEXTURE_HEIGHT, &height); + int width, height; + // initialize values + float fWt = 0.0f; + for(uint i=0; i < sqOrder; i++) { + output[i] = glm::vec3(0.0f); + resultR[i] = 0.0f; + resultG[i] = 0; + resultB[i] = 0; + } + std::vector shBuff(sqOrder); + std::vector shBuffB(sqOrder); + // get width and height width = height = cubeTexture.getWidth(); - if(width != height) - { - return; - } + if(width != height) { + return; + } - - numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount(); + const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits::max()); - auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData(); + // for each face of cube texture + for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { - // step between two texels for range [0, 1] - float invWidth = 1.0f / float(width); - // initial negative bound for range [-1, 1] - float negativeBound = -1.0f + invWidth; - // step between two texels for range [-1, 1] - float invWidthBy2 = 2.0f / float(width); + auto numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount(); + auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData(); + if (data == nullptr) { + continue; + } - for(int y=0; y < width; y++) - { - // texture coordinate V in range [-1 to 1] - const float fV = negativeBound + float(y) * invWidthBy2; + // step between two texels for range [0, 1] + float invWidth = 1.0f / float(width); + // initial negative bound for range [-1, 1] + float negativeBound = -1.0f + invWidth; + // step between two texels for range [-1, 1] + float invWidthBy2 = 2.0f / float(width); - for(int x=0; x < width; x++) - { - // texture coordinate U in range [-1 to 1] - const float fU = negativeBound + float(x) * invWidthBy2; + for(int y=0; y < width; y++) { + // texture coordinate V in range [-1 to 1] + const float fV = negativeBound + float(y) * invWidthBy2; - // determine direction from center of cube texture to current texel - glm::vec3 dir; - switch(face) - { - case gpu::Texture::CUBE_FACE_RIGHT_POS_X: - dir.x = 1.0f; - dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); - dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth); - dir = -dir; - break; - case gpu::Texture::CUBE_FACE_LEFT_NEG_X: - dir.x = -1.0f; - dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); - dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth); - dir = -dir; - break; - case gpu::Texture::CUBE_FACE_TOP_POS_Y: - dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); - dir.y = 1.0f; - dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth); - dir = -dir; - break; - case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: - dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); - dir.y = - 1.0f; - dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth); - dir = -dir; - break; - case gpu::Texture::CUBE_FACE_BACK_POS_Z: - dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); - dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); - dir.z = 1.0f; - break; - case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: - dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth); - dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); - dir.z = - 1.0f; - break; - default: - return; + for(int x=0; x < width; x++) { + // texture coordinate U in range [-1 to 1] + const float fU = negativeBound + float(x) * invWidthBy2; + + // determine direction from center of cube texture to current texel + glm::vec3 dir; + switch(face) { + case gpu::Texture::CUBE_FACE_RIGHT_POS_X: { + dir.x = 1.0f; + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth); + dir = -dir; + break; + } + case gpu::Texture::CUBE_FACE_LEFT_NEG_X: { + dir.x = -1.0f; + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth); + dir = -dir; + break; + } + case gpu::Texture::CUBE_FACE_TOP_POS_Y: { + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f; + dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth); + dir = -dir; + break; + } + case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: { + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = - 1.0f; + dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir = -dir; + break; + } + case gpu::Texture::CUBE_FACE_BACK_POS_Z: { + dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = 1.0f; + break; + } + case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: { + dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth); + dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth); + dir.z = - 1.0f; + break; + } + default: + return; + } + + // normalize direction + dir = glm::normalize(dir); + + // scale factor depending on distance from center of the face + const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) * + sqrtf(1.0f + fU*fU + fV*fV)); + fWt += fDiffSolid; + + // calculate coefficients of spherical harmonics for current direction + sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir); + + // index of texel in texture + uint pixOffsetIndex = (x + y * width) * numComponents; + + // get color from texture and map to range [0, 1] + glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT, + float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT, + float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); + + // Gamma correct + clr = sRGBToLinear(clr); + + // scale color and add to previously accumulated coefficients + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.r * fDiffSolid); + sphericalHarmonicsAdd(resultR.data(), order, + resultR.data(), shBuffB.data()); + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.g * fDiffSolid); + sphericalHarmonicsAdd(resultG.data(), order, + resultG.data(), shBuffB.data()); + sphericalHarmonicsScale(shBuffB.data(), order, + shBuff.data(), clr.b * fDiffSolid); + sphericalHarmonicsAdd(resultB.data(), order, + resultB.data(), shBuffB.data()); } - - // normalize direction - dir = glm::normalize(dir); - - // scale factor depending on distance from center of the face - const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) * - sqrtf(1.0f + fU*fU + fV*fV)); - fWt += fDiffSolid; - - // calculate coefficients of spherical harmonics for current direction - sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir); - - // index of texel in texture - uint pixOffsetIndex = (x + y * width) * numComponents; - // get color from texture and map to range [0, 1] - glm::vec3 clr( - float(data[pixOffsetIndex]) / 255, - float(data[pixOffsetIndex+1]) / 255, - float(data[pixOffsetIndex+2]) / 255 - ); - - // scale color and add to previously accumulated coefficients - sphericalHarmonicsScale(shBuffB.data(), order, - shBuff.data(), clr.r * fDiffSolid); - sphericalHarmonicsAdd(resultR.data(), order, - resultR.data(), shBuffB.data()); - sphericalHarmonicsScale(shBuffB.data(), order, - shBuff.data(), clr.g * fDiffSolid); - sphericalHarmonicsAdd(resultG.data(), order, - resultG.data(), shBuffB.data()); - sphericalHarmonicsScale(shBuffB.data(), order, - shBuff.data(), clr.b * fDiffSolid); - sphericalHarmonicsAdd(resultB.data(), order, - resultB.data(), shBuffB.data()); - } - } - } - - // final scale for coefficients - const float fNormProj = (4.0f * glm::pi()) / fWt; - sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj); - sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj); - sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj); - - // save result - for(uint i=0; i < sqOrder; i++) - { - output[i].r = resultR[i]; - output[i].g = resultG[i]; - output[i].b = resultB[i]; - } -} -/* -glm::vec3 sphericalHarmonicsFromTexture(glm::vec3 & N, std::vector & coef) -{ - return - - // constant term, lowest frequency ////// - 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 (_cubemap && _cubemap->isDefined()) { - - std::vector< glm::vec3 > coefs(10, glm::vec3(0.0f)); - sphericalHarmonicsFromTexture(*_cubemap, coefs, 3); - - _ambientSH.L00 = coefs[0]; - _ambientSH.L1m1 = coefs[1]; - _ambientSH.L10 = coefs[2]; - _ambientSH.L11 = coefs[3]; - _ambientSH.L2m2 = coefs[4]; - _ambientSH.L2m1 = coefs[5]; - _ambientSH.L20 = coefs[6]; - _ambientSH.L21 = coefs[7]; - _ambientSH.L22 = coefs[8]; - - _isSHValid = true; } } - return _ambientSH; + + // final scale for coefficients + const float fNormProj = (4.0f * glm::pi()) / fWt; + sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj); + sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj); + sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj); + + // save result + for(uint i=0; i < sqOrder; i++) { + // gamma Correct + // output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i])); + output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]); + } +} + +const SphericalHarmonics& Skybox::getIrradianceSH() const { + if (!_isSHValid) { + if (_cubemap && _cubemap->isDefined()) { + std::vector< glm::vec3 > coefs; + sphericalHarmonicsFromTexture(*_cubemap, coefs, 3); + + _irradianceSH.L00 = coefs[0]; + _irradianceSH.L1m1 = coefs[1]; + _irradianceSH.L10 = coefs[2]; + _irradianceSH.L11 = coefs[3]; + _irradianceSH.L2m2 = coefs[4]; + _irradianceSH.L2m1 = coefs[5]; + _irradianceSH.L20 = coefs[6]; + _irradianceSH.L21 = coefs[7]; + _irradianceSH.L22 = coefs[8]; + + _isSHValid = true; + } + } + return _irradianceSH; } diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h index c66cbfb627..9c413405f1 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/model/src/model/Skybox.h @@ -36,14 +36,14 @@ public: const gpu::TexturePointer& getCubemap() const { return _cubemap; } void clearCubemap(); - const SphericalHarmonics& getAmbientSH() const; + const SphericalHarmonics& getIrradianceSH() const; static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox); protected: gpu::TexturePointer _cubemap; - mutable SphericalHarmonics _ambientSH; + mutable SphericalHarmonics _irradianceSH; mutable bool _isSHValid = false; Color _color{1.0f, 1.0f, 1.0f}; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index d9a4aa0378..972858be07 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -293,7 +293,7 @@ void DeferredLightingEffect::render() { if (locations->ambientSphere >= 0) { model::SphericalHarmonics sh; if (useSkyboxCubemap) { - sh = _skybox->getAmbientSH(); + sh = _skybox->getIrradianceSH(); } else { sh = globalLight->getAmbientSphere(); } diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index a6c932b4d8..2f793755d7 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -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)); // Front = -Z 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]) {