IBL: Implement IBL

This commit is contained in:
Vincent Lejeune 2014-03-01 20:08:17 +01:00
parent 02fa3ceed0
commit 391053e368
8 changed files with 475 additions and 8 deletions

View File

@ -0,0 +1,49 @@
uniform float blueLmn[9];
uniform float greenLmn[9];
uniform float redLmn[9];
uniform sampler2D ntex;
#if __VERSION__ >= 130
in vec2 uv;
out vec4 Diff;
out vec4 Spec;
#else
varying vec2 uv;
#define Diff gl_FragData[0]
#define Spec gl_FragData[1]
#endif
vec3 DecodeNormal(vec2 n)
{
float z = dot(n, n) * 2. - 1.;
vec2 xy = normalize(n) * sqrt(1. - z * z);
return vec3(xy,z);
}
mat4 getMatrix(float L[9])
{
float c1 = 0.429043, c2 = 0.511664, c3 = 0.743125, c4 = 0.886227, c5 = 0.247708;
return mat4(
c1 * L[8] /*L22*/, c1 * L[4] /*L2-2*/, c1 * L[7] /*L21*/, c2 * L[3] /*L11*/,
c1 * L[4], - c1 * L[8], c1 * L[5] /*L2-1*/, c2 * L[1] /*L1-1*/,
c1 * L[7], c1 * L[5], c3 * L[6] /*L20*/, c2 * L[2] /*L10*/,
c2 * L[3], c2 * L[1], c2 * L[2], c4 * L[0] /*L00*/ - c5 * L[6]
);
}
void main(void)
{
vec3 normal = normalize(DecodeNormal(2. * texture(ntex, uv).xy - 1.));
vec4 extendednormal = vec4(normal, 1.);
mat4 rmat = getMatrix(redLmn);
mat4 gmat = getMatrix(greenLmn);
mat4 bmat = getMatrix(blueLmn);
float r = dot(extendednormal, rmat * extendednormal);
float g = dot(extendednormal, gmat * extendednormal);
float b = dot(extendednormal, bmat * extendednormal);
Diff = 0.25 * vec4(r, g, b, .1);
Spec = vec4(0.);
}

View File

@ -1173,6 +1173,7 @@ scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*>
assert(texture.size() == 6);
SkyboxTextures = texture;
SkyboxCubeMap = 0;
ConvolutedSkyboxCubeMap = 0;
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
texture[2], texture[3],
texture[4], texture[5]);
@ -1182,7 +1183,9 @@ void IrrDriver::suppressSkyBox()
{
SkyboxTextures.clear();
glDeleteTextures(1, &SkyboxCubeMap);
glDeleteTextures(1, &ConvolutedSkyboxCubeMap);
SkyboxCubeMap = 0;
ConvolutedSkyboxCubeMap = 0;
}
// ----------------------------------------------------------------------------

View File

@ -115,7 +115,10 @@ private:
core::matrix4 m_ViewMatrix, m_InvViewMatrix, m_ProjMatrix, m_InvProjMatrix, m_ProjViewMatrix, m_InvProjViewMatrix;
std::vector<video::ITexture *> SkyboxTextures;
GLuint SkyboxCubeMap;
GLuint SkyboxCubeMap, ConvolutedSkyboxCubeMap;
float blueSHCoeff[9];
float greenSHCoeff[9];
float redSHCoeff[9];
/** Flag to indicate if a resolution change is pending (which will be

View File

@ -302,6 +302,27 @@ void PostProcessing::renderPointlight(const std::vector<float> &positions, const
glDisable(GL_BLEND);
}
void PostProcessing::renderDiffuseEnvMap(const float *bSHCoeff, const float *gSHCoeff, const float *rSHCoeff)
{
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glUseProgram(FullScreenShader::DiffuseEnvMapShader::Program);
glBindVertexArray(FullScreenShader::DiffuseEnvMapShader::vao);
setTexture(0, getTextureGLuint(irr_driver->getRTT(RTT_NORMAL_AND_DEPTH)), GL_NEAREST, GL_NEAREST);
FullScreenShader::DiffuseEnvMapShader::setUniforms(bSHCoeff, gSHCoeff, rSHCoeff, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
}
void PostProcessing::renderSunlight()
{
SunLightProvider * const cb = (SunLightProvider *) irr_driver->getCallback(ES_SUNLIGHT);

View File

@ -79,6 +79,7 @@ public:
void renderFog(const core::matrix4 &ipvmat);
void renderSSAO(const core::matrix4 &invprojm, const core::matrix4 &projm);
void renderDiffuseEnvMap(const float *bSHCoeff, const float *gSHCoeff, const float *rSHCoeff);
/** Blur the in texture */
void renderGaussian3Blur(video::ITexture *in, video::ITexture *temprtt, float inv_width, float inv_height);

