/ Assets / Resources / Lineart / ComputeShaders / SharpEdgesToStrokes.compute
SharpEdgesToStrokes.compute
  1  #pragma kernel Initialize
  2  #pragma kernel Reduce
  3  #pragma kernel CalculateRanksAndDistances
  4  #pragma kernel FindStrokeTail
  5  #pragma kernel ResetNextPointer
  6  #pragma kernel InitializeRanksAndDistances
  7  
  8  
  9  
 10  #include "silhouette_defines.hh"
 11  
 12  RWStructuredBuffer<StrokeData> _strokes; // read from silhouette pass (the same structured layout)
 13  
 14  StructuredBuffer<uint> _nextPointerSrc;
 15  RWStructuredBuffer<uint> _nextPointerDst; // per-stroke successor index (adjacency)
 16  
 17  uint _NumVerts;
 18  
 19  // Convert float to a lexicographically sortable uint representation (preserves ordering including negatives)
 20  uint FloatToSortableUint(float f)
 21  {
 22      uint u = asuint(f);
 23      // flip sign bit so that integer ordering matches float ordering
 24      return (u & 0x80000000u) ? ~u : (u ^ 0x80000000u);
 25  }
 26  
 27  bool equals(float3 a, float3 b)
 28  {
 29      return (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
 30  }
 31  
 32  [numthreads(64,1,1)]
 33  void Initialize(uint3 id : SV_DispatchThreadID)
 34  {
 35      uint i = id.x;
 36      if (i >= _NumVerts) return;
 37      StrokeData s = _strokes[i];
 38      _strokes[i].minPoint = i;
 39  
 40      int succ = s.adj;
 41      _nextPointerDst[i] = succ;
 42      if (succ >= 0)
 43      {
 44          StrokeData succStroke = _strokes[succ];
 45          if (!equals(succStroke.pos, s.pos))
 46          {
 47              _strokes[succ].flags|=STROKE_FLAG_IS_CHILD;
 48          }
 49      }
 50      
 51  }
 52  
 53  [numthreads(64,1,1)]
 54  void ResetNextPointer(uint3 id : SV_DispatchThreadID)
 55  {
 56      uint i = id.x;
 57      if (i >= _NumVerts) return;
 58      StrokeData s = _strokes[i];
 59      _nextPointerDst[i] = (s.adj >= 0) ? (uint)s.adj : INVALID_UINT;
 60      _strokes[i].flags&=!STROKE_FLAG_IS_CHILD;
 61  }
 62  
 63  
 64  bool lessThan(float3 a, float3 b)
 65  {
 66      if (a.x != b.x) return a.x < b.x;
 67      if (a.y != b.y) return a.y < b.y;
 68      return a.z < b.z;
 69  }
 70  
 71  bool lessThan(StrokeData a, StrokeData b)
 72  {
 73      if (a.adj != b.adj)
 74      {
 75          if (a.adj == ADJ_NONE)
 76              return true;
 77          if (b.adj == ADJ_NONE)
 78              return false;
 79      }
 80      return lessThan(a.pos, b.pos);
 81  }
 82  
 83  
 84  // Pointer-jumping to propagate the minimum point
 85  [numthreads(64,1,1)]
 86  void Reduce(uint3 id : SV_DispatchThreadID)
 87  {
 88      uint i = id.x;
 89      if (i >= _NumVerts) return;
 90      StrokeData s = _strokes[i];
 91      if (s.adj == INVALID)
 92      {
 93          _nextPointerDst[i] = INVALID_UINT;
 94          return;
 95      }
 96  
 97      uint succ = _nextPointerSrc[i];
 98      _nextPointerDst[i] = succ;
 99      if (succ != INVALID_UINT)
100      {
101          if (IsChild(_strokes[succ]) && equals(_strokes[succ].pos, s.pos))
102          {
103              return;
104          }
105          uint succ2hop = _nextPointerSrc[succ];
106          if (succ2hop != INVALID_UINT)
107          {
108              if (!IsChild(_strokes[succ2hop]) && equals(_strokes[succ].pos, _strokes[succ2hop].pos))
109              {
110                  _nextPointerDst[i] = succ2hop;
111                  //TODO: remove this
112                  _strokes[i].adj = succ2hop;
113              }
114          }
115      }
116  }
117  
118  // Pointer-jumping to propagate the minimum point
119  [numthreads(64,1,1)]
120  void FindStrokeTail(uint3 id : SV_DispatchThreadID)
121  {
122      uint i = id.x;
123      if (i >= _NumVerts) return;
124      StrokeData s = _strokes[i];
125      
126      if (IsInvalid(s))
127      {
128          _nextPointerDst[i] = INVALID_UINT;
129          return;
130      }
131  
132      int succ = _nextPointerSrc[i];
133      _nextPointerDst[i] = succ;
134      if (succ >= 0)
135      {
136          uint succMin = _strokes[_strokes[succ].minPoint].minPoint;
137          if (lessThan(_strokes[succMin], _strokes[s.minPoint]))
138          {
139              _strokes[i].minPoint = succMin;
140          }
141          _nextPointerDst[i] = _nextPointerSrc[succ];
142      }
143  }
144  
145  
146  [numthreads(64,1,1)]
147  void InitializeRanksAndDistances(uint3 id : SV_DispatchThreadID)
148  {
149      uint i = id.x;
150      if (i >= _NumVerts) return;
151      StrokeData s = _strokes[i];
152      
153      // zero length edges are invalid
154      if (equals(s.pos, _strokes[s.adj].pos))
155      {
156          _strokes[i].flags |= STROKE_FLAG_IS_INVALID;
157          _strokes[i].distFromTail = 0.0f;
158          _strokes[i].rank = 0u;
159          return;
160      }
161      
162      if (IsInvalid(s))
163      {
164          _strokes[i].distFromTail = 0.0f;
165          _strokes[i].rank = 0u;
166          return;
167      }
168  
169      bool isTail = (i == s.minPoint) || s.adj == ADJ_NONE;
170      if (isTail)
171      {
172          _strokes[i].distFromTail = 0.0f;
173          _strokes[i].rank = 0u;
174      }
175      else
176      {
177          float d = distance(s.pos, _strokes[s.adj].pos);
178          _strokes[i].distFromTail = d;
179          _strokes[i].rank = 1u;
180          
181          _strokes[s.adj].flags|=STROKE_FLAG_IS_CHILD;
182      }
183  }
184  
185  // Pointer-jumping list ranking: compute distance-to-tail (float) and hop count
186  [numthreads(64,1,1)]
187  void CalculateRanksAndDistances(uint3 id : SV_DispatchThreadID)
188  {
189      uint i = id.x;
190      if (i >= _NumVerts) return;
191      StrokeData si = _strokes[i];
192      if (IsInvalid(si))
193      {
194          _nextPointerDst[i] = INVALID_UINT;
195          return;
196      }
197  
198      if (i == si.minPoint)
199      {
200          if (si.adj != ADJ_NONE) _strokes[i].flags |= STROKE_FLAG_CYCLIC;
201          _nextPointerDst[i] = _nextPointerSrc[i];
202          return;
203      }
204  
205      uint succ = _nextPointerSrc[i];
206      _nextPointerDst[i] = succ;
207  
208      bool succIsTail = (succ == si.minPoint);
209  
210      StrokeData ss = _strokes[succ];
211      _strokes[i].distFromTail += ss.distFromTail;
212      _strokes[i].rank += ss.rank;
213  
214      if (succIsTail)
215      {
216          if (ss.adj != ADJ_NONE) _strokes[i].flags |= STROKE_FLAG_CYCLIC;
217      }
218      else
219      {
220          _nextPointerDst[i] = _nextPointerSrc[succ];
221      }
222  }