Skip to content
Snippets Groups Projects

Unity Texture Adaptive Tesselation Shader

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by Thomas Schemmer

    Includes a shaderside Perlin noise generator

    Edited
    c# 9.62 KiB
    
    // Adaptive tesselation, including perlin noise in shader
    // Perlin implementation adapted from https://adrianb.io/2014/08/09/perlinnoise.html
    
    Shader "Custom/CookieShader"
    {
        Properties
        {
    		[HideInInspector]
            _MainTex ("Texture", 2D) = "white" {}
            _Frequency("Frequency", Range(1, 6)) = 1
            _Amplitude("Amplitude", Range(1, 12)) = 2
    		[Toggle(TESSELATION)]
    		_TesselationEnabled("Tesselation Enabled", Float) = 0
    		[IntRange]
    		_Tesselation("Tesselation", Range(1, 4)) = 1
        }
            SubShader
        {
            Tags { "RenderType" = "Opaque" }
            LOD 100
    
            Pass
            {
                CGPROGRAM
                #pragma target 4.6
                #pragma vertex tess
                #pragma fragment frag
                #pragma hull hull
                #pragma domain domain
    			#pragma shader_feature TESSELATION
    
                #include "UnityCG.cginc"
    
                #define PATCH_DATA(fieldname) data.fieldname = patch[0].fieldname * bCoords.x +\
    						patch[1].fieldname * bCoords.y +\
    						patch[2].fieldname * bCoords.z;
    
    			
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
    				float2 perlin : TEXCOORD1;
                    float3 normal : NORMAL;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float2 perlin : TEXCOORD1;
                    float3 worldPos : NORMAL;
                    float4 vertex : SV_POSITION;
    
                };
    
                struct controlpoint {
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float2 uv : TEXCOORD0;
                };
    
                struct tesselationfactors {
                    float edge[3]: SV_TessFactor;
                    float inside : SV_InsideTessFactor;
                };
    
    
                sampler2D _MainTex;
                float4 _MainTex_ST;
                float _Frequency;
                float _Amplitude;
                float4 _Offset;
                float _Tesselation;
                float4 _CookieBright;
                float4 _CookieDark;
                float4 _Chocolate;
    
    
                // ------------------------ PERLIN NOISE FUNCTION -----------------------------------
    
                static const float3 vectors[12] = {
                    float3(1,1,0), float3(-1,1,0), float3(1,-1,0), float3(-1,-1,0),
                    float3(1,0,1), float3(-1,0,1), float3(1,0,-1), float3(-1,0,-1),
                    float3(0,1,1), float3(0,-1,1), float3(0,1,-1), float3(0,-1,-1)
                };
    
                static const int p[256] = { 151,160,137,91,90,15,
                    131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,   
                    190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
                    88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
                    77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
                    102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
                    135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
                    5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
                    223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
                    129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
                    251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
                    49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
                    138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
                };
    
                float fade(float t) {
                    return t * t * t * (t * (t * 6 - 15) + 10);
                }
    
                float grad(uint hash, float x, float y, float z) {
                    float3 v = vectors[(hash & 0xF) % 12];
                    return v.x * x + v.y * y + v.z * z;
                }
    
                float perlin(float3 worldPos) {
                    uint3 xyzi = uint3(abs(worldPos.x) % 256, abs(worldPos.y) % 256, abs(worldPos.z) % 256);
    
                    float3 xyzf = float3(abs(worldPos.x) - xyzi.x, abs(worldPos.y) - xyzi.y, abs(worldPos.z) - xyzi.z);
                    float3 uvw = float3(fade(xyzf.x), fade(xyzf.y), fade(xyzf.z));
    
                    uint aaa, aab, aba, abb, baa, bab, bba, bbb;
                    aaa = p[p[p[xyzi.x] + xyzi.y] + xyzi.z];
                    aba = p[p[p[xyzi.x] + xyzi.y + 1] + xyzi.z];
                    aab = p[p[p[xyzi.x] + xyzi.y] + xyzi.z + 1];
                    abb = p[p[p[xyzi.x] + xyzi.y + 1] + xyzi.z + 1];
                    baa = p[p[p[xyzi.x + 1] + xyzi.y] + xyzi.z];
                    bba = p[p[p[xyzi.x + 1] + xyzi.y + 1] + xyzi.z];
                    bab = p[p[p[xyzi.x + 1] + xyzi.y] + xyzi.z + 1];
                    bbb = p[p[p[xyzi.x + 1] + xyzi.y + 1] + xyzi.z + 1];
    
    
                    float a, b, c1, c2;
                    a = lerp(grad(aaa, xyzf.x, xyzf.y, xyzf.z),
                        grad(baa, xyzf.x - 1, xyzf.y, xyzf.z), uvw.x);
                    b = lerp(grad(aba, xyzf.x, xyzf.y - 1, xyzf.z),
                        grad(bba, xyzf.x - 1, xyzf.y - 1, xyzf.z), uvw.x);
                    c1 = lerp(a, b, uvw.y);
                    a = lerp(grad(aab, xyzf.x, xyzf.y, xyzf.z - 1),
                        grad(bab, xyzf.x - 1, xyzf.y, xyzf.z - 1), uvw.x);
                    b = lerp(grad(abb, xyzf.x, xyzf.y - 1, xyzf.z - 1),
                        grad(bbb, xyzf.x - 1, xyzf.y - 1, xyzf.z - 1), uvw.x);
                    c2 = lerp(a, b, uvw.y);
                    return (lerp(c1, c2, uvw.z) + 1) / 2;
    
                }
    
                float perlinLoop(float3 worldPos, fixed Frequency, fixed Amplitude) {
                    float total = 0;
                    float maxValue = 0;
                    float frequency = 1;
                    float amplitude = 1;
                    for (int i = 0; i < Frequency; i++) {
                        total += perlin((worldPos + _Offset.xyz) * frequency) * amplitude;
                        maxValue += amplitude;
                        amplitude *= Amplitude;
                        frequency *= 2;
                    }
                    return total / maxValue;
                }
    
    			uint perlinStep(fixed4 position) {
    				return (uint)step(0.6, perlin(mul(unity_ObjectToWorld, position)));
    			}
                
    
                
    
                // ------------------------ VERTEX / FRAGMENT -----------------------------------
    
    
                v2f vert (appdata v)
                {
                    v2f o;
    
                    float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
    				o.perlin = v.perlin;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.worldPos = worldPos;
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    return o;
                }
    
    
    			float4 frag(v2f input) : SV_Target
    			{
    				return step(0.6, input.perlin.x) + 0.3 + step(0.6, perlin(input.worldPos)) * 0.2;
                }
    
    
                // ------------------------ TESSELATION -----------------------------------
                controlpoint tess(appdata v) {
                    controlpoint p;
                    p.vertex = v.vertex;
                    p.uv = v.uv;
                    p.normal = v.normal;
                    return p;
                }
    
                [UNITY_domain("tri")]
                [UNITY_outputcontrolpoints(3)]
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_partitioning("integer")]
                [UNITY_patchconstantfunc("patchconstant")]
                controlpoint hull(InputPatch<controlpoint, 3> patch, uint id: SV_OutputControlPointID) {
                    return patch[id];
                }
    
                tesselationfactors patchconstant(InputPatch<controlpoint, 3> patch) {
    				//adaptive tesselation, according to the vertex's worldPosition
    				//each edge can be sudivided up to 4 times (with updated locations)
    				//	if only the center of an edge passes the threshold, we divide the edge by 2
    				//	if only the outer (1/3 and 2/3) points of an edge pass the threshold, we divide by 3
    				//	if all 3 points pass the threshold, we divide by 4 (which technically makes the 1/3 to a 3/4 point)
    				//the center can be subdivided up to 4 times as well, if the center or any of the center edges are above the threshold
    
    				fixed4 A = patch[1].vertex, B = patch[2].vertex, C = patch[0].vertex;
    				fixed4 M = (A + B + C) / 3;
    				uint tess = _Tesselation;
    				uint v0, v1;
    				v0 = perlinStep((A + B) / 2);
    				v1 = perlinStep(A + (B - A) * 1 / 3) | perlinStep(A + (B - A) * 2 / 3);
    				uint c0 = min(max(1, max(v0 * 2, max(v1 * 3, (v0 & v1) * 4))), tess);
    
    				v0 = perlinStep((B + C) / 2);
    				v1 = perlinStep(B + (C - B) * 1 / 3) | perlinStep(B + (C - B) * 2 / 3);
    				uint c1 = min(max(1, max(v0 * 2, max(v1 * 3, (v0 & v1) * 4))), tess);
    
    				v0 = perlinStep((C + A) / 2);
    				v1 = perlinStep(C + (A - C) * 1 / 3) | perlinStep(C + (A - C) * 2 / 3);
    				uint c2 = min(max(1, max(v0 * 2, max(v1 * 3, (v0 & v1) * 4))), tess);
    
    				v0 = perlinStep(M);
    				v1 = perlinStep(M + (A - M) * 1 / 3) | perlinStep(M + (B - M) * 1 / 3) | perlinStep(M + (C - M) * 1 / 3);
    				uint cM = min(max(1, max(v0 * 2, max(v1 * 3, (v0 & v1) * 4))), tess);
    
                    tesselationfactors tf;
    				#ifdef TESSELATION
    					tf.edge[0] = c0;
    					tf.edge[1] = c1;
    					tf.edge[2] = c2;
    					tf.inside = cM;
    				#else
    					tf.edge[0] = 1;
    					tf.edge[1] = 1;
    					tf.edge[2] = 1;
    					tf.inside = 1;
    				#endif
                    return tf;
                }
    
                [UNITY_domain("tri")]
                v2f domain(tesselationfactors factors, OutputPatch<controlpoint, 3> patch, float3 bCoords : SV_DomainLocation) {
                    appdata data;
    				PATCH_DATA(vertex)
    				PATCH_DATA(uv)
    				PATCH_DATA(normal)
    				data.perlin = fixed2(perlinStep(data.vertex), 0);
                    return vert(data);
                }
    
                ENDCG
            }
        }
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment