made point light shadows
authorvjiaoblack <vjiaoblack@google.com>
Wed, 7 Sep 2016 14:48:12 +0000 (07:48 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 7 Sep 2016 14:48:12 +0000 (07:48 -0700)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2294323003

Review-Url: https://codereview.chromium.org/2294323003

samplecode/SampleShadowing.cpp
src/core/SkShadowShader.cpp
src/utils/SkShadowPaintFilterCanvas.cpp
src/utils/SkShadowPaintFilterCanvas.h

index 5715e1a..35ef2c1 100644 (file)
@@ -22,10 +22,10 @@ public:
         , fClearShadowMaps(false)
         , fSelectedRectID(-1)
         , fSelectedSliderID(-1)
-        , fLightDepth(600.0f)  {
+        , fLightDepth(400.0f)  {
         this->setBGColor(0xFFCCCCCC);
 
-        this->updateLights(200, 200);
+        this->updateLights(100, 100);
 
         fTestRects[0].fColor = 0xFFEE8888;
         fTestRects[0].fDepth = 80;
@@ -53,7 +53,7 @@ public:
 
         fShadowParams.fShadowRadius = 4.0f;
         fShadowParams.fBiasingConstant = 0.3f;
-        fShadowParams.fMinVariance = 1024;
+        fShadowParams.fMinVariance = 2048; // we need a higher min variance for point lights
         fShadowParams.fType = SkShadowParams::kVariance_ShadowType;
     }
 
@@ -162,6 +162,7 @@ protected:
 
     void updateLights(int x, int y) {
         SkLights::Builder builder;
+        builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
         builder.add(SkLights::Light::MakePoint(SkColor3f::Make(0.2f, 0.4f, 0.6f),
                                                SkVector3::Make(x - 50,
                                                                350 - y,
@@ -172,8 +173,6 @@ protected:
                                                                450 - y,
                                                                fLightDepth),
                                                100000));
-        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.2f, 0.2f, 0.2f),
-                                                     SkVector3::Make(0.2f, 0.2f, 1.0f)));
         fLights = builder.finish();
     }
 
index 49e91d6..13baec1 100644 (file)
@@ -297,27 +297,41 @@ public:
                 if (shadowFP.fIsPointLight[i]) {
                     fragBuilder->codeAppendf("vec3 fragToLight%d = %s - worldCor;",
                                              i, lightDirOrPosUniName[i]);
-                    fragBuilder->codeAppendf("float distsq%d = dot(fragToLight%d, fragToLight%d);"
-                                             "fragToLight%d = normalize(fragToLight%d);",
-                                             i, i, i, i, i);
-                    fragBuilder->codeAppendf("%s = vec2(worldCor.x - %s.x, %s.y - worldCor.y) * "
-                                                  "povDepth.b;",
-                                             offset.c_str(), lightDirOrPosUniName[i],
-                                             lightDirOrPosUniName[i]);
+                    fragBuilder->codeAppendf("float distsq%d = dot(fragToLight%d, "
+                                                                  "fragToLight%d);",
+                                             i, i, i);
+                    fragBuilder->codeAppendf("%s = vec2(-fragToLight%d) * povDepth.b;",
+                                             offset.c_str(), i);
+                    fragBuilder->codeAppendf("fragToLight%d = normalize(fragToLight%d);",
+                                             i, i);
+
+                    // the 0.375s are precalculated transform values, given that the depth
+                    // maps for pt lights are 4x the size (linearly) as diffuse maps.
+                    // The vec2(0.375, -0.375) is used to transform us to the center of the map.
+                    fragBuilder->codeAppendf("vec2 %s = ((vec2(%s, %s) *"
+                                                     "vMatrixCoord_0_1_Stage0 +"
+                                                     "vec2(0,%s - %s)"
+                                                     "+ %s) / (vec2(%s, %s))) +"
+                                                     "vec2(0.375, -0.375);",
+                                             povCoord.c_str(),
+                                             widthUniName, heightUniName,
+                                             depthMapHeightUniName[i], heightUniName,
+                                             offset.c_str(),
+                                             depthMapWidthUniName[i], depthMapWidthUniName[i]);
                 } else {
                     fragBuilder->codeAppendf("%s = vec2(%s) * povDepth.b * vec2(255.0, -255.0);",
                                              offset.c_str(), lightDirOrPosUniName[i]);
+
+                    fragBuilder->codeAppendf("vec2 %s = ((vec2(%s, %s) *"
+                                                     "vMatrixCoord_0_1_Stage0 +"
+                                                     "vec2(0,%s - %s)"
+                                                     "+ %s) / vec2(%s, %s));",
+                                             povCoord.c_str(),
+                                             widthUniName, heightUniName,
+                                             depthMapHeightUniName[i], heightUniName,
+                                             offset.c_str(),
+                                             depthMapWidthUniName[i], depthMapWidthUniName[i]);
                 }
