0
\$\begingroup\$

I'm using Direct3D 9 to draw some primitives into a texture. The format used at texture creation is D3DFMT_A16B16G16R16F, and the primitives are drawn using additive blending. The additive blending works as expected provided the result is <= 1. If it is greater than 1 then it seems to be clamped to 1.

This question includes a comment stating that COLOR0 output semantics for a pixel shader automatically clamp the output values. I can't ask for elaboration there due to low SE rep. Is there anything else that I need to do on top of changing the texture format that I pass in to CreateTexture, in order to use the full range of 16-bit floats? Given that I can otherwise draw primitives with shading (aside from the clamping issue) fine, is there any other setup that needs to be done?

To illustrate my issue, I'm doing two passes of the following shader (ping-ponging between two textures in the aforementioned format):

texture tex;

sampler2D sam = sampler_state {
    texture = <tex>;
    minfilter = none;
    magfilter = none;
    mipfilter = none;
};

float4 main(float2 uv : TEXCOORD) : COLOR0 {
    float4 cin = tex2D(sam, uv);
    float4 cout = cin;

    cout.g = 10;
    if (cin.g > 1)
        cout.b = 1;

    return cout;
}

The output (starting from black) is a green screen. If I change:

cin.g > 1

To:

cin.g >= 1

Then I get a cyan screen.

I'm working from a modified version of SDL2's Direct3D 9 driver if that helps.

\$\endgroup\$
1
\$\begingroup\$

I am the author of the comment referred to in this question.

Unfortunately since Microsoft have pulled all of the old DirectX SDKs it's no longer possible to cite a reference to one of those for the clamping behaviour; instead I'll cite The Complete Effect and HLSL Guide by Sebastien St-Laurent; Table 6-4 Pixel Output Semantics (this should also be searchable on Google Books):

Any pixel shader prior to version 3.0 should clamp a parameter that uses this semantic between 0 and 1. For pixel shaders beyond 3.0 shaders, the data range is dependent on the render target format.

(It should be immediately obvious that this citation omits what happens with PS_3_0, which is regrettable).

A possible workaround for you is to scale your pixel shader output down during your main rendering pass(es), then scale it back up again as a post-processing pass. Doom 3 used a similar technique to likewise achieve a higher dynamic range (in it's case, at the expense of some bits of precision).

\$\endgroup\$
  • \$\begingroup\$ Thanks—this answer saved me a lot of potentially wasted time. It's very unfortunate that they removed the documentation.. \$\endgroup\$ – the.punisher Nov 3 '16 at 12:12
  • \$\begingroup\$ @the.punisher - it's also unfortunate that in many cases Microsoft documentation for version(n) assumes familiarity with documentation for version(n-1); in cases where the latter is unavailable you're stuck. \$\endgroup\$ – Maximus Minimus Nov 3 '16 at 12:39
0
\$\begingroup\$

From my understanding (of GLSL though), those values are always clamped between 0.0 and 1.0. You don't get it any darker than black and any lighter than white. With more memory for color, all you get is more precision between zero and one. So setting "green" to 10.0 in your shader just sets it to 1.0. I do not actually understand what you mean by "...in order to use the full range of 16-bit floats". You ARE already using the increased precision. There is just no point of going above 1.0 if you are not intending to implement HDR or similar techniques...

P.s: maybe this is rather a comment than an answer...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.