|
@@ -178,6 +178,139 @@ namespace bs { namespace ct
|
|
|
mLightOffsets[2] = mLightOffsets[1] + lightData.getNumSpotLights();
|
|
mLightOffsets[2] = mLightOffsets[1] + lightData.getNumSpotLights();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Reverse bits functions used for Hammersley sequence
|
|
|
|
|
+ float reverseBits(UINT32 bits)
|
|
|
|
|
+ {
|
|
|
|
|
+ bits = (bits << 16u) | (bits >> 16u);
|
|
|
|
|
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
|
|
|
|
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
|
|
|
|
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
|
|
|
|
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
|
|
|
|
+
|
|
|
|
|
+ return float(bits) * 2.3283064365386963e-10; // 0x100000000
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void hammersleySequence(UINT32 i, UINT32 count, float& e0, float& e1)
|
|
|
|
|
+ {
|
|
|
|
|
+ e0 = i / (float)count;
|
|
|
|
|
+ e1 = reverseBits(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Vector3 sphericalToCartesian(float cosTheta, float sinTheta, float phi)
|
|
|
|
|
+ {
|
|
|
|
|
+ Vector3 output;
|
|
|
|
|
+ output.x = sinTheta * cos(phi);
|
|
|
|
|
+ output.y = sinTheta * sin(phi);
|
|
|
|
|
+ output.z = cosTheta;
|
|
|
|
|
+
|
|
|
|
|
+ return output;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Generates an angle in spherical coordinates, importance sampled for the specified roughness based on some uniformly
|
|
|
|
|
+ // distributed random variables in range [0, 1].
|
|
|
|
|
+ void importanceSampleGGX(float e0, float e1, float roughness4, float& cosTheta, float& phi)
|
|
|
|
|
+ {
|
|
|
|
|
+ // See GGXImportanceSample.nb for derivation (essentially, take base GGX, normalize it, generate PDF, split PDF into
|
|
|
|
|
+ // marginal probability for theta and conditional probability for phi. Plug those into the CDF, invert it.)
|
|
|
|
|
+ cosTheta = sqrt((1.0f - e0) / (1.0f + (roughness4 - 1.0f) * e1));
|
|
|
|
|
+ phi = 2.0f * Math::PI * e1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ float calcMicrofacetShadowingSmithGGX(float roughness4, float NoV, float NoL)
|
|
|
|
|
+ {
|
|
|
|
|
+ // Note: See lighting shader for derivation. Includes microfacet BRDF divisor.
|
|
|
|
|
+ float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
|
|
|
|
|
+ float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
|
|
|
|
|
+ return 1.0f / (g1V * g1L);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ SPtr<Texture> TiledDeferredLighting::generatePreintegratedEnvBRDF()
|
|
|
|
|
+ {
|
|
|
|
|
+ TEXTURE_DESC desc;
|
|
|
|
|
+ desc.type = TEX_TYPE_2D;
|
|
|
|
|
+ desc.format = PF_FLOAT16_RG;
|
|
|
|
|
+ desc.width = 128;
|
|
|
|
|
+ desc.height = 32;
|
|
|
|
|
+
|
|
|
|
|
+ SPtr<Texture> texture = Texture::create(desc);
|
|
|
|
|
+ PixelData pixelData = texture->lock(GBL_WRITE_ONLY_DISCARD);
|
|
|
|
|
+
|
|
|
|
|
+ UINT16* rawPixels = (UINT16*)pixelData.getData();
|
|
|
|
|
+ UINT32 rowPitch = pixelData.getRowPitch() * PixelUtil::getNumElemBytes(desc.format);
|
|
|
|
|
+
|
|
|
|
|
+ for (UINT32 y = 0; y < desc.height; y++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float roughness = (float)(y + 0.5f) / desc.height;
|
|
|
|
|
+ float m = roughness * roughness;
|
|
|
|
|
+ float m2 = m*m;
|
|
|
|
|
+
|
|
|
|
|
+ for (UINT32 x = 0; x < desc.width; x++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float NoV = (float)(x + 0.5f) / desc.width;
|
|
|
|
|
+
|
|
|
|
|
+ Vector3 V;
|
|
|
|
|
+ V.x = sqrt(1.0f - NoV * NoV); // sine
|
|
|
|
|
+ V.y = 0.0f;
|
|
|
|
|
+ V.z = NoV;
|
|
|
|
|
+
|
|
|
|
|
+ // These are the two integrals resulting from the second part of the split-sum approximation. Described in
|
|
|
|
|
+ // Epic's 2013 paper:
|
|
|
|
|
+ // http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
|
|
|
|
|
+ float scale = 0.0f;
|
|
|
|
|
+ float offset = 0.0f;
|
|
|
|
|
+
|
|
|
|
|
+ // We use the same importance sampling function we use for reflection cube importance sampling, only we
|
|
|
|
|
+ // sample G and F, instead of D factors of the microfactet BRDF. See GGXImportanceSample.nb for derivation.
|
|
|
|
|
+ constexpr UINT32 NumSamples = 128;
|
|
|
|
|
+ for (UINT32 i = 0; i < NumSamples; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ float e0, e1;
|
|
|
|
|
+ hammersleySequence(i, NumSamples, e0, e1);
|
|
|
|
|
+
|
|
|
|
|
+ float cosTheta, phi;
|
|
|
|
|
+ importanceSampleGGX(e0, e1, m2, cosTheta, phi);
|
|
|
|
|
+
|
|
|
|
|
+ float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
|
|
|
|
|
+ Vector3 H = sphericalToCartesian(cosTheta, sinTheta, phi);
|
|
|
|
|
+ Vector3 L = 2.0f * Vector3::dot(V, H) * H - V;
|
|
|
|
|
+
|
|
|
|
|
+ float VoH = std::max(Vector3::dot(V, H), 0.0f);
|
|
|
|
|
+ float NoL = std::max(L.z, 0.0f); // N assumed (0, 0, 1)
|
|
|
|
|
+ float NoH = std::max(H.z, 0.0f); // N assumed (0, 0, 1)
|
|
|
|
|
+
|
|
|
|
|
+ // Set second part of the split sum integral is split into two parts:
|
|
|
|
|
+ // F0*I[G * (1 - (1 - v.h)^5) * cos(theta)] + I[G * (1 - v.h)^5 * cos(theta)] (F0 * scale + bias)
|
|
|
|
|
+
|
|
|
|
|
+ // We calculate the fresnel scale (1 - (1 - v.h)^5) and bias ((1 - v.h)^5) parts
|
|
|
|
|
+ float fc = pow(1.0f - VoH, 5.0f);
|
|
|
|
|
+ float fresnelScale = 1.0f - fc;
|
|
|
|
|
+ float fresnelOffset = fc;
|
|
|
|
|
+
|
|
|
|
|
+ // We calculate the G part
|
|
|
|
|
+ float G = calcMicrofacetShadowingSmithGGX(m2, NoV, NoL);
|
|
|
|
|
+
|
|
|
|
|
+ // Note: PDF?
|
|
|
|
|
+ if (NoL > 0.0f)
|
|
|
|
|
+ {
|
|
|
|
|
+ scale += NoL * G * fresnelScale;
|
|
|
|
|
+ offset += NoL * G * fresnelOffset;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ scale /= NumSamples;
|
|
|
|
|
+ offset /= NumSamples;
|
|
|
|
|
+
|
|
|
|
|
+ UINT16* dest = rawPixels + x * 2 + y * rowPitch;
|
|
|
|
|
+ dest[0] = (UINT16)(Math::clamp01(scale) * 65535.0f + 0.5f);
|
|
|
|
|
+ dest[1] = (UINT16)(Math::clamp01(offset) * 65535.0f + 0.5f);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ texture->unlock();
|
|
|
|
|
+
|
|
|
|
|
+ return texture;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
template<int MSAA_COUNT>
|
|
template<int MSAA_COUNT>
|
|
|
TTiledDeferredLightingMat<MSAA_COUNT>::TTiledDeferredLightingMat()
|
|
TTiledDeferredLightingMat<MSAA_COUNT>::TTiledDeferredLightingMat()
|
|
|
:mInternal(mMaterial, mParamsSet, MSAA_COUNT)
|
|
:mInternal(mMaterial, mParamsSet, MSAA_COUNT)
|