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;
|
||||
|
||||
float specval = texture(ntex, uv).z;
|
||||
// From http://graphics.cs.williams.edu/papers/EnvMipReport2013/
|
||||
int texSize = textureSize(tex, 0).x;
|
||||
float lodval = clamp(log2(texSize * sqrt(3.)) - (5. * specval + 1.), 0., 10.);
|
||||
// Assume 8 level of lod (ie 256x256 texture)
|
||||
float lodval = 8. * (1. - specval);
|
||||
vec4 specular = textureLod(tex, sampleDirection, lodval);
|
||||
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 "gl_headers.hpp"
|
||||
#include "shaders.hpp"
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
|
||||
static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z)
|
||||
{
|
||||
@ -204,3 +206,107 @@ void SphericalHarmonics(Color *CubemapFace[6], size_t edge_size, float *blueSHCo
|
||||
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
|
||||
#define IBL_HPP
|
||||
|
||||
#include "gl_headers.hpp"
|
||||
|
||||
struct Color
|
||||
{
|
||||
float Red;
|
||||
@ -14,4 +16,6 @@ using the cubemap provided by CubemapFace.
|
||||
* \param row/columns count of textures.
|
||||
*/
|
||||
void SphericalHarmonics(Color *CubemapFace[6], size_t edge_size, float *blueSHCoeff, float *greenSHCoeff, float *redSHCoeff);
|
||||
|
||||
GLuint generateSpecularCubemap(GLuint probe);
|
||||
#endif
|
@ -1365,6 +1365,7 @@ scene::ISceneNode *IrrDriver::addSkyBox(const std::vector<video::ITexture*> &tex
|
||||
SkyboxTextures = texture;
|
||||
SphericalHarmonicsTextures = sphericalHarmonics;
|
||||
SkyboxCubeMap = 0;
|
||||
SkyboxSpecularProbe = 0;
|
||||
m_SH_dirty = true;
|
||||
return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1],
|
||||
texture[2], texture[3],
|
||||
@ -1377,8 +1378,12 @@ void IrrDriver::suppressSkyBox()
|
||||
SphericalHarmonicsTextures.clear();
|
||||
m_SH_dirty = true;
|
||||
if ((SkyboxCubeMap) && (!ProfileWorld::isNoGraphics()))
|
||||
{
|
||||
glDeleteTextures(1, &SkyboxCubeMap);
|
||||
glDeleteTextures(1, &SkyboxSpecularProbe);
|
||||
}
|
||||
SkyboxCubeMap = 0;
|
||||
SkyboxSpecularProbe = 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -254,6 +254,7 @@ private:
|
||||
|
||||
public:
|
||||
GLuint SkyboxCubeMap;
|
||||
GLuint SkyboxSpecularProbe;
|
||||
/** A simple class to store video resolutions. */
|
||||
class VideoMode
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ void IrrDriver::renderLights(unsigned pointlightcount, bool hasShadow)
|
||||
|
||||
{
|
||||
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
|
||||
|
@ -280,6 +280,7 @@ void IrrDriver::generateSkyboxCubemap()
|
||||
|
||||
assert(SkyboxTextures.size() == 6);
|
||||
SkyboxCubeMap = generateCubeMapFromTextures(SkyboxTextures);
|
||||
SkyboxSpecularProbe = generateSpecularCubemap(SkyboxCubeMap);
|
||||
}
|
||||
|
||||
void IrrDriver::generateDiffuseCoefficients()
|
||||
@ -322,8 +323,8 @@ void IrrDriver::generateDiffuseCoefficients()
|
||||
}
|
||||
else
|
||||
{
|
||||
int sh_w = 16;
|
||||
int sh_h = 16;
|
||||
sh_w = 16;
|
||||
sh_h = 16;
|
||||
|
||||
video::SColor ambient = m_scene_manager->getAmbientLight().toSColor();
|
||||
|
||||
|
@ -889,6 +889,18 @@ unsigned 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
|
||||
{
|
||||
// Solid Normal and depth pass shaders
|
||||
|
@ -48,6 +48,12 @@ public:
|
||||
static void init();
|
||||
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