Moving the load of the texture and the generation of the cube map irradiance in the same thread

This commit is contained in:
Sam Cake 2015-05-15 11:28:59 -07:00
parent ad9834283d
commit 765145b898
9 changed files with 560 additions and 506 deletions

View file

@ -462,7 +462,7 @@ void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode,
if (_bestZone->getBackgroundMode() == BACKGROUND_MODE_SKYBOX) {
stage->getSkybox()->setColor(_bestZone->getSkyboxProperties().getColorVec3());
if (_bestZone->getSkyboxProperties().getURL().isEmpty()) {
stage->getSkybox()->clearCubemap();
stage->getSkybox()->setCubemap(gpu::TexturePointer());
} else {
// Update the Texture of the Skybox with the one pointed by this zone
auto cubeMap = DependencyManager::get<TextureCache>()->getTexture(_bestZone->getSkyboxProperties().getURL(), CUBE_TEXTURE);

View file

@ -11,6 +11,8 @@
#include "Texture.h"
#include <math.h>
#include <glm/gtc/constants.hpp>
#include <QDebug>
using namespace gpu;
@ -407,3 +409,362 @@ void Texture::setSampler(const Sampler& sampler) {
_sampler = sampler;
_samplerStamp++;
}
bool Texture::generateIrradiance() {
if (getType() != TEX_CUBE) {
return false;
}
if (!isDefined()) {
return false;
}
if (!_irradiance) {
_irradiance.reset(new SphericalHarmonics());
}
_irradiance->evalFromTexture(*this);
return true;
}
void SphericalHarmonics::assignPreset(int p) {
switch (p) {
case OLD_TOWN_SQUARE: {
L00 = glm::vec3( 0.871297f, 0.875222f, 0.864470f);
L1m1 = glm::vec3( 0.175058f, 0.245335f, 0.312891f);
L10 = glm::vec3( 0.034675f, 0.036107f, 0.037362f);
L11 = glm::vec3(-0.004629f,-0.029448f,-0.048028f);
L2m2 = glm::vec3(-0.120535f,-0.121160f,-0.117507f);
L2m1 = glm::vec3( 0.003242f, 0.003624f, 0.007511f);
L20 = glm::vec3(-0.028667f,-0.024926f,-0.020998f);
L21 = glm::vec3(-0.077539f,-0.086325f,-0.091591f);
L22 = glm::vec3(-0.161784f,-0.191783f,-0.219152f);
}
break;
case GRACE_CATHEDRAL: {
L00 = glm::vec3( 0.79f, 0.44f, 0.54f);
L1m1 = glm::vec3( 0.39f, 0.35f, 0.60f);
L10 = glm::vec3(-0.34f, -0.18f, -0.27f);
L11 = glm::vec3(-0.29f, -0.06f, 0.01f);
L2m2 = glm::vec3(-0.11f, -0.05f, -0.12f);
L2m1 = glm::vec3(-0.26f, -0.22f, -0.47f);
L20 = glm::vec3(-0.16f, -0.09f, -0.15f);
L21 = glm::vec3( 0.56f, 0.21f, 0.14f);
L22 = glm::vec3( 0.21f, -0.05f, -0.30f);
}
break;
case EUCALYPTUS_GROVE: {
L00 = glm::vec3( 0.38f, 0.43f, 0.45f);
L1m1 = glm::vec3( 0.29f, 0.36f, 0.41f);
L10 = glm::vec3( 0.04f, 0.03f, 0.01f);
L11 = glm::vec3(-0.10f, -0.10f, -0.09f);
L2m2 = glm::vec3(-0.06f, -0.06f, -0.04f);
L2m1 = glm::vec3( 0.01f, -0.01f, -0.05f);
L20 = glm::vec3(-0.09f, -0.13f, -0.15f);
L21 = glm::vec3(-0.06f, -0.05f, -0.04f);
L22 = glm::vec3( 0.02f, 0.00f, -0.05f);
}
break;
case ST_PETERS_BASILICA: {
L00 = glm::vec3( 0.36f, 0.26f, 0.23f);
L1m1 = glm::vec3( 0.18f, 0.14f, 0.13f);
L10 = glm::vec3(-0.02f, -0.01f, 0.00f);
L11 = glm::vec3( 0.03f, 0.02f, -0.00f);
L2m2 = glm::vec3( 0.02f, 0.01f, -0.00f);
L2m1 = glm::vec3(-0.05f, -0.03f, -0.01f);
L20 = glm::vec3(-0.09f, -0.08f, -0.07f);
L21 = glm::vec3( 0.01f, 0.00f, 0.00f);
L22 = glm::vec3(-0.08f, -0.03f, -0.00f);
}
break;
case UFFIZI_GALLERY: {
L00 = glm::vec3( 0.32f, 0.31f, 0.35f);
L1m1 = glm::vec3( 0.37f, 0.37f, 0.43f);
L10 = glm::vec3( 0.00f, 0.00f, 0.00f);
L11 = glm::vec3(-0.01f, -0.01f, -0.01f);
L2m2 = glm::vec3(-0.02f, -0.02f, -0.03f);
L2m1 = glm::vec3(-0.01f, -0.01f, -0.01f);
L20 = glm::vec3(-0.28f, -0.28f, -0.32f);
L21 = glm::vec3( 0.00f, 0.00f, 0.00f);
L22 = glm::vec3(-0.24f, -0.24f, -0.28f);
}
break;
case GALILEOS_TOMB: {
L00 = glm::vec3( 1.04f, 0.76f, 0.71f);
L1m1 = glm::vec3( 0.44f, 0.34f, 0.34f);
L10 = glm::vec3(-0.22f, -0.18f, -0.17f);
L11 = glm::vec3( 0.71f, 0.54f, 0.56f);
L2m2 = glm::vec3( 0.64f, 0.50f, 0.52f);
L2m1 = glm::vec3(-0.12f, -0.09f, -0.08f);
L20 = glm::vec3(-0.37f, -0.28f, -0.32f);
L21 = glm::vec3(-0.17f, -0.13f, -0.13f);
L22 = glm::vec3( 0.55f, 0.42f, 0.42f);
}
break;
case VINE_STREET_KITCHEN: {
L00 = glm::vec3( 0.64f, 0.67f, 0.73f);
L1m1 = glm::vec3( 0.28f, 0.32f, 0.33f);
L10 = glm::vec3( 0.42f, 0.60f, 0.77f);
L11 = glm::vec3(-0.05f, -0.04f, -0.02f);
L2m2 = glm::vec3(-0.10f, -0.08f, -0.05f);
L2m1 = glm::vec3( 0.25f, 0.39f, 0.53f);
L20 = glm::vec3( 0.38f, 0.54f, 0.71f);
L21 = glm::vec3( 0.06f, 0.01f, -0.02f);
L22 = glm::vec3(-0.03f, -0.02f, -0.03f);
}
break;
case BREEZEWAY: {
L00 = glm::vec3( 0.32f, 0.36f, 0.38f);
L1m1 = glm::vec3( 0.37f, 0.41f, 0.45f);
L10 = glm::vec3(-0.01f, -0.01f, -0.01f);
L11 = glm::vec3(-0.10f, -0.12f, -0.12f);
L2m2 = glm::vec3(-0.13f, -0.15f, -0.17f);
L2m1 = glm::vec3(-0.01f, -0.02f, 0.02f);
L20 = glm::vec3(-0.07f, -0.08f, -0.09f);
L21 = glm::vec3( 0.02f, 0.03f, 0.03f);
L22 = glm::vec3(-0.29f, -0.32f, -0.36f);
}
break;
case CAMPUS_SUNSET: {
L00 = glm::vec3( 0.79f, 0.94f, 0.98f);
L1m1 = glm::vec3( 0.44f, 0.56f, 0.70f);
L10 = glm::vec3(-0.10f, -0.18f, -0.27f);
L11 = glm::vec3( 0.45f, 0.38f, 0.20f);
L2m2 = glm::vec3( 0.18f, 0.14f, 0.05f);
L2m1 = glm::vec3(-0.14f, -0.22f, -0.31f);
L20 = glm::vec3(-0.39f, -0.40f, -0.36f);
L21 = glm::vec3( 0.09f, 0.07f, 0.04f);
L22 = glm::vec3( 0.67f, 0.67f, 0.52f);
}
break;
case FUNSTON_BEACH_SUNSET: {
L00 = glm::vec3( 0.68f, 0.69f, 0.70f);
L1m1 = glm::vec3( 0.32f, 0.37f, 0.44f);
L10 = glm::vec3(-0.17f, -0.17f, -0.17f);
L11 = glm::vec3(-0.45f, -0.42f, -0.34f);
L2m2 = glm::vec3(-0.17f, -0.17f, -0.15f);
L2m1 = glm::vec3(-0.08f, -0.09f, -0.10f);
L20 = glm::vec3(-0.03f, -0.02f, -0.01f);
L21 = glm::vec3( 0.16f, 0.14f, 0.10f);
L22 = glm::vec3( 0.37f, 0.31f, 0.20f);
}
break;
}
}
glm::vec3 sRGBToLinear(glm::vec3& color) {
const float GAMMA_CORRECTION = 2.2f;
return glm::pow(color, glm::vec3(GAMMA_CORRECTION));
}
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++) {
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);
}
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
const uint sqOrder = order*order;
// allocate memory for calculations
output.resize(sqOrder);
std::vector<float> resultR(sqOrder);
std::vector<float> resultG(sqOrder);
std::vector<float> resultB(sqOrder);
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<float> shBuff(sqOrder);
std::vector<float> shBuffB(sqOrder);
// get width and height
width = height = cubeTexture.getWidth();
if(width != height) {
return false;
}
const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());
// 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();
if (data == nullptr) {
continue;
}
// 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 false;
}
// 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());
}
}
}
// final scale for coefficients
const float fNormProj = (4.0f * glm::pi<float>()) / 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]);
}
return true;
}
void SphericalHarmonics::evalFromTexture(const Texture& texture) {
if (texture.isDefined()) {
std::vector< glm::vec3 > coefs;
sphericalHarmonicsFromTexture(texture, coefs, 3);
L00 = coefs[0];
L1m1 = coefs[1];
L10 = coefs[2];
L11 = coefs[3];
L2m2 = coefs[4];
L2m1 = coefs[5];
L20 = coefs[6];
L21 = coefs[7];
L22 = coefs[8];
}
}

