Search Unity

Question Tessellation shader on metal without surface shader

Discussion in 'Shaders' started by AtheMathmo, May 27, 2021.

  1. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    I'm not sure if this is expected behaviour but it seems surprising that I don't even receive an error (and thus can't set a fallback).

    I have a tessellation shader that runs fine with DirectX but renders nothing on metal without throwing up any errors/warnings. I've only found examples of tessellation shaders running via surface shaders. But this doesn't work for me, as I'm using `SV_VertexID` to index into a compute buffer in my vertex program. Looking at the shader compile targets documentation, it states that:

    Does this mean that I cannot write tessellation shaders targeting metal using hull/domain programs? If not, how can I write a metal tessellation shader without using a surface shader?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Metal does tessellation in a completely different way than all other APIs. Metal doesn't support Hull, Domain, or Geometry shader stages, only vertex and fragment shader stages. Instead Metal uses a compute shader to calculate the tessellation for the mesh, and then the mesh is rendered as if it's using a normal vertex fragment shader.

    Unity did some "magic" to support tessellation on Metal. I'm not entirely sure how it works, but it seems to copy the code you add to the tessellation functions to a generated compute shader. This certainly did work at one point, however Unity's Tessellation support for the Metal API may be broken in the current builds.
    https://issuetracker.unity3d.com/is...acement-feature-is-removed-from-number-pragma

    There might also be some other limitations, like only certain kinds of hull and domain functions. It's not exactly well documented, and judging by how often people ask about it on the forums (not often) it also doesn't seem to be a frequently used feature.
     
  3. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    Thanks for the reply bgolus!

    Ah, well that seems problematic! Even beyond that, from what you said, it sounds like a bad idea to go this route at all. Unfortunately, I can't use OpenGL on Mac OS X as it doesn't support v4.3 (and therefore no compute shaders). This is an annoying gap...

    Some options I see:
    • Have a separate shader for Mac without tessellation. I'm not sure how to do this conditional shader selection (in both cs and shaderlab) though
    • Handle the tessellation myself in a separate compute shader (a bad idea, even if it's possible)
    • Just don't support Mac OS X. This is a shame as our game runs fine there otherwise and I do some development on a macbook. But it's not the end of the world
    Is there anything else I might be missing?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    You still only need a single shader, you just need more than one
    SubShader
    defined in the shader. Have the first one use tessellation, and add this to the shader code of each
    Pass
    :

    Code (csharp):
    1. #pragma exclude_renderers metal
    https://docs.unity3d.com/Manual/SL-ShaderCompilationAPIs.html

    or:
    Code (csharp):
    1. #pragma require tessellation
    https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
    Which says the shader requires hardware that supports hull and domain shader stages. There's a separate feature name for tessellation that includes Metal's implementation,
    tesshw
    .

    You then have the second
    SubShader
    with passes that do not use tessellation at all. Unity will automatically use the first
    SubShader
    that's valid for the platform it's running on.


    Alternatively, yes, you could write a compute based tessellation shader. The advantage of that option is it'll likely be faster even on non-Metal APIs.
     
  5. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    Thanks for the reply! The subshader approach seems reasonable. I'd need to change some other parts of my pipeline for the material but nothing too painful.

    Maybe this is something I should look into then... If you have any references on how I might do this I'd be pleased to see that. But otherwise I'll do some research.

    Thanks again.
     
  6. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    If you don't mind, could you outline how this would work? I assume the tessellation compute shader needs to run before the vertex program? And I'd need to store the number of vertices/triangles and do a `DrawProceduralIndirect` with the right vertex count in the arg buffer? And presumably access the vertex data via indexing a compute buffer with `SV_VertexId`?

    Can you explain how this would likely be faster than relying on the hull-tessellate-domain stages?
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Yeah, you got it basically right. There are some compute based procedural mesh tutorials for Unity out there, along with some examples of doing compute based tessellation ... though not with Unity explicitly.

    As for the faster comment, geometry and tesselation shader stages are slow. They add unpredictability into the middle of the GPU rasterization steps that can cause memory bandwidth and stalls. Modern GPUs are stupid fast, so it might seem like these aren't that big a deal and still run "fast enough", but many of the big AAA games of the last several years moved to compute based tessellation for speed and flexibility over using the hardware tesselation. Fixed function hardware isn't guaranteed to be the fastest way to do something anymore. I mean we're starting to see games and engines that skip fixed function rasterization in favor of compute based solutions because it can be faster!
     
  8. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    It took me a little while, but I managed to implement the compute based tessellation in unity (specialized a bit to my shader).

    Surprisingly, while both are pretty fast on my GPU the hardware tessellation is moderately faster. This is true even though I get to skip the tessellation stage entirely in some cases (when the camera hasn't moved for a few frames and the LOD doesn't need to change). I'm not sure what could be causing that discrepancy, but there seems to be some mistiming between the CPU and GPU...

    Compute-based tessellation profile

    Here is some profiling for 1million triangles, which are then subdivided up to 16 times. Here is my render thread profile, 0.5-0.7ms for the rendering is typical. And I spend around 40% of that waiting for gfx commands from the main thread.

    upload_2021-6-7_19-25-15.png

    The main thread tells a different story.
    upload_2021-6-7_19-26-18.png

    It spends a long time locked waiting for the GPU.

    Hardware tessellation profile

    Render thread:

    upload_2021-6-7_19-30-8.png

    Main thread:

    upload_2021-6-7_19-30-37.png


    And for good measure, here is how it looks!

    upload_2021-6-7_19-31-3.png
     
    aoikiwi and bgolus like this.
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    What's the actual GPU time? It might be slower on the CPU (due to spinning waiting for the compute ... which you could try Async compute to work around), but might be faster on the GPU. You'd need to turn on GPU profiling to see.
    https://docs.unity3d.com/Manual/ProfilerGPU.html
     
  10. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    RenderDoc's capture shows 13ms for the draw call of the compute-based tessellation (which is ~8 million instances of single triangles). Note that this doesn't include the tessellation time, which happens only for a few frames and has a minimal overhead anyway.

    The hardware tessellation draw call is only 4ms.

    It's pretty surprising to me that there is a 3x overhead for the instanced draw call per subdivided triangle vs using hardware tessellation. It is possible that my tessellation amount varies across the two methods, but they should be fairly similar in terms of total number of triangles.

    I haven't used unity's GPU profile before. I'll double-check that it gives similar results later.
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    It'll depend heavily on how you've setup your compute shader. I'm not an expert in writing optimized compute, so I'm not going to be a huge help here, but I almost wonder if you're doing the entire mesh in a single for loop.
     
  12. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    The above time doesn't include the tessellation compute shader, only the draw call. I'm following this paper: https://onrendering.com/data/papers/isubd/isubd.pdf .

    Roughly, you maintain a compact integer buffer that points to the triangle in the mesh and a subdivision key for each subdivided triangle. Then you do an instanced draw call, with an instance per subdivision key in the buffer and decode the key to get the triangle to draw. My setting is slightly difference because the grass itself is procedural and represented compactly.

    The tessellation shader itself is super quick and can be split over multiple frames nicely anyway.

    I'll need to dig into the timing a bit more. Hopefully I can figure out how to count the number of total triangles from the hardware tessellation shader and check that they are indeed in the same ball park.
     
  13. AtheMathmo

    AtheMathmo

    Joined:
    Apr 21, 2019
    Posts:
    9
    So I'm pretty sure that I had accidentally used different tessellation amounts. I was more careful with some testing this morning and as expected the instanced draw call with compute based tessellation is faster than the hardware tessellation approach!
     
    bgolus likes this.
  14. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Glad I didn't send you down a rabbit hole needlessly!
     
  15. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    May be useful - example how to handle triangle tessellation directly with Vertex Shader:

    Tessellation factor 8 for built-in quad:

    upload_2021-8-20_13-38-8.png

    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections.Generic;
    4.  
    5. [RequireComponent (typeof(Camera))]
    6. public class VertexShaderTessellation : MonoBehaviour
    7. {
    8.     public Shader TessellationShader;
    9.     [Range(1, 1024)] public int TessellationFactor = 5;
    10.     public UnityEngine.Rendering.CullMode CullMode = UnityEngine.Rendering.CullMode.Off;
    11.  
    12.     ComputeBuffer _VertexBuffer;
    13.     Material _Material;
    14.     int _VertexCount = 0;
    15.     Vector4[] _Vertices;
    16.  
    17.     byte[] ToByteArray(Vector4[] vectors)
    18.     {
    19.         byte[] bytes = new byte[sizeof(float) * vectors.Length * 4];
    20.         for (int i = 0; i < vectors.Length * 4; i++)
    21.             Buffer.BlockCopy(BitConverter.GetBytes(vectors[i / 4][i % 4]), 0, bytes, i*sizeof(float), sizeof(float));
    22.         return bytes;
    23.     }
    24.  
    25.     void Start()
    26.     {
    27.         Mesh mesh = Resources.GetBuiltinResource<Mesh>("Quad.fbx");
    28.         List<Vector4> vertices = new List<Vector4>();
    29.         for (int i = 0; i < mesh.triangles.Length; i++)
    30.         {
    31.             Vector3 p = mesh.vertices[mesh.triangles[i]];
    32.             vertices.Add(new Vector4(p.x, p.y, p.z, 1.0f));
    33.         }
    34.         _Vertices = vertices.ToArray();
    35.         Camera.main.clearFlags = CameraClearFlags.SolidColor;
    36.         _Material = new Material(TessellationShader);
    37.         _VertexBuffer = new ComputeBuffer(4 * _Vertices.Length, sizeof(float), ComputeBufferType.Raw);
    38.          byte[] bytes = ToByteArray(_Vertices);
    39.         _VertexBuffer.SetData(bytes);
    40.     }
    41.  
    42.     void OnPreRender()
    43.     {
    44.         GL.wireframe = true;
    45.     }
    46.  
    47.     void OnPostRender()
    48.     {
    49.         _Material.SetBuffer("_VertexBuffer", _VertexBuffer);
    50.         _Material.SetInt("_TessellationFactor", TessellationFactor);
    51.         _Material.SetInt("_CullMode", (int)CullMode);
    52.         _Material.SetPass(0);
    53.         _VertexCount = TessellationFactor * TessellationFactor * _Vertices.Length;
    54.         Graphics.DrawProcedural(MeshTopology.Triangles, _VertexCount, 1);
    55.     }
    56.  
    57.     void OnGUI()
    58.     {
    59.         GL.wireframe = false;
    60.         GUI.Label(new Rect(10, 10, 200, 30), "Vertex Count: " + _VertexCount.ToString());
    61.     }
    62.  
    63.     void OnDestroy()
    64.     {
    65.         _VertexBuffer.Release();
    66.         Destroy(_Material);
    67.     }
    68. }
    Code (CSharp):
    1. // GPU PRO 3, Advanced Rendering Techniques, A K Peters/CRC Press 2012
    2. // Chapter 1 - Vertex shader tessellation, Holger Gruen
    3.  
    4. Shader "Vertex Shader Tessellation"
    5. {
    6.     SubShader
    7.     {
    8.         Pass
    9.         {
    10.             Cull [_CullMode]
    11.             CGPROGRAM
    12.             #pragma vertex VSMain
    13.             #pragma fragment PSMain
    14.             #pragma target 5.0
    15.  
    16.             ByteAddressBuffer  _VertexBuffer;
    17.             int _TessellationFactor;
    18.  
    19.             float4 VSMain (uint id : SV_VertexID) : SV_POSITION
    20.             {
    21.                 uint subtriangles = (_TessellationFactor * _TessellationFactor);
    22.                 float triangleID = float (( id / 3 ) % subtriangles);
    23.                 float row = floor (sqrt( triangleID ));
    24.                 uint column = triangleID - ( row * row );
    25.                 float incuv = 1.0 / _TessellationFactor;
    26.                 float u = ( 1.0 + row ) / _TessellationFactor;
    27.                 float v = incuv * floor (float(column) * 0.5);
    28.                 u -= v;
    29.                 float w = 1.0 - u - v;
    30.                 uint address = id / (3u * subtriangles) * 3u;
    31.                 float3 p1 = asfloat(_VertexBuffer.Load4(((address + 0) * 4) << 2)).xyz;
    32.                 float3 p2 = asfloat(_VertexBuffer.Load4(((address + 1) * 4) << 2)).xyz;
    33.                 float3 p3 = asfloat(_VertexBuffer.Load4(((address + 2) * 4) << 2)).xyz;
    34.                 uint vertexID = ((id / 3u) / subtriangles) * 3u + (id % 3u);
    35.                 switch(vertexID % 3)
    36.                 {
    37.                     case 0u:
    38.                         if ((column & 1u) != 0)
    39.                         {
    40.                             v += incuv, u -= incuv;
    41.                         }
    42.                         break;
    43.                     case 1u:
    44.                         if ((column & 1u) == 0)
    45.                         {
    46.                             v += incuv, u -= incuv;
    47.                         }
    48.                         else
    49.                         {
    50.                             v += incuv, u -= incuv;
    51.                             w += incuv, u -= incuv;
    52.                         }
    53.                         break;
    54.                     case 2u:
    55.                         if ((column & 1u) == 0)
    56.                         {
    57.                             u -= incuv, w += incuv;
    58.                         }
    59.                         else
    60.                         {
    61.                             w += incuv, u -= incuv;
    62.                         }
    63.                         break;
    64.                 }
    65.                 return UnityObjectToClipPos(float4(u * p1 + v * p2 + w * p3, 1.0));
    66.             }
    67.  
    68.             float4 PSMain (float4 vertex : SV_POSITION) : SV_TARGET
    69.             {
    70.                 return (float4) 1.0;
    71.             }
    72.             ENDCG
    73.         }
    74.     }
    75. }
     
    Duch_, kripto289 and Egad_McDad like this.
  16. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Improved version (Phong tessellation), which works directly with common meshes based on 16-bit indices (without data repacking).
    Altrough this is standard shader, feel free to use Tessellation() function anywhere you want (when without Surface Shader, you can remove #if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12))

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering;
    5. using System.Runtime.InteropServices;
    6.  
    7. public class VertexShaderTessellationStandard : MonoBehaviour
    8. {
    9.     public Mesh BaseMesh;
    10.     public Shader TessellationShader;
    11.     [Range(1, 1024)] public int TessellationFactor = 5;
    12.     [Range(0f,0.5f)] public float Phong = 0.0f;
    13.     public CullMode CullMode = CullMode.Off;
    14.  
    15.     GraphicsBuffer _VertexBuffer, _IndexBuffer;
    16.     ComputeBuffer _ConstantBuffer;
    17.     Bounds _Bounds;
    18.     Material _Material;
    19.     int _VertexCount = 0, _Dimension = 0;
    20.  
    21.     struct Element
    22.     {
    23.         public int VertexOffset;
    24.         public int NormalOffset;
    25.         public int TangentOffset;
    26.         public int ColorOffset;
    27.         public int Texcoord0Offset;
    28.         public int Texcoord1Offset;
    29.         public int Texcoord2Offset;
    30.         public int Texcoord3Offset;
    31.     }
    32.  
    33.     void Start()
    34.     {
    35.         BaseMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    36.         BaseMesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
    37.         _VertexBuffer = BaseMesh.GetVertexBuffer(0);
    38.         _IndexBuffer = BaseMesh.GetIndexBuffer();
    39.         _ConstantBuffer = new ComputeBuffer(1, Marshal.SizeOf(typeof(Element)), ComputeBufferType.Constant);
    40.         _Bounds = BaseMesh.bounds;
    41.         _Material = new Material(TessellationShader);
    42.         VertexAttributeDescriptor[] attributes = BaseMesh.GetVertexAttributes();
    43.         Element element = new Element();
    44.         for (int i = 0; i < attributes.Length; i++)
    45.         {
    46.             if (attributes[i].attribute == VertexAttribute.Position) element.VertexOffset = _Dimension;
    47.             if (attributes[i].attribute == VertexAttribute.Normal) element.NormalOffset = _Dimension;
    48.             if (attributes[i].attribute == VertexAttribute.Tangent) element.TangentOffset = _Dimension;
    49.             if (attributes[i].attribute == VertexAttribute.Color) element.ColorOffset = _Dimension;
    50.             if (attributes[i].attribute == VertexAttribute.TexCoord0) element.Texcoord0Offset = _Dimension;
    51.             if (attributes[i].attribute == VertexAttribute.TexCoord1) element.Texcoord1Offset = _Dimension;
    52.             if (attributes[i].attribute == VertexAttribute.TexCoord2) element.Texcoord2Offset = _Dimension;
    53.             if (attributes[i].attribute == VertexAttribute.TexCoord3) element.Texcoord3Offset = _Dimension;
    54.             _Dimension += attributes[i].dimension;
    55.         }
    56.         _ConstantBuffer.SetData(new Element[]{element});
    57.     }
    58.  
    59.     void Update()
    60.     {
    61.         _Material.SetBuffer("_VertexBuffer", _VertexBuffer);
    62.         _Material.SetBuffer("_IndexBuffer", _IndexBuffer);
    63.         _Material.SetConstantBuffer("_ConstantBuffer", _ConstantBuffer, 0, Marshal.SizeOf(typeof(Element)));
    64.         _Material.SetInt("_TessellationFactor", TessellationFactor);
    65.         _Material.SetInt("_CullMode", (int)CullMode);
    66.         _Material.SetInt("_Dimension", _Dimension);
    67.         _Material.SetFloat("_Phong", Phong);
    68.         _VertexCount = TessellationFactor * TessellationFactor * _IndexBuffer.count;
    69.         Graphics.DrawProcedural(_Material, _Bounds, MeshTopology.Triangles, _VertexCount, 1, null, null, ShadowCastingMode.On, true, gameObject.layer);
    70.     }
    71.  
    72.     void OnGUI()
    73.     {
    74.         GUI.Label(new Rect(10, 10, 200, 30), "Vertex Count: " + _VertexCount.ToString());
    75.     }
    76.  
    77.     void OnDestroy()
    78.     {
    79.         _VertexBuffer.Release();
    80.         _IndexBuffer.Release();
    81.         _ConstantBuffer.Release();
    82.         Destroy(_Material);
    83.     }
    84. }
    Code (CSharp):
    1. Shader "Vertex Shader Tessellation Standard"
    2. {
    3.     Properties
    4.     {
    5.         [HideInInspector] _texcoord( "", 2D ) = "white" {}
    6.     }
    7.     Subshader
    8.     {
    9.         Tags { "RenderType" = "Opaque" }
    10.         Cull [_CullMode]
    11.         CGPROGRAM
    12.         #pragma surface SurfaceShader Standard vertex:VSMain fullforwardshadows addshadow
    13.         #pragma target 5.0
    14.  
    15.         struct appdata
    16.         {
    17.             float4 vertex : POSITION;
    18.             float3 normal : NORMAL;
    19.             float4 tangent : TANGENT;
    20.             float2 texcoord : TEXCOORD0;
    21.             float2 texcoord1 : TEXCOORD1;
    22.             float2 texcoord2 : TEXCOORD2;
    23.             float2 texcoord3 : TEXCOORD3;
    24.             float4 color : COLOR;
    25.             uint id : SV_VertexID;
    26.         };
    27.  
    28.         struct Input
    29.         {
    30.             float2 uv_texcoord;
    31.         };
    32.  
    33.         #if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)
    34.             ByteAddressBuffer _VertexBuffer, _IndexBuffer;
    35.             cbuffer _ConstantBuffer
    36.             {
    37.                 int VertexOffset;
    38.                 int NormalOffset;
    39.                 int TangentOffset;
    40.                 int ColorOffset;
    41.                 int Texcoord0Offset;
    42.                 int Texcoord1Offset;
    43.                 int Texcoord2Offset;
    44.                 int Texcoord3Offset;
    45.             };
    46.         #endif
    47.         int _TessellationFactor, _Dimension;
    48.         float _Phong;
    49.  
    50.         // decode one 32-bit unsigned number to two 16-bit unsigned numbers
    51.         uint2 AsUint16 (uint a)
    52.         {
    53.             uint x = a >> 16;
    54.             uint y = a & 0xFFFF;
    55.             return uint2(x, y);
    56.         }
    57.  
    58.         // modulo division
    59.         float Mod (float x, float y)
    60.         {
    61.             return x - y * floor(x/y);
    62.         }
    63.  
    64.         void Tessellation (uint id, out float3 p, out float3 n, out float4 tg, out float4 col, out float2 t0, out float2 t1, out float2 t2, out float2 t3)
    65.         {
    66.             uint subtriangles = (_TessellationFactor * _TessellationFactor);
    67.             float triangleID = float (( id / 3 ) % subtriangles);
    68.             float row = floor (sqrt( triangleID ));
    69.             uint column = triangleID - ( row * row );
    70.             float incuv = 1.0 / _TessellationFactor;
    71.             float u = ( 1.0 + row ) / _TessellationFactor;
    72.             float v = incuv * floor (float(column) * 0.5);
    73.             u -= v;
    74.             float w = 1.0 - u - v;
    75.             uint vertexID = ((id / 3u) / subtriangles) * 3u + (id % 3u);
    76.             uint offset = (vertexID / 6u) * 3;
    77.             uint3 indices;
    78.             #if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)
    79.                 indices = _IndexBuffer.Load3(offset << 2);
    80.             #endif
    81.             uint2 a = AsUint16(indices.x);
    82.             uint2 b = AsUint16(indices.y);
    83.             uint2 c = AsUint16(indices.z);
    84.             float remainder = Mod(float((vertexID / 3u)), 2.0);
    85.             uint f1 = (remainder == 0.0) ? a.y : b.x;
    86.             uint f2 = (remainder == 0.0) ? a.x : c.y;
    87.             uint f3 = (remainder == 0.0) ? b.y : c.x;
    88.             float3 p1, p2, p3, n1, n2, n3;
    89.             float4 tan1, tan2, tan3, col1, col2, col3;
    90.             float2 tx01, tx02, tx03, tx11, tx12, tx13, tx21, tx22, tx23, tx31, tx32, tx33;
    91.             #if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12)
    92.                 p1   = asfloat(_VertexBuffer.Load3((f1 * _Dimension) + VertexOffset << 2));
    93.                 p2   = asfloat(_VertexBuffer.Load3((f2 * _Dimension) + VertexOffset << 2));
    94.                 p3   = asfloat(_VertexBuffer.Load3((f3 * _Dimension) + VertexOffset << 2));
    95.                 n1   = asfloat(_VertexBuffer.Load3((f1 * _Dimension) + NormalOffset << 2));
    96.                 n2   = asfloat(_VertexBuffer.Load3((f2 * _Dimension) + NormalOffset << 2));
    97.                 n3   = asfloat(_VertexBuffer.Load3((f3 * _Dimension) + NormalOffset << 2));
    98.                 tan1 = asfloat(_VertexBuffer.Load4((f1 * _Dimension) + TangentOffset << 2));
    99.                 tan2 = asfloat(_VertexBuffer.Load4((f2 * _Dimension) + TangentOffset << 2));
    100.                 tan3 = asfloat(_VertexBuffer.Load4((f3 * _Dimension) + TangentOffset << 2));
    101.                 col1 = asfloat(_VertexBuffer.Load4((f1 * _Dimension) + ColorOffset << 2));
    102.                 col2 = asfloat(_VertexBuffer.Load4((f2 * _Dimension) + ColorOffset << 2));
    103.                 col3 = asfloat(_VertexBuffer.Load4((f3 * _Dimension) + ColorOffset << 2));
    104.                 tx01 = asfloat(_VertexBuffer.Load2((f1 * _Dimension) + Texcoord0Offset << 2));
    105.                 tx02 = asfloat(_VertexBuffer.Load2((f2 * _Dimension) + Texcoord0Offset << 2));
    106.                 tx03 = asfloat(_VertexBuffer.Load2((f3 * _Dimension) + Texcoord0Offset << 2));
    107.                 tx11 = asfloat(_VertexBuffer.Load2((f1 * _Dimension) + Texcoord1Offset << 2));
    108.                 tx12 = asfloat(_VertexBuffer.Load2((f2 * _Dimension) + Texcoord1Offset << 2));
    109.                 tx13 = asfloat(_VertexBuffer.Load2((f3 * _Dimension) + Texcoord1Offset << 2));
    110.                 tx21 = asfloat(_VertexBuffer.Load2((f1 * _Dimension) + Texcoord2Offset << 2));
    111.                 tx22 = asfloat(_VertexBuffer.Load2((f2 * _Dimension) + Texcoord2Offset << 2));
    112.                 tx23 = asfloat(_VertexBuffer.Load2((f3 * _Dimension) + Texcoord2Offset << 2));
    113.                 tx31 = asfloat(_VertexBuffer.Load2((f1 * _Dimension) + Texcoord3Offset << 2));
    114.                 tx32 = asfloat(_VertexBuffer.Load2((f2 * _Dimension) + Texcoord3Offset << 2));
    115.                 tx33 = asfloat(_VertexBuffer.Load2((f3 * _Dimension) + Texcoord3Offset << 2));
    116.             #endif
    117.             switch(vertexID % 3)
    118.             {
    119.                 case 0u:
    120.                     if ((column & 1u) != 0)
    121.                     {
    122.                         v += incuv, u -= incuv;
    123.                     }
    124.                     break;
    125.                 case 1u:
    126.                     if ((column & 1u) == 0)
    127.                     {
    128.                         v += incuv, u -= incuv;
    129.                     }
    130.                     else
    131.                     {
    132.                         v += incuv, u -= incuv;
    133.                         w += incuv, u -= incuv;
    134.                     }
    135.                     break;
    136.                 case 2u:
    137.                     if ((column & 1u) == 0)
    138.                     {
    139.                         u -= incuv, w += incuv;
    140.                     }
    141.                     else
    142.                     {
    143.                         w += incuv, u -= incuv;
    144.                     }
    145.                     break;
    146.             }
    147.             float3 location = float3(u * p1 + v * p2 + w * p3);
    148.             float3 d1 = location - n1 * (dot(location, n1) - dot(p1, n1));
    149.             float3 d2 = location - n2 * (dot(location, n2) - dot(p2, n2));
    150.             float3 d3 = location - n3 * (dot(location, n3) - dot(p3, n3));
    151.             p = _Phong * (d1 * u + d2 * v + d3 * w) + (1.0 - _Phong) * location;
    152.             n = float3(u * n1 + v * n2 + w * n3);
    153.             tg = float4(u * tan1 + v * tan2 + w * tan3);
    154.             col = float4(u * col1 + v * col2 + w * col3);
    155.             t0 = float2(u * tx01 + v * tx02 + w * tx03);
    156.             t1 = float2(u * tx11 + v * tx12 + w * tx13);
    157.             t2 = float2(u * tx21 + v * tx22 + w * tx23);
    158.             t3 = float2(u * tx31 + v * tx32 + w * tx33);
    159.         }
    160.  
    161.         void VSMain(inout appdata v)
    162.         {
    163.             float3 position = 0;
    164.             float3 normal = 0;
    165.             float4 tangent = 0;
    166.             float4 color = 0;
    167.             float2 texcoord = 0;
    168.             float2 texcoord1 = 0;
    169.             float2 texcoord2 = 0;
    170.             float2 texcoord3 = 0;
    171.             Tessellation(v.id, position, normal, tangent, color, texcoord, texcoord1, texcoord2, texcoord3);
    172.             v.vertex = float4(position, 1.0);
    173.             v.normal = normal;
    174.             v.tangent = tangent;
    175.             v.color = color;
    176.             v.texcoord = texcoord;
    177.             v.texcoord1 = texcoord1;
    178.             v.texcoord2 = texcoord2;
    179.             v.texcoord3 = texcoord3;
    180.         }
    181.  
    182.         void SurfaceShader (Input IN, inout SurfaceOutputStandard o)
    183.         {
    184.             o.Albedo = float4(1,1,1,1);
    185.             o.Normal = float3(0,0,1);
    186.             o.Metallic = 0.0;
    187.             o.Smoothness = 0.0;
    188.         }
    189.  
    190.         ENDCG
    191.     }
    192. }
     
    Duch_ and Invertex like this.