-
Notifications
You must be signed in to change notification settings - Fork 869
Expand file tree
/
Copy pathGeometryUtils.hlsl
More file actions
157 lines (136 loc) · 5.84 KB
/
GeometryUtils.hlsl
File metadata and controls
157 lines (136 loc) · 5.84 KB
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
// Given a triangle edge defined by 2 verts, calculate 2 new verts that define the same edge,
// pushed outwards enough to cover centroids of any pixels which may intersect the triangle.
void OffsetEdge(float2 v1, float2 v2, float2 pixelSize, out float2 v1Offset, out float2 v2Offset)
{
// Find the normal of the edge
float2 edge = v2 - v1;
float2 normal = normalize(float2(-edge.y, edge.x));
// Find the amount to offset by. This is the semidiagonal of the pixel box in the same quadrant as the normal.
float2 semidiagonal = pixelSize / sqrt(2.0);
semidiagonal *= float2(normal.x > 0 ? 1 : -1, normal.y > 0 ? 1 : -1);
// Offset the edge
v1Offset = v1 + semidiagonal;
v2Offset = v2 + semidiagonal;
}
// Given 2 lines defined by 2 points each, find the intersection point.
float2 LineIntersect(float2 p1, float2 p2, float2 p3, float2 p4, float eps = 0.0f)
{
// Line p1p2 represented as a1x + b1y = c1
float a1 = p2.y - p1.y;
float b1 = p1.x - p2.x;
float c1 = a1 * p1.x + b1 * p1.y;
// Line p3p4 represented as a2x + b2y = c2
float a2 = p4.y - p3.y;
float b2 = p3.x - p4.x;
float c2 = a2 * p3.x + b2 * p3.y;
float determinant = a1 * b2 - a2 * b1;
if (abs(determinant) <= eps) // Parallel lines - return any valid point.
return p1;
float x = b2 * c1 - b1 * c2;
float y = a1 * c2 - a2 * c1;
return float2(x, y) / determinant;
}
// Check which side the point p is on, relative to the line cp1-cp2.
// Optionally with an epsilon value within which points are considered outside.
bool IsInside(float2 p, float2 cp1, float2 cp2, float eps = 0.0f)
{
return (cp2.x - cp1.x) * (p.y - cp1.y) - (cp2.y - cp1.y) * (p.x - cp1.x) >= eps;
}
// Clip a polygon with a line defined by two points, in place.
// https://en.wikipedia.org/wiki/Sutherland-Hodgman_algorithm
void ClipPolygonWithLine(inout float2 polygon[6], inout uint polygonLength, float2 cp1, float2 cp2, float inside_eps = 0.0f, float parallel_eps = 0.0f)
{
float2 result[6];
uint resultLength = 0;
for (uint i = 0; i < polygonLength; i++)
{
float2 s = polygon[i];
float2 e = polygon[(i + 1) % polygonLength];
if (IsInside(e, cp1, cp2, inside_eps)) // At least one endpoint is inside
{
if (!IsInside(s, cp1, cp2, inside_eps)) // Only the end point is inside, add the intersection
{
result[resultLength] = LineIntersect(cp1, cp2, s, e, parallel_eps);
resultLength++;
}
result[resultLength] = e;
resultLength++;
}
else if (IsInside(s, cp1, cp2, inside_eps)) // Only the start point is inside, add the intersection
{
result[resultLength] = LineIntersect(cp1, cp2, s, e, parallel_eps);
resultLength++;
}
}
polygonLength = resultLength;
for (uint p = 0; p < polygonLength; p++)
polygon[p] = result[p];
}
// Clip a polygon to the bounding box of a texel at the given position
bool ClipPolygonWithTexel(float2 texelPosition, inout float2 polygon[6], inout uint polygonLength, float inside_eps = 0.0f, float parallel_eps = 0.0f)
{
// Get the bounding box of the texel to clip with.
float2 clipPolygon[4] =
{
texelPosition + float2(-0.5, -0.5),
texelPosition + float2(0.5, -0.5),
texelPosition + float2(0.5, 0.5),
texelPosition + float2(-0.5, 0.5),
};
// Clip to each edge of the quad
for (uint edge = 0; edge < 4; edge++)
{
ClipPolygonWithLine(polygon, polygonLength, clipPolygon[edge], clipPolygon[(edge + 1) % 4], inside_eps, parallel_eps);
}
return polygonLength > 0;
}
// Read the parent triangle of a vertex from a vertex buffer, and transform it to the desired space.
void ReadParentTriangle(StructuredBuffer<float2> vertexBuffer, uint vertexId, float4 scaleAndOffset, out float2 tri[3])
{
uint triId = vertexId / 3;
uint baseVertexId = triId * 3;
tri[0] = vertexBuffer[baseVertexId + 0].xy * scaleAndOffset.xy + scaleAndOffset.zw;
tri[1] = vertexBuffer[baseVertexId + 1].xy * scaleAndOffset.xy + scaleAndOffset.zw;
tri[2] = vertexBuffer[baseVertexId + 2].xy * scaleAndOffset.xy + scaleAndOffset.zw;
// Flip the winding order if it doesn't follow Unity's convention
float2 edgeAB = tri[1] - tri[0];
float2 edgeAC = tri[2] - tri[0];
bool flipWinding = ((edgeAB.x * edgeAC.y) - (edgeAB.y * edgeAC.x)) > 0;
if (flipWinding)
{
float2 temp = tri[1];
tri[1] = tri[2];
tri[2] = temp;
}
}
// Given an effective resolution, a triangle, and a specific vertex [0; 2], calculate a new position for the
// vertex, which will result in conservative rasterization when fed to a non-conservative rasterizer.
void ExpandTriangleForConservativeRasterization(
uint2 resolution,
float2 tri[3],
uint vertexId,
out float4 triangleAABB,
out float4 vertex,
float eps = 0.0f)
{
float2 pixelSize = (1.0 / resolution);
// Calculate the AABB to clip off the overly conservative edges
float2 aabbMin = min(min(tri[0], tri[1]), tri[2]);
float2 aabbMax = max(max(tri[0], tri[1]), tri[2]);
triangleAABB = (float4(aabbMin - (pixelSize / 2), aabbMax + (pixelSize / 2))) * resolution.xyxy;
// Get offset lines
float2 v1Off1, v2Off1;
OffsetEdge(tri[0], tri[1], pixelSize, v1Off1, v2Off1);
float2 v2Off2, v3Off2;
OffsetEdge(tri[1], tri[2], pixelSize, v2Off2, v3Off2);
float2 v3Off3, v1Off3;
OffsetEdge(tri[2], tri[0], pixelSize, v3Off3, v1Off3);
// Find their intersections. This is the new triangle
tri[0] = LineIntersect(v1Off1, v2Off1, v3Off3, v1Off3, eps);
tri[1] = LineIntersect(v2Off2, v3Off2, v1Off1, v2Off1, eps);
tri[2] = LineIntersect(v3Off3, v1Off3, v2Off2, v3Off2, eps);
vertex = float4(tri[vertexId % 3]*2-1, 0, 1);
#if UNITY_UV_STARTS_AT_TOP
vertex.y = -vertex.y;
#endif
}