XNA Creators Club Online
Page 1 of 1 (5 items)
Sort Posts: Previous Next

HLSL: explain it like I'm your Mom

Last post 3/6/2009 3:24 PM by Akai kaze. 4 replies.
  • 3/4/2009 6:17 AM

    HLSL: explain it like I'm your Mom

    I have next to me a copy of DirectX 9 Programmable Graphics Pipeline. I've just remembered why I hate books: no search function. Aside from that a lot of what's in there is unfathomable DirectX code, so finding the right gems of information can be hard. Screw books.

    I need a few plain english explanations of some HLSL syntax. I've tooled around with effect files a bit and it's easy editing them, but certain things still elude me.

    What version shaders should I target? Typically I see samples targeting Vertex Shader 1.1 and Pixel Shader 2.0. Is there any reason other than legacy support not to set these both to 3.0?

    Edit 1: Figured most of it out myself so took out all but the shader version question. A fresh look really helped.
    _______________________________________
    www.skull.co.nz
  • 3/4/2009 8:39 AM In reply to

    Re: HLSL: explain it like I'm your Mom

    No none that I can think of, but I think generally you want to choose the lowest possible shader model so as many cards as possible can use it. For some effects you may need SM 3 to get the absolute coolest looking effect and then sure declare it as such but make a fall back technique so older cards can still play the game. You get the added bonus of not having to rewrite as many shaders in lower shader models if you already use the lowest possible model when writing it the first time.

    Note that sometimes you want to use SM3 instead of SM2 or lower for performance reasons, maybe you want to take advantage of dynamic branching or other features not availible in lower models. In those cases you may need to use a fallback technique that doesn't need branching at all so it's cheap enough to work on a SM2 card while still looking cool in sm3.

    Also sometimes it doesn't really make sense to support all shader models. For Colosseum we only support 3.0 (although all shaders that could be of lower shader model is) because our renderer is too complex for a shader model 2 card to support it anyway so then we'd have to reduce complexity of the renderer also which just wasn't feasible with our time frame.

    Regards
  • 3/4/2009 8:43 AM In reply to

    Re: HLSL: explain it like I'm your Mom

    The Semantics such as POSITION0 are sort of a mixed bag of purely "hint" semantics and some with relevant purposes and repercussions. For example, the Vertex Shader must output a POSITION0 semantic. Failure to do so and the code simply won't compile. Also, as an input the Position variable is a float4, but in XNA you send position as a Vector3. There's one less float going in than what's going out! Because the semantic is POSITION, the 4th component is automatically set to 1 if it isn't provided. If the semantic were TEXCOORD instead, the value of the W component would default to 0 and the shader would likely not draw correctly. The reason this happens is because most of the time that value is going to be 1 anyway, so just assuming it to be 1 unless told otherwise means you can send more vertices in the same amount of space.

    Pixel Shader semantics are slightly more confusing, for instance your vertex shader MUST output a variable with the POSITION0 semantic. If there is no semantic like that, the code won't compile. That stated, trying to manipulate or read the value attached to the POSITION0 semantic in the pixel shader will usually cause a compile error. This is because the POSITION0 semantic doesn't really get passed to the Pixel Shader. This leads to some code that tends to confuse people when they first see it, if you do need to use the pixel position in the pixel shader, for example to calculate shadows, it might look like this:

    struct VertexShaderOutput  
    {  
        float4 Position : POSITION0;  
        float2 TextureCoordinate : TEXCOORD0;  
        float4 CurrentPosition : TEXCOORD1;  

    Notice the CurrentPosition is a TEXCOORD. Pixel Shaders don't really have very many valid semantics. You tend to stick with a POSITION0 and then lots of TEXCOORDS for everything else. In the vertex shader the coder fills CurrentPosition simply as:

    output.CurrentPosition = output.Position;

    I tell you this, the first time I saw this code I "optimized" CurrentPosition away and sat for hours trying to figure out how to get the code to compile because the compile error for doing that is VERY unhelpful, but I think that might be fixed in the next release. Anyway, that's also why there's a number in the back of the semantic. If there wasn't, you'd only have two semantics you could use and POSITION0 has already been earmarked. So you'd only have one semantic to work with!

    struct VertexShaderOutput  
    {  
        float4 Position : POSITION;  
        float4x4 TexCoordLightPowerReflectionRefractionObjectPositionCameraPositionNormalOtherStuffEtc : TEXCOORD;  

    Hooray for numbered semantics, huh?


    Another cool thing you can do with Semantics is to draw to multiple render targets with one effect. You're probably familiar with GraphicsDevice.SetRenderTarget(0, someRenderTarget); But have you ever wondered why you always put a zero there too? Try setting a one and a two as well and you can write a pixel shader that outputs to multiple COLOR semantics. Pretty cool, huh? Some good candidates for COLOR1 and COLOR2 include the pixel's Normal and Depth from the camera. These allow you to write lighting shaders, post processing effects like Depth of Field, etc.

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



    Regarding the naming, you're right that's a bad name. I've never seen anyone call their texture register Texture before. Encasing () around the name requests the compiler to take the name literally rather than attempt to compile it as an instruction. Its the same thing you have to do in stored procedures working with databases if you have a table named Transaction, a column named Date, etc, since the object names are the same as instructions in the language.

    Much better to write that as:

    texture ColorMap;  
    sampler ColorMapSampler = sampler_state  
    {  
        Texture = ColorMap;  
     
        ....  
    }; 

    Now you can very clearly see which parts are variable names and not instructions. You write a new sampler for each texture registered. Samplers are used for the texture look-ups in the shaders like this:

    float4 color = tex2d(ColorMapSampler, input.TexCoords);

    And setting the ColorMap in XNA would look like this:

    myEffect.Parameters["ColorMap"].SetValue(myTexture);

    So pretty much the texture is the variable as XNA recognizes it and the sampler is the texture the way HLSL recognizes is.

    The lower version you target, the more people who can run the program. If you're targetting 360 that isn't really a problem. I would start at 1_1, then switch to 2_0 if I got a too many instructions error. A more pure programming approach would be to start at 1_1 and upon the instructions error attempt to optimize the the HLSL code. I wouldn't recommend this to a first-timer though because there isn't exactly a one-to-one correlation between lines of code and instructions. Some lines are many instructions and sometimes you can write longer code that makes up fewer instructions.
  • 3/4/2009 9:18 AM In reply to

    Re: HLSL: explain it like I'm your Mom

    HerrUppoHoppa:
    Also sometimes it doesn't really make sense to support all shader models. For Colosseum we only support 3.0 (although all shaders that could be of lower shader model is) because our renderer is too complex for a shader model 2 card to support it anyway so then we'd have to reduce complexity of the renderer also which just wasn't feasible with our time frame.


    This is the direction I'm thinking I'll take. I use a lot of rendertargets and I intend to use more, not to mention a lot of post-processing effects. It's easy enough to go back and make a 'good enough' effect file for older cards so they can join in the fun. Sometimes a decent framerate beats out fancy looks when you're having fun.

    @MrLeebo: Nice! Now I can properly get my head straight on this. What the world really needs is an HLSL guide specifically for XNA, this might be a good start.

    I had sort of worked out that TEXCOORD was the way to go for any kind of coordinate after POSITION0, it's nice to have some confirmation of that.

    Time to find me some good phong/lambert examples. I've been tooling around with Nvidia's FX thing and MentalMill as well, so I can probably pull a bit of code from there once I start figuring it all out. I'm really looking forward to this. Thanks muchly!
    _______________________________________
    www.skull.co.nz
  • 3/6/2009 3:24 PM In reply to

    Re: HLSL: explain it like I'm your Mom

    digital tutors xna pipeline is a good tutorial. i learn this way because I don't like book either.
Page 1 of 1 (5 items) Previous Next