2
\$\begingroup\$

Just starting out with HLSL, please bare with me :)

There are times I'm a little confused by the syntax for instance in the pixel shader of an example I am following we have the following input and output:

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 Depth : TEXCOORD0;
    float3 Normal : TEXCOORD1;
};


struct PixelShaderOutput
{
    float4 Normal : COLOR0;
    float4 Depth : COLOR1;
};


PixelShaderOutput PixelShaderFunction(VertexShaderOutput input)
{
    PixelShaderOutput output;

    // Depth is stored as distance from camera / far plane distance
    // to get value between 0 and 1
    output.Depth = input.Depth.x / input.Depth.y;

    // Normal map simply stores X, Y and Z components of normal
    // shifted from (-1 to 1) range to (0 to 1) range
    output.Normal.xyz = (normalize(input.Normal).xyz / 2) + .5;

    // Other components must be initialized to compile
    output.Depth.a = 1;
    output.Normal.a = 1;
    return output;
}

Now one of the parts that confuses me is

output.Depth = input.Depth.x / input.Depth.y;

I'm confused because I thought Depth is a float4, which to my understanding is a 4-component vector. Doesn't input.Depth.x / input.Depth.y return a scalar value? What is happening in this assignment?

Secondly, and similarly

output.Normal.xyz = (normalize(input.Normal).xyz / 2) + .5;

Wouldn't normalize(input.Normal).xyz / 2 return a 3-component vector, with each element divided by 2? What does it mean then to add that 3-component vector to a scalar .5?

I feel like I'm missing something obvious here that I'll probably slap my forehead when someone points it out.

Also one more question - by the time the float4 Position arrives at the pixel shader from the vertex shader output, at this point are the coordinates in clip-space [-1,1] or are they still in camera-coordinates but with the homogeneous w-component (eg before the perspective divide) ? Is the w-component usually equal to the z-component?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The normal code snippet is kind of out of context and just outputs a normal that's linearly shifted from having components in the [-1, 1] interval to the [0,1] interval via a scaling (divide by 2) and a translation (add 0.5). It looks like this Pixel shader isn't the best example for ya. It computes a normal map texture. If you're just starting HLSL, this is one hell of an example (as it lacks the statement of purpose, it's very specific and it should write its output to a non-screen render target).. kinda complex for a newbie. \$\endgroup\$
    – teodron
    Jul 17, 2012 at 8:15
  • \$\begingroup\$ Apologies for removing the annotations - the example did have documentation, but I stripped it for clarity as I was actually more focused on the HLSL syntax / expression evaluation. In this example, you're right the XNA code sets 2 render for the depth and normal maps. I've developed interest in deferred rendering so this was a logical topic. But I find some of the HLSL syntax is difficult to predict the result of, when one is new to the language and not able to simply attach a breakpoint because it looks like in some instances you're trying to add a vector to a scalar \$\endgroup\$
    – manning18
    Jul 18, 2012 at 1:19

1 Answer 1

5
\$\begingroup\$

HLSL lets you compress a vector with the same value down to a scalar. When you do output.Depth = input.Depth.x / input.Depth.y what you're actually doing is:

float x = input.Depth.x / input.Depth.y;
output.Depth = float4( x, x, x, x );

And the same with the normal example above.

To your second question: Pixel shader inputs depend on the shader semantic you access. Shader semantics are the extra bit after the colon in variable definitions that specify where the data comes from for the input, or where the output goes.

In the above shader you don't define a position semantic so you won't get one. If you added SV_Position to your pixel shader input (D3D10 and higher), the position is in screenspace. You can read more about the available shader semantics here.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks alot! The MSDN HLSL reference is a little dry and I'm not familar with many of these syntactic short-cuts. I'm afraid I still need some clarification with the normal example: (normalize(input.Normal).xyz / 2) + .5 ..does this break down to a normalized input.Normal with each element divided by 2 with .5 added to each component? Would it make sense / how would it behave, if you added .5 to a matrix? In the above example, isn't my VertexShaderOutput (the input to the pixel shader) declaring a POSITION0 ? In any case, is that screenspace between [-1,1] or game window viewport pixel coords? \$\endgroup\$
    – manning18
    Jul 18, 2012 at 1:27
  • \$\begingroup\$ You normal example does what it says it will - it divides each component by 2 and then adds .5 to each component. This moves each component from the range [-1, 1] to [0,1]. Adding .5 to a matrix: I don't know what it would do, mainly because I've never tried - I don't see the need. :) \$\endgroup\$
    – Matt Kemp
    Jul 18, 2012 at 8:16
  • \$\begingroup\$ If you're working in DirectX 9 (which I think XNA is) then you don't have access to the system-value semantics anyhow, so you don't get SV_Position. That said, the old equivalent is POSITION which you're using anyway, which is (according to MSDN): Position of a vertex in homogenous space. Compute position in screen-space by dividing (x,y,z) by w. Every vertex shader must write out a parameter with this semantic. If you're using it to just write out positions, you don't need to divide by W. If you're doing something clever with screen-space positions, you do. \$\endgroup\$
    – Matt Kemp
    Jul 18, 2012 at 8:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .