Search Unity

StructuredBuffer with Surface shader

Discussion in 'Shaders' started by Arkhivrag, Mar 6, 2013.

  1. Arkhivrag

    Arkhivrag

    Joined:
    Apr 25, 2012
    Posts:
    2,984
    Hi,
    Is there a way to use StructuredBuffer inside Surface shader?
     
    Last edited: Mar 10, 2013
    DragonCoder likes this.
  2. Arkhivrag

    Arkhivrag

    Joined:
    Apr 25, 2012
    Posts:
    2,984
    Unfortunately I could not find a way to use StructuredBuffer with Surface shader.
    I want to finish and release my "Defragment " shader, that I used for - DirectX 11 contest. I added a lot of new features and now its time to add forward/defferred lighting and shadowing. And as shader uses StructuredBuffers I will have to rewrite all rendering passes for that.

    $fragmentum.png
     
    Last edited: Jun 8, 2022
  3. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    I think it should work. Here's a snipped that will be in the documentation really soon:

    "DirectX 11 HLSL syntax and Surface Shaders

    Currently some parts of surface shader compilation pipeline do not understand DX11-specific HLSL syntax. If you're HLSL features like StructuredBuffers, RWTextures and other non-DX9 syntax, you have to wrap it into a DX11-only preprocessor macro:

    #ifdef SHADER_API_D3D11
    // DX11-specific code, e.g.
    StructuredBuffer<float4> myColors;
    RWTexture2D<float4> myRandomWriteTexture;
    #endif
    "
     
  4. Arkhivrag

    Arkhivrag

    Joined:
    Apr 25, 2012
    Posts:
    2,984
    @Aras
    thanks
     
  5. AgentFire

    AgentFire

    Joined:
    Jul 19, 2014
    Posts:
    5
    @Aras well, yeah, it now compiles, though it compiles as if there is no definition of a sctructured-array variable at all. It simply throws "variable is not defined" exception. So how can one use StructuredBuffer inside a surface shader?
     
    DragonCoder and PraetorBlue like this.
  6. AgentFire

    AgentFire

    Joined:
    Jul 19, 2014
    Posts:
    5
    Okay, for the future visitors: you will gonna have to wrap in [#ifdef SHADER_API_D3D11 ... #endif] not only the definition of a structured buffer, but all the referenced to it as well in a cascade manner.

    And yet it won't be working as expected. I'm still trying to figure out HOW is it working, it is, somehow, yet the workflow is so annoying in its inconsistency.
     
    Last edited: Apr 30, 2016
  7. v3_matt

    v3_matt

    Joined:
    Aug 10, 2015
    Posts:
    8
    Bump. @Aras, This "works," but it is incredibly difficult to use the data in conjunction with other input. Inside the #ifdef, all of my Input values are junk. For instance (for example only), I am trying to access vertex color:

    Code (CSharp):
    1.  
    2. struct Input
    3. {
    4.     float4 vColor : COLOR;
    5. };
    6.  
    7. // ...
    8.  
    9. void surf (Input IN, inout SurfaceOutputStandard o)
    10. {
    11.     #ifdef SHADER_API_D3D11
    12.     o.Albedo = IN.vColor.rgb;
    13.     #endif
    14. }
    The result paints the whole mesh red, but I have varied vertex color throughout my model. If I place that statement outside the #ifdef, it works fine. I know for sure my code is being reached, because if I write o.Albedo = fixed4(0, 0, 1) inside the #ifdef, the result paints the whole mesh blue.

    Why can't #pragma target 5.0 solve this issue? Why are we getting the compiler error in the first place? And how does it make any sense that the Inputs change inside the #ifdef?
     
    VirtusH and JohnnyBackflip like this.
  8. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    For future reference to people reading this ... as of 2019:

    1. @AgentFire's problem with having to put #ifdef everywhere doesn't seem to be needed. As per @Aras's comment, it's only needed on the declaration of the buffer
    2. @v3_matt's approach is completely wrong ... this is now documented fairly well in the manual, on the page: https://docs.unity3d.com/Manual/SL-ShaderSemantics.html -- but you have to scroll down a long way to get to the section "Vertex ID: SV_VertexID", and you MUST read the section header of the parent section, to understand what's going on.

    My simple example that worked first time:

    Firstly, you need to write some C# script to populate the buffer. e.g.

    Code (CSharp):
    1.  
    2. public class PopulateVertexAttributes : MonoBehaviour
    3. {
    4. [ContextMenu( "PopulateMaterials" )]
    5. public void DEBUG_PopulateMaterials()
    6. {
    7.   int numVerticesTotal = ... // how many verts in your mesh
    8.  
    9.   int bytesPerFloat = 4;
    10.  
    11.   int floatsInCustomAttribute = 3; ... // how many floats in your struct. I use 3 in this example
    12.   int structSize = bytesPerFloat * floatsInCustomAttribute;
    13.   ComputeBuffer cb = new ComputeBuffer( numVerticesTotal, structSize );
    14.   float[,] values = new float[numVerticesTotal,floatesInCustomAttribute];
    15.   for( int i = 0; i < values.GetLength( 0 ); i++ )
    16.   {
    17.    for( int structFloat=0; structFloat<floatsInCustomAttribute; structFloat++)
    18. {
    19. // Fill in your data here. I'm putting [..,0] = 1, [..,1] = 0, [..,2] = 0
    20. // i.e. a float3( 1,0,0 )
    21. // ... which the shader will read as "the color red"
    22.  
    23. if( structFloat == 0 )
    24.    values[ i, structFloat ] = 1f;
    25. else
    26.   values[ i, structFloat ] = 0f;
    27. }
    28.   }
    29.   cb.SetData( values );
    30.   Debug.Log( "Generated data for "+values.Length+" vertices" );
    31.  
    32.   MeshRenderer mr = .. // your mesh renderer
    33.   foreach( Material subMaterial in mr.materials ) // will leak, but works for doing a debug / test
    34.   {
    35.    subMaterial.SetBuffer( "customAttributes", cb );
    36.    Debug.Log( "Added vertex-attributes to material = "+subMaterial );
    37.   }
    38. }
    39. }
    40.  
    Then you need to modify your shader, something like this:

    Code (CSharp):
    1.  
    2. Shader "CustomVertexAttributes"
    3. {
    4. Properties
    5. {
    6. }
    7. SubShader
    8. {
    9.     Tags
    10.     {
    11.         "Queue"="Geometry"
    12.     }
    13.        
    14.     Pass
    15.     {
    16.         Tags {"LightMode" = "ForwardBase"}
    17.  
    18.         CGPROGRAM
    19.         #pragma target 3.5
    20.         #pragma vertex vert
    21.         #pragma fragment frag
    22.                
    23.         struct CustomVertexAttribute
    24.         {
    25.             float3 testColor;
    26.         };
    27.         #ifdef SHADER_API_D3D11
    28.             StructuredBuffer<CustomVertexAttribute> customAttributes;
    29.         #endif
    30.        
    31.         struct v2f
    32.         {
    33.             float4 pos : SV_POSITION;
    34.             float3 vertexColor : TEXCOORD0; // so we can do something with the custom color
    35.         };
    36.        
    37.         fixed _ShadowStrength;
    38.         v2f vert (
    39.         uint id : SV_VertexID, // note that we ADD this extra parameter to the vertex shader
    40.         appdata v
    41.         )
    42.         {
    43.             v2f o;
    44.             o.pos = UnityObjectToClipPos (v.vertex);
    45.             o.vertexColor = customAttributes[id].testColor;
    46.             return o;
    47.         }
    48.            
    49.         fixed4 frag (v2f i) : COLOR
    50.         {
    51.             float4 c;
    52.             c.xyz = i.vertexColour.xyz;
    53.             c.a = 1;
    54.             return c;
    55.         }
    56.         ENDCG
    57.     }
    58. }
    59.  
     
    AquaGeneral likes this.
  9. trapazza

    trapazza

    Joined:
    Sep 3, 2012
    Posts:
    7
    The OP asked to do this in a surface shader not in a regular vertex/fragment shader
     
    astracat111 and callumhay_unity like this.
  10. Arthur-LVGameDev

    Arthur-LVGameDev

    Joined:
    Mar 14, 2016
    Posts:
    228
    This does work fine but to get it working I continue to have to use conditionals on API -- shader target level should work but does not specifically for Metal. We end up having to conditional on both SHADER_API_D3D11 and SHADER_API_METAL.

    You also need to fill in both branches (ie the "else") or the method introspection may remove your vars/not feed the data in & you can get unexpected results.
     
    Propagant likes this.