Unity Texture Adaptive Tesselation Shader
The snippet can be accessed without any authentication.
Authored by
Thomas Schemmer
Includes a shaderside Perlin noise generator
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
}
}
}
Please register or sign in to comment