Search Unity

Blending 5+ materials with rgb, normal, height, specular/rough/metal

Discussion in 'Shaders' started by SGStino, Jul 28, 2015.

  1. SGStino

    SGStino

    Joined:
    Jul 8, 2014
    Posts:
    10
    Hi,

    How can I blend multiple textures in a shader if there are only 16 texture samplers and multipass blending of opaque geometry doesn't work in deferred mode?


    What I tried:
    • A single "uber" surface shader: a sampler for albedo, normal+height and specular+metal+roughtness+selfillum
      The result is: "not enough texture samplers" for 5 layers: 15 + splat + build in samplers.
    • A multipass surface shader:
      • clip(alpha - 0.5) in each layer's surface shader:
        Result: no blending, only hard cutoff
      • o.Alpha = alpha; as output of surface shader:
        • without "alpha" in pragma
          Result: nothing happens
        • with "alpha" in pragma
          Result: the entire layer doesn't render at all, no matter what blend mode
      • Blend One One
        Result: sortof works, but need to know (1-alpha) in the other layer ahead of time to correctly blend, so with the heightmap per layer, i would need to sample all other heightmaps in each pass. Getting close to the limit of the single shader again. And i imagine that would be just way to expensive anyway to be usable.

    Code (csharp):
    1.        
    2. Tags{
    3.             "RenderType" = "Transparent" // doesn't change much
    4.             "Queue" = "Geometry"
    5.             }
    6.         LOD 200
    7.        
    8.         Blend SrcAlpha OneMinusSrcAlpha // completly ignored unless it's One One
    9.  
    10.         CGPROGRAM
    11.         #pragma surface surf Standard
    12.  
    13.         #pragma target 3.0
    14.  
    15.         sampler2D _layer0_Albedo;
    16.         sampler2D _layer0_NormalHeight;
    17.         uniform float4 _layer0_Albedo_ST;
    18.  
    19.             sampler2D _Splat1Tex;
    20.             sampler2D _Splat2Tex;
    21.  
    22.             float _TerrainWidth;
    23.             float _TerrainHeight;
    24.            
    25.         struct Input {
    26.             float3 worldPos;
    27.         };
    28.  
    29.         void surf (Input IN, inout SurfaceOutputStandard o) {
    30.                      
    31.             float3 color = tex2D(_layer0_Albedo, TRANSFORM_TEX(IN.worldPos.xz,_layer0_Albedo));
    32.             float4 normalBlend = tex2D(_layer0_NormalHeight, TRANSFORM_TEX(IN.worldPos.xz,_layer0_Albedo));
    33.                      
    34.             o.Albedo = color;
    35.             o.Alpha = 0.5f;
    36.         }
    37.         ENDCG
    38.        
    39.        
    40.             CGPROGRAM
    41.             #pragma surface surf Standard
    42.  
    43.             // Use shader model 3.0 target, to get nicer looking lighting
    44.             #pragma target 3.0
    45.            
    46.             sampler2D _layer1_Albedo;
    47.             sampler2D _layer1_NormalHeight;
    48.             uniform float4 _layer1_Albedo_ST;
    49.  
    50.             sampler2D _Splat1Tex;
    51.             sampler2D _Splat2Tex;
    52.  
    53.             float _TerrainWidth;
    54.             float _TerrainHeight;
    55.            
    56.             struct Input {
    57.                 float3 worldPos;
    58.             };
    59.    
    60.             void surf (Input IN, inout SurfaceOutputStandard o) {
    61.            
    62.                 float3 coords = IN.worldPos;
    63.                 coords.x -= _TerrainWidth/2;
    64.                 coords.z -= _TerrainHeight/2;
    65.                 float splat = tex2D(_Splat1Tex, coords.xz /float2(_TerrainWidth, _TerrainHeight)).r;
    66.            
    67.                
    68.                 float4 normalBlend = tex2D(_layer1_NormalHeight, TRANSFORM_TEX(IN.worldPos.xz,_layer1_Albedo));
    69.                 //clip(splat-normalBlend.a); // this is the only thing i can get working, far from an ideal blend.
    70.                
    71.                 float3 color = tex2D(_layer1_Albedo, TRANSFORM_TEX(IN.worldPos.xz,_layer1_Albedo));
    72.                 o.Albedo = color;
    73.                 o.Alpha = splat; // completly ignored, acts as 1 without #pragma alpha, acts as 0 with it.
    74.             }
    75.            
    76.             ENDCG
    77.  
    78. // AND SO ON AND SO ON...
    79.  
     
  2. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    If you don't mind developing for windows only, you can define the samplers separate from the textures. The sampler defines the interpolation, filtering, etc, but there's a limited number of settings for that.

    Define them separately;

    Code (csharp):
    1.  
    2. Texture2D<float4> _Diffuse0; uniform float4 _Diffuse0_ST; SamplerState sampler_Diffuse0;
    3. Texture2D<float4> _Diffuse1; uniform float4 _Diffuse1_ST;
    4.  
    And access them with the first sampler;
    Code (csharp):
    1.  
    2. tex += _Diffuse0.Sample(sampler_Diffuse0, TRANSFORM_TEX(UV, _Diffuse0)) * vertexColor.r;
    3. tex += _Diffuse1.Sample(sampler_Diffuse0, TRANSFORM_TEX(UV, _Diffuse1)) * vertexColor.g;
    4.  
    I haven't really run up to any cases where it's made a difference, but I also filter with if's and multi_compile's assigned and enabled/disabled in a separate script to prevent sampling textures I don't need.

    Unity might have some macros that would work on GLSL, but this is specific HLSL code here.
     
  3. SGStino

    SGStino

    Joined:
    Jul 8, 2014
    Posts:
    10
    Oh, really? Shaderlab allows that?

    I knew about this in SM4 HLSL but somehow i was under the impression that unity doesn't support direct use of hlsl/glsl shaders? But how do i accomplish this?
     
  4. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    Yep, it allows direct use of HLSL, and you can directly compile GLSL shaders with a couple keywords.

    http://docs.unity3d.com/Manual/SL-GLSLShaderPrograms.html

    That's for GLSL. For normal use, HLSL is the first and normal compiled language. Shaderlab actually rewrites all the CG language code to HLSL through the use of compiler macros, so you are actually writing HLSL shaders in the end. Unity first uses macros to create the HLSL code, and then converts HLSL to GLSL when possible (not always possible).

    I use the above code in my terrain shaders already, so it's been quite tested. However, OpenGL 3.0 does not support the separation of samplers and textures, so it's not suitable for macs, until we get OpenGL 4.0 support (which I think is coming? if not already here, I haven't kept up to date on that, but they had a big post about supporting it just a few months ago, not sure if it 'is' or 'will be').
     
  5. SGStino

    SGStino

    Joined:
    Jul 8, 2014
    Posts:
    10
    Opengl 4 is for december/march if i remember correctly.

    Somehow using the hlsl SM4 code gives me compiler errors, what directives do i feed it to tell it that it's indeed HLSL? CGPROGRAM should probably be HLSLPROGRAM or something?
     
  6. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    I believe Unity can only compile to SM3.0 and SM5.0. I think you'll need to explicitly target (and maybe exclude renderers that can't take SM5.0) from compilation.