View File

@ -741,6 +741,8 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox,
video::SOverrideMaterial &overridemat,
int cam, float dt)
{
if (SkyboxCubeMap)
irr_driver->getSceneManager()->setAmbientLight(SColor(0, 0, 0, 0));
for (unsigned i = 0; i < sun_ortho_matrix.size(); i++)
sun_ortho_matrix[i] *= getInvViewMatrix();
core::array<video::IRenderTarget> rtts;
@ -831,6 +833,8 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox,
accumulatedLightEnergy.push_back(0.);
}
m_post_processing->renderPointlight(accumulatedLightPos, accumulatedLightColor, accumulatedLightEnergy);
if (SkyboxCubeMap)
m_post_processing->renderDiffuseEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff);
// Handle SSAO
m_video_driver->setRenderTarget(irr_driver->getRTT(RTT_SSAO), true, false,
SColor(255, 255, 255, 255));
@ -916,13 +920,351 @@ static void createcubevao()
}
#define MAX2(a, b) ((a) > (b) ? (a) : (b))
#define MIN2(a, b) ((a) > (b) ? (b) : (a))
static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z)
{
switch (face)
{
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
x = 1.;
y = -i;
z = -j;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
x = -1.;
y = -i;
z = j;
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
x = j;
y = 1.;
z = i;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
x = j;
y = -1;
z = -i;
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
x = j;
y = -i;
z = 1;
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
x = -j;
y = -i;
z = -1;
break;
}
float norm = sqrt(x * x + y * y + z * z);
x /= norm, y /= norm, z /= norm;
return;
}
static void getYml(GLenum face, size_t width, size_t height,
float *Y00,
float *Y1minus1, float *Y10, float *Y11,
float *Y2minus2, float *Y2minus1, float *Y20, float *Y21, float *Y22)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
float x, y, z;
float fi = i, fj = j;
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
getXYZ(face, fi, fj, x, y, z);
// constant part of Ylm
float c00 = 0.282095;
float c1minus1 = 0.488603;
float c10 = 0.488603;
float c11 = 0.488603;
float c2minus2 = 1.092548;
float c2minus1 = 1.092548;
float c21 = 1.092548;
float c20 = 0.315392;
float c22 = 0.546274;
size_t idx = i * height + j;
Y00[idx] = c00;
Y1minus1[idx] = c1minus1 * y;
Y10[idx] = c10 * z;
Y11[idx] = c11 * x;
Y2minus2[idx] = c2minus2 * x * y;
Y2minus1[idx] = c2minus1 * y * z;
Y21[idx] = c21 * x * z;
Y20[idx] = c20 * (3 * z * z - 1);
Y22[idx] = c22 * (x * x - y * y);
}
}
}
static float getTexelValue(unsigned i, unsigned j, size_t width, size_t height, float *Coeff, float *Y00, float *Y1minus1, float *Y10, float *Y11,
float *Y2minus2, float * Y2minus1, float * Y20, float *Y21, float *Y22)
{
float d = sqrt(i * i + j * j + 1);
float solidangle = 1.;
size_t idx = i * height + j;
float reconstructedVal = Y00[idx] * Coeff[0];
reconstructedVal += Y1minus1[i * height + j] * Coeff[1] + Y10[i * height + j] * Coeff[2] + Y11[i * height + j] * Coeff[3];
reconstructedVal += Y2minus2[idx] * Coeff[4] + Y2minus1[idx] * Coeff[5] + Y20[idx] * Coeff[6] + Y21[idx] * Coeff[7] + Y22[idx] * Coeff[8];
reconstructedVal /= solidangle;
return MAX2(255 * reconstructedVal, 0.);
}
static void unprojectSH(float *output[], size_t width, size_t height,
float *Y00[],
float *Y1minus1[], float *Y10[], float *Y11[],
float *Y2minus2[], float *Y2minus1[], float * Y20[],float *Y21[], float *Y22[],
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff)
{
for (unsigned face = 0; face < 6; face++)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
float fi = i, fj = j;
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
output[face][4 * height * i + 4 * j + 2] = getTexelValue(i, j, width, height,
redSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
output[face][4 * height * i + 4 * j + 1] = getTexelValue(i, j, width, height,
greenSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
output[face][4 * height * i + 4 * j] = getTexelValue(i, j, width, height,
blueSHCoeff,
Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
}
}
}
}
static void projectSH(float *color[], size_t width, size_t height,
float *Y00[],
float *Y1minus1[], float *Y10[], float *Y11[],
float *Y2minus2[], float *Y2minus1[], float * Y20[], float *Y21[], float *Y22[],
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff
)
{
for (unsigned i = 0; i < 9; i++)
{
blueSHCoeff[i] = 0;
greenSHCoeff[i] = 0;
redSHCoeff[i] = 0;
}
float wh = width * height;
for (unsigned face = 0; face < 6; face++)
{
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < height; j++)
{
size_t idx = i * height + j;
float fi = i, fj = j;
fi /= width, fj /= height;
fi = 2 * fi - 1, fj = 2 * fj - 1;
float d = sqrt(fi * fi + fj * fj + 1);
// Constant obtained by projecting unprojected ref values
float solidangle = 2.75 / (wh * pow(d, 1.5));
float b = color[face][4 * height * i + 4 * j] / 255.;
float g = color[face][4 * height * i + 4 * j + 1] / 255.;
float r = color[face][4 * height * i + 4 * j + 2] / 255.;
assert(b >= 0.);
blueSHCoeff[0] += b * Y00[face][idx] * solidangle;
blueSHCoeff[1] += b * Y1minus1[face][idx] * solidangle;
blueSHCoeff[2] += b * Y10[face][idx] * solidangle;
blueSHCoeff[3] += b * Y11[face][idx] * solidangle;
blueSHCoeff[4] += b * Y2minus2[face][idx] * solidangle;
blueSHCoeff[5] += b * Y2minus1[face][idx] * solidangle;
blueSHCoeff[6] += b * Y20[face][idx] * solidangle;
blueSHCoeff[7] += b * Y21[face][idx] * solidangle;
blueSHCoeff[8] += b * Y22[face][idx] * solidangle;
greenSHCoeff[0] += g * Y00[face][idx] * solidangle;
greenSHCoeff[1] += g * Y1minus1[face][idx] * solidangle;
greenSHCoeff[2] += g * Y10[face][idx] * solidangle;
greenSHCoeff[3] += g * Y11[face][idx] * solidangle;
greenSHCoeff[4] += g * Y2minus2[face][idx] * solidangle;
greenSHCoeff[5] += g * Y2minus1[face][idx] * solidangle;
greenSHCoeff[6] += g * Y20[face][idx] * solidangle;
greenSHCoeff[7] += g * Y21[face][idx] * solidangle;
greenSHCoeff[8] += g * Y22[face][idx] * solidangle;
redSHCoeff[0] += r * Y00[face][idx] * solidangle;
redSHCoeff[1] += r * Y1minus1[face][idx] * solidangle;
redSHCoeff[2] += r * Y10[face][idx] * solidangle;
redSHCoeff[3] += r * Y11[face][idx] * solidangle;
redSHCoeff[4] += r * Y2minus2[face][idx] * solidangle;
redSHCoeff[5] += r * Y2minus1[face][idx] * solidangle;
redSHCoeff[6] += r * Y20[face][idx] * solidangle;
redSHCoeff[7] += r * Y21[face][idx] * solidangle;
redSHCoeff[8] += r * Y22[face][idx] * solidangle;
}
}
}
}
static void displayCoeff(float *SHCoeff)
{
printf("L00:%f\n", SHCoeff[0]);
printf("L1-1:%f, L10:%f, L11:%f\n", SHCoeff[1], SHCoeff[2], SHCoeff[3]);
printf("L2-2:%f, L2-1:%f, L20:%f, L21:%f, L22:%f\n", SHCoeff[4], SHCoeff[5], SHCoeff[6], SHCoeff[7], SHCoeff[8]);
}
// Only for 9 coefficients
static void testSH(char *color[6], size_t width, size_t height,
float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff)
{
float *Y00[6];
float *Y1minus1[6];
float *Y10[6];
float *Y11[6];
float *Y2minus2[6];
float *Y2minus1[6];
float *Y20[6];
float *Y21[6];
float *Y22[6];
float *testoutput[6];
for (unsigned i = 0; i < 6; i++)
{
testoutput[i] = new float[width * height * 4];
for (unsigned j = 0; j < width * height; j++)
{
testoutput[i][4 * j] = 0xFF & color[i][4 * j];
testoutput[i][4 * j + 1] = 0xFF & color[i][4 * j + 1];
testoutput[i][4 * j + 2] = 0xFF & color[i][4 * j + 2];
}
}
for (unsigned face = 0; face < 6; face++)
{
Y00[face] = new float[width * height];
Y1minus1[face] = new float[width * height];
Y10[face] = new float[width * height];
Y11[face] = new float[width * height];
Y2minus2[face] = new float[width * height];
Y2minus1[face] = new float[width * height];
Y20[face] = new float[width * height];
Y21[face] = new float[width * height];
Y22[face] = new float[width * height];
getYml(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, width, height, Y00[face], Y1minus1[face], Y10[face], Y11[face], Y2minus2[face], Y2minus1[face], Y20[face], Y21[face], Y22[face]);
}
/* blueSHCoeff[0] = 0.54,
blueSHCoeff[1] = .6, blueSHCoeff[2] = -.27, blueSHCoeff[3] = .01,
blueSHCoeff[4] = -.12, blueSHCoeff[5] = -.47, blueSHCoeff[6] = -.15, blueSHCoeff[7] = .14, blueSHCoeff[8] = -.3;
greenSHCoeff[0] = .44,
greenSHCoeff[1] = .35, greenSHCoeff[2] = -.18, greenSHCoeff[3] = -.06,
greenSHCoeff[4] = -.05, greenSHCoeff[5] = -.22, greenSHCoeff[6] = -.09, greenSHCoeff[7] = .21, greenSHCoeff[8] = -.05;
redSHCoeff[0] = .79,
redSHCoeff[1] = .39, redSHCoeff[2] = -.34, redSHCoeff[3] = -.29,
redSHCoeff[4] = -.11, redSHCoeff[5] = -.26, redSHCoeff[6] = -.16, redSHCoeff[7] = .56, redSHCoeff[8] = .21;
printf("Blue:\n");
displayCoeff(blueSHCoeff);
printf("Green:\n");
displayCoeff(greenSHCoeff);
printf("Red:\n");
displayCoeff(redSHCoeff);*/
projectSH(testoutput, width, height,
Y00,
Y1minus1, Y10, Y11,
Y2minus2, Y2minus1, Y20, Y21, Y22,
blueSHCoeff, greenSHCoeff, redSHCoeff
);
printf("Blue:\n");
displayCoeff(blueSHCoeff);
printf("Green:\n");
displayCoeff(greenSHCoeff);
printf("Red:\n");
displayCoeff(redSHCoeff);
// Convolute in frequency space
/* float A0 = 3.141593;
float A1 = 2.094395;
float A2 = 0.785398;
blueSHCoeff[0] *= A0;
greenSHCoeff[0] *= A0;
redSHCoeff[0] *= A0;
for (unsigned i = 0; i < 3; i++)
{
blueSHCoeff[1 + i] *= A1;
greenSHCoeff[1 + i] *= A1;
redSHCoeff[1 + i] *= A1;
}
for (unsigned i = 0; i < 5; i++)
{
blueSHCoeff[4 + i] *= A2;
greenSHCoeff[4 + i] *= A2;
redSHCoeff[4 + i] *= A2;
}
unprojectSH(testoutput, width, height,
Y00,
Y1minus1, Y10, Y11,
Y2minus2, Y2minus1, Y20, Y21, Y22,
blueSHCoeff, greenSHCoeff, redSHCoeff
);*/
/* printf("Blue:\n");
displayCoeff(blueSHCoeff);
printf("Green:\n");
displayCoeff(greenSHCoeff);
printf("Red:\n");
displayCoeff(redSHCoeff);
printf("\nAfter projection\n\n");*/
for (unsigned i = 0; i < 6; i++)
{
for (unsigned j = 0; j < width * height; j++)
{
color[i][4 * j] = MIN2(testoutput[i][4 * j], 255);
color[i][4 * j + 1] = MIN2(testoutput[i][4 * j + 1], 255);
color[i][4 * j + 2] = MIN2(testoutput[i][4 * j + 2], 255);
}
}
for (unsigned face = 0; face < 6; face++)
{
delete[] testoutput[face];
delete[] Y00[face];
delete[] Y1minus1[face];
delete[] Y10[face];
delete[] Y11[face];
}
}
void IrrDriver::generateSkyboxCubemap()
{
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glGenTextures(1, &SkyboxCubeMap);
glGenTextures(1, &ConvolutedSkyboxCubeMap);
GLint w = 0, h = 0;
for (unsigned i = 0; i < 6; i++)
@ -932,7 +1274,9 @@ void IrrDriver::generateSkyboxCubemap()
}
const unsigned texture_permutation[] = { 2, 3, 0, 1, 5, 4 };
char *rgba = new char[w * h * 4];
char *rgba[6];
for (unsigned i = 0; i < 6; i++)
rgba[i] = new char[w * h * 4];
for (unsigned i = 0; i < 6; i++)
{
unsigned idx = texture_permutation[i];
@ -945,13 +1289,22 @@ void IrrDriver::generateSkyboxCubemap()
);
SkyboxTextures[idx]->unlock();
image->copyToScaling(rgba, w, h);
image->copyToScaling(rgba[i], w, h);
image->drop();
glBindTexture(GL_TEXTURE_CUBE_MAP, SkyboxCubeMap);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba);
image->drop();
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
}
delete[] rgba;
testSH(rgba, w, h, blueSHCoeff, greenSHCoeff, redSHCoeff);
for (unsigned i = 0; i < 6; i++)
{
glBindTexture(GL_TEXTURE_CUBE_MAP, ConvolutedSkyboxCubeMap);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
}
for (unsigned i = 0; i < 6; i++)
delete[] rgba[i];
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
@ -981,7 +1334,7 @@ void IrrDriver::renderSkybox()
transform.getInverse(invtransform);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, SkyboxCubeMap);
glBindTexture(GL_TEXTURE_CUBE_MAP, ConvolutedSkyboxCubeMap);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glUseProgram(MeshShader::SkyboxShader::Program);

