Search Unity

multi target shaders?

Discussion in 'Shaders' started by vargata, Apr 19, 2015.

  1. vargata

    vargata

    Joined:
    Nov 26, 2013
    Posts:
    120
    hi guys,

    i have a little (big) problem. i wrote a shader effect which works fine in dx9 with sm3. then i've added tessellation and with that it runs perfectly in dx11 BUT it doesnt anymore do anything in dx9. which is ok, if it has tessellation its compiled to dx11 automatically...
    the question is. can a shader be built up from 2 parts (maybe with multiple passes or multi_compile etc) so the dx9 bit would run on both dx9 and 11 and the tessellation would run on dx11 only?

    thanks for all the help.
     
  2. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    That's what SubShader tag is for.
    Your Shader can contain multiple SubShaders and the first one that can run on current device will be used.
     
    vargata likes this.
  3. vargata

    vargata

    Joined:
    Nov 26, 2013
    Posts:
    120
    ok, i have to write the entire shader in 2 version in the 2 subshaders or can i use one sub-s output in an other sub? i cant really understand this from the unity doc of subshaders and i havent found a good example yet. could you just write in a few lines how would you separate these 2 bits in 2 subshaders?
    1. CGPROGRAM
    2. #ifdef SHADER_API_D3D11
    3. #pragma surface surf NoLight alpha:fade vertex:dispNone tessellate:tessEdge tessphong:_Phong nolightmap
    4. #include"Tessellation.cginc"
    5. #pragma target 5.0
    6. #else
    7. #pragma surface surf NoLight alpha:fade nolightmap
    8. #pragma target 3.0
    9. #endif
    thanks a lot
     
  4. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    You can't use output of one SubShader in another, because only one SubShader can be used.

    Code (csharp):
    1. Shader{
    2.     Properties { ... }
    3.     SubShader { //This one uses direct3d11, Unity will try to use this first
    4.         Pass { ...some code... }
    5.         Pass { ...some code... }
    6.     }
    7.     SubShader { //If previous subshader couldn't run then this one will run using direct3d9
    8.         Pass { ...some code... }
    9.         Pass { ...some code... }
    10.     }
    11.     //Note, if you put the SubShader for direct3d9 above the one that uses direct3d11 the second one will never be used.
    12.     //Also note that mac and linux can't use direct3d at all, they use OpenGL,
    13.     //but I assume that your direct3d9 code can be compiled using OpenGL
    14. }
     
  5. defaxer

    defaxer

    Joined:
    Nov 15, 2010
    Posts:
    140
    You can define SurfaceOutput function within CGINCLUDE and use in any subshader you write
    Something like that:

    Code (CSharp):
    1. Shader{
    2. Properties { ... }
    3. CGINCLUDE
    4.     float4 YourTessellateFunction (appdata v0, appdata v1, appdata v2){...}
    5.     void YourSurfFunction(Input IN, inout SurfaceOutput o){...}
    6. ENDCG
    7. SubShader
    8. {
    9.     Tags { "RenderType"="Opaque" }
    10.     LOD 300
    11.    
    12.     CGPROGRAM
    13.     #pragma surface YourSurfFunction Lambert tessellate:YourTessellateFunction
    14.     ENDCG
    15. }
    16. SubShader
    17. {
    18.     Tags { "RenderType"="Opaque" }
    19.     LOD 200
    20.    
    21.     CGPROGRAM
    22.     #pragma surface YourSurfFunction Lambert
    23.     ENDCG
    24. }
    25. }
     
  6. vargata

    vargata

    Joined:
    Nov 26, 2013
    Posts:
    120
    ok, things get cleared up, greatly thanks for both of you. this knowledge is already enough to make it working :)

    my problem is based on defaxers code is, as i've understood from my googling cginclude will put the entire functions i write between into both subshaders. generally the same as if i would copy paste them. isnt it? (it still looks better, just asking)

    so if i understood well i can do it like write the surface shader in cginclude
    then write the dx11 subshader with the tags, #pragma lines, vertex and tessellation routines as the surf shader will be included
    and then really just an empty subshader for dx9 with just the tags and the #pragma line, as cginclude will insert the surf shader code there.

    but after compiling there will be the surf shader code in the file twice? is there a way to have it like one pass for my lighting code, one for my surf code and then having a subshader just for the tessellation?
    for me this logic is just strange. i would think you build up things like first the least demanding stuff and then add the more demanding ones if the system supports them...
     
  7. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    The include comand just means "copy this here". You will end up with the same functions in both subshaders, but that doesn't matter because only one will be used.

    That would be fine IF the graphic card didn't need to execute the complex stuff like tessellation before the rest of the code.
     
  8. vargata

    vargata

    Joined:
    Nov 26, 2013
    Posts:
    120
    ahh, i see, thanks a lot for all the help and explanation. i will start rewriting the code :) have a nice day both of you.