diff --git a/gm/shadowmaps.cpp b/gm/shadowmaps.cpp index f953550d4b..6c9738b247 100644 --- a/gm/shadowmaps.cpp +++ b/gm/shadowmaps.cpp @@ -76,6 +76,11 @@ public: SkVector3::Make(0.1f, 0.2f, 1.0f))); builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.4f, 0.4f, 0.4f))); fLights = builder.finish(); + + fShadowParams.fShadowRadius = 4.0f; + fShadowParams.fBiasingConstant = 0.3f; + fShadowParams.fMinVariance = 1024; + fShadowParams.fType = SkShadowParams::kVariance_ShadowType; } protected: @@ -95,11 +100,12 @@ protected: // It's used to generate the depth maps. sk_sp pic(make_test_picture(kWidth, kHeight)); canvas->setLights(fLights); - canvas->drawShadowedPicture(pic, nullptr, nullptr); + canvas->drawShadowedPicture(pic, nullptr, nullptr, fShadowParams); } private: sk_sp fLights; + SkShadowParams fShadowParams; typedef GM INHERITED; }; diff --git a/gyp/core.gypi b/gyp/core.gypi index 0a30618da3..e895a732d9 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -443,6 +443,7 @@ '<(skia_include_path)/private/SkOnce.h', '<(skia_include_path)/private/SkRecords.h', '<(skia_include_path)/private/SkSemaphore.h', + '<(skia_include_path)/private/SkShadowParams.h', '<(skia_include_path)/private/SkSpinlock.h', '<(skia_include_path)/private/SkTemplates.h', '<(skia_include_path)/private/SkTArray.h', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 1e0301179a..823c30fd7a 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -18,6 +18,7 @@ #include "SkSurfaceProps.h" #include "SkXfermode.h" #include "SkLights.h" +#include "../private/SkShadowParams.h" class GrContext; class GrDrawContext; @@ -1073,7 +1074,7 @@ public: #ifdef SK_EXPERIMENTAL_SHADOWING /** - * Draw the picture into this canvas. + * Draw the picture into this canvas, with shadows! * * We will use the canvas's lights along with the picture information (draw depths of * objects, etc) to first create a set of shadowmaps for the light-picture pairs, and @@ -1088,14 +1089,33 @@ public: * This is logically equivalent to * saveLayer(paint)/drawPicture/restore * + * We also support using variance shadow maps for blurred shadows; the user can specify + * what shadow mapping algorithm to use with params. + * - Variance Shadow Mapping works by storing both the depth and depth^2 in the shadow map. + * - Then, the shadow map can be blurred, and when reading from it, the fragment shader + * can calculate the variance of the depth at a position by doing E(x^2) - E(x)^2. + * - We can then use the depth variance and depth at a fragment to arrive at an upper bound + * of the probability that the current surface is shadowed by using Chebyshev's + * inequality, and then use that to shade the fragment. + * + * - There are a few problems with VSM. + * * Light Bleeding | Areas with high variance, such as near the edges of high up rects, + * will cause their shadow penumbras to overwrite otherwise solid + * shadows. + * * Shape Distortion | We can combat Light Bleeding by biasing the shadow (setting + * mostly shaded fragments to completely shaded) and increasing + * the minimum allowed variance. However, this warps and rounds + * out the shape of the shadow. */ void drawShadowedPicture(const SkPicture*, const SkMatrix* matrix, - const SkPaint* paint); + const SkPaint* paint, + const SkShadowParams& params); void drawShadowedPicture(const sk_sp& picture, const SkMatrix* matrix, - const SkPaint* paint) { - this->drawShadowedPicture(picture.get(), matrix, paint); + const SkPaint* paint, + const SkShadowParams& params) { + this->drawShadowedPicture(picture.get(), matrix, paint, params); } #endif @@ -1434,7 +1454,8 @@ protected: #ifdef SK_EXPERIMENTAL_SHADOWING virtual void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, - const SkPaint*); + const SkPaint*, + const SkShadowParams& params); #endif // Returns the canvas to be used by DrawIter. Default implementation diff --git a/include/private/SkRecords.h b/include/private/SkRecords.h index 14b653190a..637a2ef68c 100644 --- a/include/private/SkRecords.h +++ b/include/private/SkRecords.h @@ -268,7 +268,8 @@ RECORD(DrawPicture, kDraw_Tag|kHasPaint_Tag, RECORD(DrawShadowedPicture, kDraw_Tag|kHasPaint_Tag, Optional paint; sk_sp picture; - TypedMatrix matrix); + TypedMatrix matrix; + const SkShadowParams& params); RECORD(DrawPoints, kDraw_Tag|kHasPaint_Tag, SkPaint paint; SkCanvas::PointMode mode; diff --git a/include/private/SkShadowParams.h b/include/private/SkShadowParams.h new file mode 100644 index 0000000000..3df0a44279 --- /dev/null +++ b/include/private/SkShadowParams.h @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkShadowParams_DEFINED +#define SkShadowParams_DEFINED + +/** \struct SkShadowParams + + This struct holds information needed for drawing shadows. + + fShadowRadius - radius of the shadow blur + + fBiasingConstant - A constant used in variance shadow mapping to directly + 0.0 - 1.0 reduce light bleeding. Essentially sets all shadows + ~.25 below a certain brightness equal to no light, and does + a linear step on the rest. Essentially makes shadows + darker and more rounded at higher values. + + fMinVariance - Too low of a variance (near the outer edges of blurry + ~512, 1024 shadows) will lead to ugly sharp shadow brightness + distortions. This enforces a minimum amount of variance + in the calculation to smooth out the outside edges of + blurry shadows. However, too high of a value for this will + cause all shadows to be lighter by visibly different + amounts varying on depth. + + fType - Decides which algorithm to use to draw shadows. +*/ +struct SkShadowParams { + SkScalar fShadowRadius; + SkScalar fBiasingConstant; + SkScalar fMinVariance; + + enum ShadowType { + kNoBlur_ShadowType, + kVariance_ShadowType, + + kLast_ShadowType = kVariance_ShadowType + }; + static const int kShadowTypeCount = kLast_ShadowType + 1; + + ShadowType fType; +}; + +#endif diff --git a/samplecode/SampleShadowing.cpp b/samplecode/SampleShadowing.cpp index 37eec18ba4..e27b4cff32 100644 --- a/samplecode/SampleShadowing.cpp +++ b/samplecode/SampleShadowing.cpp @@ -38,13 +38,31 @@ public: fTestRects[2].fDepth = 240; fTestRects[2].fGeometry = SkRect::MakeLTRB(100,100,250,250); + fSliders[0].fGeometry = SkRect::MakeLTRB(20, 400, 30, 420); + fSliders[0].fOffset = 0.0f; + fSliders[0].fScale = 0.1f; + + fSliders[1].fGeometry = SkRect::MakeLTRB(100, 420, 110, 440); + fSliders[1].fOffset = 0.0f; + fSliders[1].fScale = 10.0f; + + fSliders[2].fGeometry = SkRect::MakeLTRB(0, 440, 10, 460); + fSliders[2].fOffset = 0.0f; + fSliders[2].fScale = 0.0025f; + fSceneChanged = true; fLightsChanged = true; fSelectedRect = -1; + fSelectedSlider = -1; fMoveLight = false; fClearShadowMaps = false; + + fShadowParams.fShadowRadius = 2.0f; + fShadowParams.fBiasingConstant = 0.3f; + fShadowParams.fMinVariance = 1024; + fShadowParams.fType = SkShadowParams::kVariance_ShadowType; } protected: @@ -67,6 +85,15 @@ protected: // the shadow maps will be re-generated according to the new backend. fClearShadowMaps = true; break; + case 'B': + if (SkShadowParams::kVariance_ShadowType == fShadowParams.fType) { + fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType; + } else if (SkShadowParams::kNoBlur_ShadowType == + fShadowParams.fType) { + fShadowParams.fType = SkShadowParams::kVariance_ShadowType; + } + fLightsChanged = true; + break; default: break; } @@ -113,19 +140,46 @@ protected: for (int i = 0; i < fLights->numLights(); i++) { fLights->light(i).setShadowMap(nullptr); } + fSceneChanged = false; fLightsChanged = false; fClearShadowMaps = false; } canvas->setLights(fLights); - canvas->drawShadowedPicture(fPicture, nullptr, nullptr); + canvas->drawShadowedPicture(fPicture, nullptr, nullptr, fShadowParams); + + for (int i = 0; i < kNumSliders; i++) { + SkPaint paint; + paint.setColor(SK_ColorBLACK); + canvas->drawRect(fSliders[i].fGeometry, paint); + } } SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { return new SkView::Click(this); } + void updateFromSelectedSlider() { + SkScalar newValue = fSliders[fSelectedSlider].fGeometry.fLeft * + fSliders[fSelectedSlider].fScale + + fSliders[fSelectedSlider].fOffset; + + switch (fSelectedSlider) { + case 0: + fShadowParams.fShadowRadius = newValue; + break; + case 1: + fShadowParams.fMinVariance = newValue; + break; + case 2: + fShadowParams.fBiasingConstant = newValue; + break; + default: + break; + } + } + bool onClick(Click *click) override { SkScalar x = click->fCurr.fX; SkScalar y = click->fCurr.fY; @@ -161,6 +215,7 @@ protected: if (click->fState == Click::State::kUp_State) { fSelectedRect = -1; + fSelectedSlider = -1; return true; } @@ -172,6 +227,16 @@ protected: return true; } + if (fSelectedSlider > -1) { + fSliders[fSelectedSlider].fGeometry.offset(dx, 0); + + this->updateFromSelectedSlider(); + + fLightsChanged = true; + this->inval(nullptr); + return true; + } + // assume last elements are highest for (int i = kNumTestRects - 1; i >= 0; i--) { if (fTestRects[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) { @@ -184,30 +249,51 @@ protected: } } + for (int i = 0; i <= kNumSliders; i++) { + if (fSliders[i].fGeometry.contains(SkRect::MakeXYWH(x, y, 1, 1))) { + fSelectedSlider = i; + fSliders[i].fGeometry.offset(dx, 0); + + this->updateFromSelectedSlider(); + + fLightsChanged = true; + + this->inval(nullptr); + break; + } + } + return true; } private: static constexpr int kNumTestRects = 3; + static constexpr int kNumSliders = 3; static const int kWidth = 400; static const int kHeight = 400; - bool fClearShadowMaps; struct { SkRect fGeometry; int fDepth; SkColor fColor; } fTestRects[kNumTestRects]; - int fSelectedRect; + + struct { + SkRect fGeometry; + SkScalar fOffset; + SkScalar fScale; + } fSliders[kNumSliders]; + int fSelectedSlider; + + bool fClearShadowMaps; bool fMoveLight; - - sk_sp fPicture; - bool fSceneChanged; bool fLightsChanged; + sk_sp fPicture; + SkShadowParams fShadowParams; sk_sp fLights; typedef SampleView INHERITED; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 8f77afe78e..0b14368b6e 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -38,13 +38,13 @@ #include "SkTextFormatParams.h" #include "SkTLazy.h" #include "SkTraceEvent.h" - #include #if SK_SUPPORT_GPU #include "GrContext.h" #include "GrRenderTarget.h" #include "SkGrPriv.h" + #endif #define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0) @@ -3163,17 +3163,19 @@ void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, #ifdef SK_EXPERIMENTAL_SHADOWING void SkCanvas::drawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { + const SkPaint* paint, + const SkShadowParams& params) { RETURN_ON_NULL(picture); TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()"); - this->onDrawShadowedPicture(picture, matrix, paint); + this->onDrawShadowedPicture(picture, matrix, paint, params); } void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { + const SkPaint* paint, + const SkShadowParams& params) { if (!paint || paint->canComputeFastBounds()) { SkRect bounds = picture->cullRect(); if (paint) { @@ -3189,6 +3191,11 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); + sk_sp povDepthMap; + sk_sp diffuseMap; + + // TODO: pass the depth to the shader in vertices, or uniforms + // so we don't have to render depth and color separately for (int i = 0; i < fLights->numLights(); ++i) { // skip over ambient lights; they don't cast shadows // lights that have shadow maps do not need updating (because lights are immutable) @@ -3217,24 +3224,37 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, // Wrap another SPFCanvas around the surface sk_sp depthMapCanvas = sk_make_sp(surf->getCanvas()); + depthMapCanvas->setShadowParams(params); // set the depth map canvas to have the light we're drawing. SkLights::Builder builder; builder.add(fLights->light(i)); sk_sp curLight = builder.finish(); - depthMapCanvas->setLights(std::move(curLight)); + depthMapCanvas->drawPicture(picture); + sk_sp depthMap = surf->makeImageSnapshot(); - fLights->light(i).setShadowMap(surf->makeImageSnapshot()); + if (params.fType == SkShadowParams::kNoBlur_ShadowType) { + fLights->light(i).setShadowMap(std::move(depthMap)); + } else if (params.fType == SkShadowParams::kVariance_ShadowType) { + // we blur the variance map + SkPaint blurPaint; + blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius, + params.fShadowRadius, nullptr)); + + SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + sk_sp blurSurf(this->makeSurface(blurInfo)); + + blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint); + + fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot()); + } } - sk_sp povDepthMap; - sk_sp diffuseMap; - - // TODO: pass the depth to the shader in vertices, or uniforms - // so we don't have to render depth and color separately - // povDepthMap { SkLights::Builder builder; @@ -3259,7 +3279,6 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, depthMapCanvas->setLights(std::move(povLight)); depthMapCanvas->drawPicture(picture); - povDepthMap = surf->makeImageSnapshot(); } @@ -3275,20 +3294,18 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, diffuseMap = surf->makeImageSnapshot(); } - SkPaint shadowPaint; sk_sp povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - sk_sp diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); - sk_sp shadowShader = SkShadowShader::Make(std::move(povDepthShader), std::move(diffuseShader), std::move(fLights), diffuseMap->width(), - diffuseMap->height()); + diffuseMap->height(), + params); shadowPaint.setShader(shadowShader); diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp index f7fe578c4e..d94e1399d6 100644 --- a/src/core/SkLiteDL.cpp +++ b/src/core/SkLiteDL.cpp @@ -270,17 +270,20 @@ namespace { }; struct DrawShadowedPicture final : Op { static const auto kType = Type::DrawShadowedPicture; - DrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) + DrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, + const SkPaint* paint, const SkShadowParams& params) : picture(sk_ref_sp(picture)) { if (matrix) { this->matrix = *matrix; } if (paint) { this->paint = *paint; } + this->params = params; } sk_sp picture; SkMatrix matrix = SkMatrix::I(); SkPaint paint; + SkShadowParams params; void draw(SkCanvas* c, const SkMatrix&) { #ifdef SK_EXPERIMENTAL_SHADOWING - c->drawShadowedPicture(picture.get(), &matrix, &paint); + c->drawShadowedPicture(picture.get(), &matrix, &paint, params); #endif } void makeThreadsafe() { make_threadsafe(nullptr, &matrix); } @@ -615,9 +618,9 @@ void SkLiteDL::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { this->push(0, picture, matrix, paint); } -void SkLiteDL::drawShadowedPicture(const SkPicture* picture, - const SkMatrix* matrix, const SkPaint* paint) { - this->push(0, picture, matrix, paint); +void SkLiteDL::drawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, + const SkPaint* paint, const SkShadowParams& params) { + push(0, picture, matrix, paint, params); } void SkLiteDL::drawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, const SkPaint* paint) { diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h index 400b242322..4f28486840 100644 --- a/src/core/SkLiteDL.h +++ b/src/core/SkLiteDL.h @@ -48,7 +48,8 @@ public: void drawAnnotation (const SkRect&, const char*, SkData*); void drawDrawable (SkDrawable*, const SkMatrix*); void drawPicture (const SkPicture*, const SkMatrix*, const SkPaint*); - void drawShadowedPicture(const SkPicture*, const SkMatrix*, const SkPaint*); + void drawShadowedPicture(const SkPicture*, const SkMatrix*, + const SkPaint*, const SkShadowParams& params); void drawText (const void*, size_t, SkScalar, SkScalar, const SkPaint&); void drawPosText (const void*, size_t, const SkPoint[], const SkPaint&); diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp index 3b3c24cf5c..42218acd93 100644 --- a/src/core/SkLiteRecorder.cpp +++ b/src/core/SkLiteRecorder.cpp @@ -189,6 +189,7 @@ void SkLiteRecorder::didTranslateZ(SkScalar dz) { } void SkLiteRecorder::onDrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { - fDL->drawShadowedPicture(picture, matrix, paint); + const SkPaint* paint, + const SkShadowParams& params) { + fDL->drawShadowedPicture(picture, matrix, paint, params); } diff --git a/src/core/SkLiteRecorder.h b/src/core/SkLiteRecorder.h index e2d754d30d..c4e80cd246 100644 --- a/src/core/SkLiteRecorder.h +++ b/src/core/SkLiteRecorder.h @@ -76,10 +76,12 @@ public: #ifdef SK_EXPERIMENTAL_SHADOWING void didTranslateZ(SkScalar) override; - void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, + const SkPaint*, const SkShadowParams& params) override; #else void didTranslateZ(SkScalar); - void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, const SkPaint*); + void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, + const SkPaint*, const SkShadowParams& params); #endif private: diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp index 2140795b3d..c461bfbbac 100644 --- a/src/core/SkPictureRecord.cpp +++ b/src/core/SkPictureRecord.cpp @@ -666,11 +666,13 @@ void SkPictureRecord::onDrawPicture(const SkPicture* picture, const SkMatrix* ma void SkPictureRecord::onDrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { + const SkPaint* paint, + const SkShadowParams& params) { // op + picture index size_t size = 2 * kUInt32Size; size_t initialOffset; + // TODO: handle recording params. if (nullptr == matrix && nullptr == paint) { initialOffset = this->addDraw(DRAW_PICTURE, &size); this->addPicture(picture); diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h index 610e6d1c4c..5601d2ac08 100644 --- a/src/core/SkPictureRecord.h +++ b/src/core/SkPictureRecord.h @@ -212,13 +212,11 @@ protected: void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; #ifdef SK_EXPERIMENTAL_SHADOWING - void onDrawShadowedPicture(const SkPicture*, - const SkMatrix*, - const SkPaint*) override; + void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, + const SkPaint*, const SkShadowParams& params) override; #else - void onDrawShadowedPicture(const SkPicture*, - const SkMatrix*, - const SkPaint*); + void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, + const SkPaint*, const SkShadowParams& params); #endif void onDrawDrawable(SkDrawable*, const SkMatrix*) override; diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index 2b7b9726bd..5b4bc6af6c 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -118,7 +118,7 @@ DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.xmode, r.paint)); DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint)); #ifdef SK_EXPERIMENTAL_SHADOWING -DRAW(DrawShadowedPicture, drawShadowedPicture(r.picture.get(), &r.matrix, r.paint)); +DRAW(DrawShadowedPicture, drawShadowedPicture(r.picture.get(), &r.matrix, r.paint, r.params)); #else template <> void Draw::draw(const DrawShadowedPicture& r) { } #endif diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index caa9bb58c3..214b075ce7 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -303,15 +303,16 @@ void SkRecorder::onDrawPicture(const SkPicture* pic, const SkMatrix* matrix, con } } -void SkRecorder::onDrawShadowedPicture(const SkPicture* pic, - const SkMatrix* matrix, - const SkPaint* paint) { +void SkRecorder::onDrawShadowedPicture(const SkPicture* pic, const SkMatrix* matrix, + const SkPaint* paint, const SkShadowParams& params) { if (fDrawPictureMode == Record_DrawPictureMode) { fApproxBytesUsedBySubPictures += SkPictureUtils::ApproximateBytesUsed(pic); APPEND(DrawShadowedPicture, this->copy(paint), sk_ref_sp(pic), - matrix ? *matrix : SkMatrix::I()); + matrix ? *matrix : SkMatrix::I(), + params); } else { + // TODO update pic->playback(this) to draw the shadowed pic SkASSERT(fDrawPictureMode == Playback_DrawPictureMode); SkAutoCanvasMatrixPaint acmp(this, matrix, paint, pic->cullRect()); pic->playback(this); diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index 9f8824f85a..6892894927 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -141,11 +141,13 @@ public: #ifdef SK_EXPERIMENTAL_SHADOWING void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, - const SkPaint*) override; + const SkPaint*, + const SkShadowParams& params) override; #else void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, - const SkPaint*); + const SkPaint*, + const SkShadowParams& params); #endif void onDrawAnnotation(const SkRect&, const char[], SkData*) override; diff --git a/src/core/SkShadowShader.cpp b/src/core/SkShadowShader.cpp index 804258321a..9ad23b7c17 100644 --- a/src/core/SkShadowShader.cpp +++ b/src/core/SkShadowShader.cpp @@ -5,11 +5,9 @@ * found in the LICENSE file. */ - -#include "SkLights.h" +#include "SkCanvas.h" #include "SkReadBuffer.h" #include "SkShadowShader.h" -#include "SkPoint3.h" //////////////////////////////////////////////////////////////////////////// #ifdef SK_EXPERIMENTAL_SHADOWING @@ -26,12 +24,14 @@ public: SkShadowShaderImpl(sk_sp povDepthShader, sk_sp diffuseShader, sk_sp lights, - int diffuseWidth, int diffuseHeight) + int diffuseWidth, int diffuseHeight, + const SkShadowParams& params) : fPovDepthShader(std::move(povDepthShader)) , fDiffuseShader(std::move(diffuseShader)) , fLights(std::move(lights)) , fDiffuseWidth(diffuseWidth) - , fDiffuseHeight(diffuseHeight) { } + , fDiffuseHeight(diffuseHeight) + , fShadowParams(params) { } bool isOpaque() const override; @@ -80,6 +80,8 @@ private: int fDiffuseWidth; int fDiffuseHeight; + SkShadowParams fShadowParams; + friend class SkShadowShader; typedef SkShader INHERITED; @@ -106,6 +108,7 @@ public: sk_sp diffuse, sk_sp lights, int diffuseWidth, int diffuseHeight, + const SkShadowParams& params, GrContext* context) { // fuse all ambient lights into a single one @@ -137,7 +140,9 @@ public: fWidth = diffuseWidth; fHeight = diffuseHeight; - this->registerChildProcessor(std::move(povDepth)); + fShadowParams = params; + + this->registerChildProcessor(std::move(povDepth)); this->registerChildProcessor(std::move(diffuse)); this->initClassID(); } @@ -155,6 +160,8 @@ public: int32_t numLights = args.fFp.cast().fNumDirLights; SkASSERT(numLights <= SkShadowShader::kMaxNonAmbientLights); + int blurAlgorithm = args.fFp.cast().fShadowParams.fType; + const char* lightDirUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr}; const char* lightColorUniName[SkShadowShader::kMaxNonAmbientLights] = {nullptr}; @@ -203,6 +210,17 @@ public: &depthMapHeightUniName[i]); } + const char* shBiasUniName = nullptr; + const char* minVarianceUniName = nullptr; + + fBiasingConstantUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "shadowBias", &shBiasUniName); + fMinVarianceUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kFloat_GrSLType, + kDefault_GrSLPrecision, + "minVariance", &minVarianceUniName); const char* widthUniName = nullptr; const char* heightUniName = nullptr; @@ -254,17 +272,17 @@ public: fragBuilder->codeAppendf("vec2 %s = 1 - %s;\n", scaleOffsetVec.c_str(), scaleVec.c_str()); - fragBuilder->codeAppendf("vec2 %s = (vMatrixCoord_0_1_Stage0 + " - "vec2(%s.x, 0 - %s.y)) " - " * %s + vec2(0,1) * %s;\n", - + "vec2(%s.x, 0 - %s.y)) " + " * %s + vec2(0,1) * %s;\n", povCoord.c_str(), offset.c_str(), offset.c_str(), scaleVec.c_str(), scaleOffsetVec.c_str()); fragBuilder->appendTextureLookup(&depthMaps[i], args.fTexSamplers[i], povCoord.c_str(), kVec2f_GrSLType); + + } const char* ambientColorUniName = nullptr; @@ -274,25 +292,58 @@ public: fragBuilder->codeAppendf("vec4 resultDiffuseColor = %s;", diffuseColor.c_str()); - // Essentially, - // diffColor * (ambientLightTot + foreachDirLight(lightColor * (N . L))) SkString totalLightColor("totalLightColor"); - fragBuilder->codeAppendf("vec3 %s = vec3(0);", totalLightColor.c_str()); + fragBuilder->codeAppendf("vec3 %s = vec3(0,0,0);", totalLightColor.c_str()); + + fragBuilder->codeAppendf("float lightProbability;"); + fragBuilder->codeAppendf("float variance;"); + fragBuilder->codeAppendf("float d;"); for (int i = 0; i < numLights; i++) { - fragBuilder->codeAppendf("if (%s.b >= %s.b) {", + fragBuilder->codeAppendf("lightProbability = 1;"); + + // 1/512 is less than half a pixel; imperceptible + fragBuilder->codeAppendf("if (%s.b <= %s.b + 1/512) {", povDepth.c_str(), depthMaps[i].c_str()); - // Note that dot(vec3(0,0,1), %s) == %s.z * %s - fragBuilder->codeAppendf("%s += %s.z * %s;", + if (blurAlgorithm == SkShadowParams::kVariance_ShadowType) { + fragBuilder->codeAppendf("vec2 moments = vec2(%s.b * 255, %s.g * 255 * 256 );", + depthMaps[i].c_str(), depthMaps[i].c_str()); + + // variance biasing lessens light bleeding + fragBuilder->codeAppendf("variance = max(moments.y - (moments.x * moments.x)," + "%s);", minVarianceUniName); + + fragBuilder->codeAppendf("d = (%s.b * 255) - moments.x;", povDepth.c_str()); + 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 mostly light bleeding. + fragBuilder->codeAppendf("}"); + + fragBuilder->codeAppendf("%s += dot(vec3(0,0,1), %s) * %s * lightProbability;", totalLightColor.c_str(), lightDirUniName[i], lightColorUniName[i]); - fragBuilder->codeAppendf("}"); } - fragBuilder->codeAppendf("%s += %s;", - totalLightColor.c_str(), - ambientColorUniName); + fragBuilder->codeAppendf("%s += %s;", totalLightColor.c_str(), ambientColorUniName); fragBuilder->codeAppendf("resultDiffuseColor *= vec4(%s, 1);", totalLightColor.c_str()); @@ -304,15 +355,14 @@ public: GrProcessorKeyBuilder* b) { const ShadowFP& shadowFP = proc.cast(); b->add32(shadowFP.fNumDirLights); + b->add32(shadowFP.fShadowParams.fType); } protected: void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { const ShadowFP &shadowFP = proc.cast(); - fNumDirLights = shadowFP.numLights(); - - for (int i = 0; i < fNumDirLights; i++) { + for (int i = 0; i < shadowFP.fNumDirLights; i++) { const SkVector3& lightDir = shadowFP.lightDir(i); if (lightDir != fLightDir[i]) { pdman.set3fv(fLightDirUni[i], 1, &lightDir.fX); @@ -336,6 +386,18 @@ public: } } + SkScalar biasingConstant = shadowFP.shadowParams().fBiasingConstant; + if (biasingConstant != fBiasingConstant) { + pdman.set1f(fBiasingConstantUni, biasingConstant); + fBiasingConstant = biasingConstant; + } + + SkScalar minVariance = shadowFP.shadowParams().fMinVariance; + if (minVariance != fMinVariance) { + pdman.set1f(fMinVarianceUni, minVariance); + fMinVariance = minVariance; + } + int width = shadowFP.width(); if (width != fWidth) { pdman.set1i(fWidthUni, width); @@ -376,10 +438,13 @@ public: int fHeight; GrGLSLProgramDataManager::UniformHandle fHeightUni; + SkScalar fBiasingConstant; + GrGLSLProgramDataManager::UniformHandle fBiasingConstantUni; + SkScalar fMinVariance; + GrGLSLProgramDataManager::UniformHandle fMinVarianceUni; + SkColor3f fAmbientColor; GrGLSLProgramDataManager::UniformHandle fAmbientColorUni; - - int fNumDirLights; }; void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { @@ -413,6 +478,8 @@ public: int width() const {return fWidth; } int height() const {return fHeight; } + const SkShadowParams& shadowParams() const {return fShadowParams; } + private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLShadowFP; } @@ -454,6 +521,8 @@ private: int fHeight; int fWidth; + SkShadowParams fShadowParams; + SkColor3f fAmbientColor; }; @@ -469,7 +538,7 @@ sk_sp SkShadowShaderImpl::asFragmentProcessor(const AsFPArg std::move(diffuseFP), std::move(fLights), fDiffuseWidth, fDiffuseHeight, - fpargs.fContext); + fShadowParams, fpargs.fContext); return shadowfp; } @@ -594,6 +663,12 @@ sk_sp SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) { sk_sp lights = SkLights::MakeFromBuffer(buf); + SkShadowParams params; + params.fMinVariance = buf.readScalar(); + params.fBiasingConstant = buf.readScalar(); + params.fType = (SkShadowParams::ShadowType) buf.readInt(); + params.fShadowRadius = buf.readScalar(); + int diffuseWidth = buf.readInt(); int diffuseHeight = buf.readInt(); @@ -603,7 +678,8 @@ sk_sp SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) { return sk_make_sp(std::move(povDepthShader), std::move(diffuseShader), std::move(lights), - diffuseWidth, diffuseHeight); + diffuseWidth, diffuseHeight, + params); } void SkShadowShaderImpl::flatten(SkWriteBuffer& buf) const { @@ -611,6 +687,11 @@ void SkShadowShaderImpl::flatten(SkWriteBuffer& buf) const { fLights->flatten(buf); + buf.writeScalar(fShadowParams.fMinVariance); + buf.writeScalar(fShadowParams.fBiasingConstant); + buf.writeInt(fShadowParams.fType); + buf.writeScalar(fShadowParams.fShadowRadius); + buf.writeInt(fDiffuseWidth); buf.writeInt(fDiffuseHeight); @@ -656,7 +737,8 @@ SkShader::Context* SkShadowShaderImpl::onCreateContext(const ContextRec& rec, sk_sp SkShadowShader::Make(sk_sp povDepthShader, sk_sp diffuseShader, sk_sp lights, - int diffuseWidth, int diffuseHeight) { + int diffuseWidth, int diffuseHeight, + const SkShadowParams& params) { if (!povDepthShader || !diffuseShader) { // TODO: Use paint's color in absence of a diffuseShader // TODO: Use a default implementation of normalSource instead @@ -666,7 +748,8 @@ sk_sp SkShadowShader::Make(sk_sp povDepthShader, return sk_make_sp(std::move(povDepthShader), std::move(diffuseShader), std::move(lights), - diffuseWidth, diffuseHeight); + diffuseWidth, diffuseHeight, + params); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkShadowShader.h b/src/core/SkShadowShader.h index 163d58acd7..3263950a93 100644 --- a/src/core/SkShadowShader.h +++ b/src/core/SkShadowShader.h @@ -23,7 +23,8 @@ public: static sk_sp Make(sk_sp povDepthShader, sk_sp diffuseShader, sk_sp lights, - int diffuseWidth, int diffuseHeight); + int diffuseWidth, int diffuseHeight, + const SkShadowParams& params); // The shadow shader supports any number of ambient lights, but only // 4 non-ambient lights (currently just refers to directional lights). diff --git a/src/utils/SkShadowPaintFilterCanvas.cpp b/src/utils/SkShadowPaintFilterCanvas.cpp index f59facb17c..2a090b78e2 100644 --- a/src/utils/SkShadowPaintFilterCanvas.cpp +++ b/src/utils/SkShadowPaintFilterCanvas.cpp @@ -11,7 +11,12 @@ #ifdef SK_EXPERIMENTAL_SHADOWING SkShadowPaintFilterCanvas::SkShadowPaintFilterCanvas(SkCanvas *canvas) - : SkPaintFilterCanvas(canvas) { } + : SkPaintFilterCanvas(canvas) { + fShadowParams.fShadowRadius = 0.0f; + fShadowParams.fType = SkShadowParams::kNoBlur_ShadowType; + fShadowParams.fBiasingConstant = 0.0f; + fShadowParams.fMinVariance = 0.0f; +} // TODO use a shader instead bool SkShadowPaintFilterCanvas::onFilter(SkTCopyOnFirstWrite* paint, Type type) const { @@ -24,6 +29,17 @@ bool SkShadowPaintFilterCanvas::onFilter(SkTCopyOnFirstWrite* paint, Ty SkColor color = 0xFF000000; // init color to opaque black color |= z; // Put the index into the blue component + + if (fShadowParams.fType == SkShadowParams::kVariance_ShadowType) { + int z2 = z * z; + if (z2 > 255 * 256) { + color |= 0xff00; + } else { + // Let's only store the more significant bits of z2 to save space. + // In practice, this should barely impact shadow blur quality. + color |= z2 & 0x0000ff00; + } + } newPaint.setColor(color); *paint->writable() = newPaint; @@ -42,6 +58,9 @@ SkISize SkShadowPaintFilterCanvas::ComputeDepthMapSize(const SkLights::Light& li return SkISize::Make(dMapWidth, dMapHeight); } +void SkShadowPaintFilterCanvas::setShadowParams(const SkShadowParams ¶ms) { + fShadowParams = params; +} void SkShadowPaintFilterCanvas::onDrawPicture(const SkPicture *picture, const SkMatrix *matrix, const SkPaint *paint) { diff --git a/src/utils/SkShadowPaintFilterCanvas.h b/src/utils/SkShadowPaintFilterCanvas.h index 892ec556e5..698f6e1173 100644 --- a/src/utils/SkShadowPaintFilterCanvas.h +++ b/src/utils/SkShadowPaintFilterCanvas.h @@ -35,6 +35,7 @@ public: static SkISize ComputeDepthMapSize(const SkLights::Light& light, int maxDepth, int width, int height); + void setShadowParams(const SkShadowParams ¶ms); protected: void onDrawPicture(const SkPicture *picture, const SkMatrix *matrix, const SkPaint *paint) override; @@ -107,6 +108,7 @@ protected: void onDrawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y, const SkPaint &paint) override; private: + SkShadowParams fShadowParams; typedef SkPaintFilterCanvas INHERITED; }; diff --git a/tools/debugger/SkDebugCanvas.cpp b/tools/debugger/SkDebugCanvas.cpp index 3816ca4978..6d5619f591 100644 --- a/tools/debugger/SkDebugCanvas.cpp +++ b/tools/debugger/SkDebugCanvas.cpp @@ -60,9 +60,10 @@ protected: void onDrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { + const SkPaint* paint, + const SkShadowParams& params) { #ifdef SK_EXPERIMENTAL_SHADOWING - this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint); + this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint, params); #else this->SkCanvas::onDrawPicture(picture, matrix, paint); #endif @@ -620,8 +621,9 @@ void SkDebugCanvas::onDrawPicture(const SkPicture* picture, void SkDebugCanvas::onDrawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) { - this->addDrawCommand(new SkBeginDrawShadowedPictureCommand(picture, matrix, paint)); + const SkPaint* paint, + const SkShadowParams& params) { + this->addDrawCommand(new SkBeginDrawShadowedPictureCommand(picture, matrix, paint, params)); SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); picture->playback(this); this->addDrawCommand(new SkEndDrawShadowedPictureCommand(SkToBool(matrix) || SkToBool(paint))); diff --git a/tools/debugger/SkDebugCanvas.h b/tools/debugger/SkDebugCanvas.h index fd71f6921c..6a3d92543f 100644 --- a/tools/debugger/SkDebugCanvas.h +++ b/tools/debugger/SkDebugCanvas.h @@ -258,11 +258,13 @@ protected: #ifdef SK_EXPERIMENTAL_SHADOWING void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, - const SkPaint*) override; + const SkPaint*, + const SkShadowParams& params) override; #else void onDrawShadowedPicture(const SkPicture*, const SkMatrix*, - const SkPaint*); + const SkPaint*, + const SkShadowParams& params); #endif void markActiveCommands(int index); diff --git a/tools/debugger/SkDrawCommand.cpp b/tools/debugger/SkDrawCommand.cpp index 65de258ed2..2a6234c580 100644 --- a/tools/debugger/SkDrawCommand.cpp +++ b/tools/debugger/SkDrawCommand.cpp @@ -2449,14 +2449,35 @@ void SkEndDrawPictureCommand::execute(SkCanvas* canvas) const { SkBeginDrawShadowedPictureCommand::SkBeginDrawShadowedPictureCommand(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint) + const SkPaint* paint, + const SkShadowParams& params) : INHERITED(kBeginDrawShadowedPicture_OpType) +#ifdef SK_EXPERIMENTAL_SHADOWING + , fPicture(SkRef(picture)) + , fShadowParams(params) { +#else , fPicture(SkRef(picture)) { - +#endif SkString* str = new SkString; - str->appendf("SkPicture: L: %f T: %f R: %f B: %f", + str->appendf("SkPicture: L: %f T: %f R: %f B: %f\n", picture->cullRect().fLeft, picture->cullRect().fTop, picture->cullRect().fRight, picture->cullRect().fBottom); + str->appendf("SkShadowParams: bias:%f, minVariance:%f, shRadius:%f, shType:", + params.fBiasingConstant, + params.fMinVariance, + params.fShadowRadius); + + SkASSERT(SkShadowParams::kShadowTypeCount == 2); + + switch (params.fType) { + case SkShadowParams::ShadowType::kNoBlur_ShadowType: + str->append("kNoBlur_ShadowType\n"); + break; + case SkShadowParams::ShadowType::kVariance_ShadowType: + str->append("kVariance_ShadowType\n"); + break; + } + fInfo.push(str); if (matrix) { @@ -2492,9 +2513,11 @@ bool SkBeginDrawShadowedPictureCommand::render(SkCanvas* canvas) const { canvas->save(); xlate_and_scale_to_bounds(canvas, fPicture->cullRect()); - - canvas->drawPicture(fPicture.get()); - +#ifdef SK_EXPERIMENTAL_SHADOWING + canvas->drawShadowedPicture(fPicture.get(), fMatrix.get(), fPaint.get(), fShadowParams); +#else + canvas->drawPicture(fPicture.get(), fMatrix.get(), fPaint.get()); +#endif canvas->restore(); return true; diff --git a/tools/debugger/SkDrawCommand.h b/tools/debugger/SkDrawCommand.h index d3f2908ee6..2e5f9c659a 100644 --- a/tools/debugger/SkDrawCommand.h +++ b/tools/debugger/SkDrawCommand.h @@ -480,7 +480,8 @@ class SkBeginDrawShadowedPictureCommand : public SkDrawCommand { public: SkBeginDrawShadowedPictureCommand(const SkPicture* picture, const SkMatrix* matrix, - const SkPaint* paint); + const SkPaint* paint, + const SkShadowParams& params); void execute(SkCanvas* canvas) const override; bool render(SkCanvas* canvas) const override; @@ -489,6 +490,9 @@ private: SkAutoTUnref fPicture; SkTLazy fMatrix; SkTLazy fPaint; +#ifdef SK_EXPERIMENTAL_SHADOWING + SkShadowParams fShadowParams; +#endif typedef SkDrawCommand INHERITED; }; @@ -796,3 +800,4 @@ private: typedef SkDrawCommand INHERITED; }; #endif +