/ Assets / scripts / GreasePencilRendererTrashed.cs
GreasePencilRendererTrashed.cs
  1  using System;
  2  using System.Collections.Generic;
  3  using System.Runtime.InteropServices;
  4  using Unity.Mathematics;
  5  using UnityEngine;
  6  
  7  [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
  8  public class GreasePencilRendererTrashed : MonoBehaviour
  9  {
 10      public GreasePencilSO greasePencil;
 11      public int frameidx = 0;
 12  
 13      // ComputeBuffers
 14      ComputeBuffer posBuffer;
 15      ComputeBuffer strokesBuffer;
 16      ComputeBuffer uvOpacityBuffer;
 17      ComputeBuffer vcolBuffer;
 18      ComputeBuffer fcolBuffer;
 19      ComputeBuffer materialBuffer;
 20  
 21      // Keep references so we can dispose
 22      bool buffersCreated = false;
 23  
 24      Mesh mesh;
 25      
 26      // static void grease_pencil_geom_batch_ensure(Object &object,
 27      // const GreasePencil &grease_pencil,
 28      // const Scene &scene)
 29      [StructLayout(LayoutKind.Sequential)]
 30      public struct GreasePencilStrokeVert
 31      {
 32          // float3 pos;
 33          public Vector3 pos;
 34  
 35          // float radius;
 36          public float radius;
 37  
 38          // int4 mat, stroke_id, point_id, packed_asp_hard_rot;
 39          public int mat;
 40          public int stroke_id;
 41          public int point_id;
 42          public int packed_asp_hard_rot;
 43  
 44          // float2 uv_fill;
 45          public Vector2 uv_fill;
 46  
 47          // float u_stroke, opacity;
 48          public float u_stroke;
 49          public float opacity;
 50      };
 51      
 52      struct GreasePencilColorVert {
 53          public float4 vcol; /* Vertex color */
 54          public float4 fcol; /* Fill color */
 55      };
 56      
 57      [ContextMenu("Build Stroke Mesh")]
 58      void CreateMesh()
 59      {
 60          var vertsStartOffsetsPerLayer = CalculateOffsets(out var totalNumPoints, out var totalVertexOffset);
 61          // struct GreasePencilStrokeVert {
 62          //     /** Position and radius packed in the same attribute. */
 63          //     float pos[3], radius;
 64          //     /** Material Index, Stroke Index, Point Index, Packed aspect + hardness + rotation. */
 65          //     int32_t mat, stroke_id, point_id, packed_asp_hard_rot;
 66          //     /** UV and opacity packed in the same attribute. */
 67          //     float uv_fill[2], u_stroke, opacity;
 68          // };
 69          
 70          // Add extra space at the end of the buffer because of quad load.
 71          GreasePencilStrokeVert[] verts = new GreasePencilStrokeVert[totalVertexOffset+2];
 72          GreasePencilColorVert[] cols = new GreasePencilColorVert[totalVertexOffset+2];
 73          
 74          // a quad for every strokePoint
 75          int[] triangleIbo = new int[totalNumPoints*2*3];
 76          var triVbo = new Vector3[totalVertexOffset*4];
 77          int triangleIboIndex = 0;
 78  
 79          for (int layerIdx = 0; layerIdx < greasePencil.data.layers.Count; layerIdx++)
 80          {
 81              var layer = greasePencil.data.layers[layerIdx];
 82              var strokes = layer.frames[frameidx].strokes;
 83              if (strokes.Count == 0)
 84              {
 85                  continue;
 86              }
 87  
 88              var vertsStartOffSets = vertsStartOffsetsPerLayer[layerIdx];
 89              for (int strokeIdx = 0; strokeIdx < strokes.Count; strokeIdx++)
 90              {
 91                  var stroke = strokes[strokeIdx];
 92                  var pointsCount = stroke.points.Count;
 93                  bool isCyclic = stroke.cyclic && pointsCount >= 3;
 94                  var vertsStartOffset = vertsStartOffSets[strokeIdx];
 95                  var numVerts = 1 + pointsCount + (isCyclic ? 1 : 0) + 1;
 96  
 97                  // First vertex is not drawn
 98                  verts[vertsStartOffset].mat = -1;
 99                  // The first vertex will have the index of the last vertex.
100                  verts[vertsStartOffset].stroke_id = vertsStartOffset + numVerts - 1;
101                  //
102                  // // If the stroke has more than 2 points, add the triangle indices to the index buffer.
103                  // if (pointsCount >= 3)
104                  // {
105                  //     var trisSlice = new Span<int3>(triangles.ToArray(), trisStartOffset, numVerts);
106                  //     foreach (var tri in trisSlice)
107                  //     {
108                  //         triangleIbo[triangleIboIndex + 0] = (vertsStartOffset + 1 + tri.x) << 2;
109                  //         triangleIbo[triangleIboIndex + 1] = (vertsStartOffset + 1 + tri.y) << 2;
110                  //         triangleIbo[triangleIboIndex + 2] = (vertsStartOffset + 1 + tri.z) << 2;
111                  //         triangleIboIndex += 3;
112                  //     }
113                  // }
114  
115                  // Write all the point attributes to the vertex buffers. Create a quad for each point. 
116                  for (int pointIdx = 0; pointIdx < pointsCount; pointIdx++)
117                  {
118                      var strokePoint = stroke.points[pointIdx];
119                      var idx = vertsStartOffset + pointIdx + 1;
120                      PopulatePoint(strokePoint, out verts[idx], out cols[idx], idx, isCyclic);
121                  }
122  
123                  void PopulatePoint(PointData strokePoint, out GreasePencilStrokeVert sVert, out GreasePencilColorVert cVert, int idx, bool cyclic)
124                  {
125                      sVert.pos = strokePoint.Position;
126                      // sVert.pos = new float3(idx, 0, 0);
127                      var posNext = stroke.points[(idx) % pointsCount].Position;
128                      sVert.radius = strokePoint.radius;
129                      sVert.opacity = strokePoint.opacity;
130                      var offset = vertsStartOffset + idx + 1;
131                      // Store if the curve is cyclic in the sign of the point index.
132                      sVert.point_id = cyclic ? -offset : offset;
133                      sVert.stroke_id = vertsStartOffset;
134  
135                      /* The material index is allowed to be negative as it's stored as a generic attribute. To
136                       * ensure the material used by the shader is valid this needs to be clamped to zero. */
137                      sVert.mat = Math.Max(stroke.material_index, 0) % 256;
138  
139                      sVert.packed_asp_hard_rot = 0; //todo
140                      sVert.u_stroke = 0; //todo
141                      sVert.uv_fill = float2.zero; //todo
142  
143                      cVert.vcol = strokePoint.VertexColor;
144                      cVert.fcol = Vector4.one;
145  
146                      // quad
147                      triangleIbo[triangleIboIndex + 0] = ((vertsStartOffset + idx) << 2) + 0;
148                      triangleIbo[triangleIboIndex + 1] = ((vertsStartOffset + idx) << 2) + 1;
149                      triangleIbo[triangleIboIndex + 2] = ((vertsStartOffset + idx) << 2) + 2;
150                      triangleIboIndex += 3;
151                      triangleIbo[triangleIboIndex + 0] = ((vertsStartOffset + idx) << 2) + 2;
152                      triangleIbo[triangleIboIndex + 1] = ((vertsStartOffset + idx) << 2) + 1;
153                      triangleIbo[triangleIboIndex + 2] = ((vertsStartOffset + idx) << 2) + 3;
154                      triangleIboIndex += 3;
155                      triVbo[((vertsStartOffset + idx) << 2) + 0] = strokePoint.Position + Vector3.up;
156                      triVbo[((vertsStartOffset + idx) << 2) + 1] = strokePoint.Position - Vector3.up;
157                      triVbo[((vertsStartOffset + idx) << 2) + 2] = posNext + Vector3.up;
158                      triVbo[((vertsStartOffset + idx) << 2) + 3] = posNext - Vector3.up;
159                  }
160              }
161          }
162          
163          ReleaseBuffers();
164          
165          if (mesh == null)
166          {
167              mesh = new Mesh();
168              mesh.name = "GreasePencilMesh";
169              GetComponent<MeshFilter>().sharedMesh = mesh;
170          }
171          else
172          {
173              mesh.Clear();
174          }
175          mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
176          mesh.vertices = triVbo;
177          mesh.SetIndices(triangleIbo, MeshTopology.Triangles, 0);
178          posBuffer = new ComputeBuffer(verts.Length, sizeof(float) * 4 * 3);
179          // var posArray = new Vector4[verts.Length];
180          // for (int i = 0; i < verts.Length; i++)
181          // {
182          //     posArray[i] = new Vector4(verts[i].pos.x, verts[i].pos.y, verts[i].pos.z, verts[i].radius);
183          // }
184          // posBuffer.SetData(posArray);
185          posBuffer.SetData(verts);
186          vcolBuffer = new ComputeBuffer(cols.Length, sizeof(float) * 4 * 2);
187          vcolBuffer.SetData(cols);
188          CreateMaterialBuffer();
189          buffersCreated = true;
190          
191          var targetMaterial = GetComponent<MeshRenderer>().sharedMaterial; 
192          // 4) Bind to material
193          if (targetMaterial != null)
194          {
195              targetMaterial.SetBuffer("_Pos", posBuffer);
196              targetMaterial.SetBuffer("_Color", vcolBuffer);
197              targetMaterial.SetBuffer("gp_materials", materialBuffer);
198          }
199          else
200          {
201              Debug.LogWarning("targetMaterial not assigned; buffers created but not bound.");
202          }
203  
204      }
205  
206      private List<int[]> CalculateOffsets(out int totalNumPoints, out int totalVertexOffset)
207      {
208          totalNumPoints = 0;
209          totalVertexOffset = 0;
210          var vertsStartOffsetsPerLayer = new List<int[]>();
211  
212          foreach (var layer in greasePencil.data.layers)
213          {
214              var strokes = layer.frames[frameidx].strokes;
215              int numStrokes = strokes.Count;
216              int[] vertsStartOffsets = new int[numStrokes];
217              
218              // Calculate the triangle and vertex offsets for all the strokes
219              int numCyclic = 0;
220              int numPoints = 0;
221              for (int strokeIdx = 0; strokeIdx < numStrokes; strokeIdx++)
222              {
223                  var stroke = strokes[strokeIdx];
224                  var pointsCount = stroke.points.Count;
225                  bool isCyclic = stroke.cyclic && pointsCount >= 3;
226                  if (isCyclic) numCyclic++;
227                  vertsStartOffsets[strokeIdx] = totalVertexOffset;
228                  // One vertex is stored before and after as padding. Cyclic strokes have one extra vertex.
229                  totalVertexOffset += 1 + pointsCount + (isCyclic ? 1 : 0) + 1;
230                  numPoints += pointsCount;
231              }
232  
233              // the strokes
234              totalNumPoints += (numPoints + numCyclic);
235              
236              vertsStartOffsetsPerLayer.Add(vertsStartOffsets);
237          }
238  
239          return vertsStartOffsetsPerLayer;
240      }
241  
242      // BLI_INLINE int32_t pack_rotation_aspect_hardness(float rot, float asp, float softness)
243      // {
244      //     int32_t packed = 0;
245      //     /* Aspect uses 9 bits */
246      //     float asp_normalized = (asp > 1.0f) ? (1.0f / asp) : asp;
247      //     packed |= int32_t(unit_float_to_uchar_clamp(asp_normalized));
248      //     /* Store if inverted in the 9th bit. */
249      //     if (asp > 1.0f) {
250      //         packed |= 1 << 8;
251      //     }
252      //     /* Rotation uses 9 bits */
253      //     /* Rotation are in [-90..90] degree range, so we can encode the sign of the angle + the cosine
254      //      * because the cosine will always be positive. */
255      //     packed |= int32_t(unit_float_to_uchar_clamp(cosf(rot))) << 9;
256      //     /* Store sine sign in 9th bit. */
257      //     if (rot < 0.0f) {
258      //         packed |= 1 << 17;
259      //     }
260      //     /* Hardness uses 8 bits */
261      //     packed |= int32_t(unit_float_to_uchar_clamp(1.0f - softness)) << 18;
262      //     return packed;
263      // }
264      
265      void BuildMesh()
266      {
267          // This method remains largely the same as the mesh structure is separate from the buffer packing.
268          if (greasePencil == null)
269          {
270              Debug.LogError("No GreasePencil asset assigned.");
271              return;
272          }
273  
274          var vertices = new List<Vector3>();
275          var uvs = new List<Vector2>();   // stroke index, point index
276          var uvs2 = new List<Vector2>();  // could hold vertex ID for buffer lookup
277          var colors = new List<Color>();
278          var indices = new List<int>();
279  
280          int vertBase = 0;
281  
282          int strokeIndex = 0;
283          foreach (var layer in greasePencil.data.layers)
284          {
285              foreach (var stroke in layer.frames[frameidx].strokes)
286              {
287                  if (stroke.points == null || stroke.points.Count < 2)
288                      continue;
289  
290                  for (int i = 0; i < stroke.points.Count - 1; i++)
291                  {
292                      PointData p0 = stroke.points[i];
293                      PointData p1 = stroke.points[i + 1];
294  
295                      // build a quad segment between p0 and p1
296                      Vector3 dir = (p1.Position - p0.Position).normalized;
297                      Vector3 normal = Vector3.up; // assume Z-up camera; adjust later
298                      Vector3 side = Vector3.Cross(dir, normal).normalized;
299  
300                      float r0 = p0.radius;
301                      float r1 = p1.radius;
302  
303                      // 4 vertices for the quad
304                      vertices.Add(p0.Position + side * r0);
305                      vertices.Add(p0.Position - side * r0);
306                      vertices.Add(p1.Position + side * r1);
307                      vertices.Add(p1.Position - side * r1);
308  
309                      // UVs (store strokeIndex + pointIndex)
310                      uvs.Add(new Vector2(strokeIndex, i));
311                      uvs.Add(new Vector2(strokeIndex, i));
312                      uvs.Add(new Vector2(strokeIndex, i + 1));
313                      uvs.Add(new Vector2(strokeIndex, i + 1));
314  
315                      // UV2 (for vertex id if you later map to buffers)
316                      uvs2.Add(new Vector2(vertices.Count - 4, 0));
317                      uvs2.Add(new Vector2(vertices.Count - 3, 0));
318                      uvs2.Add(new Vector2(vertices.Count - 2, 0));
319                      uvs2.Add(new Vector2(vertices.Count - 1, 0));
320  
321                      // Color (pack opacity or stroke color if you have one)
322                      Color c0 = new Color(1, 1, 1, p0.opacity);
323                      Color c1 = new Color(1, 1, 1, p1.opacity);
324                      colors.Add(c0);
325                      colors.Add(c0);
326                      colors.Add(c1);
327                      colors.Add(c1);
328  
329                      // Indices (two triangles)
330                      indices.Add(vertBase + 0);
331                      indices.Add(vertBase + 2);
332                      indices.Add(vertBase + 1);
333                      indices.Add(vertBase + 2);
334                      indices.Add(vertBase + 3);
335                      indices.Add(vertBase + 1);
336  
337                      vertBase += 4;
338                  }
339                  strokeIndex++;
340              }
341          }
342  
343          if (mesh == null)
344          {
345              mesh = new Mesh();
346              mesh.name = "GreasePencilMesh";
347              GetComponent<MeshFilter>().sharedMesh = mesh;
348          }
349          else
350          {
351              mesh.Clear();
352          }
353  
354          mesh.SetVertices(vertices);
355          mesh.SetUVs(0, uvs);
356          mesh.SetUVs(1, uvs2);
357          mesh.SetColors(colors);
358          mesh.SetTriangles(indices, 0);
359          mesh.RecalculateBounds();
360      }
361      // Define the C# struct that matches the GPU struct layout.
362      // The GPU struct is packed, so we must be careful with alignment.
363      // The C# struct must have a size of 88 bytes to match the GPU side (22 floats * 4 bytes/float).
364      // Unity's GPU struct packing might differ slightly, but this is a close approximation.
365      struct GpMaterialData
366      {
367          public Vector4 stroke_color;
368          public Vector4 fill_color;
369          public Vector4 fill_mix_color;
370          public Vector4 fill_uv_rot_scale;
371          public Vector4 fill_uv_offset_alignment_rot; // Combined for packing
372          public float stroke_texture_mix;
373          public float stroke_u_scale;
374          public float fill_texture_mix;
375          public int flag;
376      }
377      [ContextMenu("pack Attributes")]
378      void ApplyIndices()
379      {
380          if (greasePencil == null)
381          {
382              Debug.LogError("GreasePencil asset not assigned.");
383              return;
384          }
385  
386          MeshFilter mf = GetComponent<MeshFilter>();
387          if (mf == null || mf.sharedMesh == null)
388          {
389              Debug.LogError("MeshFilter or sharedMesh missing.");
390              return;
391          }
392          
393          var vertsStartOffsetsPerLayer = CalculateOffsets(out var totalNumPoints, out var totalVertexOffset);
394  
395          // 1) write vertex ids into uv2.x (so shader can index)
396          var uvs2 = new List<Vector2>(totalVertexOffset);
397          for (int i = 0; i < totalVertexOffset; i++)
398              uvs2.Add(new Vector2(i, 0));
399          mesh.SetUVs(1, uvs2); // TEXCOORD1 / uv2
400  
401          // 2) Flatten grease pencil points and create stroke metadata
402          var posData = new Vector4[totalVertexOffset]; // (x,y,z,radius)
403          var strokeData = new Vector4[totalVertexOffset]; // (mat_idx, stroke_id, point_id, packed_data)
404          var uvOpacityData = new Vector4[totalVertexOffset]; // (uv_fill.x, uv_fill.y, u_stroke, opacity)
405          var vcolData = new Vector4[totalVertexOffset]; // (r, g, b, a)
406          var fcolData = new Vector4[totalVertexOffset]; // (r, g, b, a)
407  
408          int runningPointIndex = 0;
409          int runningStrokeIndex = 0;
410  
411          foreach (var layer in greasePencil.data.layers)
412          {
413              foreach (var stroke in layer.frames[frameidx].strokes)
414              {
415                  bool is_cyclic = stroke.cyclic && (stroke.points.Count > 2);
416                  // First point is not drawn
417                  strokeData[runningPointIndex][0] = -1;
418                  // The first vertex will have the index of the last vertex.
419                  strokeData[runningPointIndex][1] = runningPointIndex + 1 + stroke.points.Count + (is_cyclic ? 1 : 0);
420                  runningPointIndex++;    
421                  if (stroke.points != null)
422                  {
423                      int pointIndex = 0;
424                      foreach (var p in stroke.points)
425                      {
426                          // Populating the new buffers
427                          posData[runningPointIndex] = new Vector4(p.Position.x, p.Position.y, p.Position.z, p.radius);
428                          
429                          // We need more data from the grease pencil asset to properly pack this,
430                          // for now, use placeholders. Assuming mat_idx, stroke_id, and point_id.
431                          int matIdx = stroke.material_index;
432                          if (pointIndex == 0 || pointIndex == stroke.points.Count - 1)
433                          {
434                              matIdx = -1;
435                          }
436                          strokeData[runningPointIndex] = new Vector4(matIdx, runningStrokeIndex, pointIndex, 0f);
437                          
438                          // Assuming you have uv_fill and u_stroke data in your PointData or you need to generate it
439                          // Here's a placeholder, you'll need to adjust based on your data source.
440                          uvOpacityData[runningPointIndex] = new Vector4(0f, 0f, (float)pointIndex / (stroke.points.Count - 1), p.opacity); 
441                          
442                          // Assuming you have vertex colors and fill colors
443                          // For this example, we'll use placeholder colors.
444                          vcolData[runningPointIndex] = new Vector4(1f, 1f, 1f, 1f);
445                          fcolData[runningPointIndex] = new Vector4(1f, 1f, 1f, 1f);
446  
447                          runningPointIndex++;
448                          pointIndex++;
449                      }
450                  }
451                  runningStrokeIndex++;
452                  runningPointIndex++;
453              }
454          }
455  
456          // 3) Create / update ComputeBuffers
457          ReleaseBuffers();
458  
459          if (posData.Length == 0)
460          {
461              Debug.LogWarning("No points in grease pencil asset.");
462              return;
463          }
464  
465          posBuffer = new ComputeBuffer(posData.Length, sizeof(float) * 4);
466          posBuffer.SetData(posData);
467  
468          strokesBuffer = new ComputeBuffer(strokeData.Length, sizeof(int) * 4);
469          // Cast Vector4 to int4, as the shader expects ints
470          var strokeInts = new int[strokeData.Length * 4];
471          for (int i = 0; i < strokeData.Length; i++)
472          {
473              strokeInts[i * 4 + 0] = (int)strokeData[i].x;
474              strokeInts[i * 4 + 1] = (int)strokeData[i].y;
475              strokeInts[i * 4 + 2] = (int)strokeData[i].z;
476              strokeInts[i * 4 + 3] = (int)strokeData[i].w;
477          }
478          strokesBuffer.SetData(strokeInts);
479          
480  
481  
482  
483          CreateMaterialBuffer();
484  
485          uvOpacityBuffer = new ComputeBuffer(uvOpacityData.Length, sizeof(float) * 4);
486          uvOpacityBuffer.SetData(uvOpacityData);
487  
488          vcolBuffer = new ComputeBuffer(vcolData.Length, sizeof(float) * 4);
489          vcolBuffer.SetData(vcolData);
490  
491          fcolBuffer = new ComputeBuffer(fcolData.Length, sizeof(float) * 4);
492          fcolBuffer.SetData(fcolData);
493          
494          var targetMaterial = GetComponent<MeshRenderer>().sharedMaterial; 
495          // 4) Bind to material
496          if (targetMaterial != null)
497          {
498              targetMaterial.SetBuffer("_Pos", posBuffer);
499              targetMaterial.SetBuffer("_Strokes", strokesBuffer);
500              targetMaterial.SetBuffer("_UvOpacity", uvOpacityBuffer);
501              targetMaterial.SetBuffer("_Vcol", vcolBuffer);
502              targetMaterial.SetBuffer("_Fcol", fcolBuffer);
503              targetMaterial.SetBuffer("gp_materials", materialBuffer);
504          }
505          else
506          {
507              Debug.LogWarning("targetMaterial not assigned; buffers created but not bound.");
508          }
509  
510          buffersCreated = true;
511  
512          Debug.Log($"GreasePencilRenderer: uploaded {posData.Length} points, {strokeData.Length} strokes.");
513      }
514  
515      private void CreateMaterialBuffer()
516      {
517          var materialDataList = new List<GpMaterialData>();
518          foreach (var mat in greasePencil.data.materials)
519          {
520              GpMaterialData gpuMat = new GpMaterialData();
521              
522              // Populate the C# struct from your GreasePencilSO data.
523              gpuMat.stroke_color = new Vector4(mat.stroke_color[0], mat.stroke_color[1], mat.stroke_color[2], 1.0f);
524              gpuMat.fill_color = new Vector4(mat.fill_color[0], mat.fill_color[1], mat.fill_color[2], 1.0f);
525              gpuMat.fill_mix_color = new Vector4(mat.fill_mix_color[0], mat.fill_mix_color[1], mat.fill_mix_color[2], 1.0f);
526              gpuMat.fill_uv_rot_scale = new Vector4(mat.fill_uv_rot_scale[0], mat.fill_uv_rot_scale[1], mat.fill_uv_rot_scale[2], mat.fill_uv_rot_scale[3]);
527              
528              // Pack fill_uv_offset and alignment_rot into a single Vector4
529              gpuMat.fill_uv_offset_alignment_rot = new Vector4(mat.fill_uv_offset[0], mat.fill_uv_offset[1], mat.alignment_rot[0], mat.alignment_rot[1]);
530  
531              gpuMat.stroke_texture_mix = mat.stroke_texture_mix;
532              gpuMat.stroke_u_scale = mat.stroke_u_scale;
533              gpuMat.fill_texture_mix = mat.fill_texture_mix;
534              gpuMat.flag = mat.flag;
535              
536              materialDataList.Add(gpuMat);
537          }
538      
539          // Create the ComputeBuffer and upload the data.
540          materialBuffer = new ComputeBuffer(materialDataList.Count, System.Runtime.InteropServices.Marshal.SizeOf(typeof(GpMaterialData)));
541          materialBuffer.SetData(materialDataList);
542      }
543  
544      void ReleaseBuffers()
545      {
546          if (posBuffer != null)
547          {
548              posBuffer.Release();
549              posBuffer = null;
550          }
551          if (strokesBuffer != null)
552          {
553              strokesBuffer.Release();
554              strokesBuffer = null;
555          }
556          if (uvOpacityBuffer != null)
557          {
558              uvOpacityBuffer.Release();
559              uvOpacityBuffer = null;
560          }
561          if (vcolBuffer != null)
562          {
563              vcolBuffer.Release();
564              vcolBuffer = null;
565          }
566          if (fcolBuffer != null)
567          {
568              fcolBuffer.Release();
569              fcolBuffer = null;
570          }
571  
572          if (materialBuffer != null)
573          {
574              materialBuffer.Release();
575              materialBuffer = null;
576          }
577          buffersCreated = false;
578      }
579  
580      void OnDestroy()
581      {
582          ReleaseBuffers();
583      }
584  
585      void OnDisable()
586      {
587          var targetMaterial = GetComponent<MeshRenderer>().sharedMaterial; 
588          
589          // Unbind buffers from material to avoid dangling references
590          if (targetMaterial != null)
591          {
592              targetMaterial.SetBuffer("_Pos", (ComputeBuffer)null);
593              targetMaterial.SetBuffer("_Strokes", (ComputeBuffer)null);
594              targetMaterial.SetBuffer("_UvOpacity", (ComputeBuffer)null);
595              targetMaterial.SetBuffer("_Vcol", (ComputeBuffer)null);
596              targetMaterial.SetBuffer("_Fcol", (ComputeBuffer)null);
597              targetMaterial.SetBuffer("gp_materials", (ComputeBuffer)null);
598          }
599          ReleaseBuffers();
600      }
601  }