VectSharp  2.2.1
A light library for C# vector graphics
Lights.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Linq;
21 
23 {
24  /// <summary>
25  /// Represents the intensity of a light source at a particular point.
26  /// </summary>
27  public struct LightIntensity
28  {
29  /// <summary>
30  /// The intensity of the light.
31  /// </summary>
32  public double Intensity;
33 
34  /// <summary>
35  /// The direction towards from which the light comes.
36  /// </summary>
37  public NormalizedVector3D Direction;
38 
39  /// <summary>
40  /// Creates a new <see cref="LightIntensity"/>.
41  /// </summary>
42  /// <param name="intensity">The intensity of the light.</param>
43  /// <param name="direction">The direction from which the light comes.</param>
44  public LightIntensity(double intensity, NormalizedVector3D direction)
45  {
46  this.Intensity = intensity;
47  this.Direction = direction;
48  }
49 
50  /// <summary>
51  /// Deconstructs the struct.
52  /// </summary>
53  /// <param name="intensity">This parameter will hold the <see cref="Intensity"/> of the light.</param>
54  /// <param name="direction">This parameter will hold the <see cref="Direction"/> of the light.</param>
55  public void Deconstruct(out double intensity, out NormalizedVector3D direction)
56  {
57  intensity = this.Intensity;
58  direction = this.Direction;
59  }
60  }
61 
62  /// <summary>
63  /// Represents a light source.
64  /// </summary>
65  public interface ILightSource
66  {
67  /// <summary>
68  /// Computes the light intensity at the specified point, without taking into account any obstructions.
69  /// </summary>
70  /// <param name="point">The <see cref="Point3DElement"/> at which the light intensity should be computed.</param>
71  /// <returns></returns>
72  LightIntensity GetLightAt(Point3D point);
73 
74  /// <summary>
75  /// Determines whether the light casts a shadow or not.
76  /// </summary>
77  bool CastsShadow { get; }
78 
79  /// <summary>
80  /// Determines the amount of obstruction of the light that results at <paramref name="point"/> due to the specified <paramref name="shadowingTriangles"/>.
81  /// </summary>
82  /// <param name="point">The <see cref="Point3D"/> at which the obstruction should be computed.</param>
83  /// <param name="shadowingTriangles">A collection of <see cref="Triangle3DElement"/> casting shadows.</param>
84  /// <returns>1 if the light is completely obstructed, 0 if the light is completely visible, a value between these if the light is partially obstructed.</returns>
85  double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles);
86  }
87 
88  /// <summary>
89  /// Represents a uniform ambien light source.
90  /// </summary>
92  {
93  /// <summary>
94  /// The intensity of the light.
95  /// </summary>
96  public double Intensity { get; set; }
97 
98  /// <inheritdoc/>
99  public bool CastsShadow => false;
100 
101  /// <summary>
102  /// Creates a new <see cref="AmbientLightSource"/> instance.
103  /// </summary>
104  /// <param name="intensity">The intensity of the light.</param>
105  public AmbientLightSource(double intensity)
106  {
107  this.Intensity = intensity;
108  }
109 
110  /// <inheritdoc/>
111  public LightIntensity GetLightAt(Point3D point)
112  {
113  return new LightIntensity(Intensity, new NormalizedVector3D(double.NaN, double.NaN, double.NaN));
114  }
115 
116  /// <inheritdoc/>
117  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
118  {
119  return 0;
120  }
121  }
122 
123  /// <summary>
124  /// Represents a parallel light source.
125  /// </summary>
127  {
128  /// <summary>
129  /// The intensity of the light.
130  /// </summary>
131  public double Intensity { get; set; }
132 
133  /// <summary>
134  /// The direction along which the light travels.
135  /// </summary>
136  public NormalizedVector3D Direction { get; }
137 
138  /// <summary>
139  /// The reverse of <see cref="Direction"/>.
140  /// </summary>
141  public NormalizedVector3D ReverseDirection { get; }
142 
143  /// <inheritdoc/>
144  public bool CastsShadow { get; set; } = true;
145 
146  /// <summary>
147  /// Creates a new <see cref="ParallelLightSource"/> instance.
148  /// </summary>
149  /// <param name="intensity">The intensity of the light.</param>
150  /// <param name="direction">The direction along which the light travels.</param>
151  public ParallelLightSource(double intensity, NormalizedVector3D direction)
152  {
153  this.Intensity = intensity;
154  this.Direction = direction;
155  this.ReverseDirection = direction.Reverse();
156  }
157 
158  /// <inheritdoc/>
159  public LightIntensity GetLightAt(Point3D point)
160  {
161  return new LightIntensity(Intensity, Direction);
162  }
163 
164  /// <inheritdoc/>
165  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
166  {
167  foreach (Triangle3DElement triangle in shadowingTriangles)
168  {
169  Point3D? projected = triangle.ProjectOnThisPlane(point, ReverseDirection, true, double.PositiveInfinity);
170 
171  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
172  {
173  return 1;
174  }
175  }
176 
177  return 0;
178  }
179  }
180 
181  /// <summary>
182  /// Represents a point light source.
183  /// </summary>
185  {
186  /// <inheritdoc/>
187  public bool CastsShadow { get; set; } = true;
188 
189  /// <summary>
190  /// The position of the light source.
191  /// </summary>
192  public Point3D Position { get; set; }
193 
194  /// <summary>
195  /// The base intensity of the light.
196  /// </summary>
197  public double Intensity { get; set; }
198 
199  /// <summary>
200  /// An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable distance attenuation.
201  /// </summary>
202  public double DistanceAttenuationExponent { get; set; } = 2;
203 
204  /// <summary>
205  /// Creates a new <see cref="PointLightSource"/> instance.
206  /// </summary>
207  /// <param name="intensity">The intensity of the light.</param>
208  /// <param name="position">The position of the light source.</param>
209  public PointLightSource(double intensity, Point3D position)
210  {
211  this.Position = position;
212  this.Intensity = intensity;
213  }
214 
215  /// <inheritdoc/>
216  public LightIntensity GetLightAt(Point3D point)
217  {
219  {
220  return new LightIntensity(Intensity / ((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z)), (point - Position).Normalize());
221  }
222  else if (DistanceAttenuationExponent == 0)
223  {
224  return new LightIntensity(Intensity, (point - Position).Normalize());
225  }
226  else
227  {
228  return new LightIntensity(Intensity / Math.Pow((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z), DistanceAttenuationExponent * 0.5), (point - Position).Normalize());
229  }
230  }
231 
232  /// <inheritdoc/>
233  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
234  {
235  Vector3D reverseDir = this.Position - point;
236  double maxD = reverseDir.Modulus;
237  NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false);
238 
239  foreach (Triangle3DElement triangle in shadowingTriangles)
240  {
241  Point3D? projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD);
242 
243  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
244  {
245  return 1;
246  }
247  }
248 
249  return 0;
250  }
251  }
252 
253  /// <summary>
254  /// Represents a conic spotlight.
255  /// </summary>
257  {
258  /// <inheritdoc/>
259  public bool CastsShadow { get; set; } = true;
260 
261  /// <summary>
262  /// The position of the light source.
263  /// </summary>
264  public Point3D Position { get; set; }
265 
266  /// <summary>
267  /// The direction of the cone axis.
268  /// </summary>
269  public NormalizedVector3D Direction { get; set; }
270 
271  /// <summary>
272  /// The base intensity of the light.
273  /// </summary>
274  public double Intensity { get; set; }
275 
276  /// <summary>
277  /// The angular size of the light cone, in radians.
278  /// </summary>
279  public double BeamWidthAngle { get; set; }
280 
281  /// <summary>
282  /// The angular size of the cutoff cone, in radians.
283  /// </summary>
284  public double CutoffAngle { get; set; }
285 
286  /// <summary>
287  /// An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable distance attenuation.
288  /// </summary>
289  public double DistanceAttenuationExponent { get; set; } = 2;
290 
291  /// <summary>
292  /// An exponent determining how fast the light attenuates between the main light cone and the cutoff cone.
293  /// </summary>
294  public double AngleAttenuationExponent { get; set; } = 1;
295 
296  /// <summary>
297  /// Creates a new <see cref="SpotlightLightSource"/> instance.
298  /// </summary>
299  /// <param name="intensity">The intensity of the light.</param>
300  /// <param name="position">The position of the light source.</param>
301  /// <param name="direction">The direction of the cone's axis.</param>
302  /// <param name="beamWidthAngle">The angular size of the light cone, in radians.</param>
303  /// <param name="cutoffAngle">The angular size of the cutoff cone, in radians.</param>
304  public SpotlightLightSource(double intensity, Point3D position, NormalizedVector3D direction, double beamWidthAngle, double cutoffAngle)
305  {
306  this.Position = position;
307  this.Direction = direction;
308  this.Intensity = intensity;
309  this.BeamWidthAngle = beamWidthAngle;
310  this.CutoffAngle = cutoffAngle;
311  }
312 
313  /// <inheritdoc/>
314  public LightIntensity GetLightAt(Point3D point)
315  {
316  double angle = Math.Atan2(((point - Position) ^ Direction).Modulus, (point - Position) * Direction);
317  if (angle > Math.PI)
318  {
319  angle -= 2 * Math.PI;
320  }
321  angle = Math.Abs(angle);
322  double intensity;
323 
325  {
326  intensity = Intensity;
327  }
328  else if (DistanceAttenuationExponent == 2)
329  {
330  intensity = Intensity / ((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z));
331  }
332  else
333  {
334  intensity = Intensity / Math.Pow((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z), 0.5 * DistanceAttenuationExponent);
335  }
336 
337  if (angle <= BeamWidthAngle)
338  {
339  // * 1
340  }
341  else if (angle >= CutoffAngle)
342  {
343  intensity = 0;
344  }
345  else if (AngleAttenuationExponent == 0)
346  {
347  // * 1
348  }
349  else if (AngleAttenuationExponent == 1)
350  {
351  intensity *= (CutoffAngle - angle) / (CutoffAngle - BeamWidthAngle);
352  }
353  else
354  {
355  intensity *= Math.Pow((CutoffAngle - angle) / (CutoffAngle - BeamWidthAngle), AngleAttenuationExponent);
356  }
357 
358  return new LightIntensity(intensity, (point - Position).Normalize());
359  }
360 
361  /// <inheritdoc/>
362  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
363  {
364  Vector3D reverseDir = this.Position - point;
365  double maxD = reverseDir.Modulus;
366  NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false);
367 
368  foreach (Triangle3DElement triangle in shadowingTriangles)
369  {
370  Point3D? projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD);
371 
372  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
373  {
374  return 1;
375  }
376  }
377 
378  return 0;
379  }
380  }
381 
382  /// <summary>
383  /// Represents a point light source with a stencil in front of it.
384  /// </summary>
386  {
387  /// <inheritdoc/>
388  public bool CastsShadow { get; set; } = true;
389 
390  /// <summary>
391  /// The position of the light source.
392  /// </summary>
393  public Point3D Position { get; }
394 
395  /// <summary>
396  /// The projection of the <see cref="Position"/> on the mask plane along the light's <see cref="Direction"/>.
397  /// </summary>
398  public Point3D Origin { get; }
399 
400  /// <summary>
401  /// The direction of the light.
402  /// </summary>
403  public NormalizedVector3D Direction { get; }
404 
405  /// <summary>
406  /// The distance between the light source and the mask plane.
407  /// </summary>
408  public double Distance { get; }
409 
410  private List<Point3D[]> TriangulatedMask { get; }
411 
412  /// <summary>
413  /// The base intensity of the light.
414  /// </summary>
415  public double Intensity { get; set; }
416 
417  /// <summary>
418  /// An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable distance attenuation.
419  /// </summary>
420  public double DistanceAttenuationExponent { get; set; } = 2;
421 
422  /// <summary>
423  /// An exponent determining how fast the light attenuates away from the light's axis. Set to 0 to disable angular attenuation.
424  /// </summary>
425  public double AngleAttenuationExponent { get; set; } = 1;
426 
427  /// <summary>
428  /// Creates a new <see cref="MaskedLightSource"/> by triangulating the specified <see cref="GraphicsPath"/>.
429  /// </summary>
430  /// <param name="intensity">The base intensity of the light.</param>
431  /// <param name="position">The position of the light source.</param>
432  /// <param name="direction">The direction of the light.</param>
433  /// <param name="distance">The distance between the light source and the mask plane.</param>
434  /// <param name="mask">A <see cref="GraphicsPath"/> representing the transparent part of the mask.</param>
435  /// <param name="maskOrientation">An angle in radians determining the orientation of the 2D mask in the mask plane.</param>
436  /// <param name="triangulationResolution">The resolution to use to triangulate the <paramref name="mask"/>.</param>
437  public MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, GraphicsPath mask, double maskOrientation, double triangulationResolution) : this(intensity, position, direction, distance, mask.Triangulate(triangulationResolution, true), maskOrientation)
438  {
439 
440  }
441 
442  /// <summary>
443  /// Creates a new <see cref="MaskedLightSource"/> using the specified <paramref name="triangulatedMask"/>.
444  /// </summary>
445  /// <param name="intensity">The base intensity of the light.</param>
446  /// <param name="position">The position of the light source.</param>
447  /// <param name="direction">The direction of the light.</param>
448  /// <param name="distance">The distance between the light source and the mask plane.</param>
449  /// <param name="triangulatedMask">A collection of <see cref="GraphicsPath"/>s representing the transparent part of the mask. Each <see cref="GraphicsPath"/> should represent a single triangle.</param>
450  /// <param name="maskOrientation">An angle in radians determining the orientation of the 2D mask in the mask plane.</param>
451  public MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, IEnumerable<GraphicsPath> triangulatedMask, double maskOrientation)
452  {
453  this.Intensity = intensity;
454  this.Position = position;
455  this.Direction = direction;
456  this.Distance = distance;
457  this.Origin = this.Position + distance * this.Direction;
458 
459  double[,] rotation = Matrix3D.RotationToAlignWithZ(direction).Inverse();
460 
461  double[,] rotation2 = Matrix3D.RotationAroundAxis(direction, maskOrientation);
462 
463  List<Point3D[]> maskList = new List<Point3D[]>();
464 
465  foreach (GraphicsPath trianglePath in triangulatedMask)
466  {
467  Point3D[] triangle = new Point3D[3];
468 
469  List<Point> points = trianglePath.GetPoints().First();
470 
471  for (int i = 0; i < 3; i++)
472  {
473  triangle[i] = (Point3D)((Vector3D)(rotation2 * (rotation * new Point3D(points[i].X, points[i].Y, 0))) + Origin);
474  }
475 
476  maskList.Add(triangle);
477  }
478 
479  this.TriangulatedMask = maskList;
480  }
481 
482  /// <inheritdoc/>
483  public LightIntensity GetLightAt(Point3D point)
484  {
485  double d = Distance / ((point - this.Position) * this.Direction);
486  Point3D pt = this.Position + (point - this.Position) * d;
487 
488  bool contained = false;
489 
490  foreach (Point3D[] triangle in TriangulatedMask)
491  {
492  if (Intersections3D.PointInTriangle(pt, triangle[0], triangle[1], triangle[2]))
493  {
494  contained = true;
495  break;
496  }
497  }
498 
499  if (contained)
500  {
501  double intensity;
502 
504  {
505  intensity = Intensity;
506  }
507  else if (DistanceAttenuationExponent == 2)
508  {
509  intensity = Intensity / ((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z));
510  }
511  else
512  {
513  intensity = Intensity / Math.Pow((point.X - Position.X) * (point.X - Position.X) + (point.Y - Position.Y) * (point.Y - Position.Y) + (point.Z - Position.Z) * (point.Z - Position.Z), 0.5 * DistanceAttenuationExponent);
514  }
515 
516  if (AngleAttenuationExponent == 0)
517  {
518  // * 1
519  }
520  else
521  {
522  double angle = Math.Atan2(((point - Position) ^ Direction).Modulus, (point - Position) * Direction);
523  if (angle > Math.PI)
524  {
525  angle -= 2 * Math.PI;
526  }
527 
528  if (Math.Abs(angle) < Math.PI / 2)
529  {
530  angle = Math.Abs(angle) / Math.PI * 2;
531 
532  if (AngleAttenuationExponent == 1)
533  {
534  intensity *= 1 - angle;
535  }
536  else
537  {
538  intensity *= Math.Pow(1 - angle, AngleAttenuationExponent);
539  }
540  }
541  else
542  {
543  intensity = 0;
544  }
545  }
546 
547  return new LightIntensity(intensity, (point - Position).Normalize());
548  }
549  else
550  {
551  return new LightIntensity(0, (point - Position).Normalize());
552  }
553  }
554 
555  /// <inheritdoc/>
556  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
557  {
558  Vector3D reverseDir = this.Position - point;
559  double maxD = reverseDir.Modulus;
560  NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false);
561 
562  foreach (Triangle3DElement triangle in shadowingTriangles)
563  {
564  Point3D? projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD);
565 
566  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
567  {
568  return 1;
569  }
570  }
571 
572  return 0;
573  }
574  }
575 
576  /// <summary>
577  /// Represents a light source emitting light from a circular area.
578  /// </summary>
580  {
581  /// <inheritdoc/>
582  public bool CastsShadow { get; set; } = true;
583 
584  /// <summary>
585  /// The centre of the light-emitting area.
586  /// </summary>
587  public Point3D Center { get; }
588 
589  private Point3D VirtualSource { get; }
590 
591  /// <summary>
592  /// The direction of the light's main axis, i.e. the normal to the plane containing the light-emitting area.
593  /// </summary>
594  public NormalizedVector3D Direction { get; }
595 
596  /// <summary>
597  /// The radius of the light emitting area.
598  /// </summary>
599  public double Radius { get; }
600 
601  /// <summary>
602  /// The radius of the penumbra area.
603  /// </summary>
604  public double PenumbraRadius { get; }
605 
606  /// <summary>
607  /// The base intensity of the light.
608  /// </summary>
609  public double Intensity { get; set; }
610 
611  /// <summary>
612  /// The distance between the focal point of the light and the light's <see cref="Center"/>.
613  /// </summary>
614  public double SourceDistance { get; }
615 
616  /// <summary>
617  /// An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable distance attenuation.
618  /// </summary>
619  public double DistanceAttenuationExponent { get; set; } = 2;
620 
621  /// <summary>
622  /// An exponent determining how fast the light attenuates between the light-emitting area radius and the penumbra radius.
623  /// </summary>
624  public double PenumbraAttenuationExponent { get; set; } = 1;
625 
626  /// <summary>
627  /// The number of points to use when determining the amount of light that is obstructed at a certain point.
628  /// </summary>
629  public int ShadowSamplingPointCount { get; }
630 
631  private Point3D[] ShadowSamplingPoints { get; }
632 
633  /// <summary>
634  /// Creates a new <see cref="AreaLightSource"/> instance.
635  /// </summary>
636  /// <param name="intensity">The base intensity of the light.</param>
637  /// <param name="center">The centre of the light-emitting area.</param>
638  /// <param name="radius">The radius of the light-emitting area.</param>
639  /// <param name="penumbraRadius">The radius of the penumbra area.</param>
640  /// <param name="direction">The direction of the light.</param>
641  /// <param name="sourceDistance">The distance between the focal point of the light and the light's center.</param>
642  /// <param name="shadowSamplingPointCount">The number of points to use when determining the amount of light that is obstructed at a certain point.</param>
643  public AreaLightSource(double intensity, Point3D center, double radius, double penumbraRadius, NormalizedVector3D direction, double sourceDistance, int shadowSamplingPointCount)
644  {
645  if (shadowSamplingPointCount < 1)
646  {
647  throw new ArgumentOutOfRangeException(nameof(shadowSamplingPointCount), shadowSamplingPointCount, "The number of shadow sampling points must be greater than or equal to 1!");
648  }
649 
650  this.Center = center;
651  this.Direction = direction;
652  this.Intensity = intensity;
653  this.Radius = radius;
654  this.PenumbraRadius = penumbraRadius;
655  this.SourceDistance = sourceDistance;
656 
657  this.VirtualSource = this.Center - this.Direction * this.SourceDistance;
658 
659  this.ShadowSamplingPointCount = shadowSamplingPointCount;
660 
661  NormalizedVector3D yAxis;
662 
663  if (Math.Abs(new Vector3D(0, 1, 0) * this.Direction) < 1)
664  {
665  yAxis = (new Vector3D(0, 1, 0) - (new Vector3D(0, 1, 0) * this.Direction) * this.Direction).Normalize();
666  }
667  else
668  {
669  yAxis = (new Vector3D(0, 0, 1) - (new Vector3D(0, 0, 1) * this.Direction) * this.Direction).Normalize();
670  }
671 
672  NormalizedVector3D xAxis = (this.Direction ^ yAxis).Normalize();
673 
674  this.ShadowSamplingPoints = new Point3D[shadowSamplingPointCount - 1];
675 
676  for (int i = 0; i < shadowSamplingPointCount - 1; i++)
677  {
678  double r = ((double)i / (shadowSamplingPointCount - 2)) * this.Radius;
679  double theta = (double)i / (shadowSamplingPointCount - 2) * 2 * Math.PI * ((shadowSamplingPointCount - 1) / 3.7);
680 
681  double x = r * Math.Cos(theta);
682  double y = r * Math.Sin(theta);
683 
684  Point3D pt = this.Center + x * xAxis + y * yAxis;
685  this.ShadowSamplingPoints[i] = pt;
686  }
687  }
688 
689  /// <inheritdoc/>
690  public LightIntensity GetLightAt(Point3D point)
691  {
692  double denom = ((point - this.VirtualSource) * this.Direction);
693 
694  if (denom <= 0)
695  {
696  return new LightIntensity(0, (point - this.VirtualSource).Normalize());
697  }
698  else
699  {
700  double d = this.SourceDistance / denom;
701  Point3D pt = this.VirtualSource + d * (point - this.VirtualSource);
702  double distSq = (pt.X - this.Center.X) * (pt.X - this.Center.X) + (pt.Y - this.Center.Y) * (pt.Y - this.Center.Y) + (pt.Z - this.Center.Z) * (pt.Z - this.Center.Z);
703 
704  if (distSq < this.Radius * this.Radius)
705  {
706  double intensity;
708  {
709  intensity = Intensity;
710  }
711  else if (DistanceAttenuationExponent == 2)
712  {
713  intensity = Intensity / ((point.X - this.VirtualSource.X) * (point.X - this.VirtualSource.X) + (point.Y - this.VirtualSource.Y) * (point.Y - this.VirtualSource.Y) + (point.Z - this.VirtualSource.Z) * (point.Z - this.VirtualSource.Z));
714  }
715  else
716  {
717  intensity = Intensity / Math.Pow((point.X - this.VirtualSource.X) * (point.X - this.VirtualSource.X) + (point.Y - this.VirtualSource.Y) * (point.Y - this.VirtualSource.Y) + (point.Z - this.VirtualSource.Z) * (point.Z - this.VirtualSource.Z), 0.5 * DistanceAttenuationExponent);
718  }
719 
720  return new LightIntensity(intensity, (point - this.VirtualSource).Normalize());
721  }
722  else if (distSq < this.PenumbraRadius * this.PenumbraRadius)
723  {
724  double intensity;
726  {
727  intensity = Intensity;
728  }
729  else if (DistanceAttenuationExponent == 2)
730  {
731  intensity = Intensity / ((point.X - this.VirtualSource.X) * (point.X - this.VirtualSource.X) + (point.Y - this.VirtualSource.Y) * (point.Y - this.VirtualSource.Y) + (point.Z - VirtualSource.Z) * (point.Z - this.VirtualSource.Z));
732  }
733  else
734  {
735  intensity = Intensity / Math.Pow((point.X - this.VirtualSource.X) * (point.X - this.VirtualSource.X) + (point.Y - this.VirtualSource.Y) * (point.Y - this.VirtualSource.Y) + (point.Z - this.VirtualSource.Z) * (point.Z - this.VirtualSource.Z), 0.5 * DistanceAttenuationExponent);
736  }
737 
739  {
740  //intensity *= 1;
741  }
742  else if (PenumbraAttenuationExponent == 1)
743  {
744  double factor = (Math.Sqrt(distSq) - this.Radius) / (this.PenumbraRadius - this.Radius);
745  intensity *= 1 - factor;
746  }
747  else
748  {
749  double factor = (Math.Sqrt(distSq) - this.Radius) / (this.PenumbraRadius - this.Radius);
750  intensity *= Math.Pow(1 - factor, PenumbraAttenuationExponent);
751  }
752 
753  return new LightIntensity(intensity, (point - this.VirtualSource).Normalize());
754  }
755  else
756  {
757  return new LightIntensity(0, (point - this.VirtualSource).Normalize());
758  }
759  }
760  }
761 
762  /// <inheritdoc/>
763  public double GetObstruction(Point3D point, IEnumerable<Triangle3DElement> shadowingTriangles)
764  {
765  double totalObstruction = 0;
766  int sampleCount = 0;
767 
768  {
769  Vector3D reverseDir = this.VirtualSource - point;
770 
771  double denom = (reverseDir * this.Direction);
772 
773  if (denom == 0)
774  {
775  //totalObstruction += 0;
776  sampleCount++;
777  }
778  else
779  {
780  double d = this.SourceDistance / denom;
781  Point3D pt = this.VirtualSource + d * reverseDir;
782 
783  double maxD = (point - pt).Modulus;
784  NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false);
785 
786  bool found = false;
787 
788  foreach (Triangle3DElement triangle in shadowingTriangles)
789  {
790  Point3D? projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD);
791 
792  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
793  {
794  totalObstruction += 1;
795  sampleCount++;
796  found = true;
797  break;
798  }
799  }
800 
801  if (!found)
802  {
803  //totalObstruction += 0;
804  sampleCount++;
805  }
806  }
807  }
808 
809 
810  for (int i = 0; i < ShadowSamplingPoints.Length; i++)
811  {
812  Vector3D reverseDir = ShadowSamplingPoints[i] - point;
813 
814  double maxD = reverseDir.Modulus;
815  NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false);
816 
817  bool found = false;
818 
819  foreach (Triangle3DElement triangle in shadowingTriangles)
820  {
821  Point3D? projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD);
822 
823  if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3))
824  {
825  totalObstruction += 1;
826  sampleCount++;
827  found = true;
828  break;
829  }
830  }
831 
832  if (!found)
833  {
834  //totalObstruction += 0;
835  sampleCount++;
836  }
837  }
838 
839  return totalObstruction / sampleCount;
840  }
841  }
842 }
VectSharp.ThreeD.AmbientLightSource.AmbientLightSource
AmbientLightSource(double intensity)
Creates a new AmbientLightSource instance.
Definition: Lights.cs:105
VectSharp.ThreeD.SpotlightLightSource.DistanceAttenuationExponent
double DistanceAttenuationExponent
An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable d...
Definition: Lights.cs:289
VectSharp.ThreeD.SpotlightLightSource.AngleAttenuationExponent
double AngleAttenuationExponent
An exponent determining how fast the light attenuates between the main light cone and the cutoff cone...
Definition: Lights.cs:294
VectSharp.ThreeD.PointLightSource.Position
Point3D Position
The position of the light source.
Definition: Lights.cs:192
VectSharp.ThreeD.ParallelLightSource.Intensity
double Intensity
The intensity of the light.
Definition: Lights.cs:131
VectSharp.ThreeD.ParallelLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:159
VectSharp.ThreeD.ParallelLightSource
Represents a parallel light source.
Definition: Lights.cs:127
VectSharp.ThreeD.MaskedLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:556
VectSharp.ThreeD.PointLightSource
Represents a point light source.
Definition: Lights.cs:185
VectSharp.ThreeD.ILightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
VectSharp.GraphicsPath
Represents a graphics path that can be filled or stroked.
Definition: GraphicsPath.cs:29
VectSharp.ThreeD.AreaLightSource
Represents a light source emitting light from a circular area.
Definition: Lights.cs:580
VectSharp.ThreeD.PointLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:187
VectSharp.ThreeD.ParallelLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:144
VectSharp.ThreeD.PointLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:233
VectSharp.ThreeD.AreaLightSource.PenumbraAttenuationExponent
double PenumbraAttenuationExponent
An exponent determining how fast the light attenuates between the light-emitting area radius and the ...
Definition: Lights.cs:624
VectSharp.ThreeD.AreaLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:690
VectSharp.ThreeD.MaskedLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:483
VectSharp.ThreeD.LightIntensity.Intensity
double Intensity
The intensity of the light.
Definition: Lights.cs:32
VectSharp.ThreeD.ParallelLightSource.Direction
NormalizedVector3D Direction
The direction along which the light travels.
Definition: Lights.cs:136
VectSharp.ThreeD.AreaLightSource.Center
Point3D Center
The centre of the light-emitting area.
Definition: Lights.cs:587
VectSharp.ThreeD.LightIntensity.Deconstruct
void Deconstruct(out double intensity, out NormalizedVector3D direction)
Deconstructs the struct.
Definition: Lights.cs:55
VectSharp.ThreeD.AreaLightSource.Direction
NormalizedVector3D Direction
The direction of the light's main axis, i.e. the normal to the plane containing the light-emitting ar...
Definition: Lights.cs:594
VectSharp.ThreeD.AreaLightSource.PenumbraRadius
double PenumbraRadius
The radius of the penumbra area.
Definition: Lights.cs:604
VectSharp.ThreeD
Definition: Lights.cs:23
VectSharp.ThreeD.AreaLightSource.ShadowSamplingPointCount
int ShadowSamplingPointCount
The number of points to use when determining the amount of light that is obstructed at a certain poin...
Definition: Lights.cs:629
VectSharp.ThreeD.MaskedLightSource.Direction
NormalizedVector3D Direction
The direction of the light.
Definition: Lights.cs:403
VectSharp.ThreeD.AmbientLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:117
VectSharp.ThreeD.SpotlightLightSource.BeamWidthAngle
double BeamWidthAngle
The angular size of the light cone, in radians.
Definition: Lights.cs:279
VectSharp.ThreeD.SpotlightLightSource.Position
Point3D Position
The position of the light source.
Definition: Lights.cs:264
VectSharp.ThreeD.MaskedLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:388
VectSharp.ThreeD.AmbientLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:111
VectSharp.ThreeD.AreaLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:582
VectSharp.ThreeD.PointLightSource.Intensity
double Intensity
The base intensity of the light.
Definition: Lights.cs:197
VectSharp.ThreeD.ILightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
VectSharp.ThreeD.ParallelLightSource.ReverseDirection
NormalizedVector3D ReverseDirection
The reverse of Direction.
Definition: Lights.cs:141
VectSharp.ThreeD.PointLightSource.DistanceAttenuationExponent
double DistanceAttenuationExponent
An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable d...
Definition: Lights.cs:202
VectSharp.ThreeD.AreaLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:763
VectSharp.ThreeD.MaskedLightSource.MaskedLightSource
MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, GraphicsPath mask, double maskOrientation, double triangulationResolution)
Creates a new MaskedLightSource by triangulating the specified GraphicsPath.
Definition: Lights.cs:437
VectSharp.ThreeD.ParallelLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:165
VectSharp.ThreeD.MaskedLightSource.DistanceAttenuationExponent
double DistanceAttenuationExponent
An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable d...
Definition: Lights.cs:420
VectSharp.ThreeD.SpotlightLightSource.GetObstruction
double GetObstruction(Point3D point, IEnumerable< Triangle3DElement > shadowingTriangles)
Determines the amount of obstruction of the light that results at point due to the specified shadowi...
Definition: Lights.cs:362
VectSharp.ThreeD.LightIntensity
Represents the intensity of a light source at a particular point.
Definition: Lights.cs:28
VectSharp.ThreeD.MaskedLightSource.Distance
double Distance
The distance between the light source and the mask plane.
Definition: Lights.cs:408
VectSharp.ThreeD.SpotlightLightSource
Represents a conic spotlight.
Definition: Lights.cs:257
VectSharp.ThreeD.AmbientLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:99
VectSharp.ThreeD.ParallelLightSource.ParallelLightSource
ParallelLightSource(double intensity, NormalizedVector3D direction)
Creates a new ParallelLightSource instance.
Definition: Lights.cs:151
VectSharp.ThreeD.MaskedLightSource.AngleAttenuationExponent
double AngleAttenuationExponent
An exponent determining how fast the light attenuates away from the light's axis. Set to 0 to disable...
Definition: Lights.cs:425
VectSharp.ThreeD.AmbientLightSource
Represents a uniform ambien light source.
Definition: Lights.cs:92
VectSharp.ThreeD.LightIntensity.LightIntensity
LightIntensity(double intensity, NormalizedVector3D direction)
Creates a new LightIntensity.
Definition: Lights.cs:44
VectSharp.ThreeD.SpotlightLightSource.Intensity
double Intensity
The base intensity of the light.
Definition: Lights.cs:274
VectSharp.ThreeD.SpotlightLightSource.CastsShadow
bool CastsShadow
Definition: Lights.cs:259
VectSharp.ThreeD.SpotlightLightSource.SpotlightLightSource
SpotlightLightSource(double intensity, Point3D position, NormalizedVector3D direction, double beamWidthAngle, double cutoffAngle)
Creates a new SpotlightLightSource instance.
Definition: Lights.cs:304
VectSharp.ThreeD.AreaLightSource.Radius
double Radius
The radius of the light emitting area.
Definition: Lights.cs:599
VectSharp.ThreeD.ILightSource.CastsShadow
bool CastsShadow
Determines whether the light casts a shadow or not.
Definition: Lights.cs:77
VectSharp.ThreeD.SpotlightLightSource.CutoffAngle
double CutoffAngle
The angular size of the cutoff cone, in radians.
Definition: Lights.cs:284
VectSharp.ThreeD.AreaLightSource.DistanceAttenuationExponent
double DistanceAttenuationExponent
An exponent determining how fast the light attenuates with increasing distance. Set to 0 to disable d...
Definition: Lights.cs:619
VectSharp.ThreeD.AreaLightSource.Intensity
double Intensity
The base intensity of the light.
Definition: Lights.cs:609
VectSharp.ThreeD.PointLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:216
VectSharp.ThreeD.MaskedLightSource
Represents a point light source with a stencil in front of it.
Definition: Lights.cs:386
VectSharp.ThreeD.AmbientLightSource.Intensity
double Intensity
The intensity of the light.
Definition: Lights.cs:96
VectSharp.ThreeD.MaskedLightSource.Origin
Point3D Origin
The projection of the Position on the mask plane along the light's Direction.
Definition: Lights.cs:398
VectSharp.ThreeD.LightIntensity.Direction
NormalizedVector3D Direction
The direction towards from which the light comes.
Definition: Lights.cs:37
VectSharp.ThreeD.PointLightSource.PointLightSource
PointLightSource(double intensity, Point3D position)
Creates a new PointLightSource instance.
Definition: Lights.cs:209
VectSharp.GraphicsPath.GetPoints
IEnumerable< List< Point > > GetPoints()
Gets a collection of the end points of all the segments in the GraphicsPath, divided by figure.
Definition: GraphicsPath.cs:1872
VectSharp.ThreeD.AreaLightSource.AreaLightSource
AreaLightSource(double intensity, Point3D center, double radius, double penumbraRadius, NormalizedVector3D direction, double sourceDistance, int shadowSamplingPointCount)
Creates a new AreaLightSource instance.
Definition: Lights.cs:643
VectSharp.ThreeD.SpotlightLightSource.Direction
NormalizedVector3D Direction
The direction of the cone axis.
Definition: Lights.cs:269
VectSharp.ThreeD.SpotlightLightSource.GetLightAt
LightIntensity GetLightAt(Point3D point)
Computes the light intensity at the specified point, without taking into account any obstructions.
Definition: Lights.cs:314
VectSharp.ThreeD.MaskedLightSource.MaskedLightSource
MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, IEnumerable< GraphicsPath > triangulatedMask, double maskOrientation)
Creates a new MaskedLightSource using the specified triangulatedMask .
Definition: Lights.cs:451
VectSharp.ThreeD.AreaLightSource.SourceDistance
double SourceDistance
The distance between the focal point of the light and the light's Center.
Definition: Lights.cs:614
VectSharp.ThreeD.ILightSource
Represents a light source.
Definition: Lights.cs:66
VectSharp.ThreeD.MaskedLightSource.Intensity
double Intensity
The base intensity of the light.
Definition: Lights.cs:415
VectSharp.ThreeD.MaskedLightSource.Position
Point3D Position
The position of the light source.
Definition: Lights.cs:393