View file

@ -17,6 +17,44 @@
namespace gpu {
// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated
// with the cube texture
class Texture;
class SphericalHarmonics {
public:
glm::vec3 L00 ; float spare0;
glm::vec3 L1m1 ; float spare1;
glm::vec3 L10 ; float spare2;
glm::vec3 L11 ; float spare3;
glm::vec3 L2m2 ; float spare4;
glm::vec3 L2m1 ; float spare5;
glm::vec3 L20 ; float spare6;
glm::vec3 L21 ; float spare7;
glm::vec3 L22 ; float spare8;
static const int NUM_COEFFICIENTS = 9;
enum Preset {
OLD_TOWN_SQUARE = 0,
GRACE_CATHEDRAL,
EUCALYPTUS_GROVE,
ST_PETERS_BASILICA,
UFFIZI_GALLERY,
GALILEOS_TOMB,
VINE_STREET_KITCHEN,
BREEZEWAY,
CAMPUS_SUNSET,
FUNSTON_BEACH_SUNSET,
NUM_PRESET,
};
void assignPreset(int p);
void evalFromTexture(const Texture& texture);
};
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
class Sampler {
public:
@ -173,6 +211,7 @@ public:
static Texture* createFromStorage(Storage* storage);
Texture();
Texture(const Texture& buf); // deep copy of the sysmem texture
Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
~Texture();
@ -302,6 +341,10 @@ public:
bool isDefined() const { return _defined; }
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture
bool generateIrradiance();
const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; }
bool isIrradianceValid() const { return _isIrradianceValid; }
// Own sampler
void setSampler(const Sampler& sampler);
@ -332,11 +375,14 @@ protected:
uint16 _maxMip = 0;
Type _type = TEX_1D;
SHPointer _irradiance;
bool _autoGenerateMips = false;
bool _isIrradianceValid = false;
bool _defined = false;
static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler);
Texture();
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
@ -392,7 +438,7 @@ public:
explicit operator bool() const { return bool(_texture); }
bool operator !() const { return (!_texture); }
};
typedef std::vector<TextureView> TextureViews;
typedef std::vector<TextureView> TextureViews;
};

