summaryrefslogtreecommitdiff
path: root/VRCSDK3Avatars/Assets/_PoiyomiShaders/ModularShader/Editor/TPS/Dev Resources/tps.cginc
blob: c427a2d6747fbd4a1792a5b174904ec956661ddb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
float _TPS_PenetratorLength;
float3 _TPS_PenetratorScale;
float3 _TPS_PenetratorForward;
float3 _TPS_PenetratorRight;
float3 _TPS_PenetratorUp;
float _TPS_VertexColors;
float _TPS_MinimumOrificeDistance;
float _TPS_BezierStart;
float _TPS_BezierSmoothness;
float _TPS_Squeeze;
float _TPS_SqueezeDistance;
float _TPS_Buldge;
float _TPS_BuldgeDistance;
float _TPS_BuldgeFalloffDistance;

float _TPS_PumpingStrength;
float _TPS_PumpingSpeed;
float _TPS_PumpingWidth;

float _TPS_IdleSkrinkWidth;
float _TPS_IdleSkrinkLength;

float _TPS_BufferedDepth;
float _TPS_BufferedStrength;

UNITY_DECLARE_SCREENSPACE_TEXTURE(_TPS2_Grabpass);

#define ID_HOLE 0.41
#define ID_RING 0.42
#define ID_NORM 0.45

#define TPS_RECIEVER_DIST 0.01

#define PENETRATORTYPE_RING 1
#define PENETRATORTYPE_HOLE 2

//https://forum.unity.com/threads/point-light-in-v-f-shader.499717/ UGA BUGA, Unity Forums pogggggggg
float LightRange(int i) {
	return (0.005 * sqrt(1000000.0 - unity_4LightAtten0[i])) / sqrt(unity_4LightAtten0[i]);
}

float4 GetLightPositionInObjectSpace(int i) {
	return mul(unity_WorldToObject, float4(unity_4LightPosX0[i], unity_4LightPosY0[i], unity_4LightPosZ0[i], 1));
}

uint4 f32touint8(float4 input)
{
	input.r = LinearToGammaSpaceExact(input.r);
	input.g = LinearToGammaSpaceExact(input.g);
	input.b = LinearToGammaSpaceExact(input.b);
	return round(input * 255);
}

float decodeFloatFromARGB8(float4 rgba)
{
	uint4 u = f32touint8(rgba);
	return asfloat(u.x + (u.y << 8) + (u.z << 16) + (u.w << 24));
}

#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)
	#define SampleGrabpass(tex,uv) UNITY_SAMPLE_TEX2DARRAY_LOD(tex, float3(uv.xy, 0), 0)
#elif UNITY_SINGLE_PASS_STEREO
	#define SampleGrabpass(tex,uv) tex2Dlod(tex, float4(uv.x * 0.5, uv.y,0,0))
#else
	#define SampleGrabpass(tex,uv) tex2Dlod(tex, float4(uv.xy,0,0))
#endif

#define IsLightOrificeType(range,type) (abs(range - type) < 0.001)
#define IsLightAnyOrificeType(range) ((IsLightOrificeType(range,ID_RING)) || (IsLightOrificeType(range,ID_HOLE)))
#define VectorLengthIntoDirection(v,d) dot(v,d)

float FindTPSSystem(inout float3 orificePosition, inout float3 orificeNormal, inout float penetratorType) {
	//find lights
	float lightRanges[4];
	float3 lightPositions[4];
	float3 lightPositionsWorld[4];
	[loop] for (int f = 0; f < 4; f++) {
		lightPositions[f] = GetLightPositionInObjectSpace(f);
		lightPositionsWorld[f] = length(mul(unity_ObjectToWorld, float4(lightPositions[f], 1)));
		lightRanges[f] = LightRange(f);
	}
	//orifice hole + type
	float closestOrfDist = 100000000;
	[loop]for (int i = 0; i < 4; i++) {
		if (abs(lightRanges[i] - ID_RING) < 0.001 || abs(lightRanges[i] - ID_HOLE) < 0.001) {
			//Ceck if orifice light is in forward direction
			if (dot(_TPS_PenetratorForward, lightPositions[i]) > 0) {
				//Check if normal exisits
				float3 foundNormal = -_TPS_PenetratorForward;
				bool hasNormal = false;
				[loop] for (int n = 0; n < 4; n++) {
					//orifice normal
					if (IsLightOrificeType(lightRanges[n], ID_NORM) && distance(lightPositionsWorld[n] , lightPositionsWorld[i]) < 0.05f) {
						foundNormal = normalize(lightPositions[n] - lightPositions[i]);
						hasNormal = true;
					}
				}
				//if normal right direction and is cloest orifice
				if (dot(foundNormal, _TPS_PenetratorForward) < 0 && length(lightPositions[i]) < closestOrfDist) {
					closestOrfDist = length(lightPositions[i]);
					orificePosition = lightPositions[i];
					penetratorType = abs(lightRanges[i] - ID_HOLE) < 0.001 ? PENETRATORTYPE_HOLE : PENETRATORTYPE_RING;
					orificeNormal = (float3)0 * (1 - hasNormal) + foundNormal * hasNormal;
				}
			}
		}
	}
	//Guess normal of normal not existing
	if (length(orificeNormal) == 0) {
		orificeNormal = lerp(normalize(-orificePosition), -_TPS_PenetratorForward, max(dot(normalize(orificePosition), _TPS_PenetratorForward), 0.5));
	}

#if TPS_IsSkinnedMesh
	return (closestOrfDist) < (2 * _TPS_PenetratorLength);
#else
	return (closestOrfDist) < (1.5 * _TPS_PenetratorLength);
#endif
}

