Fixed GGC radiance filtering.

This commit is contained in:
Branimir Karadžić
2018-07-17 14:43:31 -07:00
parent ef8639ca40
commit 8f382eb408

View File

@@ -356,6 +356,7 @@ namespace bimg
// Reference: // Reference:
// - https://web.archive.org/web/20180614195754/http://www.mpia.de/~mathar/public/mathar20051002.pdf // - https://web.archive.org/web/20180614195754/http://www.mpia.de/~mathar/public/mathar20051002.pdf
// - https://web.archive.org/web/20180614195725/http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/ // - https://web.archive.org/web/20180614195725/http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
//
const float x0 = _u - _invFaceSize; const float x0 = _u - _invFaceSize;
const float x1 = _u + _invFaceSize; const float x1 = _u + _invFaceSize;
const float y0 = _v - _invFaceSize; const float y0 = _v - _invFaceSize;
@@ -665,7 +666,7 @@ namespace bimg
{ {
Sampler(const ImageContainer& _image, uint16_t _side, float _lod, float (*func)(float) ) Sampler(const ImageContainer& _image, uint16_t _side, float _lod, float (*func)(float) )
{ {
const float lod = bx::clamp(_lod, 0.0f, 1.0f) * (_image.m_numMips - 1); const float lod = bx::clamp(_lod, 0.0f, float(_image.m_numMips - 1) );
imageGetRawData( imageGetRawData(
_image _image
, _side , _side
@@ -761,7 +762,7 @@ namespace bimg
_rgba[3] = bx::lerp(rgbaA[3], rgbaB[3], fl); _rgba[3] = bx::lerp(rgbaA[3], rgbaB[3], fl);
} }
void importanceSampleGgx(float* _result, float _u, float _v, float _roughness, const float* _normal) void importanceSampleGgx(float* _result, float _u, float _v, float _roughness, const float* _normal, const float* _tangentX, const float* _tangentY)
{ {
const float aa = bx::square(_roughness); const float aa = bx::square(_roughness);
const float phi = bx::kPi2 * _u; const float phi = bx::kPi2 * _u;
@@ -775,21 +776,9 @@ namespace bimg
cosTheta, cosTheta,
}; };
float up[3] = { 0.0f, 0.0f, 0.0f }; _result[0] = _tangentX[0] * hh[0] + _tangentY[0] * hh[1] + _normal[0] * hh[2];
up[bx::abs(_normal[2]) < 0.999f ? 2 : 0] = 1.0f; _result[1] = _tangentX[1] * hh[0] + _tangentY[1] * hh[1] + _normal[1] * hh[2];
_result[2] = _tangentX[2] * hh[0] + _tangentY[2] * hh[1] + _normal[2] * hh[2];
float right[3];
bx::vec3Cross(right, up, _normal);
float tangentX[3];
bx::vec3Norm(tangentX, right);
float tangentY[3];
bx::vec3Cross(tangentY, _normal, tangentX);
_result[0] = tangentX[0] * hh[0] + tangentY[0] * hh[1] + _normal[0] * hh[2];
_result[1] = tangentX[1] * hh[0] + tangentY[1] * hh[1] + _normal[1] * hh[2];
_result[2] = tangentX[2] * hh[0] + tangentY[2] * hh[1] + _normal[2] * hh[2];
} }
float normalDistributionGgx(float _ndoth, float _roughness) float normalDistributionGgx(float _ndoth, float _roughness)
@@ -814,25 +803,35 @@ namespace bimg
const uint32_t bpp = getBitsPerPixel(_image.m_format); const uint32_t bpp = getBitsPerPixel(_image.m_format);
constexpr int32_t kNumSamples = 64; constexpr int32_t kNumSamples = 512;
const uint32_t pitch = mip.m_width*bpp/8; const uint32_t pitch = mip.m_width*bpp/8;
const float widthMinusOne = float(mip.m_width-1); const float widthMinusOne = float(mip.m_width-1);
const float mipBias = 0.5f*bx::log2(bx::square(float(mip.m_width) )/float(kNumSamples) ); const float mipBias = 0.5f*bx::log2(bx::square(float(_image.m_width) )/float(kNumSamples) );
UnpackFn unpack = getUnpack(_image.m_format); UnpackFn unpack = getUnpack(_image.m_format);
float color[3] = { 0.0f, 0.0f, 0.0f }; float color[3] = { 0.0f, 0.0f, 0.0f };
float totalWeight = 0.0f; float totalWeight = 0.0f;
bx::RngMwc mwc; // Golden Ratio Sequences for Low-Discrepancy Sampling
// https://web.archive.org/web/20180717194847/https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
//
// kGoldenSection = (0.5f*bx::sqrt(5.0f) + 0.5f) - 1.0f = 0.61803398875f
//
const float kGoldenSection = 0.61803398875f;
float offset = kGoldenSection;
float tangentX[3];
float tangentY[3];
bx::vec3TangentFrame(_dir, tangentX, tangentY);
for (uint32_t ii = 0; ii < kNumSamples; ++ii) for (uint32_t ii = 0; ii < kNumSamples; ++ii)
{ {
const float uu = ii/float(kNumSamples); offset += kGoldenSection;
const float vv = bx::frnd(&mwc); const float vv = ii/float(kNumSamples);
float hh[3]; float hh[3];
importanceSampleGgx(hh, uu, vv, _roughness, _dir); importanceSampleGgx(hh, offset, vv, _roughness, _dir, tangentX, tangentY);
const float ddoth2 = 2.0f * bx::vec3Dot(_dir, hh); const float ddoth2 = 2.0f * bx::vec3Dot(_dir, hh);
@@ -845,30 +844,49 @@ namespace bimg
if (ndotl > 0.0f) if (ndotl > 0.0f)
{ {
const float ndoth = ndotl; const float ndoth = bx::clamp(bx::vec3Dot(_dir, hh), 0.0f, 1.0f);
const float vdoth = ndotl; const float vdoth = ndoth;
// Chapter 20. GPU-Based Importance Sampling // Chapter 20. GPU-Based Importance Sampling
// http://archive.today/2018.07.14-004914/https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html // http://archive.today/2018.07.14-004914/https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
//
const float pdf = normalDistributionGgx(ndoth, _roughness) * ndoth / (4.0f * vdoth); const float pdf = normalDistributionGgx(ndoth, _roughness) * ndoth / (4.0f * vdoth);
const float lod = bx::max(0.0f, mipBias - 0.5f*bx::log2(pdf) ); const float lod = bx::max(0.0f, mipBias - 0.5f*bx::log2(pdf));
float rgba[4]; float rgba[4];
sampleCubeMap(rgba, _image, ll, lod); sampleCubeMap(rgba, _image, ll, lod);
color[0] += rgba[0] * ndotl; // Optimized Reversible Tonemapper for Resolve
color[1] += rgba[1] * ndotl; // https://web.archive.org/web/20180717182019/https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
color[2] += rgba[2] * ndotl; // "a single sample with a large HDR value can over-power all other samples"
// "instead accept a bias in the resolve and reduce the weighting of samples
// as a function of how bright they are"
// Include ndotl here to "fold the weighting into the tonemap operation"
//
const float tm = ndotl / (bx::max(rgba[0], rgba[1], rgba[2]) + 1.0f);
color[0] += rgba[0] * tm;
color[1] += rgba[1] * tm;
color[2] += rgba[2] * tm;
totalWeight += ndotl; totalWeight += ndotl;
} }
} }
if (0.0f < totalWeight) if (0.0f < totalWeight)
{ {
// Optimized Reversible Tonemapper for Resovle
// https://web.archive.org/web/20180717182019/https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
// Average, then reverse the tonemapper
//
const float invWeight = 1.0f/totalWeight; const float invWeight = 1.0f/totalWeight;
_result[0] = color[0] * invWeight; color[0] = color[0] * invWeight;
_result[1] = color[1] * invWeight; color[1] = color[1] * invWeight;
_result[2] = color[2] * invWeight; color[2] = color[2] * invWeight;
const float invTm = 1.0f / (1.0f - bx::max(0.00001f, bx::max(color[0], color[1], color[2])));
_result[0] = color[0] * invTm;
_result[1] = color[1] * invTm;
_result[2] = color[2] * invTm;
} }
else else
{ {
@@ -1069,11 +1087,12 @@ namespace bimg
return bx::max(0.0f, 1.0f - _mip/(_mipCount-1.0000001f) ); return bx::max(0.0f, 1.0f - _mip/(_mipCount-1.0000001f) );
} }
float applyLightningModel(float _specularPower, LightingModel::Enum _lightingModel) float applyLightingModel(float _specularPower, LightingModel::Enum _lightingModel)
{ {
// Reference: // Reference:
// - https://web.archive.org/web/20180622232018/https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/ // - https://web.archive.org/web/20180622232018/https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
// - https://web.archive.org/web/20180622232041/https://seblagarde.wordpress.com/2012/03/29/relationship-between-phong-and-blinn-lighting-model/ // - https://web.archive.org/web/20180622232041/https://seblagarde.wordpress.com/2012/03/29/relationship-between-phong-and-blinn-lighting-model/
//
switch (_lightingModel) switch (_lightingModel)
{ {
case LightingModel::PhongBrdf: return _specularPower + 1.0f; case LightingModel::PhongBrdf: return _specularPower + 1.0f;
@@ -1143,7 +1162,7 @@ namespace bimg
const float glossiness = glossinessFor(lod, float(numMips) ); const float glossiness = glossinessFor(lod, float(numMips) );
const float roughness = 1.0f-glossiness; const float roughness = 1.0f-glossiness;
const float specularPowerRef = bx::pow(2.0f, glossiness*glossScale + glossBias); const float specularPowerRef = bx::pow(2.0f, glossiness*glossScale + glossBias);
const float specularPower = applyLightningModel(specularPowerRef, _lightingModel); const float specularPower = applyLightingModel(specularPowerRef, _lightingModel);
const float filterAngle = bx::clamp(cosinePowerFilterAngle(specularPower), minAngle, maxAngle); const float filterAngle = bx::clamp(cosinePowerFilterAngle(specularPower), minAngle, maxAngle);
const float cosAngle = bx::max(0.0f, bx::cos(filterAngle) ); const float cosAngle = bx::max(0.0f, bx::cos(filterAngle) );
const float texelSize = 1.0f/float(dstWidth); const float texelSize = 1.0f/float(dstWidth);