Add support for directional lights.
Adds a new kDirectionLight_ShadowFlag which indicates that lightPosition
is a vector pointing towards the light, and lightRadius is the blur
radius when the occluder is at z == 1. For other values of z it will
scale linearly. The direction is specified in device space.
Bug: skia:10781
Change-Id: I14d530f006e5541ed6e22bfbeb29c7441c21fb8e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/321792
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 615b5d7..0c2b21b 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -141,7 +141,9 @@
// The umbra can be dropped where it is occluded.
kOpaquePartialUmbra,
// It is known that the entire umbra is occluded.
- kOpaqueNoUmbra
+ kOpaqueNoUmbra,
+ // The light is directional
+ kDirectional
};
SkVector fOffset;
@@ -171,6 +173,9 @@
return true;
}
return false;
+ case OccluderType::kDirectional:
+ *translate = that.fOffset - fOffset;
+ return true;
}
SK_ABORT("Uninitialized occluder type?");
}
@@ -178,11 +183,16 @@
sk_sp<SkVertices> makeVertices(const SkPath& path, const SkMatrix& ctm,
SkVector* translate) const {
bool transparent = OccluderType::kTransparent == fOccluderType;
+ bool directional = OccluderType::kDirectional == fOccluderType;
SkPoint3 zParams = SkPoint3::Make(0, 0, fOccluderHeight);
- if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
+ if (directional) {
translate->set(0, 0);
- return SkShadowTessellator::MakeSpot(path, ctm, zParams,
- fDevLightPos, fLightRadius, transparent);
+ return SkShadowTessellator::MakeSpot(path, ctm, zParams, fDevLightPos, fLightRadius,
+ transparent, true);
+ } else if (ctm.hasPerspective() || OccluderType::kOpaquePartialUmbra == fOccluderType) {
+ translate->set(0, 0);
+ return SkShadowTessellator::MakeSpot(path, ctm, zParams, fDevLightPos, fLightRadius,
+ transparent, false);
} else {
// pick a canonical place to generate shadow, with light centered over path
SkMatrix noTrans(ctm);
@@ -193,7 +203,7 @@
SkPoint3 centerLightPos = SkPoint3::Make(devCenter.fX, devCenter.fY, fDevLightPos.fZ);
*translate = fOffset;
return SkShadowTessellator::MakeSpot(path, noTrans, zParams,
- centerLightPos, fLightRadius, transparent);
+ centerLightPos, fLightRadius, transparent, false);
}
}
};
@@ -499,13 +509,6 @@
return !SkScalarNearlyZero(zPlaneParams.fX) || !SkScalarNearlyZero(zPlaneParams.fY);
}
-static SkPoint3 map(const SkMatrix& m, const SkPoint3& pt) {
- SkPoint3 result;
- m.mapXY(pt.fX, pt.fY, (SkPoint*)&result.fX);
- result.fZ = pt.fZ;
- return result;
-}
-
void SkShadowUtils::ComputeTonalColors(SkColor inAmbientColor, SkColor inSpotColor,
SkColor* outAmbientColor, SkColor* outSpotColor) {
// For tonal color we only compute color values for the spot shadow.
@@ -563,18 +566,24 @@
// Draw an offset spot shadow and outlining ambient shadow for the given path.
void SkShadowUtils::DrawShadow(SkCanvas* canvas, const SkPath& path, const SkPoint3& zPlaneParams,
- const SkPoint3& devLightPos, SkScalar lightRadius,
+ const SkPoint3& lightPos, SkScalar lightRadius,
SkColor ambientColor, SkColor spotColor,
uint32_t flags) {
- SkMatrix inverse;
- if (!canvas->getTotalMatrix().invert(&inverse)) {
- return;
+
+ SkPoint pt = { lightPos.fX, lightPos.fY };
+ if (!SkToBool(flags & kDirectionalLight_ShadowFlag)) {
+ // If light position is in device space, need to transform to local space
+ // before applying to SkCanvas.
+ SkMatrix inverse;
+ if (!canvas->getTotalMatrix().invert(&inverse)) {
+ return;
+ }
+ inverse.mapPoints(&pt, 1);
}
- SkPoint pt = inverse.mapXY(devLightPos.fX, devLightPos.fY);
SkDrawShadowRec rec;
rec.fZPlaneParams = zPlaneParams;
- rec.fLightPos = { pt.fX, pt.fY, devLightPos.fZ };
+ rec.fLightPos = { pt.fX, pt.fY, lightPos.fZ };
rec.fLightRadius = lightRadius;
rec.fAmbientColor = ambientColor;
rec.fSpotColor = spotColor;
@@ -614,10 +623,16 @@
bool tiltZPlane = tilted(rec.fZPlaneParams);
bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+ bool directional = SkToBool(rec.fFlags & kDirectionalLight_ShadowFlag);
bool uncached = tiltZPlane || path.isVolatile();
SkPoint3 zPlaneParams = rec.fZPlaneParams;
- SkPoint3 devLightPos = map(viewMatrix, rec.fLightPos);
+ SkPoint3 devLightPos = rec.fLightPos;
+ if (directional) {
+ ((SkPoint*)&devLightPos.fX)->normalize();
+ } else {
+ viewMatrix.mapPoints((SkPoint*)&devLightPos.fX, 1);
+ }
float lightRadius = rec.fLightRadius;
if (SkColorGetA(rec.fAmbientColor) > 0) {
@@ -706,7 +721,8 @@
sk_sp<SkVertices> vertices = SkShadowTessellator::MakeSpot(path, viewMatrix,
zPlaneParams,
devLightPos, lightRadius,
- transparent);
+ transparent,
+ directional);
if (vertices) {
SkPaint paint;
// Run the vertex color through a GaussianColorFilter and then modulate the
@@ -730,14 +746,24 @@
factory.fLocalCenter = center;
viewMatrix.mapPoints(¢er, 1);
SkScalar radius, scale;
- SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
- devLightPos.fY - center.fY, devLightPos.fZ,
- lightRadius, &radius, &scale, &factory.fOffset);
+ if (SkToBool(rec.fFlags & kDirectionalLight_ShadowFlag)) {
+ SkDrawShadowMetrics::GetDirectionalParams(zPlaneParams.fZ, devLightPos.fX,
+ devLightPos.fY, devLightPos.fZ,
+ lightRadius, &radius, &scale,
+ &factory.fOffset);
+ } else {
+ SkDrawShadowMetrics::GetSpotParams(zPlaneParams.fZ, devLightPos.fX - center.fX,
+ devLightPos.fY - center.fY, devLightPos.fZ,
+ lightRadius, &radius, &scale, &factory.fOffset);
+ }
+
SkRect devBounds;
viewMatrix.mapRect(&devBounds, path.getBounds());
- if (transparent ||
- SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
- SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
+ if (directional) {
+ factory.fOccluderType = SpotVerticesFactory::OccluderType::kDirectional;
+ } else if (transparent ||
+ SkTAbs(factory.fOffset.fX) > 0.5f*devBounds.width() ||
+ SkTAbs(factory.fOffset.fY) > 0.5f*devBounds.height()) {
// if the translation of the shadow is big enough we're going to end up
// filling the entire umbra, so we can treat these as all the same
factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
@@ -765,6 +791,9 @@
case SpotVerticesFactory::OccluderType::kOpaqueNoUmbra:
color = 0xFFE5E500; // corn yellow for covered
break;
+ case SpotVerticesFactory::OccluderType::kDirectional:
+ color = 0xFF550000; // dark red for directional
+ break;
}
#endif
if (!draw_shadow(factory, drawVertsProc, shadowedPath, color)) {
@@ -772,7 +801,7 @@
SkMatrix shadowMatrix;
if (!SkDrawShadowMetrics::GetSpotShadowTransform(devLightPos, lightRadius,
viewMatrix, zPlaneParams,
- path.getBounds(),
+ path.getBounds(), directional,
&shadowMatrix, &radius)) {
return;
}