/ Assets / shaders / silhoutte / edgeToStroke.shader
edgeToStroke.shader
  1  Shader "Custom/edgeToStroke"
  2  {
  3      Properties
  4      {
  5          [MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
  6          [MainTexture] _BaseMap("Base Map", 2D) = "white"
  7          _PyramidHeight("Pyramid Height", Float) = 1
  8      }
  9      SubShader
 10      {
 11          Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
 12  
 13          Pass
 14          {
 15              Name "ForwardLit"
 16              Tags { "LightMode" = "UniversalForward" }
 17              Cull Off
 18              
 19              HLSLPROGRAM
 20  
 21              #pragma prefer_hlslcc gles
 22              #pragma exclude_renderers d3d11_9x
 23              #pragma target 2.0
 24              #pragma require geometry
 25                          
 26              #pragma vertex vert
 27              #pragma fragment frag
 28  
 29              #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
 30              #include "Assets/Resources/GreasePencil/common_shader_util.hlsl"
 31              // Define a struct to match the one in your C# script
 32              struct VertexData
 33              {
 34                  float3 position;
 35                  float3 normal;
 36              };
 37              
 38              // Declare the GraphicsBuffers that we will set from the C# script
 39              StructuredBuffer<int> _MeshIndices;
 40              // adjacency not needed here; we read adj from _inEdges
 41  
 42              struct StrokeData
 43              {
 44                  float3 pos[2];
 45                  int adj[2];   // Adjacent face index for each endpoint's edge (-1 none, -2 invalid)
 46                  float3 faceNormal;
 47                  uint minPoint[2];
 48              };
 49              StructuredBuffer<StrokeData> _inEdges;
 50  
 51              struct v2f
 52              {
 53                  float3 normalWS : TEXCOORD0; // World space normal
 54                  float4 vertex : SV_POSITION;
 55              };
 56  
 57              TEXTURE2D(_BaseMap);
 58              SAMPLER(sampler_BaseMap);
 59              
 60              CBUFFER_START(UnityPerMaterial)
 61                  half4 _BaseColor;
 62                  float4 _BaseMap_ST;
 63              CBUFFER_END
 64  
 65              float2 project_to_screenspace(float4 v)
 66              {
 67                  return ((v.xy / v.w) * 0.5f + 0.5f) * _ScreenParams.xy;
 68              }
 69              float stroke_radius_modulate(float radius)
 70              {
 71                  float3x3 obj3x3 = (float3x3)unity_ObjectToWorld;
 72                  float3 scaled = mul(obj3x3, float3(radius * 0.57735, radius * 0.57735, radius * 0.57735));
 73                  radius = length(scaled);
 74  
 75                  float screen_radius = radius * -(UNITY_MATRIX_P[1][1]) * _ScreenParams.y;
 76                  
 77                  return screen_radius;
 78              }
 79  
 80              // Vertex Shader
 81              // We use SV_VertexID to get the index of the vertex being processed
 82              v2f vert (uint vertexID : SV_VertexID)
 83              {
 84                  v2f o;
 85  
 86                  // 1. Use the vertexID to find the correct index from the index buffer
 87                  uint faceIdx = vertexID / 6;
 88  
 89                  
 90                  float3 p1 = _inEdges[faceIdx].pos[0];
 91                  float3 p2 = _inEdges[faceIdx].pos[1];
 92                  float3 faceNormal = _inEdges[faceIdx].faceNormal; // fetch face normal if needed
 93  
 94                  if (_inEdges[faceIdx].adj[0] < 0 && _inEdges[faceIdx].adj[1] < 0)
 95                  {
 96                      o.vertex = float4(0.0f, 0.0f, -3e36f, 0.0f);
 97                      o.normalWS = float3(0,0,0);
 98                      return o;
 99                  }
100                  
101                  bool isSecond = (vertexID%6 > 2);
102                  int vI = vertexID%6;
103  
104                  float x;
105                  float y;
106  
107                  if (!isSecond)
108                  {
109                      x = float(vI & 1) * 2.0f - 1.0f; /* [-1..1] */
110                      y = float(vI & 2) - 1.0f;        /* [-1..1] */
111                  }
112                  else
113                  {
114                      x = -(float(vI+1 & 1) * 2.0f - 1.0f); /* [-1..1] */
115                      y = -(float(vI+1 & 2) - 1.0f);        /* [-1..1] */
116                  }
117  
118                  bool is_on_p1 = (x == -1.0f);
119  
120                  int adj;
121                  if (is_on_p1)
122                  {
123                      adj = _inEdges[faceIdx].adj[0];
124                  }
125                  else
126                  {
127                      adj = _inEdges[faceIdx].adj[1];
128                  }
129                  
130                  if (adj < 0)
131                  {
132                      o.vertex = float4(0.0f, 0.0f, -3e36f, 0.0f);
133                      o.normalWS = float3(0,0,0);
134                      return o;
135                  }
136                  
137                  float3 pos_adj = _inEdges[adj].pos[0];
138                  if (is_on_p1)
139                  {
140                      if (length(pos_adj-p1) < 0.001)
141                      {
142                          pos_adj = _inEdges[adj].pos[1];
143                      }
144                  }
145                  else
146                  {
147                      if (length(pos_adj-p2) < 0.001)
148                      {
149                          pos_adj = _inEdges[adj].pos[1];
150                      }
151                  }
152  
153                  float3 wpos_adj = TransformObjectToWorld(pos_adj);
154                  float3 wpos1 = TransformObjectToWorld(p1);
155                  float3 wpos2 = TransformObjectToWorld(p2);
156                  
157                  float4 ndc_adj = TransformWorldToHClip(wpos_adj);
158                  float4 ndc1 = TransformWorldToHClip(wpos1.xyz);
159                  float4 ndc2 = TransformWorldToHClip(wpos2.xyz);
160  
161                  o.vertex = (is_on_p1) ? ndc1 : ndc2;
162  
163                  float2 ss_adj = project_to_screenspace(ndc_adj);
164                  float2 ss1 = project_to_screenspace(ndc1);
165                  float2 ss2 = project_to_screenspace(ndc2);
166  
167                  float edge_len;
168                  float2 edge_dir = safe_normalize_and_get_length(ss2 - ss1, edge_len);
169                  float2 edge_adj_dir = safe_normalize((is_on_p1) ? (ss1 - ss_adj) : (ss_adj - ss2));
170  
171                  float radius = 0.05;
172                  radius = stroke_radius_modulate(radius);
173                  float clamped_radius = max(0.0f, radius);
174  
175                  // OUT.uv = float2(x, y) * 0.5f + 0.5f;
176  
177                  //TODO dot
178                  // bool is_stroke_start = (p0.mat == -1 && x == -1);
179                  // bool is_stroke_end = (p3.mat == -1 && x == 1);
180  
181                  bool is_stroke_endpoint = (adj == -1);
182  
183                  /* Mitter tangent vector. */
184                  float2 miter_tan = safe_normalize(edge_adj_dir + edge_dir);
185                  float miter_dot = dot(miter_tan, edge_adj_dir);
186                  /* Break corners after a certain angle to avoid really thick corners. */
187                  const float miter_limit = 0.5f; /* cos(60 degrees) */
188                  bool miter_break = (miter_dot < miter_limit);
189                  miter_tan = (miter_break || is_stroke_endpoint) ? edge_dir : (miter_tan / miter_dot);
190  
191                  /* Rotate 90 degrees counter-clockwise. */
192                  float2 miter = float2(-miter_tan.y, miter_tan.x);
193                  
194                  float3 ss_faceNormal = TransformWorldToHClipDir(faceNormal);
195                  
196                  if (dot(ss_faceNormal.xy, miter) < 0.0f)
197                  {
198                      miter = -miter;
199                  }
200                  
201                  float2 screen_ofs = miter * (y+1);
202  
203                  // /* Reminder: we packed the cap flag into the sign of strength and thickness sign. */
204                  // if ((is_stroke_start && p1.opacity > 0.0f) || (is_stroke_end && p1.radius > 0.0f) ||
205                  //     (miter_break && !is_stroke_start && !is_stroke_end))
206                  // {
207                  //     screen_ofs += edge_dir * x;
208                  // }
209  
210                  
211                  float2 clip_space_per_pixel = float2(1.0 / _ScreenParams.x, 1.0 / _ScreenParams.y);
212                  o.vertex.xy += screen_ofs * clip_space_per_pixel * clamped_radius;
213                  // if (isSecond)
214                  // {
215                  //     p0+=n0;
216                  //     p1+=n1;
217                  //     p2+=n2;
218                  // }
219                  // // 3. Transform the vertex position from object space to clip space
220                  // if (vI==0)
221                  // {
222                  //     o.vertex = TransformObjectToHClip(p0);
223                  //     o.normalWS = TransformObjectToWorldDir(n0);
224                  // }
225                  // else if (vI==1)
226                  // {
227                  //     o.vertex = TransformObjectToHClip(p1);
228                  //     o.normalWS = TransformObjectToWorldDir(n1);
229                  // }
230                  // else if (vI==2)
231                  // {
232                  //     o.vertex = TransformObjectToHClip(p2);
233                  //     o.normalWS = TransformObjectToWorldDir(n2);
234                  // }
235  
236                  // else // (vI==3)
237                  // {
238                  //     o.vertex = TransformObjectToHClip(p2);
239                  //     o.normalWS = TransformObjectToWorldDir(n2);
240                  // }
241                  //get normals
242                  //find zero points
243  
244                  return o;
245              }
246  
247              // Fragment Shader
248              // This is a simple implementation that uses the normal for basic lighting
249              half4 frag (v2f i) : SV_Target
250              {
251                  // Normalize the incoming normal vector
252                  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, float2(0,0)) * _BaseColor;
253                  return color;
254                  // // Get the main light direction from Unity's built-in variables
255                  // float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
256                  //
257                  // // Calculate basic Lambertian lighting
258                  // float lightIntensity = saturate(dot(normal, lightDir));
259                  //
260                  // // Define a base color (e.g., grey)
261                  // fixed3 albedo = fixed3(0.8, 0.8, 0.8);
262                  //
263                  // // Combine lighting and color
264                  // fixed3 finalColor = albedo * lightIntensity;
265                  //
266                  // return fixed4(finalColor, 1.0);
267              }
268              ENDHLSL
269          }
270      }
271  }