<?xml version="1.0" encoding="UTF-8" standalone="yes"?><oembed><version><![CDATA[1.0]]></version><provider_name><![CDATA[Krzysztof Narkowicz]]></provider_name><provider_url><![CDATA[https://knarkowicz.wordpress.com]]></provider_url><author_name><![CDATA[Krzysztof Narkowicz]]></author_name><author_url><![CDATA[https://knarkowicz.wordpress.com/author/knarkowicz/]]></author_url><title><![CDATA[Unreal Engine 4 gaussian specular&nbsp;normalization]]></title><type><![CDATA[link]]></type><html><![CDATA[<p>Recently Epic did a nice presentation about their new tech: &#8220;<a href="http://www.unrealengine.com/files/misc/The_Technology_Behind_the_Elemental_Demo_16x9_%282%29.pdf">The technology behind Unreal 4 Elemental demo</a>&#8220;. Among a lot of impressive stuff they showed their <a href="http://www.arcsynthesis.org/gltut/Illumination/Tut11%20Gaussian.html">gaussian specular</a> aproximation. Here is a BRDF with U4 specular for <a href="http://www.disneyanimation.com/technology/brdf.html">Disney&#8217;s BRDF explorer</a>:</p>
<pre class="brush: plain; title: ; notranslate" title="">
analytic

::begin parameters
float n 1 512 100
bool normalized 1
::end parameters

::begin shader

vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y )
{
    vec3 H = normalize( L + V );
    float Dot = clamp( dot( N, H ), 0, 1 );
    float Threshold = 0.04;
    float CosAngle = pow( Threshold, 1 / n );
    float NormAngle = ( Dot - 1 ) / ( CosAngle - 1 );
    float D = exp( -NormAngle * NormAngle );

    if ( normalized )
    {
        D *= 0.17287429 + 0.01388682 * n;
    }

    return vec3( D );
}
::end shader
</pre>
<p>This aproximation was tweaked to have less aliasing than the standard Blinn-Phong specular (it has smoother falloff):</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png"><img loading="lazy" data-attachment-id="160" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/unrealgaussian16/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png" data-orig-size="360,225" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="unrealGaussian16" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png?w=300" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png?w=360" class="aligncenter size-full wp-image-160" src="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png?w=360&#038;h=225" alt="unrealGaussian16" width="360" height="225" srcset="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png 360w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png?w=150&amp;h=94 150w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian161.png?w=300&amp;h=188 300w" sizes="(max-width: 360px) 100vw, 360px" /></a></p>
<p>&nbsp;</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png"><img loading="lazy" data-attachment-id="161" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/unrealgaussian64/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png" data-orig-size="360,225" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="unrealGaussian64" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png?w=300" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png?w=360" class="aligncenter size-full wp-image-161" src="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png?w=360&#038;h=225" alt="unrealGaussian64" width="360" height="225" srcset="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png 360w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png?w=150&amp;h=94 150w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian641.png?w=300&amp;h=188 300w" sizes="(max-width: 360px) 100vw, 360px" /></a></p>
<p>&nbsp;</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png"><img loading="lazy" data-attachment-id="162" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/unrealgaussian128/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png" data-orig-size="360,225" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="unrealGaussian128" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png?w=300" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png?w=360" class="aligncenter size-full wp-image-162" src="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png?w=360&#038;h=225" alt="unrealGaussian128" width="360" height="225" srcset="https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png 360w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png?w=150&amp;h=94 150w, https://knarkowicz.files.wordpress.com/2012/10/unrealgaussian1281.png?w=300&amp;h=188 300w" sizes="(max-width: 360px) 100vw, 360px" /></a></p>
<p>Mentioned presentation doesn&#8217;t include a normalization factor for it. It was a nice excuse for spending some time with Mathematica and try to derive it myself.</p>
<p>Basic idea of normalization factor is that lighting needs to be energy conserving (outgoing energy can&#8217;t be greater than incoming energy). This means that integral of BRDF times cos(theta) over upper hemisphere can&#8217;t exceed 1 or more specifically in our case we want it to be equal 1:</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/integral11.png"><img loading="lazy" data-attachment-id="163" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/integral1/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/integral11.png" data-orig-size="160,43" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="integral1" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/integral11.png?w=160" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/integral11.png?w=160" class="aligncenter size-full wp-image-163" src="https://knarkowicz.files.wordpress.com/2012/10/integral11.png?w=160&#038;h=43" alt="integral1" width="160" height="43" srcset="https://knarkowicz.files.wordpress.com/2012/10/integral11.png 160w, https://knarkowicz.files.wordpress.com/2012/10/integral11.png?w=150&amp;h=40 150w" sizes="(max-width: 160px) 100vw, 160px" /></a></p>
<p>The highest values will be when light direction equals normal (L=N). This means that we can replace dot(N,H) with cos(theta/2), as now angle between H (halfway vector) and N equals to half of angle between L and N. This greatly simplifies the integral. Now we can replace the f(l,v) with U4 gaussian aproximation:</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/integral21.png"><img loading="lazy" data-attachment-id="164" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/integral2/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/integral21.png" data-orig-size="190,52" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="integral2" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/integral21.png?w=190" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/integral21.png?w=190" class="aligncenter size-full wp-image-164" src="https://knarkowicz.files.wordpress.com/2012/10/integral21.png?w=190&#038;h=52" alt="integral2" width="190" height="52" srcset="https://knarkowicz.files.wordpress.com/2012/10/integral21.png 190w, https://knarkowicz.files.wordpress.com/2012/10/integral21.png?w=150&amp;h=41 150w" sizes="(max-width: 190px) 100vw, 190px" /></a></p>
<p>Unfortunately neither I nor Mathematica could solve it analytically. So I had to calculate values numerically and try to fit various simple functions over range [1;512]. The best aproximation which I could find was: 0.17287429 + 0.01388682 * n. Where n is Blinn-Phong specular power.</p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png"><img loading="lazy" data-attachment-id="165" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/aprox64/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png" data-orig-size="550,323" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="aprox64" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png?w=300" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png?w=550" class="aligncenter size-full wp-image-165" src="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png?w=550&#038;h=323" alt="aprox64" width="550" height="323" srcset="https://knarkowicz.files.wordpress.com/2012/10/aprox641.png 550w, https://knarkowicz.files.wordpress.com/2012/10/aprox641.png?w=150&amp;h=88 150w, https://knarkowicz.files.wordpress.com/2012/10/aprox641.png?w=300&amp;h=176 300w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p><a href="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png"><img loading="lazy" data-attachment-id="166" data-permalink="https://knarkowicz.wordpress.com/2012/10/06/unreal-engine-4-gaussian-specular-normalization/aprox512/" data-orig-file="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png" data-orig-size="550,329" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="aprox512" data-image-description="" data-image-caption="" data-medium-file="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?w=300" data-large-file="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?w=550" class="aligncenter size-full wp-image-166" src="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?w=550&#038;h=329" alt="aprox512" width="550" height="329" srcset="https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png 550w, https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?w=150&amp;h=90 150w, https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?w=300&amp;h=179 300w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>As you can see it isn&#8217;t accurate for small specular power values, but on the other hand it&#8217;s very fast and specular power below 16 aren&#8217;t used often.</p>
]]></html><thumbnail_url><![CDATA[https://knarkowicz.files.wordpress.com/2012/10/aprox5121.png?fit=440%2C330]]></thumbnail_url><thumbnail_width><![CDATA[440]]></thumbnail_width><thumbnail_height><![CDATA[263]]></thumbnail_height></oembed>