<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress/2.2.1" -->
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
	<title>Comments on: Converting RGB to LogLuv in a fragment shader</title>
	<link>http://realtimecollisiondetection.net/blog/?p=15</link>
	<description>Coding wisdom and rants of Christer Ericson</description>
	<pubDate>Tue, 07 Sep 2010 21:04:29 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.2.1</generator>

	<item>
		<title>By: Article: HDR Rendering with XNA &#171; Sgt. Conker</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-3119</link>
		<author>Article: HDR Rendering with XNA &#171; Sgt. Conker</author>
		<pubDate>Fri, 01 Jan 2010 17:59:18 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-3119</guid>
		<description>[...] NOTE: credit for the optimized encoding function goes to Christer Ericcson, who posted it on his his blog. [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] NOTE: credit for the optimized encoding function goes to Christer Ericcson, who posted it on his his blog. [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: realtimecollisiondetection.net - the blog &#187; Catching up (part 2)</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-2780</link>
		<author>realtimecollisiondetection.net - the blog &#187; Catching up (part 2)</author>
		<pubDate>Mon, 08 Jun 2009 08:51:51 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-2780</guid>
		<description>[...] Karis links to my LogLUV post while pointing out that there&#8217;s another kid in town: RGBM color encoding. In fact, we are [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] Karis links to my LogLUV post while pointing out that there&#8217;s another kid in town: RGBM color encoding. In fact, we are [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: XNA On The 360, Part 2: HDR</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-2253</link>
		<author>XNA On The 360, Part 2: HDR</author>
		<pubDate>Thu, 14 Aug 2008 20:31:13 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-2253</guid>
		<description>[...] in many other PS3 games, as well.  My actual shader implementation was helped along quite a bit by Christer Ericson&#39;s blog post, which described how to derive optimized shader code for encoding RGB into the LogLuv format.&#160; [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] in many other PS3 games, as well.  My actual shader implementation was helped along quite a bit by Christer Ericson&#39;s blog post, which described how to derive optimized shader code for encoding RGB into the LogLuv format.&nbsp; [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: christer</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-2139</link>
		<author>christer</author>
		<pubDate>Sat, 12 Jul 2008 07:25:04 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-2139</guid>
		<description>Just to round out the above blog post and its comments, I thought I'd mention that users &lt;a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=500219" rel="nofollow"&gt;MJP and remigius over at gamedev.net&lt;/a&gt; incorporated Marco's packing code with my code snippet and also worked out the details of the matching LogLuv_Decode() function.  So, for completeness, and with credits to MJP and remigus, here's the full Encode/Decode pair (in HLSL):
&lt;pre class=code&gt;
// M matrix, for encoding
const static float3x3 M = float3x3(
    0.2209, 0.3390, 0.4184,
    0.1138, 0.6780, 0.7319,
    0.0102, 0.1130, 0.2969);

// Inverse M matrix, for decoding
const static float3x3 InverseM = float3x3(
    6.0014, -2.7008, -1.7996,
   -1.3320,  3.1029, -5.7721,
    0.3008, -1.0882,  5.6268);

float4 LogLuvEncode(in float3 vRGB)  {		 
    float4 vResult; 
    float3 Xp_Y_XYZp = mul(vRGB, M);
    Xp_Y_XYZp = max(Xp_Y_XYZp, float3(1e-6, 1e-6, 1e-6));
    vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
    float Le = 2 * log2(Xp_Y_XYZp.y) + 127;
    vResult.w = frac(Le);
    vResult.z = (Le - (floor(vResult.w*255.0f))/255.0f)/255.0f;
    return vResult;
}

float3 LogLuvDecode(in float4 vLogLuv) {
    float Le = vLogLuv.z * 255 + vLogLuv.w;
    float3 Xp_Y_XYZp;
    Xp_Y_XYZp.y = exp2((Le - 127) / 2);
    Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
    Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
    float3 vRGB = mul(Xp_Y_XYZp, InverseM);
    return max(vRGB, 0);
}
&lt;/pre&gt;
I hope people who visit this post (and according to the stats, its a fairly popular post) will find this info useful. Make sure to visit the gamedev.net thread too (as linked above). Rim van Wersch (remigus) also posted a &lt;a href="http://xnainfo.com/content.php?content=17" rel="nofollow"&gt;simple test project&lt;/a&gt; that you might want to check out.</description>
		<content:encoded><![CDATA[<p>Just to round out the above blog post and its comments, I thought I&#8217;d mention that users <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=500219" rel="nofollow">MJP and remigius over at gamedev.net</a> incorporated Marco&#8217;s packing code with my code snippet and also worked out the details of the matching LogLuv_Decode() function.  So, for completeness, and with credits to MJP and remigus, here&#8217;s the full Encode/Decode pair (in HLSL):</p>
<pre class=code>
// M matrix, for encoding
const static float3x3 M = float3x3(
    0.2209, 0.3390, 0.4184,
    0.1138, 0.6780, 0.7319,
    0.0102, 0.1130, 0.2969);

// Inverse M matrix, for decoding
const static float3x3 InverseM = float3x3(
    6.0014, -2.7008, -1.7996,
   -1.3320,  3.1029, -5.7721,
    0.3008, -1.0882,  5.6268);

float4 LogLuvEncode(in float3 vRGB)  {
    float4 vResult;
    float3 Xp_Y_XYZp = mul(vRGB, M);
    Xp_Y_XYZp = max(Xp_Y_XYZp, float3(1e-6, 1e-6, 1e-6));
    vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
    float Le = 2 * log2(Xp_Y_XYZp.y) + 127;
    vResult.w = frac(Le);
    vResult.z = (Le - (floor(vResult.w*255.0f))/255.0f)/255.0f;
    return vResult;
}

float3 LogLuvDecode(in float4 vLogLuv) {
    float Le = vLogLuv.z * 255 + vLogLuv.w;
    float3 Xp_Y_XYZp;
    Xp_Y_XYZp.y = exp2((Le - 127) / 2);
    Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y;
    Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z;
    float3 vRGB = mul(Xp_Y_XYZp, InverseM);
    return max(vRGB, 0);
}
</pre>
<p>I hope people who visit this post (and according to the stats, its a fairly popular post) will find this info useful. Make sure to visit the gamedev.net thread too (as linked above). Rim van Wersch (remigus) also posted a <a href="http://xnainfo.com/content.php?content=17" rel="nofollow">simple test project</a> that you might want to check out.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Another day, another HDR rendering trick and some hope for the future. &#171; Pixels, Too Many..</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-2047</link>
		<author>Another day, another HDR rendering trick and some hope for the future. &#171; Pixels, Too Many..</author>
		<pubDate>Sat, 05 Jul 2008 16:43:44 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-2047</guid>
		<description>[...] but even without re-introducing a floating point buffer (or some funky color space technique, see Christer Ericson&#8217;s blog entry about some of the work I did on Heavenly Sword and his very clever take on it) we can still [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] but even without re-introducing a floating point buffer (or some funky color space technique, see Christer Ericson&#8217;s blog entry about some of the work I did on Heavenly Sword and his very clever take on it) we can still [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: realtimecollisiondetection.net - the blog &#187; I like spilled beans!</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-20</link>
		<author>realtimecollisiondetection.net - the blog &#187; I like spilled beans!</author>
		<pubDate>Mon, 03 Sep 2007 03:33:42 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-20</guid>
		<description>[...] the RGN values in the fragment shader. I guess it would also be possible to encode textures using LogLuv, although that seems a bit [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] the RGN values in the fragment shader. I guess it would also be possible to encode textures using LogLuv, although that seems a bit [&#8230;]</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: nAo</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-6</link>
		<author>nAo</author>
		<pubDate>Sat, 14 Jul 2007 17:50:13 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-6</guid>
		<description>Hi Christer,

You're right, there are many different options when it comes down to render HDR images.
Since we can't really do alpha blending using this color space, if a game really needs to blend in a HDR color space then this technique only make sense only if used in conjunction with multisampling, writing a custom AA resolve filter that downsample a LogLuv image to a FP16 image where we can do HDR blending
(Though I think that HDR blending is overrated, we can live without it just blending on a RGBA8 render target, tone mapping in our alpha blending pass pixel shaders, even better if we do it using exposure computed in the previous frame read back with the CPU so that we can avoid a texture read in our pxel shaders and set exposure as a pixel shader constant.)
It's also worth to notice that the vast majority of games probably don't fully use the full FP16 range bur rather a narrower range, in this case we can drop the logarithm and just store a linear luminance scaled to just fit the luminance range we want to support, even in this case I doubt we can tell the difference, can we? :)
BTW..the code I used to encode luminance is really ugly but I did not have much time to spend on it and it was the only code that did the job (perfect LogLuv -&#62; RGB/FP64 conversion) as when I (sneakily!) introduced it the game already had a ton of content developed using FP16 and I did not want the artists/art director to scream in pain cause our images were slighty darker (!!):

&lt;pre class=code&gt;
// pack the luminance into 2 channels..
float Le_LSBs = frac(Le);
float Le_MSBs = (Le - (floor(Le_LSBs*255.0f))/255.0f)/255.0f;

// unpack log luminance
float Le = Le_MSBs + Le_LSBs / 255.0f;
&lt;/pre&gt;

Thanks for your compliments Christer, I'm looking forward to what you and your team can do on PS3!

Marco</description>
		<content:encoded><![CDATA[<p>Hi Christer,</p>
<p>You&#8217;re right, there are many different options when it comes down to render HDR images.<br />
Since we can&#8217;t really do alpha blending using this color space, if a game really needs to blend in a HDR color space then this technique only make sense only if used in conjunction with multisampling, writing a custom AA resolve filter that downsample a LogLuv image to a FP16 image where we can do HDR blending<br />
(Though I think that HDR blending is overrated, we can live without it just blending on a RGBA8 render target, tone mapping in our alpha blending pass pixel shaders, even better if we do it using exposure computed in the previous frame read back with the CPU so that we can avoid a texture read in our pxel shaders and set exposure as a pixel shader constant.)<br />
It&#8217;s also worth to notice that the vast majority of games probably don&#8217;t fully use the full FP16 range bur rather a narrower range, in this case we can drop the logarithm and just store a linear luminance scaled to just fit the luminance range we want to support, even in this case I doubt we can tell the difference, can we? :)<br />
BTW..the code I used to encode luminance is really ugly but I did not have much time to spend on it and it was the only code that did the job (perfect LogLuv -&gt; RGB/FP64 conversion) as when I (sneakily!) introduced it the game already had a ton of content developed using FP16 and I did not want the artists/art director to scream in pain cause our images were slighty darker (!!):</p>
<pre class=code>
// pack the luminance into 2 channels..
float Le_LSBs = frac(Le);
float Le_MSBs = (Le - (floor(Le_LSBs*255.0f))/255.0f)/255.0f;

// unpack log luminance
float Le = Le_MSBs + Le_LSBs / 255.0f;
</pre>
<p>Thanks for your compliments Christer, I&#8217;m looking forward to what you and your team can do on PS3!</p>
<p>Marco</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: christer</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-5</link>
		<author>christer</author>
		<pubDate>Sat, 14 Jul 2007 07:10:37 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-5</guid>
		<description>Hi Marco, good to see you here! Back when I looked at this I only worked out how to optimize the RGB-&gt;LogLuv code as per above, not the other way around, so I never looked into the precision issues but I did scribble in my notes that splitting the luminance value into two bytes could be an issue and that you might have to do something else. A possible option could be to write it this way:
&lt;pre class=code&gt;
float4 res;
float3 v = mul(rgb, m);
v = max(v, float3(1e-6, 1e-6, 1e-6));
float k = 2*log2(v.y) + 128;
res.xy = unpack_4ubyte(k).xy;
res.zw = v.xy / v.z;
return res;
&lt;/pre&gt;
Although this might not work so well either; I can't remember if the pack/unpack instructions were hosed or not. (I'm not the one doing the shader coding.) I recall the unpack approach produced worse code too, but this was with a pretty old Cg compiler, so who knows.

Feel free to steal the dot product folding trick! (If you haven't already.)

I don't know of any other games using LogLuv at this point. I considered it for our engine based on your posts about your approach, but we haven't committed to how to deal with HDR yet so the ball is still in the air. (As you know quite well, there are several possible options and which is best depends a lot on what other choices you've made - and we haven't made all our choices yet.)

BTW, I don't think it's a secret to mention publicly that I've seen builds of Heavenly Sword and the graphics are absolutely gorgeous. Kudos to you and the team!</description>
		<content:encoded><![CDATA[<p>Hi Marco, good to see you here! Back when I looked at this I only worked out how to optimize the RGB->LogLuv code as per above, not the other way around, so I never looked into the precision issues but I did scribble in my notes that splitting the luminance value into two bytes could be an issue and that you might have to do something else. A possible option could be to write it this way:</p>
<pre class=code>
float4 res;
float3 v = mul(rgb, m);
v = max(v, float3(1e-6, 1e-6, 1e-6));
float k = 2*log2(v.y) + 128;
res.xy = unpack_4ubyte(k).xy;
res.zw = v.xy / v.z;
return res;
</pre>
<p>Although this might not work so well either; I can&#8217;t remember if the pack/unpack instructions were hosed or not. (I&#8217;m not the one doing the shader coding.) I recall the unpack approach produced worse code too, but this was with a pretty old Cg compiler, so who knows.</p>
<p>Feel free to steal the dot product folding trick! (If you haven&#8217;t already.)</p>
<p>I don&#8217;t know of any other games using LogLuv at this point. I considered it for our engine based on your posts about your approach, but we haven&#8217;t committed to how to deal with HDR yet so the ball is still in the air. (As you know quite well, there are several possible options and which is best depends a lot on what other choices you&#8217;ve made - and we haven&#8217;t made all our choices yet.)</p>
<p>BTW, I don&#8217;t think it&#8217;s a secret to mention publicly that I&#8217;ve seen builds of Heavenly Sword and the graphics are absolutely gorgeous. Kudos to you and the team!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: nAo</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-4</link>
		<author>nAo</author>
		<pubDate>Sat, 14 Jul 2007 06:35:15 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-4</guid>
		<description>Well done Christer!
Your implementation is actually faster than mine as I did not fold the dot product into the matrix multiplication and I also had to split the log luminance in a more convoluted way as using the same transformation you used I was not being able to perfectly go back to a RGB colour without losing a tiny bit of intensity.
BTW..do you know any game that is making use of the same base technique?
The only one I'm aware of is Heavenly Sword..</description>
		<content:encoded><![CDATA[<p>Well done Christer!<br />
Your implementation is actually faster than mine as I did not fold the dot product into the matrix multiplication and I also had to split the log luminance in a more convoluted way as using the same transformation you used I was not being able to perfectly go back to a RGB colour without losing a tiny bit of intensity.<br />
BTW..do you know any game that is making use of the same base technique?<br />
The only one I&#8217;m aware of is Heavenly Sword..</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Deano&#8217;s Home From Home &#187; Almost there</title>
		<link>http://realtimecollisiondetection.net/blog/?p=15#comment-3</link>
		<author>Deano&#8217;s Home From Home &#187; Almost there</author>
		<pubDate>Tue, 10 Jul 2007 13:31:31 +0000</pubDate>
		<guid>http://realtimecollisiondetection.net/blog/?p=15#comment-3</guid>
		<description>[...] Ericson has made a cool post describing LogLuv HDR (which Marco did for HS and we christianed NAO32) on PS3. So go read if you [...]</description>
		<content:encoded><![CDATA[<p>[&#8230;] Ericson has made a cool post describing LogLuv HDR (which Marco did for HS and we christianed NAO32) on PS3. So go read if you [&#8230;]</p>
]]></content:encoded>
	</item>
</channel>
</rss>