-                fragBuilder->codeAppendf("vec2 %s = (vec2(%s, %s) *"
-                                                    "vMatrixCoord_0_1_Stage0 +"
-                                                    "vec2(0,%s - %s)"
-                                                    " + "
-                                                    "%s) / vec2(%s, %s);",
-                                         povCoord.c_str(),
-                                         widthUniName, heightUniName,
-                                         depthMapHeightUniName[i], heightUniName,
-                                         offset.c_str(),
-                                         depthMapWidthUniName[i], depthMapHeightUniName[i]);
 
                 fragBuilder->appendTextureLookup(&depthMaps[i], args.fTexSamplers[i],
                                                  povCoord.c_str(),
@@ -336,57 +350,58 @@ public:
 
             // add up light contributions from all lights to totalLightColor
             for (int i = 0; i < numLights; i++) {
-                if (!shadowFP.isPointLight(i)) {
+                fragBuilder->codeAppendf("lightProbability = 1;");
+
+                // 1/512 == .00195... is less than half a pixel; imperceptible
+                fragBuilder->codeAppendf("if (%s.b <= %s.b + .001953125) {",
+                                         povDepth.c_str(), depthMaps[i].c_str());
+                if (blurAlgorithm == SkShadowParams::kVariance_ShadowType) {
+                    // We mess with depth and depth^2 in their given scales.
+                    // (i.e. between 0 and 1)
+                    fragBuilder->codeAppendf("vec2 moments%d = vec2(%s.b, %s.g);",
+                                             i, depthMaps[i].c_str(), depthMaps[i].c_str());
+
+                    // variance biasing lessens light bleeding
+                    fragBuilder->codeAppendf("variance = max(moments%d.y - "
+                                                            "(moments%d.x * moments%d.x),"
+                                                            "%s);", i, i, i,
+                                             minVarianceUniName);
+
+                    fragBuilder->codeAppendf("d = (%s.b) - moments%d.x;",
+                                             povDepth.c_str(), i);
+                    fragBuilder->codeAppendf("lightProbability = "
+                                                     "(variance / (variance + d * d));");
+
+                    SkString clamp("clamp");
+                    clamp.appendf("%d", i);
+
+                    // choosing between light artifacts or correct shape shadows
+                    // linstep
+                    fragBuilder->codeAppendf("float %s = clamp((lightProbability - %s) /"
+                                                              "(1 - %s), 0, 1);",
+                                             clamp.c_str(), shBiasUniName, shBiasUniName);
+
+                    fragBuilder->codeAppendf("lightProbability = %s;", clamp.c_str());
+                } else {
+                    fragBuilder->codeAppendf("if (%s.b >= %s.b) {",
+                                             povDepth.c_str(), depthMaps[i].c_str());
                     fragBuilder->codeAppendf("lightProbability = 1;");
+                    fragBuilder->codeAppendf("} else { lightProbability = 0; }");
+                }
 
-                    // 1/512 == .00195... is less than half a pixel; imperceptible
-                    fragBuilder->codeAppendf("if (%s.b <= %s.b + .001953125) {",
-                                             povDepth.c_str(), depthMaps[i].c_str());
-                    if (blurAlgorithm == SkShadowParams::kVariance_ShadowType) {
-                        // We mess with depth and depth^2 in their given scales.
-                        // (i.e. between 0 and 1)
-                        fragBuilder->codeAppendf("vec2 moments%d = vec2(%s.b, %s.g);",
-                                                 i, depthMaps[i].c_str(), depthMaps[i].c_str());
-
-                        // variance biasing lessens light bleeding
-                        fragBuilder->codeAppendf("variance = max(moments%d.y - "
-                                                                "(moments%d.x * moments%d.x),"
-                                                                "%s);", i, i, i,
-                                                 minVarianceUniName);
-
-                        fragBuilder->codeAppendf("d = (%s.b) - moments%d.x;",
-                                                 povDepth.c_str(), i);
-                        fragBuilder->codeAppendf("lightProbability = "
-                                                         "(variance / (variance + d * d));");
-
-                        SkString clamp("clamp");
-                        clamp.appendf("%d", i);
-
-                        // choosing between light artifacts or correct shape shadows
-                        // linstep
-                        fragBuilder->codeAppendf("float %s = clamp((lightProbability - %s) /"
-                                                                  "(1 - %s), 0, 1);",
-                                                 clamp.c_str(), shBiasUniName, shBiasUniName);
-
-                        fragBuilder->codeAppendf("lightProbability = %s;", clamp.c_str());
-                    } else {
-                        fragBuilder->codeAppendf("if (%s.b >= %s.b) {",
-                                                 povDepth.c_str(), depthMaps[i].c_str());
-                        fragBuilder->codeAppendf("lightProbability = 1;");
-                        fragBuilder->codeAppendf("} else { lightProbability = 0; }");
-                    }
+                // VSM: The curved shadows near plane edges are artifacts from blurring
+                // lightDir.z is equal to the lightDir dot the surface normal.
+                fragBuilder->codeAppendf("}");
 
-                    // VSM: The curved shadows near plane edges are artifacts from blurring
-                    fragBuilder->codeAppendf("}");
-                    fragBuilder->codeAppendf("totalLightColor += %s.z * %s * "
+                if (shadowFP.isPointLight(i)) {
+                    fragBuilder->codeAppendf("totalLightColor += max(fragToLight%d.z, 0) * %s /"
+                                                                "(1 + distsq%d) *"
                                                                 "lightProbability;",
+                                             i, lightColorUniName[i], i);
+                } else {
+                    fragBuilder->codeAppendf("totalLightColor += %s.z * %s * lightProbability;",
                                              lightDirOrPosUniName[i],
                                              lightColorUniName[i]);
-                } else {
-                    // fragToLight%d.z is equal to the fragToLight dot the surface normal.
-                    fragBuilder->codeAppendf("totalLightColor += max(fragToLight%d.z, 0) * %s /"
-                                                                "(1 + distsq%d);",
-                                             i, lightColorUniName[i], i);
                 }
             }
 
index ef29c37..289ae3c 100644 (file)
@@ -51,7 +51,12 @@ bool SkShadowPaintFilterCanvas::onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Ty
 SkISize SkShadowPaintFilterCanvas::ComputeDepthMapSize(const SkLights::Light& light, int maxDepth,
                                                        int width, int height) {
     if (light.type() != SkLights::Light::kDirectional_LightType) {
-        return SkISize::Make(width *2 , height * 2);
+        // Calculating the right depth map size for point lights is complex,
+        // as it depends on the max depth, the max depth delta, the location
+        // of the point light and the shapes, etc... If we take upper bounds
+        // on those metrics, the shadow map will be pretty big in any case.
+        // Thus, just using 4x the width and height seems to work for most scenes.
+        return SkISize::Make(width * 4, height * 4);
     }
 
     int dMapWidth = SkMin32(maxDepth * fabs(light.dir().fX) + width,
@@ -74,8 +79,6 @@ void SkShadowPaintFilterCanvas::onDrawPicture(const SkPicture *picture, const Sk
 }
 
 void SkShadowPaintFilterCanvas::updateMatrix() {
-    this->save();
-
     //  It is up to the user to set the 0th light in fLights to
     //  the light the want to render the depth map with.
     if (this->fLights->light(0).type() == SkLights::Light::kDirectional_LightType) {
@@ -84,10 +87,42 @@ void SkShadowPaintFilterCanvas::updateMatrix() {
         SkScalar y = lightDir.fY * this->getZ();
 
         this->translate(x, y);
+    } else if (this->fLights->light(0).type() == SkLights::Light::kPoint_LightType) {
+        SkISize size = this->getBaseLayerSize();
+
+        SkPoint3 lightPos = this->fLights->light(0).pos();
+
+        // shadow maps for point lights are 4x the size of the diffuse map, by experimentation
+        // (see SPFCanvas::ComputeDepthMapSize())
+        SkScalar diffuseHeight = size.fHeight / 4.0f;
+
+        // move point light with canvas's CTM
+        SkPoint lightPoint = SkPoint::Make(lightPos.fX, diffuseHeight - lightPos.fY);
+        SkMatrix mat = this->getTotalMatrix();
+        if (mat.invert(&mat)) {
+            mat.mapPoints(&lightPoint, 1);
+        }
+        lightPoint.set(lightPoint.fX, diffuseHeight - lightPoint.fY);
+
+        // center the shadow map
+        // note: the 3/8 constant is specific to the 4.0 depth map size multiplier
+        mat = this->getTotalMatrix();
+        mat.postTranslate(size.width() * 0.375f, size.height() * 0.375f);
+        this->setMatrix(mat);
+
+        // project shapes onto canvas as shadows
+        SkScalar scale = (lightPos.fZ) / (lightPos.fZ - this->getZ());
+        this->scale(scale, scale);
+
+        this->translate(-lightPoint.fX * this->getZ() /
+                        ((lightPos.fZ - this->getZ()) * scale),
+                        -(diffuseHeight - lightPoint.fY) * this->getZ() /
+                        ((lightPos.fZ - this->getZ()) * scale));
     }
 }
 
 void SkShadowPaintFilterCanvas::onDrawPaint(const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPaint(paint);
     this->restore();
@@ -95,18 +130,21 @@ void SkShadowPaintFilterCanvas::onDrawPaint(const SkPaint &paint) {
 
 void SkShadowPaintFilterCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
                                              const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPoints(mode, count, pts, paint);
     this->restore();
 }
 
 void SkShadowPaintFilterCanvas::onDrawRect(const SkRect &rect, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawRect(rect, paint);
     this->restore();
 }
 
 void SkShadowPaintFilterCanvas::onDrawRRect(const SkRRect &rrect, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawRRect(rrect, paint);
     this->restore();
@@ -114,12 +152,14 @@ void SkShadowPaintFilterCanvas::onDrawRRect(const SkRRect &rrect, const SkPaint
 
 void SkShadowPaintFilterCanvas::onDrawDRRect(const SkRRect &outer, const SkRRect &inner,
                   const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawDRRect(outer, inner, paint);
     this->restore();
 }
 
 void SkShadowPaintFilterCanvas::onDrawOval(const SkRect &rect, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawOval(rect, paint);
     this->restore();
@@ -128,12 +168,14 @@ void SkShadowPaintFilterCanvas::onDrawOval(const SkRect &rect, const SkPaint &pa
 void SkShadowPaintFilterCanvas::onDrawArc(const SkRect &rect, SkScalar startAngle,
                                           SkScalar sweepAngle, bool useCenter,
                                           const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawArc(rect, startAngle, sweepAngle, useCenter, paint);
     this->restore();
 }
 
 void SkShadowPaintFilterCanvas::onDrawPath(const SkPath &path, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPath(path, paint);
     this->restore();
@@ -141,6 +183,7 @@ void SkShadowPaintFilterCanvas::onDrawPath(const SkPath &path, const SkPaint &pa
 
 void SkShadowPaintFilterCanvas::onDrawBitmap(const SkBitmap &bm, SkScalar left, SkScalar top,
                                              const SkPaint *paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawBitmap(bm, left, top, paint);
     this->restore();
@@ -149,6 +192,7 @@ void SkShadowPaintFilterCanvas::onDrawBitmap(const SkBitmap &bm, SkScalar left,
 void SkShadowPaintFilterCanvas::onDrawBitmapRect(const SkBitmap &bm, const SkRect *src,
                                                  const SkRect &dst, const SkPaint *paint,
                                                  SrcRectConstraint constraint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawBitmapRect(bm, src, dst, paint, constraint);
     this->restore();
@@ -156,6 +200,7 @@ void SkShadowPaintFilterCanvas::onDrawBitmapRect(const SkBitmap &bm, const SkRec
 
 void SkShadowPaintFilterCanvas::onDrawBitmapNine(const SkBitmap &bm, const SkIRect &center,
                                                  const SkRect &dst, const SkPaint *paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawBitmapNine(bm, center, dst, paint);
     this->restore();
@@ -163,6 +208,7 @@ void SkShadowPaintFilterCanvas::onDrawBitmapNine(const SkBitmap &bm, const SkIRe
 
 void SkShadowPaintFilterCanvas::onDrawImage(const SkImage *image, SkScalar left,
                                             SkScalar top, const SkPaint *paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawImage(image, left, top, paint);
     this->restore();
@@ -171,6 +217,7 @@ void SkShadowPaintFilterCanvas::onDrawImage(const SkImage *image, SkScalar left,
 void SkShadowPaintFilterCanvas::onDrawImageRect(const SkImage *image, const SkRect *src,
                                                 const SkRect &dst, const SkPaint *paint,
                                                 SrcRectConstraint constraint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawImageRect(image, src, dst, paint, constraint);
     this->restore();
@@ -178,6 +225,7 @@ void SkShadowPaintFilterCanvas::onDrawImageRect(const SkImage *image, const SkRe
 
 void SkShadowPaintFilterCanvas::onDrawImageNine(const SkImage *image, const SkIRect &center,
                                                 const SkRect &dst, const SkPaint *paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawImageNine(image, center, dst, paint);
     this->restore();
@@ -189,6 +237,7 @@ void SkShadowPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount
                                                const SkColor colors[], SkXfermode *xmode,
                                                const uint16_t indices[], int indexCount,
                                                const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors,
                                     xmode, indices, indexCount, paint);
@@ -198,6 +247,7 @@ void SkShadowPaintFilterCanvas::onDrawVertices(VertexMode vmode, int vertexCount
 void SkShadowPaintFilterCanvas::onDrawPatch(const SkPoint cubics[], const SkColor colors[],
                                             const SkPoint texCoords[], SkXfermode *xmode,
                                             const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, paint);
     this->restore();
@@ -205,6 +255,7 @@ void SkShadowPaintFilterCanvas::onDrawPatch(const SkPoint cubics[], const SkColo
 
 void SkShadowPaintFilterCanvas::onDrawText(const void *text, size_t byteLength, SkScalar x,
                                            SkScalar y, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawText(text, byteLength, x, y, paint);
     this->restore();
@@ -212,6 +263,7 @@ void SkShadowPaintFilterCanvas::onDrawText(const void *text, size_t byteLength,
 
 void SkShadowPaintFilterCanvas::onDrawPosText(const void *text, size_t byteLength,
                                               const SkPoint pos[], const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPosText(text, byteLength, pos, paint);
     this->restore();
@@ -220,6 +272,7 @@ void SkShadowPaintFilterCanvas::onDrawPosText(const void *text, size_t byteLengt
 void SkShadowPaintFilterCanvas::onDrawPosTextH(const void *text, size_t byteLength,
                                                const SkScalar xpos[],
                                                SkScalar constY, const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, paint);
     this->restore();
@@ -228,6 +281,7 @@ void SkShadowPaintFilterCanvas::onDrawPosTextH(const void *text, size_t byteLeng
 void SkShadowPaintFilterCanvas::onDrawTextOnPath(const void *text, size_t byteLength,
                                                  const SkPath &path, const SkMatrix *matrix,
                                                  const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint);
     this->restore();
@@ -236,6 +290,7 @@ void SkShadowPaintFilterCanvas::onDrawTextOnPath(const void *text, size_t byteLe
 void SkShadowPaintFilterCanvas::onDrawTextRSXform(const void *text, size_t byteLength,
                                                   const SkRSXform xform[], const SkRect *cull,
                                                   const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawTextRSXform(text, byteLength, xform, cull, paint);
     this->restore();
@@ -243,6 +298,7 @@ void SkShadowPaintFilterCanvas::onDrawTextRSXform(const void *text, size_t byteL
 
 void SkShadowPaintFilterCanvas::onDrawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y,
                                                const SkPaint &paint) {
+    this->save();
     this->updateMatrix();
     this->INHERITED::onDrawTextBlob(blob, x, y, paint);
     this->restore();
index 698f6e1..190c68b 100644 (file)
@@ -37,11 +37,11 @@ public:
 
     void setShadowParams(const SkShadowParams &params);
 protected:
+    void updateMatrix();
+
     void onDrawPicture(const SkPicture *picture, const SkMatrix *matrix,
                        const SkPaint *paint) override;
 
-    void updateMatrix();
-
     void onDrawPaint(const SkPaint &paint) override;
 
     void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],