View File

@ -230,6 +230,7 @@ void Shaders::loadShaders()
FullScreenShader::PointLightShader::init();
FullScreenShader::SSAOShader::init();
FullScreenShader::SunLightShader::init();
FullScreenShader::DiffuseEnvMapShader::init();
FullScreenShader::ShadowedSunLightShader::init();
FullScreenShader::MotionBlurShader::init();
FullScreenShader::GodFadeShader::init();
@ -1400,6 +1401,31 @@ namespace FullScreenShader
glUniform1i(uniform_dtex, TU_dtex);
}
GLuint DiffuseEnvMapShader::Program;
GLuint DiffuseEnvMapShader::uniform_ntex;
GLuint DiffuseEnvMapShader::uniform_blueLmn;
GLuint DiffuseEnvMapShader::uniform_greenLmn;
GLuint DiffuseEnvMapShader::uniform_redLmn;
GLuint DiffuseEnvMapShader::vao;
void DiffuseEnvMapShader::init()
{
Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/diffuseenvmap.frag").c_str());
uniform_ntex = glGetUniformLocation(Program, "ntex");
uniform_blueLmn = glGetUniformLocation(Program, "blueLmn[0]");
uniform_greenLmn = glGetUniformLocation(Program, "greenLmn[0]");
uniform_redLmn = glGetUniformLocation(Program, "redLmn[0]");
vao = createVAO(Program);
}
void DiffuseEnvMapShader::setUniforms(const float *blueSHCoeff, const float *greenSHCoeff, const float *redSHCoeff, unsigned TU_ntex)
{
glUniform1i(uniform_ntex, TU_ntex);
glUniform1fv(uniform_blueLmn, 9, blueSHCoeff);
glUniform1fv(uniform_greenLmn, 9, greenSHCoeff);
glUniform1fv(uniform_redLmn, 9, redSHCoeff);
}
GLuint ShadowedSunLightShader::Program;
GLuint ShadowedSunLightShader::uniform_ntex;
GLuint ShadowedSunLightShader::uniform_dtex;

View File

@ -408,6 +408,17 @@ public:
static void setUniforms(const core::vector3df &direction, const core::matrix4 &InvProjMatrix, float r, float g, float b, unsigned TU_ntex, unsigned TU_dtex);
};
class DiffuseEnvMapShader
{
public:
static GLuint Program;
static GLuint uniform_ntex, uniform_blueLmn, uniform_greenLmn, uniform_redLmn;
static GLuint vao;
static void init();
static void setUniforms(const float *blueSHCoeff, const float *greenSHCoeff, const float *redSHCoeff, unsigned TU_ntex);
};
class ShadowedSunLightShader
{
public: