Made shadows blurry (thru implementing variance mapping)

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2224163005

Review-Url: https://codereview.chromium.org/2224163005
This commit is contained in:
vjiaoblack 2016-08-25 06:30:23 -07:00 committed by Commit bot
parent 199a2ea665
commit e6f5d56231
24 changed files with 425 additions and 98 deletions

View File

@ -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<SkPicture> pic(make_test_picture(kWidth, kHeight));
canvas->setLights(fLights);
canvas->drawShadowedPicture(pic, nullptr, nullptr);
canvas->drawShadowedPicture(pic, nullptr, nullptr, fShadowParams);
}
private:
sk_sp<SkLights> fLights;
SkShadowParams fShadowParams;
typedef GM INHERITED;
};

View File

@ -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',

View File

@ -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<SkPicture>& 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

View File

@ -268,7 +268,8 @@ RECORD(DrawPicture, kDraw_Tag|kHasPaint_Tag,
RECORD(DrawShadowedPicture, kDraw_Tag|kHasPaint_Tag,
Optional<SkPaint> paint;
sk_sp<const SkPicture> picture;
TypedMatrix matrix);
TypedMatrix matrix;
const SkShadowParams& params);
RECORD(DrawPoints, kDraw_Tag|kHasPaint_Tag,
SkPaint paint;
SkCanvas::PointMode mode;

View File

@ -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

View File

@ -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<SkPicture> fPicture;
bool fSceneChanged;
bool fLightsChanged;
sk_sp<SkPicture> fPicture;
SkShadowParams fShadowParams;
sk_sp<SkLights> fLights;
typedef SampleView INHERITED;

View File

@ -38,13 +38,13 @@
#include "SkTextFormatParams.h"
#include "SkTLazy.h"
#include "SkTraceEvent.h"
#include <new>
#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<SkImage> povDepthMap;
sk_sp<SkImage> 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,23 +3224,36 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
// Wrap another SPFCanvas around the surface
sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
sk_make_sp<SkShadowPaintFilterCanvas>(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<SkLights> curLight = builder.finish();
depthMapCanvas->setLights(std::move(curLight));
depthMapCanvas->drawPicture(picture);
sk_sp<SkImage> 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<SkSurface> blurSurf(this->makeSurface(blurInfo));
blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
}
}
sk_sp<SkImage> povDepthMap;
sk_sp<SkImage> diffuseMap;
// TODO: pass the depth to the shader in vertices, or uniforms
// so we don't have to render depth and color separately
// povDepthMap
{
@ -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<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
std::move(diffuseShader),
std::move(fLights),
diffuseMap->width(),
diffuseMap->height());
diffuseMap->height(),
params);
shadowPaint.setShader(shadowShader);

View File

@ -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<const SkPicture> 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<DrawPicture>(0, picture, matrix, paint);
}
void SkLiteDL::drawShadowedPicture(const SkPicture* picture,
const SkMatrix* matrix, const SkPaint* paint) {
this->push<DrawShadowedPicture>(0, picture, matrix, paint);
void SkLiteDL::drawShadowedPicture(const SkPicture* picture, const SkMatrix* matrix,
const SkPaint* paint, const SkShadowParams& params) {
push<DrawShadowedPicture>(0, picture, matrix, paint, params);
}
void SkLiteDL::drawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, const SkPaint* paint) {

View File

@ -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&);

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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<SkShader> povDepthShader,
sk_sp<SkShader> diffuseShader,
sk_sp<SkLights> 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<GrFragmentProcessor> diffuse,
sk_sp<SkLights> lights,
int diffuseWidth, int diffuseHeight,
const SkShadowParams& params,
GrContext* context) {
// fuse all ambient lights into a single one
@ -137,6 +140,8 @@ public:
fWidth = diffuseWidth;
fHeight = diffuseHeight;
fShadowParams = params;
this->registerChildProcessor(std::move(povDepth));
this->registerChildProcessor(std::move(diffuse));
this->initClassID<ShadowFP>();
@ -155,6 +160,8 @@ public:
int32_t numLights = args.fFp.cast<ShadowFP>().fNumDirLights;
SkASSERT(numLights <= SkShadowShader::kMaxNonAmbientLights);
int blurAlgorithm = args.fFp.cast<ShadowFP>().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",
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("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());
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());
// Note that dot(vec3(0,0,1), %s) == %s.z * %s
fragBuilder->codeAppendf("%s += %s.z * %s;",
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<ShadowFP>();
b->add32(shadowFP.fNumDirLights);
b->add32(shadowFP.fShadowParams.fType);
}
protected:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
const ShadowFP &shadowFP = proc.cast<ShadowFP>();
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<GrFragmentProcessor> 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<SkFlattenable> SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) {
sk_sp<SkLights> 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<SkFlattenable> SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) {
return sk_make_sp<SkShadowShaderImpl>(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<SkShader> SkShadowShader::Make(sk_sp<SkShader> povDepthShader,
sk_sp<SkShader> diffuseShader,
sk_sp<SkLights> 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<SkShader> SkShadowShader::Make(sk_sp<SkShader> povDepthShader,
return sk_make_sp<SkShadowShaderImpl>(std::move(povDepthShader),
std::move(diffuseShader),
std::move(lights),
diffuseWidth, diffuseHeight);
diffuseWidth, diffuseHeight,
params);
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -23,7 +23,8 @@ public:
static sk_sp<SkShader> Make(sk_sp<SkShader> povDepthShader,
sk_sp<SkShader> diffuseShader,
sk_sp<SkLights> 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).

View File

@ -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<SkPaint>* paint, Type type) const {
@ -24,6 +29,17 @@ bool SkShadowPaintFilterCanvas::onFilter(SkTCopyOnFirstWrite<SkPaint>* 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 &params) {
fShadowParams = params;
}
void SkShadowPaintFilterCanvas::onDrawPicture(const SkPicture *picture, const SkMatrix *matrix,
const SkPaint *paint) {

View File

@ -35,6 +35,7 @@ public:
static SkISize ComputeDepthMapSize(const SkLights::Light& light, int maxDepth,
int width, int height);
void setShadowParams(const SkShadowParams &params);
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;
};

View File

@ -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)));

View File

@ -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);

View File

@ -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;

View File

@ -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<const SkPicture> fPicture;
SkTLazy<SkMatrix> fMatrix;
SkTLazy<SkPaint> fPaint;
#ifdef SK_EXPERIMENTAL_SHADOWING
SkShadowParams fShadowParams;
#endif
typedef SkDrawCommand INHERITED;
};
@ -796,3 +800,4 @@ private:
typedef SkDrawCommand INHERITED;
};
#endif