-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtri2shadow.m
More file actions
152 lines (130 loc) · 4.63 KB
/
tri2shadow.m
File metadata and controls
152 lines (130 loc) · 4.63 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
function [triOut, fva] = tri2shadow(triIn, L, Pv, Pdim, opts )
% Compute a triangulation that is the shadow cast by an input triangulation.
%
% triOut = tri2shadow(triIn, L)
% For the triangulation triIn, and the light position L create a new
% triangulation on the Zplane where Z=0 that represents the shadow of triIn.
%
% triOut = tri2shadow(triIn, L, Pv, Pdim)
% As above, but cast the shadow on a plane at value Pv, with a
% dimension Pdim indicating which dimension the plane is in.
% If Pv=2, and Pdim=3 -> Zplane = 2
% If Pv=-2, and Pdim=1 -> Xplane = -2
%
% Optional inputs
% 'Attenuation' - A value that says how far away from the light before the shadow
% dissapears.
% Missing shadow pts are removed from the output triangulation.
% This will return FaceVertex Attenuation data (can be used as alpha)
% If Attenuation is not specified, but a 2nd output is requested, this value will be computed
% as 1.5x distance from light to origin.
% - 0 means no attenuation.
% - inf means auto-compute attenuation
%
% [triOut, FVA] = tri2shadow(triIn, L, 'Attenuation', atn)
% Also return the FaceVertexAlphaData needed to represent the attenuation of the shadow.
% Copyright 2024 The MathWorks, Inc.
arguments
triIn = defaultTri()
L = [ .2 .2 1.6 ]
Pv = 0
Pdim = 3
opts.Attenuation = inf;
end
% Fix Attenuation if needed
if nargout >= 2
if isinf(opts.Attenuation)
opts.Attenuation = vecnorm(L,2,2) * 2;
end
else
opts.Attenuation = 0;
end
% Get points from the input
pts = triIn.Points;
cl = triIn.ConnectivityList;
% Init vertex array for the shadow
Spts = zeros(size(pts,1),3);
% Offset used so Light can be assumed at 0,0
offset = L;
offset(Pdim) = Pv;
% Mask used to cast onto the correct plane.
Nmask = 1:3;
Nmask(Pdim) = [];
% Light position in plane dimension
Ln = L(Pdim)-Pv;
NLpt = L;
NLpt(Pdim) = Ln;
% Translate as if light is around the XY origin
Npts = pts-offset;
% Project X&Y onto the Z plane, and for shadow verts, set Z to 0.
% Nmask allows casting onto any plane
Spts(:,Nmask) = Npts(:,Nmask).*Ln./(Ln-Npts(:,Pdim));
% Find vertices that are on the wrong side of the light, or
% wrong side of the plane we are casting shadows onto.
if 0 < Ln
mask = Npts(:,Pdim)>Ln; % behind the light
cmask= Npts(:,Pdim)<0; % behind the plane of shadow
else
mask = Npts(:,Pdim)<Ln;
cmask = Npts(:,Pdim)>0;
end
% Look for infs and mask out those too.
mask = mask | logical(sum(~isfinite(Spts),2));
if any(cmask)
% Clamp all shadow points on wrong side of plane onto the plane.
% This is imperfect, but fast
Spts(cmask,:) = Npts(cmask,:);
Spts(cmask,Pdim) = 0;
end
if any(mask)
% There are some items in MASK that are infinite in some way.
% Replace INFINITE with a value that is just fairly far away
% so partial triangles still look OK around the edges.
if opts.Attenuation
myinf = opts.Attenuation*2;
else
myinf = 1000;
end
% Compute pts centered around the light, then normalize those pts
% and extend to myinf. This will make these PTS rational, and in the
% right loose direction.
Ipts = Npts-NLpt;
Ipts(mask,Nmask) = Ipts(mask,Nmask)./vecnorm(Ipts(mask,Nmask),2,2) * myinf;
% Replace bad values with our extrapolated version
Spts(mask,:) = Ipts(mask,:)+NLpt;
% Press onto shadow plane
Spts(mask,Pdim) = 0;
end
% Translate everything back to original light location
Spts = Spts+offset;
% If we have an attenuation input, compute that.
if opts.Attenuation
ldist = vecnorm(Spts-L, 2, 2);
fva = 1-(min(ldist, opts.Attenuation)/opts.Attenuation);
else
fva = [];
end
% Create the triangulation for the shadow
if isempty(cl)
tri = [];
else
warning('off', 'MATLAB:triangulation:PtsNotInTriWarnId')
tri = triangulation(cl, Spts);
end
if nargout == 0
if opts.Attenuation
triMeshShadow(triIn, L, 'ShadowTri', tri, 'ShadowFVA', fva);
else
triMeshShadow(triIn, L, 'ShadowTri', tri);
end
else
triOut = tri;
end
end
function tri = defaultTri
tri = triangulation([1 2 3; 1 2 4], ...
[ 2 1 .9
.2 .3 .8
3 -2 .2
-1 1 .6]);
end