View file

@ -26,162 +26,6 @@ typedef glm::vec3 Vec3;
typedef glm::vec4 Vec4;
typedef glm::quat Quat;
class SphericalHarmonics {
public:
glm::vec3 L00 ; float spare0;
glm::vec3 L1m1 ; float spare1;
glm::vec3 L10 ; float spare2;
glm::vec3 L11 ; float spare3;
glm::vec3 L2m2 ; float spare4;
glm::vec3 L2m1 ; float spare5;
glm::vec3 L20 ; float spare6;
glm::vec3 L21 ; float spare7;
glm::vec3 L22 ; float spare8;
static const int NUM_COEFFICIENTS = 9;
enum Preset {
OLD_TOWN_SQUARE = 0,
GRACE_CATHEDRAL,
EUCALYPTUS_GROVE,
ST_PETERS_BASILICA,
UFFIZI_GALLERY,
GALILEOS_TOMB,
VINE_STREET_KITCHEN,
BREEZEWAY,
CAMPUS_SUNSET,
FUNSTON_BEACH_SUNSET,
NUM_PRESET,
};
void assignPreset(int p) {
switch (p) {
case OLD_TOWN_SQUARE: {
L00 = glm::vec3( 0.871297f, 0.875222f, 0.864470f);
L1m1 = glm::vec3( 0.175058f, 0.245335f, 0.312891f);
L10 = glm::vec3( 0.034675f, 0.036107f, 0.037362f);
L11 = glm::vec3(-0.004629f,-0.029448f,-0.048028f);
L2m2 = glm::vec3(-0.120535f,-0.121160f,-0.117507f);
L2m1 = glm::vec3( 0.003242f, 0.003624f, 0.007511f);
L20 = glm::vec3(-0.028667f,-0.024926f,-0.020998f);
L21 = glm::vec3(-0.077539f,-0.086325f,-0.091591f);
L22 = glm::vec3(-0.161784f,-0.191783f,-0.219152f);
}
break;
case GRACE_CATHEDRAL: {
L00 = glm::vec3( 0.79f, 0.44f, 0.54f);
L1m1 = glm::vec3( 0.39f, 0.35f, 0.60f);
L10 = glm::vec3(-0.34f, -0.18f, -0.27f);
L11 = glm::vec3(-0.29f, -0.06f, 0.01f);
L2m2 = glm::vec3(-0.11f, -0.05f, -0.12f);
L2m1 = glm::vec3(-0.26f, -0.22f, -0.47f);
L20 = glm::vec3(-0.16f, -0.09f, -0.15f);
L21 = glm::vec3( 0.56f, 0.21f, 0.14f);
L22 = glm::vec3( 0.21f, -0.05f, -0.30f);
}
break;
case EUCALYPTUS_GROVE: {
L00 = glm::vec3( 0.38f, 0.43f, 0.45f);
L1m1 = glm::vec3( 0.29f, 0.36f, 0.41f);
L10 = glm::vec3( 0.04f, 0.03f, 0.01f);
L11 = glm::vec3(-0.10f, -0.10f, -0.09f);
L2m2 = glm::vec3(-0.06f, -0.06f, -0.04f);
L2m1 = glm::vec3( 0.01f, -0.01f, -0.05f);
L20 = glm::vec3(-0.09f, -0.13f, -0.15f);
L21 = glm::vec3(-0.06f, -0.05f, -0.04f);
L22 = glm::vec3( 0.02f, 0.00f, -0.05f);
}
break;
case ST_PETERS_BASILICA: {
L00 = glm::vec3( 0.36f, 0.26f, 0.23f);
L1m1 = glm::vec3( 0.18f, 0.14f, 0.13f);
L10 = glm::vec3(-0.02f, -0.01f, 0.00f);
L11 = glm::vec3( 0.03f, 0.02f, -0.00f);
L2m2 = glm::vec3( 0.02f, 0.01f, -0.00f);
L2m1 = glm::vec3(-0.05f, -0.03f, -0.01f);
L20 = glm::vec3(-0.09f, -0.08f, -0.07f);
L21 = glm::vec3( 0.01f, 0.00f, 0.00f);
L22 = glm::vec3(-0.08f, -0.03f, -0.00f);
}
break;
case UFFIZI_GALLERY: {
L00 = glm::vec3( 0.32f, 0.31f, 0.35f);
L1m1 = glm::vec3( 0.37f, 0.37f, 0.43f);
L10 = glm::vec3( 0.00f, 0.00f, 0.00f);
L11 = glm::vec3(-0.01f, -0.01f, -0.01f);
L2m2 = glm::vec3(-0.02f, -0.02f, -0.03f);
L2m1 = glm::vec3(-0.01f, -0.01f, -0.01f);
L20 = glm::vec3(-0.28f, -0.28f, -0.32f);
L21 = glm::vec3( 0.00f, 0.00f, 0.00f);
L22 = glm::vec3(-0.24f, -0.24f, -0.28f);
}
break;
case GALILEOS_TOMB: {
L00 = glm::vec3( 1.04f, 0.76f, 0.71f);
L1m1 = glm::vec3( 0.44f, 0.34f, 0.34f);
L10 = glm::vec3(-0.22f, -0.18f, -0.17f);
L11 = glm::vec3( 0.71f, 0.54f, 0.56f);
L2m2 = glm::vec3( 0.64f, 0.50f, 0.52f);
L2m1 = glm::vec3(-0.12f, -0.09f, -0.08f);
L20 = glm::vec3(-0.37f, -0.28f, -0.32f);
L21 = glm::vec3(-0.17f, -0.13f, -0.13f);
L22 = glm::vec3( 0.55f, 0.42f, 0.42f);
}
break;
case VINE_STREET_KITCHEN: {
L00 = glm::vec3( 0.64f, 0.67f, 0.73f);
L1m1 = glm::vec3( 0.28f, 0.32f, 0.33f);
L10 = glm::vec3( 0.42f, 0.60f, 0.77f);
L11 = glm::vec3(-0.05f, -0.04f, -0.02f);
L2m2 = glm::vec3(-0.10f, -0.08f, -0.05f);
L2m1 = glm::vec3( 0.25f, 0.39f, 0.53f);
L20 = glm::vec3( 0.38f, 0.54f, 0.71f);
L21 = glm::vec3( 0.06f, 0.01f, -0.02f);
L22 = glm::vec3(-0.03f, -0.02f, -0.03f);
}
break;
case BREEZEWAY: {
L00 = glm::vec3( 0.32f, 0.36f, 0.38f);
L1m1 = glm::vec3( 0.37f, 0.41f, 0.45f);
L10 = glm::vec3(-0.01f, -0.01f, -0.01f);
L11 = glm::vec3(-0.10f, -0.12f, -0.12f);
L2m2 = glm::vec3(-0.13f, -0.15f, -0.17f);
L2m1 = glm::vec3(-0.01f, -0.02f, 0.02f);
L20 = glm::vec3(-0.07f, -0.08f, -0.09f);
L21 = glm::vec3( 0.02f, 0.03f, 0.03f);
L22 = glm::vec3(-0.29f, -0.32f, -0.36f);
}
break;
case CAMPUS_SUNSET: {
L00 = glm::vec3( 0.79f, 0.94f, 0.98f);
L1m1 = glm::vec3( 0.44f, 0.56f, 0.70f);
L10 = glm::vec3(-0.10f, -0.18f, -0.27f);
L11 = glm::vec3( 0.45f, 0.38f, 0.20f);
L2m2 = glm::vec3( 0.18f, 0.14f, 0.05f);
L2m1 = glm::vec3(-0.14f, -0.22f, -0.31f);
L20 = glm::vec3(-0.39f, -0.40f, -0.36f);
L21 = glm::vec3( 0.09f, 0.07f, 0.04f);
L22 = glm::vec3( 0.67f, 0.67f, 0.52f);
}
break;
case FUNSTON_BEACH_SUNSET: {
L00 = glm::vec3( 0.68f, 0.69f, 0.70f);
L1m1 = glm::vec3( 0.32f, 0.37f, 0.44f);
L10 = glm::vec3(-0.17f, -0.17f, -0.17f);
L11 = glm::vec3(-0.45f, -0.42f, -0.34f);
L2m2 = glm::vec3(-0.17f, -0.17f, -0.15f);
L2m1 = glm::vec3(-0.08f, -0.09f, -0.10f);
L20 = glm::vec3(-0.03f, -0.02f, -0.01f);
L21 = glm::vec3( 0.16f, 0.14f, 0.10f);
L22 = glm::vec3( 0.37f, 0.31f, 0.20f);
}
break;
}
}
};
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
class Light {
public:
enum Type {
@ -250,9 +94,9 @@ public:
float getAmbientIntensity() const { return getSchema()._ambientIntensity; }
// Spherical Harmonics storing the Ambien lighting approximation used for the Sun typed light
void setAmbientSphere(const SphericalHarmonics& sphere) { _ambientSphere = sphere; }
const SphericalHarmonics& getAmbientSphere() const { return _ambientSphere; }
void setAmbientSpherePreset(SphericalHarmonics::Preset preset) { _ambientSphere.assignPreset(preset); }
void setAmbientSphere(const gpu::SphericalHarmonics& sphere) { _ambientSphere = sphere; }
const gpu::SphericalHarmonics& getAmbientSphere() const { return _ambientSphere; }
void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) { _ambientSphere.assignPreset(preset); }
// Schema to access the attribute values of the light
class Schema {
@ -278,7 +122,7 @@ protected:
Flags _flags;
UniformBufferView _schemaBuffer;
Transform _transform;
SphericalHarmonics _ambientSphere;
gpu::SphericalHarmonics _ambientSphere;
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }

View file

@ -39,22 +39,13 @@ void Skybox::setColor(const Color& color) {
}
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
if (_isSHValid && (cubemap != _cubemap)) {
_isSHValid = false;
}
_cubemap = cubemap;
}
void Skybox::clearCubemap() {
_cubemap.reset();
}
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
skybox.getIrradianceSH();
static gpu::PipelinePointer thePipeline;
static gpu::BufferPointer theBuffer;
static gpu::Stream::FormatPointer theFormat;
@ -120,222 +111,3 @@ 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));
}
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++) {
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<glm::vec3> & output, const uint order) {
const uint sqOrder = order*order;
// allocate memory for calculations
output.resize(sqOrder);
std::vector<float> resultR(sqOrder);
std::vector<float> resultG(sqOrder);
std::vector<float> resultB(sqOrder);
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<float> shBuff(sqOrder);
std::vector<float> shBuffB(sqOrder);
// get width and height
width = height = cubeTexture.getWidth();
if(width != height) {
return;
}
const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());
// 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();
if (data == nullptr) {
continue;
}
// 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]) * 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());
}
}
}
// final scale for coefficients
const float fNormProj = (4.0f * glm::pi<float>()) / 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;
}

