Search Unity

How to enable float4 uv in surface shader?

Discussion in 'Shaders' started by dreamerflyer, Aug 28, 2017.

  1. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Code (CSharp):
    1.   struct Input {
    2.                 float4 uv_Splat0: TEXCOORD0;
    3.  
    4.             };
    5.             void vert(inout appdata_full v,out Input o)
    6.             {
    7.                   UNITY_INITIALIZE_OUTPUT(Input,o);
    8.  
    9.             //    o.uv_Splat0.xy = TRANSFORM_TEX(v.texcoord.xy, _Splat0);
    10.             //    o.uv_Splat0.zw = TRANSFORM_TEX(v.texcoord.xy, _Splat1);
    11.             }
    Hi all,I want to save constance starage in gpu ,for shader model 2.0,to can make 4 layer different texture,But can not work..Any help?
     
  2. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    and if enable : o.uv_Splat0.xy = TRANSFORM_TEX(v.texcoord.xy, _Splat0);
    will cause this error:redefinition of '_Splat0_ST' at line 172 (on d3d9);
    hwo to do?
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    In surface shaders if you define an Input struct value with the name uv_ or uv#_ it automatically defines the _ST variables, applies the TRANSFORM_TEX, and packs the UVs. It also ignores the semantics (: TEXCOORD0) on those lines.

    All you need to do to apply the scale & offset and pack is to define your UVs.
    Code (CSharp):
    1. struct Input {
    2.     float2 uv_Splat0;
    3.     float2 uv_Splat1;
    4. };
    That's it. Internally those are being packed in the vertex shader and unpacked in the fragment shader before being handed to the surf() function.

    If you look at the surface shader's generated code you'll see this:
    Code (CSharp):
    1. // in the vertex shader
    2.   o.pack0.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
    3.   o.pack0.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
    4.  
    5. // in the fragment shader
    6.   surfIN.uv_Splat0 = IN.pack0.xy;
    7.   surfIN.uv_Splat1 = IN.pack0.zw;
    The only reason to use a custom vert function for something like this is to modify the UVs manually or to skip the TRANSFORM_TEX in the vertex shader, which honestly you should do when using multiple scale & offsets on the same texcoord like you do with splat maps. For that you want to define an input without the uv_ prefix.
    Code (CSharp):
    1. struct Input {
    2.     float2 texcoord;
    3. };
    4.  
    5. void vert(inout appdata_full v, out Input o)
    6. {
    7.     o.texcoord = v.texcoord;
    8. }
    9.  
    10. float4 _Splat0_ST, _Splat1_ST;;
    11.  
    12. void surf (Input IN, inout SurfaceOutput o) {
    13.     float2 uv_Splat0 = TRANSFORM_TEX(IN.texcoord0, _Splat0);
    14.     float2 uv_Splat1 = TRANSFORM_TEX(IN.texcoord0, _Splat1);
    15.     // do stuff
    This way you're only transferring the UV once, and each TRANSFORM_TEX is only a single additional MAD instruction, so it's basically free. The only reason to do it in the vertex shader is to prevent dependent texture reads on mobile when using OpenGLES 2.0 devices, but packing the UVs also causes dependent texture reads so just using a surface shader to begin with is already causing this.

    edit: You still need the custom vert function for this case as there's a long time bug since Unity 5.1 where it culls UVs when defining Inputs with texcoord semantics under some cases.
     
    Last edited: Aug 28, 2017
    dreamerflyer likes this.
  4. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Thank you very Much!
     
  5. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Code (CSharp):
    1. Shader error in 'FireBird/UE4Terrain': maximum ps_5_0 sampler register index (16) exceeded at line 60 (on gles3)
    2.  
    3. Compiling Vertex program with DIRECTIONAL SHADOWS_SCREEN LIGHTMAP_ON DIRLIGHTMAP_COMBINED DYNAMICLIGHTMAP_OFF
    4. Platform defines: UNITY_NO_DXT5nm UNITY_NO_RGBM UNITY_NO_SCREENSPACE_SHADOWS UNITY_NO_LINEAR_COLORSPACE UNITY_ENABLE_REFLECTION_BUFFERS UNITY_FRAMEBUFFER_FETCH_AVAILABLE UNITY_PBS_USE_BRDF2 SHADER_API_MOBILE UNITY_HARDWARE_TIER2
    Code (CSharp):
    1.     sampler2D _Control;
    2.         sampler2D _Splat0, _Splat1, _Splat2, _Splat3, _detail;
    3.         sampler2D _BumpMapSplat0, _BumpMapSplat1, _BumpMapSplat2, _BumpMapSplat3;
    4.         sampler2D _SpecTexSplat0, _SpecTexSplat1, _SpecTexSplat2, _SpecTexSplat3;
    5.         float4   _Control_ST,_Splat0_ST, _Splat1_ST, _Splat2_ST, _Splat3_ST;
    6.  
    7. surf :
    8. fixed4 splat_control = tex2D(_Control, IN.uu.xy);
    9.    float4 uuSplat01 = 0;
    10.    float4 uuSplat23=0;
    11.    uuSplat01.xy = TRANSFORM_TEX(IN.uu.xy, _Splat0);
    12.    uuSplat01.zw = TRANSFORM_TEX(IN.uu.xy, _Splat1);
    13.    uuSplat23.xy = TRANSFORM_TEX(IN.uu.xy, _Splat2);
    14.    uuSplat23.zw= TRANSFORM_TEX(IN.uu.xy, _Splat3);
    15.    //base color
    16.    fixed4 lay1 = tex2D(_Splat0, uuSplat01.xy);
    17.    fixed4 lay2 = tex2D(_Splat1, uuSplat01.zw);
    18.    fixed4 lay3 = tex2D(_Splat2, uuSplat23.xy);
    19.    fixed4 lay4 = tex2D(_Splat3, uuSplat23.zw);
    20.             fixed4 diffuse=lay1 * splat_control.r + lay2 * splat_control.g + lay3 * splat_control.b + lay4*(1 - splat_control.a);
    21.   spec....
    22. normal....
    when i lerp 4 layer basecolor,normalcolor,speccolor ,it seems still use too many storage....how to fixed this?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The short version is you don't.

    You can only use 16 texture samplers per shader. You're adding 14, and Unity's shaders use between 1 and 8(?) depending on the variant. Shadow map, reflection probe, 4 lightmap textures, shadow mask, and light probe proxy. The variant that error is happening on I believe adds "just" 4, the shadow map, 2 lightmaps, and the reflection probe. With DX11 & equivalent APIs there are ways to split the sampler and the texture apart and reuse a sampler multiple times, and I believe GLES 3.0 supports this as well, but it's not easily exposed in Unity and is not possible with GLES 2.0.

    The solution, at least for mobile, is to pack your texture data into as few textures as possible. Also disable the variants you don't need, and possibly disable glossy reflections.

    This is also why Unity's terrain packs the specular into the albedo texture's alpha. You may also need to manually pack and unpack your normal map textures if you need additional texture data.
     
    dreamerflyer likes this.
  7. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    UV_BUG.jpg
    Code (CSharp):
    1.  
    2.      
    3.  float2 scaleuv = IN.uu.xy*_TileNum.x;
    4.   float2 layer1_uv = frac(scaleuv);
    5.  
    6.   layer1_uv = layer1_uv*0.5;
    7.  
    HI bgolus,i final think of this texture atlas to save texture starage,But meet other problem = =!,cannot get the correct uv in shader...what is wrong?
     

    Attached Files:

    • 4L.jpg
      4L.jpg
      File size:
      1.6 MB
      Views:
      962
    Last edited: Aug 31, 2017
  8. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I had no idea it did that. I wish all these quirks were documented properly.
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    There are two problems you'll need to address when using texture atlases for something like this. The first is what's causing those seems. Using frac() on a UV will cause pixels at the edge of the frac() to use the wrong mip. See this post:
    https://forum.unity3d.com/threads/texture2d-array-mipmap-troubles.416799/#post-2715201

    The second will be mip mapping, which can be solved in a similar fashion by clamping the values passed to the tex2Dgrad() function. You may also want to add some padding into your altas if you're not already. Basically for a 256x256 tile in the atlas you'll want your repeating texture to be something like only 240x240 in the center of that (8 pixel padding all the way around). Seems wasteful, but this is kind of what they do for games like id's Rage and other "megatexture" virtual texturing setups that use large texture atlases.
     
  10. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    Code (CSharp):
    1.  
    2. Shader "Custom/azAtlased" {
    3.  
    4.  
    5.        Properties{
    6.  
    7.  
    8.                _MainTex("Base (RGB)", 2D) = "white" {}
    9.  
    10.  
    11.  
    12.  
    13.             // xy is atlas slicing (here we have 4 sprites in 1 row)
    14.  
    15.  
    16.                 // zw is tiling (here repeat 2x in X and 3x in Y)
    17.  
    18.  
    19.                 _SlicingAndTiling("_SlicingAndTiling", Vector) = (0.25, 1.0, 2.0, 3.0)
    20.  
    21.  
    22.    
    23.        // which sub-texture to render, anything between 0 and 3 in this setup
    24.  
    25.  
    26.            _TexIndex("_TexIndex", Float) = 1
    27.  
    28.  
    29.      }
    30.  
    31.  
    32.        SubShader{
    33.  
    34.  
    35.                 Tags{
    36.  
    37.  
    38.              "RenderType" = "Opaque"
    39.  
    40.  
    41.      }
    42.  
    43.  
    44.    
    45.  
    46.  
    47.                 CGPROGRAM
    48.  
    49.  
    50.                 #pragma glsl
    51.  
    52.  
    53.                 #pragma target 3.0
    54.  
    55.  
    56.                 #pragma surface surf BlinnPhong exclude_path : prepass nolightmap noforwardadd novertexlights
    57.  
    58.  
    59.        
    60.  
    61.  
    62.                 sampler2D _MainTex;
    63.  
    64.  
    65.             float4 _SlicingAndTiling;
    66.  
    67.  
    68.         float _TexIndex;
    69.  
    70.  
    71.    
    72.  
    73.  
    74.                struct Input {
    75.  
    76.  
    77.                     float2 uv_MainTex;
    78.  
    79.  
    80.        
    81.     };
    82.  
    83.  
    84.  
    85.  
    86.  
    87.             void surf(Input IN, inout SurfaceOutput o) {
    88.  
    89.  
    90.                 float2 uv = IN.uv_MainTex;
    91.  
    92.  
    93.               uv = (frac(uv * _SlicingAndTiling.zw) + _TexIndex) * _SlicingAndTiling.xy;
    94.  
    95.             float2  uvContinuous = (uv + _TexIndex) * _SlicingAndTiling.xy;
    96.                 float4 col = tex2D(_MainTex, uv, ddx(uvContinuous), ddy(uvContinuous));
    97.  
    98.  
    99.                     o.Albedo = col.rgb;
    100.  
    101.            o.Alpha = col.a;
    102.  
    103.  
    104.        
    105.     }
    106.  
    107.  
    108.             ENDCG
    109.  
    110.  
    111.          }
    112.  
    113.  
    114.             FallBack "Diffuse"
    115.  
    116.  
    117.         }
    uv.jpg 4tex.jpg

    hi ~,ddx and ddy not work?how to fixed this?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You're calculating the derivatives (ddx & ddy) on the uv value after you've applied the frac. You need to do it on the uv value before you've applied the frac.
    Code (CSharp):
    1. float2 uv = IN.uv_MainTex;
    2. float2  uvContinuous = uv * _SlicingAndTiling.zw * _SlicingAndTiling.xy;
    3. uv = (frac(uv * _SlicingAndTiling.zw) + _TexIndex) * _SlicingAndTiling.xy;
    4. float4 col = tex2D(_MainTex, uv, ddx(uvContinuous), ddy(uvContinuous));
    I don't apply the index since only the scaling matters, not the actual position within the UV.
     
  12. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    trying your codes,still has some seam flick when zoom view
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You're seeing the effects of bilinear sampling on the edges of the atlas. This is why you need the padding.
     
  14. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    emmm...not really understand ,only add 8 blank margin?have any texture example to study ?thanks!
     
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    You've got your atlas like above


    At the borders between the tiles a UV position of "1.0" within the "tile" is on the pixel between one tile and the next, so bilinear sampling is going to get a little bit of the next or previous tile. To fix this you could disable filtering and mip maps, but that'll look terrible. You could also clamp the UVs to inset it by one pixel on the edges, but that only works for the first mip.

    Here's what it looks like after a few mips (then scaled back up for example purposes). You can see the edges between tiles bleed together a bit. That's what you're seeing now.
    4texLOD.jpg

    To deal with that you need to only use an inset area of the texture area for each tile, and "frac()" within that range.
    4texmarque.png

    Just modifying the UVs to be in that range will make the texture not properly wrap and there'll be visible seams still, though they at least won't be the wrong color. But if you want them to wrap nicely you need to shrink each tile and fill the edges with itself.

    tileinset.gif

    So you need to do this for all tiles in the atlas, then the area you need to adjust your UVs to match is that inset area.
     
    dreamerflyer likes this.
  16. dreamerflyer

    dreamerflyer

    Joined:
    Jun 11, 2011
    Posts:
    927
    thank u very much~~i trying~~,btw,i found other method by this:https://0fps.net/2013/07/09/texture-atlases-wrapping-and-mip-mapping/,need copy 4 time texture, the effect is great!but,do not know the performace as good as ddx,ddy?
     
    Last edited: Sep 2, 2017