-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathshaders.metal
116 lines (87 loc) · 4.81 KB
/
shaders.metal
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
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
struct MyNodeBuffer {
float4x4 modelViewProjectionTransform;
};
struct lineDataBuffer {
int width;
int verticleCount;
int miter;
int loop;
};
struct SimpleVertex
{
float4 position [[position]];
float4 color;
};
vertex SimpleVertex thickLinesVertex(constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]],
constant float3* vertices [[buffer(2)]],
constant lineDataBuffer& lineData [[buffer(3)]],
constant float4* color [[buffer(4)]],
uint v_id [[vertex_id]])
{
uint point_id = v_id/4; //line point id (not vertex)
float sign = v_id%2?-1:1; //should point be up or down in line
SimpleVertex vert;
vert.color = *color; //pass the color data to fragment shader
float2 aspect = float2( scn_frame.viewportSize.x / scn_frame.viewportSize.y, 1); //aspect ratio
vert.position = scn_node.modelViewProjectionTransform * float4(vertices[v_id], 1.0); //position of the point
if (lineData.miter == 0 || point_id == 0 || point_id + 1 == uint(lineData.verticleCount)){
//Active when there it's first point, last point or no mitter middle point
//TODO: get rid of conditionals
uint currentPointToProcess;
uint nextPointToProcess;
int lineToNext = 0; //should line be calcualted from current point to next, or from current to previous
lineToNext |= point_id == 0 && !lineData.loop; //always go to next if its first point
lineToNext |= point_id*4+2 == v_id; //always go to next if its third or fourth vertex of current point
lineToNext |= point_id*4+3 == v_id;
lineToNext &= point_id + 1 != uint(lineData.verticleCount) || lineData.loop; //always go to prevous if its a last point
currentPointToProcess = (point_id-1+lineToNext + lineData.verticleCount) % lineData.verticleCount;
nextPointToProcess = (point_id+lineToNext) % lineData.verticleCount;
//calculate MVP transform for both points
float4 currentProjection = scn_node.modelViewProjectionTransform * float4(vertices[currentPointToProcess*4], 1.0);
float4 nextProjection = scn_node.modelViewProjectionTransform * float4(vertices[nextPointToProcess*4], 1.0);
//get 2d position in screen space
float2 currentScreen = currentProjection.xy / currentProjection.w * aspect;
float2 nextScreen = nextProjection.xy / nextProjection.w * aspect;
//get vector of the line
float2 dir = normalize(nextScreen - currentScreen);
//vector of diretion of thickness
float2 normal = float2(-dir.y, dir.x);
normal /= aspect;
//get thickness in pixels in screen space
float thickness = float(lineData.width)/scn_frame.viewportSize.y;
//move current point up or down, by thickness, with the same distance independent on depth
vert.position += float4(sign*normal*thickness*vert.position.w, 0, 0 );
}else {
//TODO: Switch to normal mode of miter size is to big
//other points, calculate mitter
//Similar to previus case, but looking always at 3 points - current, prevoius and next
float4 previousProjection= scn_node.modelViewProjectionTransform * float4(vertices[(point_id - 1)*4], 1.0);
float4 currentProjection= scn_node.modelViewProjectionTransform * float4(vertices[point_id*4], 1.0);
float4 nextProjection = scn_node.modelViewProjectionTransform * float4(vertices[(point_id + 1)*4], 1.0);
float2 previousScreen = previousProjection.xy / previousProjection.w * aspect;
float2 currentScreen = currentProjection.xy / currentProjection.w * aspect;
float2 nextScreen = nextProjection.xy / nextProjection.w * aspect;
//vector tangential to the joint
float2 tangent = normalize( normalize(nextScreen-currentScreen) + normalize(currentScreen-previousScreen) );
float2 dir = normalize(nextScreen - currentScreen);
float2 normal = float2(-dir.y, dir.x);
//mitter line - normal to the tangent
float2 miter = float2( -tangent.y, tangent.x );
float thickness = float(lineData.width)/scn_frame.viewportSize.y;
//mitter length - crossing of one of the edges with mitter line
float miterLength = thickness / dot( miter, normal );
miter /= aspect;
vert.position += float4(sign*miter*miterLength*vert.position.w, 0,0 );
}
return vert;
}
fragment float4 thickLinesFragment(SimpleVertex in [[stage_in]])
{
float4 color;
color = in.color;
return color;
}