//https://vicrucann.github.io/tutorials/bezier-shader/
float3 toBezier(float t, float3 P0, float3 P1, float3 P2, float3 P3)
{
	float t2 = t * t;
	float one_minus_t = 1.0 - t;
	float one_minus_t2 = one_minus_t * one_minus_t;
	return (P0 * one_minus_t2 * one_minus_t + P1 * 3.0 * t * one_minus_t2 + P2 * 3.0 * t2 * one_minus_t + P3 * t2 * t);
}

void ApplyTPSPenetrator(inout float4 vertex, inout float3 normal, float3 vertexColor) {
	float orificeType = (float3)0;
	float3 orificePosition = (float3)0;
	float3 orificeNormal = (float3)0;

	// Adjust pen length for scaled non skinned renderers
#if !TPS_IsSkinnedMesh
		float3 scale = float3(
			length(float3(unity_ObjectToWorld[0].x, unity_ObjectToWorld[1].x, unity_ObjectToWorld[2].x)), // scale x axis
			length(float3(unity_ObjectToWorld[0].y, unity_ObjectToWorld[1].y, unity_ObjectToWorld[2].y)), // scale y axis
			length(float3(unity_ObjectToWorld[0].z, unity_ObjectToWorld[1].z, unity_ObjectToWorld[2].z))  // scale z axis
			);
		// Mesh Renderers arent getting skinned => verticies are still in object space => _TPS_PenetratorLength needs to be scaled to object space
		_TPS_PenetratorLength = _TPS_PenetratorLength / abs(VectorLengthIntoDirection(scale, _TPS_PenetratorForward));
#endif

	//Idle shrinkage
	float2 shrinkage = float2(_TPS_IdleSkrinkWidth, _TPS_IdleSkrinkLength);

	//Idle Position
	//Fix idle gravity
	float3 targetPosition = _TPS_PenetratorForward * _TPS_PenetratorLength;
	float3 targetNormal = -_TPS_PenetratorForward;

	//Default values
	float tpsSmoothStart = 0;
	float tpsSmoothStart2 = 0;

	float bezierSmoothness = _TPS_BezierSmoothness;
	float penetrationDepth = 0;

	//Find tps system, calculate values
	[branch] if (FindTPSSystem(orificePosition, orificeNormal, orificeType) )
	{
#if TPS_IsSkinnedMesh
		tpsSmoothStart = saturate((_TPS_PenetratorLength * 2 - length(orificePosition)) / _TPS_PenetratorLength);
#else
		tpsSmoothStart = saturate((_TPS_PenetratorLength * 1.5 - length(orificePosition)) / (_TPS_PenetratorLength * 0.5));
#endif
		tpsSmoothStart2 = saturate((_TPS_PenetratorLength - length(orificePosition)) * 20);

		targetPosition = lerp(targetPosition, orificePosition, tpsSmoothStart);
		targetNormal = lerp(targetNormal, orificeNormal, tpsSmoothStart);

		penetrationDepth = 1 - saturate(length(orificePosition) / _TPS_PenetratorLength);
		//smoothness goes to 0 when close to oriface to prevent weird batching
		bezierSmoothness = lerp(_TPS_BezierSmoothness, 0, penetrationDepth);

		shrinkage = lerp(float2(_TPS_IdleSkrinkWidth, _TPS_IdleSkrinkLength), float2(1, 1), saturate(tpsSmoothStart * 2));
	}

	//Vertex color stuff for dyn bones
	
#if TPS_IsSkinnedMesh
	// Vertex color has been baked with different rotation
	float penZ = VectorLengthIntoDirection(vertexColor * _TPS_PenetratorScale, _TPS_PenetratorForward);
#else
	float penZ = VectorLengthIntoDirection(vertex, _TPS_PenetratorForward);
#endif

	float3 bezier0 = _TPS_PenetratorForward * _TPS_BezierStart;
	float3 bezier0Out = bezier0 + _TPS_PenetratorForward * bezierSmoothness;
	float3 bezier1 = targetPosition;
	float3 bezier1In = bezier1 + targetNormal * bezierSmoothness;

	float bezierStrengthUncapped = ((penZ - _TPS_BezierStart) / distance(bezier0, bezier1));
	float bezierStrength = saturate(bezierStrengthUncapped);

	//Bezier curve, calculate two points, calculate forward, right, up vectors
	float3 bezierPoint = toBezier(bezierStrength, bezier0, bezier0Out, bezier1In, bezier1);
	float3 bezierPoint2 = toBezier(bezierStrength + 0.01f, bezier0, bezier0Out, bezier1In, bezier1);

	if (orificeType == PENETRATORTYPE_RING && bezierStrength == 1 && tpsSmoothStart == 1) {
		bezierPoint = orificePosition + (penZ - _TPS_BezierStart - length(orificePosition.xyz)) * -orificeNormal;
		bezierPoint2 = bezierPoint - orificeNormal;
	}

	float3 bezierForward = normalize(bezierPoint2 - bezierPoint);
	float3 bezierRight = normalize(cross(_TPS_PenetratorUp, bezierForward));
	float3 bezierUp = normalize(cross(bezierForward, bezierRight));

	//calculate new position and normal
	if (bezierStrength > 0) {
		//pumping
		float sizeChange = 1;
		if (_TPS_PumpingStrength > 0) {
			sizeChange *= lerp(1 - _TPS_PumpingStrength * tpsSmoothStart, 1 + _TPS_PumpingStrength * tpsSmoothStart, abs(sin(_Time.y * -_TPS_PumpingSpeed + bezierStrengthUncapped / _TPS_PumpingWidth)));
		}
		//buldging + squeezing
		float buldgeLerp = 0;
		if (bezierStrength < 1 - _TPS_BuldgeFalloffDistance)
			buldgeLerp = saturate((bezierStrength - 1 + _TPS_BuldgeDistance + _TPS_BuldgeFalloffDistance) / _TPS_BuldgeDistance);
		else
			buldgeLerp = saturate((-bezierStrength + 1) / _TPS_BuldgeFalloffDistance);

		//Squeezing while outside oriface makes penetrator looks weirly streched
		float squeeze = lerp(0, _TPS_Squeeze, saturate(penetrationDepth * 20));
		sizeChange *= lerp(1, 1 - squeeze, saturate(1 - abs(bezierStrengthUncapped - 1) / _TPS_SqueezeDistance) * tpsSmoothStart);
		sizeChange *= lerp(1, 1 + _TPS_Buldge, buldgeLerp * tpsSmoothStart2);

		//Calc idle shrinkage
		shrinkage = lerp(1, shrinkage, saturate((penZ - _TPS_BezierStart) * 20));
		//Apply vertex + normal
#if TPS_IsSkinnedMesh
		float x = VectorLengthIntoDirection(vertexColor * _TPS_PenetratorScale, _TPS_PenetratorRight);
		float y = VectorLengthIntoDirection(vertexColor * _TPS_PenetratorScale, _TPS_PenetratorUp);
#else
		float x = VectorLengthIntoDirection(vertex, _TPS_PenetratorRight);
		float y = VectorLengthIntoDirection(vertex, _TPS_PenetratorUp);
#endif
		float3 normalX = VectorLengthIntoDirection(normal, _TPS_PenetratorRight);
		float3 normalY = VectorLengthIntoDirection(normal, _TPS_PenetratorUp);
		float3 normalZ = VectorLengthIntoDirection(normal, _TPS_PenetratorForward);

		float3 vertexZ = _TPS_PenetratorForward * VectorLengthIntoDirection(vertex, _TPS_PenetratorForward);
		float3 vertexXY = vertex.xyz - vertexZ;
		vertex.xyz = shrinkage.y * vertexZ + shrinkage.x * vertexXY;

		vertex.xyz = lerp(vertex.xyz, bezierPoint + x * bezierRight * sizeChange + y * bezierUp * sizeChange, tpsSmoothStart); //for dynamic bones, lerp between original and skinned vertecies
		normal.xyz = lerp(normal.xyz, normalX * bezierRight + normalY * bezierUp + normalZ * bezierForward, tpsSmoothStart); //for dynamic bones, lerp between original and skinned vertecies
	}
}

float TPSBufferedDepth(float3 vertex, float3 vertexColor) {
#if TPS_IsSkinnedMesh
	float penZ = VectorLengthIntoDirection(vertexColor * _TPS_PenetratorScale, _TPS_PenetratorForward);
#else
	float penZ = VectorLengthIntoDirection(vertex, _TPS_PenetratorForward);
#endif
	return saturate((penZ - (1 - _TPS_BufferedDepth)) * 10) * _TPS_BufferedStrength;
}