poniedziałek, 26 lutego 2018

DirectX 11, HLSL, GatherRed

Every once in a while I am in need to use one of those Gather functions from DirectX 11's HLSL library. GatherRed in this case. This function is useful because it allows you take four samples with just one instruction and store them all in float4. As the name indicates, of the four texels that are sampled simultaneously, only the values from the red channels will be stored in float4. If you need data from other channels you can use respective functions. It is really worth using this function(s) if you only need data from one channel as calling one gather is faster than taking four samples individually.

If instead of using your original UV coordinates to take one regular sample with Sample or SampleLevel you call GatherRed which four samples (their red channels) exactly will be taken? This is something the DirectX's documentation doesn't specify so this short blog post is here to fill this gap. You can also stumble on that information in various DirectX 11 presentations.

Take a look at the picture:

The grey pixel is the one whose UV coordinates we have in the shader (the very center of that texel, to be more specific). If you call GatherRed you will get the four labeled samples (again, only their red channel's values). Probably a little bit counter-intuitively the value of the "base" sample is not stored in return value's $x$ component but $w$ as the image above shows. For better picture the two following snippets are equivalent:
float myValueR = myTexture.Sample(mySampler, uv).x;
float myValueG = myTexture.Sample(mySampler, uv).y;
float myValueB = myTexture.Sample(mySampler, uv).z;
float myValueA = myTexture.Sample(mySampler, uv).w;
float myValueR = myTexture.GatherRed(mySampler, uv).w;
float myValueG = myTexture.GatherGreen(mySampler, uv).w;
float myValueB = myTexture.GatherBlue(mySampler, uv).w;
float myValueA = myTexture.GatherAlpha(mySampler, uv).w;
And these as well:
float r1 = myTexture.Sample(mySampler, uv).x;
float r2 = myTexture.Sample(mySampler, uv, int2(1, 0)).x;
float r3 = myTexture.Sample(mySampler, uv, int2(0, 1)).x;
float r4 = myTexture.Sample(mySampler, uv, int2(1, 1)).x;
float4 samples = myTexture.GatherRed(mySampler, uv);
float r1 = samples.w;
float r2 = samples.z;
float r3 = samples.x;
float r4 = samples.y;
There. I hope you won't have to wonder anymore the order of samples returned by gathers :). At least I know I won't.