2020-11-28 16:54:41 +02:00

335 lines
12 KiB
HLSL

// Used Unitys "Standard geometry shader example" as a base to get unitys standard lighting and stuff w/ a custom geometry stage since surface shaders dont allow this
//and when writing from the ground up lighting has to be done by hand
// https://github.com/keijiro/StandardGeometryShader
#include "UnityCG.cginc"
#include "UnityGBuffer.cginc"
#include "UnityStandardUtils.cginc"
// Cube map shadow caster; Used to render point light shadows on platforms
// that lacks depth cube map support.
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
#define PASS_CUBE_SHADOWCASTER
#endif
// Shader uniforms
half4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
half _Glossiness;
half _Metallic;
sampler2D _BumpMap;
float _BumpScale;
sampler2D _OcclusionMap;
float _OcclusionStrength;
float _LocalTime;
// Vertex input attributes
struct Attributes {
float4 position : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord : TEXCOORD;
};
// Fragment varyings
struct Varyings {
float4 position : SV_POSITION;
#if defined(PASS_CUBE_SHADOWCASTER)
// Cube map shadow caster pass
float3 shadow : TEXCOORD0;
#elif defined(UNITY_PASS_SHADOWCASTER)
// Default shadow caster pass
#else
// GBuffer construction pass
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
float4 tspace0 : TEXCOORD1;
float4 tspace1 : TEXCOORD2;
float4 tspace2 : TEXCOORD3;
half3 ambient : TEXCOORD4;
#endif
};
//
// Vertex stage
//
Attributes Vertex(Attributes input) {
// Only do object space to world space transform.
input.position = mul(unity_ObjectToWorld, input.position);
input.normal = UnityObjectToWorldNormal(input.normal);
input.tangent.xyz = UnityObjectToWorldDir(input.tangent.xyz);
input.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex);
return input;
}
//
// Geometry stage
//
Varyings VertexOutput(float3 wpos, half3 wnrm, half4 wtan, float2 uv) {
Varyings o;
#if defined(PASS_CUBE_SHADOWCASTER)
// Cube map shadow caster pass: Transfer the shadow vector.
o.position = UnityWorldToClipPos(float4(wpos, 1));
o.shadow = wpos - _LightPositionRange.xyz;
#elif defined(UNITY_PASS_SHADOWCASTER)
// Default shadow caster pass: Apply the shadow bias.
float scos = dot(wnrm, normalize(UnityWorldSpaceLightDir(wpos)));
wpos -= wnrm * unity_LightShadowBias.z * sqrt(1 - scos * scos);
o.position = UnityApplyLinearShadowBias(UnityWorldToClipPos(float4(wpos, 1)));
#else
// GBuffer construction pass
half3 bi = cross(wnrm, wtan) * wtan.w * unity_WorldTransformParams.w;
o.position = UnityWorldToClipPos(float4(wpos, 1));
o.normal = wnrm;
o.texcoord = uv;
o.tspace0 = float4(wtan.x, bi.x, wnrm.x, wpos.x);
o.tspace1 = float4(wtan.y, bi.y, wnrm.y, wpos.y);
o.tspace2 = float4(wtan.z, bi.z, wnrm.z, wpos.z);
o.ambient = ShadeSHPerVertex(wnrm, 0);
#endif
return o;
}
float3 ConstructNormal(float3 v1, float3 v2, float3 v3) {
return normalize(cross(v2 - v1, v3 - v1));
}
float rand(float3 co){
return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
}
float3x3 AngleAxis3x3(float angle, float3 axis){
float c, s;
sincos(angle, s, c);
float t = 1 - c;
float x = axis.x;
float y = axis.y;
float z = axis.z;
return float3x3(
t * x * x + c, t * x * y - s * z, t * x * z + s * y,
t * x * y + s * z, t * y * y + c, t * y * z - s * x,
t * x * z - s * y, t * y * z + s * x, t * z * z + c
);
}
float _BladeHeight, _BladeHeightRandom, _BladeWidth, _BladeWidthRandom, _BendRotationRandom;
int _Test;
[instance(5)]
[maxvertexcount(42)]
void Geometry( triangle Attributes input[3], uint pid : SV_PrimitiveID, inout TriangleStream<Varyings> outStream, uint InstanceID : SV_GSInstanceID) {
//Vertex inputs;
//
//float2 uv0 = input[0].texcoord;
//float2 uv1 = input[1].texcoord;
//float2 uv2 = input[2].texcoord;
float3 pos = input[0].position;
float3 vNormal = input[0].normal;
float4 vTangent = input[0].tangent;
float3 vBinormal = cross (vNormal,vTangent)*vTangent.w;
float height = (rand(pos.zyx) * 2 - 1) * _BladeHeightRandom + _BladeHeight;
float width = (rand(pos.xzy) * 2 - 1) * _BladeWidthRandom + _BladeWidth;
float3x3 tangentToLocal = float3x3(
vTangent.x, vBinormal.x, vNormal.x,
vTangent.y, vBinormal.y, vNormal.y,
vTangent.z, vBinormal.z, vNormal.z
);
//TODO add geometry instancing
//TODO create nodes based on world pos
//TODO find out if need to remember geometry instancing index to create mor tris
//TODO keep track of how many verts used
//Add node
//float3 wn = ConstructNormal(wp3, wp4, wp5);
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3( width, 0, 0 )),half3(0,1,0),float4(1,1,0,0), float2(1.25,0) ));
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3(-width, 0, 0 )),half3(0,1,0),float4(1,1,0,0), float2(-0.25,0) ));
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3( 0 , 0, height)),half3(0,1,0),float4(1,1,0,0), float2(0.5,1.5) ));
//outStream.RestartStrip();
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3(0, width, 0 )),half3(0,1,0),float4(1,1,0,0), float2(1.25,0) ));
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3(0, -width, 0 )),half3(0,1,0),float4(1,1,0,0), float2(-0.25,0) ));
//outStream.Append(VertexOutput(pos + mul(transformationMatrix, float3(0, 0 , height)),half3(0,1,0),float4(1,1,0,0), float2(0.5,1.5) ));
//outStream.RestartStrip();
//Do triangle
float3 wp0 = input[0].position.xyz;
float3 wp1 = input[1].position.xyz;
float3 wp2 = input[2].position.xyz;
//1. Order points
float3 p1 = max(wp0,max(wp1,wp2));
float3 p2;
float3 p3 = min(wp0,min(wp1,wp2));
if (all(p1 >= wp0) && all(p3 <= wp0)) {p2 = wp0;}
else if(all(p1 >= wp1) && all(p3 <= wp1)) {p2 = wp1;}
else if(all(p1 >= wp2) && all(p3 <= wp2)) {p2 = wp2;}
//2. Get directions
float3 vec1 = p3-p1;
float3 vec2 = p2-p1;
float3 vec3 = p3-p2;
float dir1Len = length(vec1);
float dir2Len = length(vec2);
float dir3Len = length(vec3);
float3 dir1 = vec1 / dir1Len;
float3 dir2 = vec2 / dir2Len;
float3 dir3 = vec3 / dir3Len;
//3. Define values
float unit = 0.5;
float3 step1 = dir1 * unit;
float3 step2 = dir2 * unit * abs(vec2.x/vec1.x);
float3 step3 = dir3 * unit;
float3 current1 = p1;
float3 current2 = p1;
//4. loop
int unitsToX = abs(floor(vec1.x/unit));
for (int i = 0; i < unitsToX; ++i) {
current1 += step1;
current2 += step2;
int unitsToZ = abs(floor((current1.z-current2.z)/unit));
//1. create start and ends
float3 current = floor(current1);
for (int j = 0; j < unitsToZ; ++j) {//loop and create nodes
float3x3 facingRotationMatrix = AngleAxis3x3(rand(pos) * UNITY_TWO_PI, float3(0, 0, 1));
float3x3 bendRotationMatrix = AngleAxis3x3(rand(pos.zzx) * _BendRotationRandom * UNITY_PI * 0.5, float3(-1, 0, 0));
float3x3 transformationMatrix = mul(mul(tangentToLocal, facingRotationMatrix),bendRotationMatrix);
float k = i+j+1;
//-Creat node- START
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3( width, 0, 0 )),half3(0,1,0),float4(1,1,0,0), float2(1.25,0) ));
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3(-width, 0, 0 )),half3(0,1,0),float4(1,1,0,0), float2(-0.25,0) ));
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3( 0 , 0, height*k)),half3(0,1,0),float4(1,1,0,0), float2(0.5,1.5) ));
outStream.RestartStrip();
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3(0, width, 0 )),half3(0,1,0),float4(1,1,0,0), float2(1.25,0) ));
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3(0, -width, 0 )),half3(0,1,0),float4(1,1,0,0), float2(-0.25,0) ));
outStream.Append(VertexOutput(current + mul(transformationMatrix, float3(0, 0 , height*k)),half3(0,1,0),float4(1,1,0,0), float2(0.5,1.5) ));
outStream.RestartStrip();
//-Create node- END
current.z += unit;
}
}
// Extrusion amount
//float ext = saturate(0.4 - cos(_LocalTime * UNITY_PI * 2) * 0.41);
//ext *= 1 + 0.3 * sin(pid * 832.37843 + _LocalTime * 88.76);
//
//// Extrusion points
//float3 offs = ConstructNormal(wp0, wp1, wp2) * ext;
//float3 wp3 = wp0 + offs;
//float3 wp4 = wp1 + offs;
//float3 wp5 = wp2 + offs;
//
//// Cap triangle
//float3 wn = ConstructNormal(wp3, wp4, wp5);
//float np = saturate(ext * 10);
//float3 wn0 = lerp(input[0].normal, wn, np);
//float3 wn1 = lerp(input[1].normal, wn, np);
//float3 wn2 = lerp(input[2].normal, wn, np);
//outStream.Append(VertexOutput(wp3, wn0, input[0].tangent, uv0));
//outStream.Append(VertexOutput(wp4, wn1, input[1].tangent, uv1));
//outStream.Append(VertexOutput(wp5, wn2, input[2].tangent, uv2));
//outStream.RestartStrip();
//
//// Side faces
//float4 wt = float4(normalize(wp3 - wp0), 1); // world space tangent
//wn = ConstructNormal(wp3, wp0, wp4);
//outStream.Append(VertexOutput(wp3, wn, wt, uv0));
//outStream.Append(VertexOutput(wp0, wn, wt, uv0));
//outStream.Append(VertexOutput(wp4, wn, wt, uv1));
//outStream.Append(VertexOutput(wp1, wn, wt, uv1));
//outStream.RestartStrip();
//
//wn = ConstructNormal(wp4, wp1, wp5);
//outStream.Append(VertexOutput(wp4, wn, wt, uv1));
//outStream.Append(VertexOutput(wp1, wn, wt, uv1));
//outStream.Append(VertexOutput(wp5, wn, wt, uv2));
//outStream.Append(VertexOutput(wp2, wn, wt, uv2));
//outStream.RestartStrip();
//
//wn = ConstructNormal(wp5, wp2, wp3);
//outStream.Append(VertexOutput(wp5, wn, wt, uv2));
//outStream.Append(VertexOutput(wp2, wn, wt, uv2));
//outStream.Append(VertexOutput(wp3, wn, wt, uv0));
//outStream.Append(VertexOutput(wp0, wn, wt, uv0));
//outStream.RestartStrip();
}
//
// Fragment phase
//
#if defined(PASS_CUBE_SHADOWCASTER)
// Cube map shadow caster pass
half4 Fragment(Varyings input) : SV_Target {
float depth = length(input.shadow) + unity_LightShadowBias.x;
return UnityEncodeCubeShadowDepth(depth * _LightPositionRange.w);
}
#elif defined(UNITY_PASS_SHADOWCASTER)
// Default shadow caster pass
half4 Fragment() : SV_Target { return 0; }
#else
float _AlphaCutoff;
// GBuffer construction pass
void Fragment( Varyings input, out half4 outGBuffer0 : SV_Target0, out half4 outGBuffer1 : SV_Target1, out half4 outGBuffer2 : SV_Target2, out half4 outEmission : SV_Target3) {
// Sample textures
float4 sample = tex2D(_MainTex, input.texcoord);
if(sample.a <= _AlphaCutoff) clip(-1);
half3 albedo = sample.rgb * _Color.rgb;
half4 normal = tex2D(_BumpMap, input.texcoord);
normal.xyz = UnpackScaleNormal(normal, _BumpScale);
half occ = tex2D(_OcclusionMap, input.texcoord).g;
occ = LerpOneTo(occ, _OcclusionStrength);
// PBS workflow conversion (metallic -> specular)
half3 c_diff, c_spec;
half refl10;
c_diff = DiffuseAndSpecularFromMetallic(
albedo, _Metallic, // input
c_spec, refl10 // output
);
// Tangent space conversion (tangent space normal -> world space normal)
float3 wn = normalize(float3( dot(input.tspace0.xyz, normal), dot(input.tspace1.xyz, normal), dot(input.tspace2.xyz, normal) ));
// Update the GBuffer.
UnityStandardData data;
data.diffuseColor = c_diff;
data.occlusion = occ;
data.specularColor = c_spec;
data.smoothness = _Glossiness;
data.normalWorld = wn;
UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2);
// Calculate ambient lighting and output to the emission buffer.
float3 wp = float3(input.tspace0.w, input.tspace1.w, input.tspace2.w);
half3 sh = ShadeSHPerPixel(data.normalWorld, input.ambient, wp);
outEmission = half4(sh * c_diff, 1) * occ;
}
#endif