Branimir Karadžić 7 лет назад
Родитель
Сommit
8f382eb408
1 измененных файлов с 53 добавлено и 34 удалено
  1. 53 34
      src/image_cubemap_filter.cpp

+ 53 - 34
src/image_cubemap_filter.cpp

@@ -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 };
-		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)
 	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);
-			const float vv = bx::frnd(&mwc);
+			offset += kGoldenSection;
+			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 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
 				// 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;
-				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;
 				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;
-			_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
 		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);