View file

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

View file

@ -89,7 +89,7 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
lp->setColor(glm::vec3(1.0f));
lp->setIntensity(1.0f);
lp->setType(model::Light::SUN);
lp->setAmbientSpherePreset(model::SphericalHarmonics::Preset(_ambientLightMode % model::SphericalHarmonics::NUM_PRESET));
lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET));
}
void DeferredLightingEffect::bindSimpleProgram() {
@ -291,13 +291,11 @@ void DeferredLightingEffect::render() {
auto globalLight = _allocatedLights[_globalLights.front()];
if (locations->ambientSphere >= 0) {
model::SphericalHarmonics sh;
if (useSkyboxCubemap) {
sh = _skybox->getIrradianceSH();
} else {
sh = globalLight->getAmbientSphere();
gpu::SphericalHarmonics sh = globalLight->getAmbientSphere();
if (useSkyboxCubemap && _skybox->getCubemap()->getIrradiance()) {
sh = (*_skybox->getCubemap()->getIrradiance());
}
for (int i =0; i <model::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
for (int i =0; i <gpu::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
program->setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i));
}
}
@ -590,10 +588,10 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
}
void DeferredLightingEffect::setAmbientLightMode(int preset) {
if ((preset >= 0) && (preset < model::SphericalHarmonics::NUM_PRESET)) {
if ((preset >= 0) && (preset < gpu::SphericalHarmonics::NUM_PRESET)) {
_ambientLightMode = preset;
auto light = _allocatedLights.front();
light->setAmbientSpherePreset(model::SphericalHarmonics::Preset(preset % model::SphericalHarmonics::NUM_PRESET));
light->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(preset % gpu::SphericalHarmonics::NUM_PRESET));
} else {
// force to preset 0
setAmbientLightMode(0);

View file

@ -348,7 +348,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr
class ImageReader : public QRunnable {
public:
ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url = QUrl(),
ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply, const QUrl& url = QUrl(),
const QByteArray& content = QByteArray());
virtual void run();
@ -356,14 +356,25 @@ public:
private:
QWeakPointer<Resource> _texture;
TextureType _type;
QNetworkReply* _reply;
QUrl _url;
QByteArray _content;
};
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply,
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, reply));
}
void NetworkTexture::loadContent(const QByteArray& content) {
QThreadPool::globalInstance()->start(new ImageReader(_self, _type, NULL, _url, content));
}
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply,
const QUrl& url, const QByteArray& content) :
_texture(texture),
_type(type),
_reply(reply),
_url(url),
_content(content) {
@ -381,6 +392,41 @@ void listSupportedImageFormats() {
});
}
class CubeLayout {
public:
int _widthRatio = 1;
int _heightRatio = 1;
class Face {
public:
int _x = 0;
int _y = 0;
bool _horizontalMirror = false;
bool _verticalMirror = false;
Face() {}
Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}
};
Face _faceXPos;
Face _faceXNeg;
Face _faceYPos;
Face _faceYNeg;
Face _faceZPos;
Face _faceZNeg;
CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :
_widthRatio(wr),
_heightRatio(hr),
_faceXPos(fXP),
_faceXNeg(fXN),
_faceYPos(fYP),
_faceYNeg(fYN),
_faceZPos(fZP),
_faceZNeg(fZN) {}
};
void ImageReader::run() {
QSharedPointer<Resource> texture = _texture.toStrongRef();
if (texture.isNull()) {
@ -436,12 +482,17 @@ void ImageReader::run() {
}
}
int opaquePixels = 0;
int translucentPixels = 0;
int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
const int EIGHT_BIT_MAXIMUM = 255;
QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM);
if (!image.hasAlphaChannel()) {
if (image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888);
}
int redTotal = 0, greenTotal = 0, blueTotal = 0;
// int redTotal = 0, greenTotal = 0, blueTotal = 0;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb rgb = image.pixel(x, y);
@ -450,103 +501,44 @@ void ImageReader::run() {
blueTotal += qBlue(rgb);
}
}
QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM);
if (imageArea > 0) {
averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea);
}
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false),
Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
return;
}
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
} else {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// check for translucency/false transparency
int opaquePixels = 0;
int translucentPixels = 0;
int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb rgb = image.pixel(x, y);
redTotal += qRed(rgb);
greenTotal += qGreen(rgb);
blueTotal += qBlue(rgb);
int alpha = qAlpha(rgb);
alphaTotal += alpha;
if (alpha == EIGHT_BIT_MAXIMUM) {
opaquePixels++;
} else if (alpha != 0) {
translucentPixels++;
// check for translucency/false transparency
// int opaquePixels = 0;
// int translucentPixels = 0;
// int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;
for (int y = 0; y < image.height(); y++) {
for (int x = 0; x < image.width(); x++) {
QRgb rgb = image.pixel(x, y);
redTotal += qRed(rgb);
greenTotal += qGreen(rgb);
blueTotal += qBlue(rgb);
int alpha = qAlpha(rgb);
alphaTotal += alpha;
if (alpha == EIGHT_BIT_MAXIMUM) {
opaquePixels++;
} else if (alpha != 0) {
translucentPixels++;
}
}
}
if (opaquePixels == imageArea) {
qCDebug(renderutils) << "Image with alpha channel is completely opaque:" << _url;
image = image.convertToFormat(QImage::Format_RGB888);
}
averageColor = QColor(redTotal / imageArea,
greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea);
}
if (opaquePixels == imageArea) {
qCDebug(renderutils) << "Image with alpha channel is completely opaque:" << _url;
image = image.convertToFormat(QImage::Format_RGB888);
}
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
Q_ARG(bool, translucentPixels >= imageArea / 2), Q_ARG(const QColor&, QColor(redTotal / imageArea,
greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea)),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
}
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
// send the reader off to the thread pool
QThreadPool::globalInstance()->start(new ImageReader(_self, reply));
}
void NetworkTexture::loadContent(const QByteArray& content) {
QThreadPool::globalInstance()->start(new ImageReader(_self, NULL, _url, content));
}
class CubeLayout {
public:
int _widthRatio = 1;
int _heightRatio = 1;
class Face {
public:
int _x = 0;
int _y = 0;
bool _horizontalMirror = false;
bool _verticalMirror = false;
Face() {}
Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}
};
Face _faceXPos;
Face _faceXNeg;
Face _faceYPos;
Face _faceYNeg;
Face _faceZPos;
Face _faceZNeg;
CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :
_widthRatio(wr),
_heightRatio(hr),
_faceXPos(fXP),
_faceXNeg(fXN),
_faceYPos(fYP),
_faceYNeg(fYN),
_faceZPos(fZP),
_faceZNeg(fZN) {}
};
void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor, int originalWidth,
int originalHeight) {
_translucent = translucent;
_averageColor = averageColor;
_originalWidth = originalWidth;
_originalHeight = originalHeight;
_width = image.width();
_height = image.height();
finishedLoading(true);
imageLoaded(image);
if ((_width > 0) && (_height > 0)) {
gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
bool isLinearRGB = !(_type == CUBE_TEXTURE); //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
@ -666,7 +658,7 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) {
if ((image.height() * CUBEMAP_LAYOUTS[i]._widthRatio) == (image.width() * CUBEMAP_LAYOUTS[i]._heightRatio)) {
foundLayout = i;
// break;
break;
}
}
@ -682,24 +674,66 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror));
faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror));
} else {
qCDebug(renderutils) << "Failed to find a known cube map layout from this image:" << _url;
return;
}
// If the 6 faces have been created go on and define the true Texture
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
_gpuTexture = gpu::TexturePointer(gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)));
_gpuTexture->autoGenerateMips(-1);
theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP));
theTexture->autoGenerateMips(-1);
int f = 0;
for (auto& face : faces) {
_gpuTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f);
f++;
}
// GEnerate irradiance while we are at it
theTexture->generateIrradiance();
}
} else {
_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
_gpuTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
_gpuTexture->autoGenerateMips(-1);
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1);
}
}
QMetaObject::invokeMethod(texture.data(), "setImage",
Q_ARG(const QImage&, image),
Q_ARG(void*, theTexture),
Q_ARG(bool, translucentPixels >= imageArea / 2), Q_ARG(const QColor&, averageColor),
Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
/* QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false),
Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight));
return;
*/
}
void NetworkTexture::setImage(const QImage& image, void* voidTexture, bool translucent, const QColor& averageColor, int originalWidth,
int originalHeight) {
_translucent = translucent;
_averageColor = averageColor;
_originalWidth = originalWidth;
_originalHeight = originalHeight;
gpu::Texture* texture = static_cast<gpu::Texture*>(voidTexture);
// Passing ownership
_gpuTexture.reset(texture);
if (_gpuTexture) {
_width = _gpuTexture->getWidth();
_height = _gpuTexture->getHeight();
} else {
_width = _height = 0;
}
finishedLoading(true);
imageLoaded(image);
}
void NetworkTexture::imageLoaded(const QImage& image) {

View file

@ -16,6 +16,8 @@
#include <gpu/Texture.h>
#include <gpu/Framebuffer.h>
#include <model/Light.h>
#include <QImage>
#include <QMap>
#include <QGLWidget>
@ -146,6 +148,9 @@ private:
};
/// A texture loaded from the network.
Q_DECLARE_METATYPE(gpu::Texture);
class NetworkTexture : public Resource, public Texture {
Q_OBJECT
@ -170,7 +175,7 @@ protected:
virtual void downloadFinished(QNetworkReply* reply);
Q_INVOKABLE void loadContent(const QByteArray& content);
Q_INVOKABLE void setImage(const QImage& image, bool translucent, const QColor& averageColor, int originalWidth,
Q_INVOKABLE void setImage(const QImage& image, void* texture, bool translucent, const QColor& averageColor, int originalWidth,
int originalHeight);
virtual void imageLoaded(const QImage& image);