/ Assets / scripts / SilhouetteRenderer.cs
SilhouetteRenderer.cs
  1  using System;
  2  using System.Collections.Generic;
  3  using System.Runtime.InteropServices;
  4  using DefaultNamespace;
  5  using UnityEngine;
  6  using Unity.Profiling;
  7  using UnityEngine.Rendering;
  8  
  9  
 10  [RequireComponent(typeof(IGreasePencilEdgeCalculator))]
 11  public class SilhouetteRenderer : MonoBehaviour
 12  {
 13      
 14      static readonly ProfilerMarker debugMarker = new ProfilerMarker("SilhouetteRender");
 15  
 16      [StructLayout(LayoutKind.Sequential)]
 17  	public struct VertexData
 18  	{
 19  		public Vector3 position;
 20  		public Vector3 normal;
 21  	}
 22      
 23      IGreasePencilEdgeCalculator _edgeCalculator;
 24      
 25      GraphicsBuffer _indices;
 26      ComputeBuffer _materialBuffer;
 27      
 28      public Material material;
 29      
 30      
 31      public List<GreasePencilMaterial> greasePencilMaterials;
 32      
 33      [Range(0.0f, 1.0f)] public float opacity = 1.0f;
 34      public Color colorTint = new(1, 1, 1, 0);
 35      
 36      
 37      void Start()
 38      {
 39          _edgeCalculator = GetComponent<IGreasePencilEdgeCalculator>();
 40          InitBuffers();
 41      }
 42  
 43      private void Update()
 44      {
 45          _edgeCalculator.CalculateEdges();
 46          // Create the MaterialPropertyBlock to hold our per-draw data.
 47          var matProps = new MaterialPropertyBlock();
 48  
 49          // Set your custom data buffers
 50          matProps.SetBuffer("_Pos", _edgeCalculator.GetStrokeBuffer());
 51          matProps.SetBuffer("_Color", _edgeCalculator.GetColorBuffer());
 52          matProps.SetBuffer("gp_materials", _materialBuffer);
 53          
 54          // Strokes are already in world coordinates
 55          matProps.SetMatrix("_ObjectToWorld", Matrix4x4.identity); 
 56          matProps.SetFloat("gp_layer_opacity", opacity);
 57          matProps.SetColor("gp_layer_tint", colorTint);
 58          // matProps.SetFloat("gp_layer_opacity", opacity);
 59          // matProps.SetColor("gp_layer_tint", colorTint);
 60      
 61          
 62          RenderParams rp = new RenderParams(material);
 63          rp.worldBounds = new Bounds(Vector3.zero, 1000*Vector3.one); // use tighter bounds
 64          rp.matProps = matProps;
 65          
 66          Graphics.RenderPrimitivesIndexed(rp, MeshTopology.Triangles, _indices, _indices.count);
 67      }
 68  
 69      void OnDestroy()
 70      {
 71          ReleaseBuffers();
 72      }
 73  
 74      void ReleaseBuffers()
 75      {
 76          _indices?.Dispose();
 77          _indices = null;
 78      }
 79  
 80      void InitBuffers()
 81      {
 82          ReleaseBuffers();
 83          
 84  
 85          var bufferLength = _edgeCalculator.GetMaximumBufferLength();
 86          var indices = new int[bufferLength*6]; // 6 indices per triangle
 87          int triangleIboIndex = 0;
 88          for (int i = 0; i < bufferLength; i++)
 89          {
 90              int vertIdxMarkedStroke = ((i) << 2) | GreasePencilRenderer.GP_IS_STROKE_VERTEX_BIT;
 91              indices[triangleIboIndex + 0] = vertIdxMarkedStroke + 0;
 92              indices[triangleIboIndex + 1] = vertIdxMarkedStroke + 1;
 93              indices[triangleIboIndex + 2] = vertIdxMarkedStroke + 2;
 94              triangleIboIndex += 3;
 95              indices[triangleIboIndex + 0] = vertIdxMarkedStroke + 2;
 96              indices[triangleIboIndex + 1] = vertIdxMarkedStroke + 1;
 97              indices[triangleIboIndex + 2] = vertIdxMarkedStroke + 3;
 98              triangleIboIndex += 3;
 99          }
100          
101          _indices = new GraphicsBuffer(GraphicsBuffer.Target.Structured | GraphicsBuffer.Target.Index, indices.Length, sizeof(int));
102          _indices.SetData(indices);
103          
104          CreateMaterialBuffer();
105      }
106      
107      private void CreateMaterialBuffer()
108      {
109          
110          var materialDataList = new List<GreasePencilRenderer.GpMaterialData>();
111          foreach (var mat in greasePencilMaterials)
112          {
113              var gpuMat = new GreasePencilRenderer.GpMaterialData
114              {
115                  // Populate the C# struct from your GreasePencilSO data.
116                  stroke_color = new Vector4(mat.stroke_color[0], mat.stroke_color[1], mat.stroke_color[2], 1.0f),
117                  fill_color = new Vector4(mat.fill_color[0], mat.fill_color[1], mat.fill_color[2], 1.0f),
118                  fill_mix_color = new Vector4(mat.fill_mix_color[0], mat.fill_mix_color[1], mat.fill_mix_color[2], 1.0f),
119                  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]),
120                  // Pack fill_uv_offset and alignment_rot into a single Vector4
121                  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]),
122                  stroke_texture_mix = mat.stroke_texture_mix,
123                  stroke_u_scale = mat.stroke_u_scale,
124                  fill_texture_mix = mat.fill_texture_mix,
125                  flag = mat.flag
126              };
127  
128              materialDataList.Add(gpuMat);
129          }
130      
131          // Create the ComputeBuffer and upload the data.
132          _materialBuffer = new ComputeBuffer(materialDataList.Count, GreasePencilRenderer.GpMaterialData.SizeOf);
133          _materialBuffer.SetData(materialDataList);
134      }
135  }