Implement specular IBL properly
This commit is contained in:
parent
c2d7356d05
commit
8f3b8cf448
@ -50,9 +50,8 @@ void main(void)
|
|||||||
sampleDirection = (InverseViewMatrix * vec4(sampleDirection, 0.)).xyz;
|
sampleDirection = (InverseViewMatrix * vec4(sampleDirection, 0.)).xyz;
|
||||||
|
|
||||||
float specval = texture(ntex, uv).z;
|
float specval = texture(ntex, uv).z;
|
||||||
// From http://graphics.cs.williams.edu/papers/EnvMipReport2013/
|
// Assume 8 level of lod (ie 256x256 texture)
|
||||||
int texSize = textureSize(tex, 0).x;
|
float lodval = 8. * (1. - specval);
|
||||||
float lodval = clamp(log2(texSize * sqrt(3.)) - (5. * specval + 1.), 0., 10.);
|
|
||||||
vec4 specular = textureLod(tex, sampleDirection, lodval);
|
vec4 specular = textureLod(tex, sampleDirection, lodval);
|
||||||
Spec = max(specular, vec4(0.));
|
Spec = max(specular, vec4(0.));
|
||||||
}
|
}
|
||||||
|
30
data/shaders/importance_sampling_specular.frag
Normal file
30
data/shaders/importance_sampling_specular.frag
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
uniform samplerCube tex;
|
||||||
|
uniform float samples[2048];
|
||||||
|
uniform float ViewportSize;
|
||||||
|
|
||||||
|
uniform mat4 PermutationMatrix;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec2 uv = gl_FragCoord.xy / ViewportSize;
|
||||||
|
vec3 RayDir = 2. * vec3(uv, 1.) - 1.;
|
||||||
|
RayDir = normalize((PermutationMatrix * vec4(RayDir, 0.)).xyz);
|
||||||
|
|
||||||
|
vec4 FinalColor = vec4(0.);
|
||||||
|
vec3 up = (RayDir.y < .99) ? vec3(0., 1., 0.) : vec3(0., 0., 1.);
|
||||||
|
vec3 Tangent = normalize(cross(up, RayDir));
|
||||||
|
vec3 Bitangent = cross(RayDir, Tangent);
|
||||||
|
|
||||||
|
for (int i = 0; i < 1024; i++)
|
||||||
|
{
|
||||||
|
float Theta = samples[2 * i];
|
||||||
|
float Phi = samples[2 * i + 1];
|
||||||
|
|
||||||
|
vec3 sampleDir = cos(Theta) * RayDir + sin(Theta) * cos(Phi) * Tangent + sin(Theta) * sin(Phi) * Bitangent;
|
||||||
|
FinalColor += textureLod(tex, sampleDir, 0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
FragColor = FinalColor / 1024.;
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#include "IBL.hpp"
|
#include "IBL.hpp"
|
||||||
#include "gl_headers.hpp"
|
#include "gl_headers.hpp"
|
||||||
|
#include "shaders.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z)
|
static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z)
|
||||||
{
|
{
|
||||||
@ -203,4 +205,108 @@ void SphericalHarmonics(Color *CubemapFace[6], size_t edge_size, float *blueSHCo
|
|||||||
delete[] Y21[face];
|
delete[] Y21[face];
|
||||||
delete[] Y22[face];
|
delete[] Y22[face];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
|
||||||
|
/** Returns the index-th pair from Hammersley set of pseudo random set.
|
||||||
|
Hammersley set is a uniform distribution between 0 and 1 for 2 components.
|
||||||
|
We use the natural indexation on the set to avoid storing the whole set.
|
||||||
|
\param index of the pair
|
||||||
|
\param size of the set. */
|
||||||
|
std::pair<float, float> HammersleySequence(int index, int samples)
|
||||||
|
{
|
||||||
|
float InvertedBinaryRepresentation = 0.;
|
||||||
|
for (size_t i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
InvertedBinaryRepresentation += ((index >> i) & 0x1) * powf(.5, (float) (i + 1.));
|
||||||
|
}
|
||||||
|
return std::make_pair(float(index) / float(samples), InvertedBinaryRepresentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns a pseudo random (theta, phi) generated from a probability density function modeled after Phong function.
|
||||||
|
\param a pseudo random float pair from a uniform density function between 0 and 1.
|
||||||
|
\param exponent from the Phong formula. */
|
||||||
|
std::pair<float, float> ImportanceSamplingPhong(std::pair<float, float> Seeds, float exponent)
|
||||||
|
{
|
||||||
|
return std::make_pair(acosf(powf(Seeds.first, 1.f / (exponent + 1.f))), 2.f * 3.14f * Seeds.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
core::matrix4 getPermutationMatrix(size_t indexX, float valX, size_t indexY, float valY, size_t indexZ, float valZ)
|
||||||
|
{
|
||||||
|
core::matrix4 resultMat;
|
||||||
|
float *M = resultMat.pointer();
|
||||||
|
memset(M, 0, 16 * sizeof(float));
|
||||||
|
assert(indexX < 4);
|
||||||
|
assert(indexY < 4);
|
||||||
|
assert(indexZ < 4);
|
||||||
|
M[indexX] = valX;
|
||||||
|
M[4 + indexY] = valY;
|
||||||
|
M[8 + indexZ] = valZ;
|
||||||
|
return resultMat;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint generateSpecularCubemap(GLuint probe)
|
||||||
|
{
|
||||||
|
GLuint cubemap_texture;
|
||||||
|
|
||||||
|
glGenTextures(1, &cubemap_texture);
|
||||||
|
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);
|
||||||
|
size_t cubemap_size = 256;
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F, cubemap_size, cubemap_size, 0, GL_BGRA, GL_FLOAT, 0);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||||
|
|
||||||
|
GLuint fbo;
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glViewport(0, 0, cubemap_size, cubemap_size);
|
||||||
|
GLenum bufs[] = { GL_COLOR_ATTACHMENT0 };
|
||||||
|
glDrawBuffers(1, bufs);
|
||||||
|
glUseProgram(UtilShader::SpecularIBLGenerator::getInstance()->Program);
|
||||||
|
glBindVertexArray(SharedObject::FullScreenQuadVAO);
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
|
||||||
|
core::matrix4 M[6] = {
|
||||||
|
getPermutationMatrix(2, -1., 1, -1., 0, 1.),
|
||||||
|
getPermutationMatrix(2, 1., 1, -1., 0, -1.),
|
||||||
|
getPermutationMatrix(0, 1., 2, 1., 1, 1.),
|
||||||
|
getPermutationMatrix(0, 1., 2, -1., 1, -1.),
|
||||||
|
getPermutationMatrix(0, 1., 1, -1., 2, 1.),
|
||||||
|
getPermutationMatrix(0, -1., 1, -1., 2, -1.),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned level = 0; level < 8; level++)
|
||||||
|
{
|
||||||
|
// Blinn Phong can be approximated by Phong with 4x the specular coefficient
|
||||||
|
// See http://seblagarde.wordpress.com/2012/03/29/relationship-between-phong-and-blinn-lighting-model/
|
||||||
|
float roughness = (8 - level) * 4 * pow(2., 10.) / 8.;
|
||||||
|
float viewportSize = 1 << (8 - level);
|
||||||
|
|
||||||
|
std::vector<float> Samples;
|
||||||
|
for (unsigned i = 0; i < 1024; i++)
|
||||||
|
{
|
||||||
|
std::pair<float, float> sample = ImportanceSamplingPhong(HammersleySequence(i, 1024), roughness);
|
||||||
|
Samples.push_back(sample.first);
|
||||||
|
Samples.push_back(sample.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned face = 0; face < 6; face++)
|
||||||
|
{
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubemap_texture, level);
|
||||||
|
GLuint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
assert(status == GL_FRAMEBUFFER_COMPLETE);
|
||||||
|
|
||||||
|
UtilShader::SpecularIBLGenerator::getInstance()->SetTextureUnits(probe);
|
||||||
|
UtilShader::SpecularIBLGenerator::getInstance()->setUniforms(M[face], Samples, viewportSize);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteFramebuffers(1, &fbo);
|
||||||
|
return cubemap_texture;
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef IBL_HPP
|
#ifndef IBL_HPP
|
||||||
#define IBL_HPP
|
#define IBL_HPP
|
||||||
|
|
||||||
|
#include "gl_headers.hpp"
|
||||||
|
|
||||||
struct Color
|
struct Color
|
||||||
{
|
{
|
||||||
float Red;
|
float Red;
|
||||||
@ -14,4 +16,6 @@ using the cubemap provided by CubemapFace.
|
|||||||
* \param row/columns count of textures.
|
* \param row/columns count of textures.
|
||||||
*/
|
*/
|
||||||
void SphericalHarmonics(Color *CubemapFace[6], size_t edge_size, float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff);
|
void SphericalHarmonics(Color *CubemapFace[6], size_t edge_size, float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff);
|
||||||
|
|
||||||
|
GLuint generateSpecularCubemap(GLuint probe);
|
||||||
#endif
|
#endif
|
@ -1365,6 +1365,7 @@ scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*> &tex
|
|||||||
SkyboxTextures = texture;
|
SkyboxTextures = texture;
|
||||||
SphericalHarmonicsTextures = sphericalHarmonics;
|
SphericalHarmonicsTextures = sphericalHarmonics;
|
||||||
SkyboxCubeMap = 0;
|
SkyboxCubeMap = 0;
|
||||||
|
SkyboxSpecularProbe = 0;
|
||||||
m_SH_dirty = true;
|
m_SH_dirty = true;
|
||||||
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
|
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
|
||||||
texture[2], texture[3],
|
texture[2], texture[3],
|
||||||
@ -1377,8 +1378,12 @@ void IrrDriver::suppressSkyBox()
|
|||||||
SphericalHarmonicsTextures.clear();
|
SphericalHarmonicsTextures.clear();
|
||||||
m_SH_dirty = true;
|
m_SH_dirty = true;
|
||||||
if ((SkyboxCubeMap) && (!ProfileWorld::isNoGraphics()))
|
if ((SkyboxCubeMap) && (!ProfileWorld::isNoGraphics()))
|
||||||
|
{
|
||||||
glDeleteTextures(1, &SkyboxCubeMap);
|
glDeleteTextures(1, &SkyboxCubeMap);
|
||||||
|
glDeleteTextures(1, &SkyboxSpecularProbe);
|
||||||
|
}
|
||||||
SkyboxCubeMap = 0;
|
SkyboxCubeMap = 0;
|
||||||
|
SkyboxSpecularProbe = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -254,6 +254,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
GLuint SkyboxCubeMap;
|
GLuint SkyboxCubeMap;
|
||||||
|
GLuint SkyboxSpecularProbe;
|
||||||
/** A simple class to store video resolutions. */
|
/** A simple class to store video resolutions. */
|
||||||
class VideoMode
|
class VideoMode
|
||||||
{
|
{
|
||||||
|
@ -155,7 +155,7 @@ void IrrDriver::renderLights(unsigned pointlightcount, bool hasShadow)
|
|||||||
|
|
||||||
{
|
{
|
||||||
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_ENVMAP));
|
ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_ENVMAP));
|
||||||
m_post_processing->renderEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff, SkyboxCubeMap);
|
m_post_processing->renderEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff, SkyboxSpecularProbe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render sunlight if and only if track supports shadow
|
// Render sunlight if and only if track supports shadow
|
||||||
|
@ -280,6 +280,7 @@ void IrrDriver::generateSkyboxCubemap()
|
|||||||
|
|
||||||
assert(SkyboxTextures.size() == 6);
|
assert(SkyboxTextures.size() == 6);
|
||||||
SkyboxCubeMap = generateCubeMapFromTextures(SkyboxTextures);
|
SkyboxCubeMap = generateCubeMapFromTextures(SkyboxTextures);
|
||||||
|
SkyboxSpecularProbe = generateSpecularCubemap(SkyboxCubeMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrrDriver::generateDiffuseCoefficients()
|
void IrrDriver::generateDiffuseCoefficients()
|
||||||
@ -322,8 +323,8 @@ void IrrDriver::generateDiffuseCoefficients()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int sh_w = 16;
|
sh_w = 16;
|
||||||
int sh_h = 16;
|
sh_h = 16;
|
||||||
|
|
||||||
video::SColor ambient = m_scene_manager->getAmbientLight().toSColor();
|
video::SColor ambient = m_scene_manager->getAmbientLight().toSColor();
|
||||||
|
|
||||||
|
@ -889,6 +889,18 @@ unsigned getGLSLVersion()
|
|||||||
return irr_driver->getGLSLVersion();
|
return irr_driver->getGLSLVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace UtilShader
|
||||||
|
{
|
||||||
|
SpecularIBLGenerator::SpecularIBLGenerator()
|
||||||
|
{
|
||||||
|
Program = LoadProgram(OBJECT,
|
||||||
|
GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
|
||||||
|
GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/importance_sampling_specular.frag").c_str());
|
||||||
|
AssignUniforms("PermutationMatrix", "samples[0]", "ViewportSize");
|
||||||
|
AssignSamplerNames(Program, 0, "tex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace MeshShader
|
namespace MeshShader
|
||||||
{
|
{
|
||||||
// Solid Normal and depth pass shaders
|
// Solid Normal and depth pass shaders
|
||||||
|
@ -48,6 +48,12 @@ public:
|
|||||||
static void init();
|
static void init();
|
||||||
static void setUniforms(const irr::video::SColor &);
|
static void setUniforms(const irr::video::SColor &);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SpecularIBLGenerator : public ShaderHelperSingleton<SpecularIBLGenerator, core::matrix4, std::vector<float>, float >, public TextureRead<Trilinear_cubemap>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SpecularIBLGenerator();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user