diff --git a/src/image_cubemap_filter.cpp b/src/image_cubemap_filter.cpp index 6392e81..37a44d1 100644 --- a/src/image_cubemap_filter.cpp +++ b/src/image_cubemap_filter.cpp @@ -356,6 +356,7 @@ namespace bimg // Reference: // - 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/ + // const float x0 = _u - _invFaceSize; const float x1 = _u + _invFaceSize; const float y0 = _v - _invFaceSize; @@ -665,7 +666,7 @@ namespace bimg { 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( _image , _side @@ -761,7 +762,7 @@ namespace bimg _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 phi = bx::kPi2 * _u; @@ -775,21 +776,9 @@ namespace bimg cosTheta, }; - float up[3] = { 0.0f, 0.0f, 0.0f }; - up[bx::abs(_normal[2]) < 0.999f ? 2 : 0] = 1.0f; - - 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]; + _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) @@ -814,25 +803,35 @@ namespace bimg 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 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); float color[3] = { 0.0f, 0.0f, 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) { - const float uu = ii/float(kNumSamples); - const float vv = bx::frnd(&mwc); + offset += kGoldenSection; + const float vv = ii/float(kNumSamples); 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); @@ -845,30 +844,49 @@ namespace bimg if (ndotl > 0.0f) { - const float ndoth = ndotl; - const float vdoth = ndotl; + const float ndoth = bx::clamp(bx::vec3Dot(_dir, hh), 0.0f, 1.0f); + const float vdoth = ndoth; // Chapter 20. GPU-Based Importance Sampling // 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 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]; sampleCubeMap(rgba, _image, ll, lod); - color[0] += rgba[0] * ndotl; - color[1] += rgba[1] * ndotl; - color[2] += rgba[2] * ndotl; + // Optimized Reversible Tonemapper for Resolve + // https://web.archive.org/web/20180717182019/https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/ + // "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; } } 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; - _result[0] = color[0] * invWeight; - _result[1] = color[1] * invWeight; - _result[2] = color[2] * invWeight; + color[0] = color[0] * invWeight; + color[1] = color[1] * 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 { @@ -1069,11 +1087,12 @@ namespace bimg 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: // - 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/ + // switch (_lightingModel) { case LightingModel::PhongBrdf: return _specularPower + 1.0f; @@ -1143,7 +1162,7 @@ namespace bimg const float glossiness = glossinessFor(lod, float(numMips) ); const float roughness = 1.0f-glossiness; 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 cosAngle = bx::max(0.0f, bx::cos(filterAngle) ); const float texelSize = 1.0f/float(dstWidth);