I have read course notes and sample codes to use frequency domain normal mapping vMF in The Order 1886.
- "Crafting a Next-Gen Material Pipeline for The Order: 1886" SIGGRAPH 2013
- Specular Antialiasing Sample by MJP
The code for adjusting roughness by length of average normal is simple. Here are some codes quoted from the course notes.
// Quoted from "Crafting a Next-Gen Material Pipeline for The Order: 1886" SIGGRAPH 2013 float r = length(avgNormal); float kappa = 10000.0f; if(r < 1.0f) kappa = (3 * r - r * r * r) / (1 - r * r); // New roughness roughness = sqrt(roughness * roughness + (1.0f / kappa));
But the tricky part is that the "roughness" parameter above code means α the "square of roughness". (α = roughness^2).
Here are some codes of frequency domain normal mapping vMF by MJP.
If you read those code, you will notice that the "roughness" parameter means α (α = roughness^2), because it is finally passed to GGX_Sppecular() and is used as α.
// Quoted from GenerateMap.hlsl // // Specular Antialiasing Sample by MJP // https://mjp.codeplex.com/releases/view/109905 ... // Pre-compute roughness map values // // Roughness means α (α = roughness^2) // vmfRoughness = sqrt(Roughness * Roughness + (1.0f / (2.0f * kappa))); ... // Write the result to the roughness map. // α is converted to roughness by sqrt(). // Roughness map stores roughness not α. OutputRoughnessMap[outputPos] = sqrt(float2(vmfRoughness, toksvigRoughness));
// Quoted from Mesh.hlsl // // Specular Antialiasing Sample by MJP // https://mjp.codeplex.com/releases/view/109905 // #elif UsePrecomputedVMF_ // Fetch roughness from roughness map. roughness = RoughnessMap.Sample(LinearSampler, uv).x; // roughness to α roughness *= roughness; ... // Compute GGX // roughness means α float specular = GGX_Specular(roughness, normal, h, view, lightDir); // the parameter m is α float GGX_Specular(in float m, in float3 n, in float3 h, in float3 v, in float3 l) { float nDotH = saturate(dot(n, h)); float nDotL = saturate(dot(n, l)); float nDotV = saturate(dot(n, v)); float nDotH2 = nDotH * nDotH; float m2 = m * m; // Calculate the distribution term float d = m2 / (Pi * pow(nDotH * nDotH * (m2 - 1) + 1, 2.0f)); // Calculate the matching visibility term float v1i = GGX_V1(m2, nDotL); float v1o = GGX_V1(m2, nDotV); float vis = v1i * v1o; return d * vis; }