diff options
author | vjiaoblack <vjiaoblack@google.com> | 2016-09-09 09:22:39 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-09-09 09:22:40 -0700 |
commit | b2796fdd9eab88836795d6a54cf6811af6ecdc81 (patch) | |
tree | b1f8b1870ffc70683cd49264c23f03d2402e0133 /src | |
parent | 55bcc8e0af3415601b3d62252a0d579fbe87c85a (diff) |
Added in Radial Shadows
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2311223004
Review-Url: https://codereview.chromium.org/2311223004
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkCanvas.cpp | 166 | ||||
-rw-r--r-- | src/core/SkRadialShadowMapShader.cpp | 429 | ||||
-rw-r--r-- | src/core/SkRadialShadowMapShader.h | 31 | ||||
-rw-r--r-- | src/core/SkShadowShader.cpp | 207 |
4 files changed, 689 insertions, 144 deletions
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index a543a5cc5b..6e898fa1bb 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -26,6 +26,7 @@ #include "SkPaintPriv.h" #include "SkPatchUtils.h" #include "SkPicture.h" +#include "SkRadialShadowMapShader.h" #include "SkRasterClip.h" #include "SkReadPixelsRec.h" #include "SkRRect.h" @@ -3239,66 +3240,6 @@ void SkCanvas::onDrawShadowedPicture(const SkPicture* picture, 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) - - if (fLights->light(i).getShadowMap() != nullptr) { - continue; - } - - // TODO: compute the correct size of the depth map from the light properties - // TODO: maybe add a kDepth_8_SkColorType - // TODO: find actual max depth of picture - SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize( - fLights->light(i), 255, - picture->cullRect().width(), - picture->cullRect().height()); - - SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight, - kBGRA_8888_SkColorType, - kOpaque_SkAlphaType); - - // Create a new surface (that matches the backend of canvas) - // for each shadow map - sk_sp<SkSurface> surf(this->makeSurface(info)); - - // 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(); - - 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()); - } - } - // povDepthMap { SkLights::Builder builder; @@ -3338,15 +3279,116 @@ 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); + + // 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) + sk_sp<SkImage> depthMap; + SkISize shMapSize; + + if (fLights->light(i).getShadowMap() != nullptr) { + continue; + } + + if (fLights->light(i).isRadial()) { + shMapSize.fHeight = 1; + shMapSize.fWidth = (int) picture->cullRect().width(); + + SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + // Create new surface (that matches the backend of canvas) + // for each shadow map + sk_sp<SkSurface> surf(this->makeSurface(info)); + + // Wrap another SPFCanvas around the surface + SkCanvas* depthMapCanvas = surf->getCanvas(); + + SkLights::Builder builder; + builder.add(fLights->light(i)); + sk_sp<SkLights> curLight = builder.finish(); + + sk_sp<SkShader> shadowMapShader; + shadowMapShader = SkRadialShadowMapShader::Make( + povDepthShader, curLight, + (int) picture->cullRect().width(), + (int) picture->cullRect().height()); + + SkPaint shadowMapPaint; + shadowMapPaint.setShader(std::move(shadowMapShader)); + + depthMapCanvas->setLights(curLight); + + depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(), + diffuseMap->height()), + shadowMapPaint); + + depthMap = surf->makeImageSnapshot(); + + } else { + // TODO: compute the correct size of the depth map from the light properties + // TODO: maybe add a kDepth_8_SkColorType + // TODO: find actual max depth of picture + shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize( + fLights->light(i), 255, + (int) picture->cullRect().width(), + (int) picture->cullRect().height()); + + SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + // Create a new surface (that matches the backend of canvas) + // for each shadow map + sk_sp<SkSurface> surf(this->makeSurface(info)); + + // 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); + depthMap = 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()); + } + } + + SkPaint shadowPaint; sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader), std::move(diffuseShader), - std::move(fLights), + fLights, diffuseMap->width(), diffuseMap->height(), params); diff --git a/src/core/SkRadialShadowMapShader.cpp b/src/core/SkRadialShadowMapShader.cpp new file mode 100644 index 0000000000..24b54a5e2b --- /dev/null +++ b/src/core/SkRadialShadowMapShader.cpp @@ -0,0 +1,429 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkLights.h" +#include "SkPoint3.h" +#include "SkRadialShadowMapShader.h" + +//////////////////////////////////////////////////////////////////////////// +#ifdef SK_EXPERIMENTAL_SHADOWING + + +/** \class SkRadialShadowMapShaderImpl + This subclass of shader applies shadowing radially around a light +*/ +class SkRadialShadowMapShaderImpl : public SkShader { +public: + /** Create a new shadowing shader that shadows radially around a light + */ + SkRadialShadowMapShaderImpl(sk_sp<SkShader> occluderShader, + sk_sp<SkLights> lights, + int diffuseWidth, int diffuseHeight) + : fOccluderShader(std::move(occluderShader)) + , fLight(std::move(lights)) + , fWidth(diffuseWidth) + , fHeight(diffuseHeight) { } + + bool isOpaque() const override; + +#if SK_SUPPORT_GPU + sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; +#endif + + class ShadowMapRadialShaderContext : public SkShader::Context { + public: + // The context takes ownership of the states. It will call their destructors + // but will NOT free the memory. + ShadowMapRadialShaderContext(const SkRadialShadowMapShaderImpl&, const ContextRec&, + SkShader::Context* occluderContext, + void* heapAllocated); + + ~ShadowMapRadialShaderContext() override; + + void shadeSpan(int x, int y, SkPMColor[], int count) override; + + uint32_t getFlags() const override { return fFlags; } + + private: + SkShader::Context* fOccluderContext; + uint32_t fFlags; + + void* fHeapAllocated; + + typedef SkShader::Context INHERITED; + }; + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialShadowMapShaderImpl) + +protected: + void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec&) const override; + Context* onCreateContext(const ContextRec&, void*) const override; + +private: + sk_sp<SkShader> fOccluderShader; + sk_sp<SkLights> fLight; + + int fWidth; + int fHeight; + + friend class SkRadialShadowMapShader; + + typedef SkShader INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" +#include "SkGrPriv.h" +#include "SkImage_Base.h" +#include "GrInvariantOutput.h" +#include "SkSpecialImage.h" + +class RadialShadowMapFP : public GrFragmentProcessor { +public: + RadialShadowMapFP(sk_sp<GrFragmentProcessor> occluder, + sk_sp<SkLights> light, + int diffuseWidth, int diffuseHeight, + GrContext* context) { + fLightPos = light->light(0).pos(); + + fWidth = diffuseWidth; + fHeight = diffuseHeight; + + this->registerChildProcessor(std::move(occluder)); + this->initClassID<RadialShadowMapFP>(); + } + + class GLSLRadialShadowMapFP : public GrGLSLFragmentProcessor { + public: + GLSLRadialShadowMapFP() { } + + void emitCode(EmitArgs& args) override { + + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + const char* lightPosUniName = nullptr; + + fLightPosUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kVec3f_GrSLType, + kDefault_GrSLPrecision, + "lightPos", + &lightPosUniName); + + const char* widthUniName = nullptr; + const char* heightUniName = nullptr; + + fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kInt_GrSLType, + kDefault_GrSLPrecision, + "width", &widthUniName); + fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kInt_GrSLType, + kDefault_GrSLPrecision, + "height", &heightUniName); + + + SkString occluder("occluder"); + this->emitChild(0, nullptr, &occluder, args); + + // Modify the input texture coordinates to index into our 1D output + fragBuilder->codeAppend("float distHere;"); + fragBuilder->codeAppend("float closestDistHere = 0;"); + fragBuilder->codeAppend("vec2 coords = vMatrixCoord_0_0_Stage0;"); + fragBuilder->codeAppend("coords.y = 0;"); + fragBuilder->codeAppend("vec2 destCoords = vec2(0,0);"); + fragBuilder->codeAppendf("float step = 1.0 / %s;", heightUniName); + + // assume that we are at 0, 0 light pos + // TODO use correct light positions + + // this goes through each depth value in the final output buffer, + // basically raycasting outwards, and finding the first collision. + // we also increment coords.y to 2 instead 1 so our shadows stretch the whole screen. + fragBuilder->codeAppendf("for (coords.y = 0; coords.y <= 2; coords.y += step) {"); + + fragBuilder->codeAppend("float theta = (coords.x * 2.0 - 1.0) * 3.1415;"); + fragBuilder->codeAppend("float r = coords.y;"); + fragBuilder->codeAppend("destCoords = " + "vec2(r * cos(theta), - r * sin(theta)) /2.0 + 0.5;"); + fragBuilder->codeAppendf("vec2 lightOffset = (vec2(%s)/vec2(%s,%s) - 0.5)" + "* vec2(1.0, 1.0);", + lightPosUniName, widthUniName, heightUniName); + + fragBuilder->codeAppend("distHere = texture(uTextureSampler0_Stage1," + "destCoords + lightOffset).b;"); + fragBuilder->codeAppend("if (distHere > 0.0) {" + "closestDistHere = coords.y;" + "break;}"); + fragBuilder->codeAppend("}"); + + fragBuilder->codeAppendf("%s = vec4(vec3(closestDistHere / 2.0),1);", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + b->add32(0); // nothing to add here + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { + const RadialShadowMapFP &radialShadowMapFP = proc.cast<RadialShadowMapFP>(); + + const SkVector3& lightPos = radialShadowMapFP.lightPos(); + if (lightPos != fLightPos) { + pdman.set3fv(fLightPosUni, 1, &lightPos.fX); + fLightPos = lightPos; + } + + int width = radialShadowMapFP.width(); + if (width != fWidth) { + pdman.set1i(fWidthUni, width); + fWidth = width; + } + int height = radialShadowMapFP.height(); + if (height != fHeight) { + pdman.set1i(fHeightUni, height); + fHeight = height; + } + } + + private: + SkVector3 fLightPos; + GrGLSLProgramDataManager::UniformHandle fLightPosUni; + + int fWidth; + GrGLSLProgramDataManager::UniformHandle fWidthUni; + int fHeight; + GrGLSLProgramDataManager::UniformHandle fHeightUni; + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLRadialShadowMapFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "RadialShadowMapFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->mulByUnknownFourComponents(); + } + const SkVector3& lightPos() const { + return fLightPos; + } + + int width() const { return fWidth; } + int height() const { return fHeight; } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLSLRadialShadowMapFP; + } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const RadialShadowMapFP& radialShadowMapFP = proc.cast<RadialShadowMapFP>(); + + if (fWidth != radialShadowMapFP.fWidth || fHeight != radialShadowMapFP.fHeight) { + return false; + } + + if (fLightPos != radialShadowMapFP.fLightPos) { + return false; + } + + return true; + } + + SkVector3 fLightPos; + + int fHeight; + int fWidth; +}; + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<GrFragmentProcessor> SkRadialShadowMapShaderImpl::asFragmentProcessor + (const AsFPArgs& fpargs) const { + + sk_sp<GrFragmentProcessor> occluderFP = fOccluderShader->asFragmentProcessor(fpargs); + + sk_sp<GrFragmentProcessor> shadowFP = sk_make_sp<RadialShadowMapFP>(std::move(occluderFP), + fLight, fWidth, fHeight, + fpargs.fContext); + return shadowFP; +} + +#endif + +//////////////////////////////////////////////////////////////////////////// + +bool SkRadialShadowMapShaderImpl::isOpaque() const { + return fOccluderShader->isOpaque(); +} + +SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::ShadowMapRadialShaderContext( + const SkRadialShadowMapShaderImpl& shader, const ContextRec& rec, + SkShader::Context* occluderContext, + void* heapAllocated) + : INHERITED(shader, rec) + , fOccluderContext(occluderContext) + , fHeapAllocated(heapAllocated) { + bool isOpaque = shader.isOpaque(); + + // update fFlags + uint32_t flags = 0; + if (isOpaque && (255 == this->getPaintAlpha())) { + flags |= kOpaqueAlpha_Flag; + } + + fFlags = flags; +} + +SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::~ShadowMapRadialShaderContext() { + // The dependencies have been created outside of the context on memory that was allocated by + // the onCreateContext() method. Call the destructors and free the memory. + fOccluderContext->~Context(); + + sk_free(fHeapAllocated); +} + +static inline SkPMColor convert(SkColor3f color, U8CPU a) { + if (color.fX <= 0.0f) { + color.fX = 0.0f; + } else if (color.fX >= 255.0f) { + color.fX = 255.0f; + } + + if (color.fY <= 0.0f) { + color.fY = 0.0f; + } else if (color.fY >= 255.0f) { + color.fY = 255.0f; + } + + if (color.fZ <= 0.0f) { + color.fZ = 0.0f; + } else if (color.fZ >= 255.0f) { + color.fZ = 255.0f; + } + + return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ); +} + +// larger is better (fewer times we have to loop), but we shouldn't +// take up too much stack-space (each one here costs 16 bytes) +#define BUFFER_MAX 16 +void SkRadialShadowMapShaderImpl::ShadowMapRadialShaderContext::shadeSpan + (int x, int y, SkPMColor result[], int count) { + do { + int n = SkTMin(count, BUFFER_MAX); + + // just fill with white for now + SkPMColor accum = convert(SkColor3f::Make(1.0f, 1.0f, 1.0f), 0xFF); + + for (int i = 0; i < n; ++i) { + result[i] = accum; + } + + result += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkRadialShadowMapShaderImpl::toString(SkString* str) const { + str->appendf("RadialShadowMapShader: ()"); +} +#endif + +sk_sp<SkFlattenable> SkRadialShadowMapShaderImpl::CreateProc(SkReadBuffer& buf) { + + // Discarding SkShader flattenable params + bool hasLocalMatrix = buf.readBool(); + SkAssertResult(!hasLocalMatrix); + + sk_sp<SkLights> light = SkLights::MakeFromBuffer(buf); + + int diffuseWidth = buf.readInt(); + int diffuseHeight = buf.readInt(); + + sk_sp<SkShader> occluderShader(buf.readFlattenable<SkShader>()); + + return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader), + std::move(light), + diffuseWidth, diffuseHeight); +} + +void SkRadialShadowMapShaderImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + fLight->flatten(buf); + + buf.writeInt(fWidth); + buf.writeInt(fHeight); + + buf.writeFlattenable(fOccluderShader.get()); +} + +size_t SkRadialShadowMapShaderImpl::onContextSize(const ContextRec& rec) const { + return sizeof(ShadowMapRadialShaderContext); +} + +SkShader::Context* SkRadialShadowMapShaderImpl::onCreateContext(const ContextRec& rec, + void* storage) const { + size_t heapRequired = fOccluderShader->contextSize(rec); + + void* heapAllocated = sk_malloc_throw(heapRequired); + + void* occluderContextStorage = heapAllocated; + + SkShader::Context* occluderContext = + fOccluderShader->createContext(rec, occluderContextStorage); + + if (!occluderContext) { + sk_free(heapAllocated); + return nullptr; + } + + return new (storage) ShadowMapRadialShaderContext(*this, rec, occluderContext, heapAllocated); +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkShader> SkRadialShadowMapShader::Make(sk_sp<SkShader> occluderShader, + sk_sp<SkLights> light, + int diffuseWidth, int diffuseHeight) { + if (!occluderShader) { + // TODO: Use paint's color in absence of a diffuseShader + // TODO: Use a default implementation of normalSource instead + return nullptr; + } + + return sk_make_sp<SkRadialShadowMapShaderImpl>(std::move(occluderShader), + std::move(light), + diffuseWidth, diffuseHeight); +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRadialShadowMapShader) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRadialShadowMapShaderImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +/////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/core/SkRadialShadowMapShader.h b/src/core/SkRadialShadowMapShader.h new file mode 100644 index 0000000000..4d6956ca82 --- /dev/null +++ b/src/core/SkRadialShadowMapShader.h @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkReadBuffer.h" + +#ifndef SkRadialShadowMapShader_DEFINED +#define SkRadialShadowMapShader_DEFINED + +#ifdef SK_EXPERIMENTAL_SHADOWING + +class SkLights; +class SkShader; + +class SK_API SkRadialShadowMapShader { +public: + /** This shader creates a 1D strip depth map for radial lights. + * It can only take in 1 light to generate one shader at a time. + */ + static sk_sp<SkShader> Make(sk_sp<SkShader> occluderShader, + sk_sp<SkLights> light, + int diffuseWidth, int diffuseHeight); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif +#endif diff --git a/src/core/SkShadowShader.cpp b/src/core/SkShadowShader.cpp index 13baec1fce..59152bfc37 100644 --- a/src/core/SkShadowShader.cpp +++ b/src/core/SkShadowShader.cpp @@ -132,6 +132,8 @@ public: fIsPointLight[fNumNonAmbLights] = SkLights::Light::kPoint_LightType == lights->light(i).type(); + fIsRadialLight[fNumNonAmbLights] = lights->light(i).isRadial(); + SkImage_Base* shadowMap = ((SkImage_Base*)lights->light(i).getShadowMap()); // gets deleted when the ShadowFP is destroyed, and frees the GrTexture* @@ -281,62 +283,82 @@ public: SkString povCoord("povCoord"); povCoord.appendf("%d", i); - // povDepth.b * 255 scales it to 0 - 255, bringing it to world space, - // and the / vec2(width, height) brings it back to a sampler coordinate SkString offset("offset"); offset.appendf("%d", i); - - SkString scaleVec("scaleVec"); - scaleVec.appendf("%d", i); - fragBuilder->codeAppendf("vec2 %s;", offset.c_str()); - // note that we flip the y-coord of the offset and then later add - // a value just to the y-coord of povCoord. This is to account for - // the shifted origins from switching from raster into GPU. 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);", i, i, i); fragBuilder->codeAppendf("%s = vec2(-fragToLight%d) * povDepth.b;", offset.c_str(), i); fragBuilder->codeAppendf("fragToLight%d = normalize(fragToLight%d);", i, i); + } + + if (shadowFP.fIsRadialLight[i]) { + fragBuilder->codeAppendf("vec2 %s = vec2(vMatrixCoord_0_1_Stage0.x, " + "1 - vMatrixCoord_0_1_Stage0.y);\n", + povCoord.c_str()); + + fragBuilder->codeAppendf("%s = (%s) * 2.0 - 1.0 + (vec2(%s)/vec2(%s,%s) - 0.5)" + "* vec2(-2.0, 2.0);\n", + povCoord.c_str(), povCoord.c_str(), + lightDirOrPosUniName[i], + widthUniName, heightUniName); + + fragBuilder->codeAppendf("float theta = atan(%s.y, %s.x);", + povCoord.c_str(), povCoord.c_str()); + fragBuilder->codeAppendf("float r = length(%s);", povCoord.c_str()); - // 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]); + // map output of atan to [0, 1] + fragBuilder->codeAppendf("%s.x = (theta + 3.1415) / (2.0 * 3.1415);", + povCoord.c_str()); + fragBuilder->codeAppendf("%s.y = 0.0;", povCoord.c_str()); } 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]); + // note that we flip the y-coord of the offset and then later add + // a value just to the y-coord of povCoord. This is to account for + // the shifted origins from switching from raster into GPU. + if (shadowFP.fIsPointLight[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->appendTextureLookup(&depthMaps[i], args.fTexSamplers[i], povCoord.c_str(), kVec2f_GrSLType); - } // helper variables for calculating shadowing @@ -352,73 +374,88 @@ public: for (int i = 0; i < numLights; 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()); + if (shadowFP.fIsRadialLight[i]) { + fragBuilder->codeAppend("totalLightColor = vec3(0);"); + + fragBuilder->codeAppend("vec2 tc = vec2(povCoord0.x, 0.0);"); + fragBuilder->codeAppend("float depth = texture(uTextureSampler0_Stage1," + "povCoord0).b * 2.0;"); + + fragBuilder->codeAppendf("lightProbability = step(r, depth);"); + + fragBuilder->codeAppendf("if (%s.b != 0 || depth == 0) {" + "lightProbability = 1.0; }", + povDepth.c_str()); } else { - fragBuilder->codeAppendf("if (%s.b >= %s.b) {", + // 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()); - fragBuilder->codeAppendf("lightProbability = 1;"); - fragBuilder->codeAppendf("} else { lightProbability = 0; }"); - } + 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 + // lightDir.z is equal to the lightDir dot the surface normal. + fragBuilder->codeAppendf("}"); + } if (shadowFP.isPointLight(i)) { fragBuilder->codeAppendf("totalLightColor += max(fragToLight%d.z, 0) * %s /" - "(1 + distsq%d) *" - "lightProbability;", + "(1 + distsq%d) * lightProbability;", i, lightColorUniName[i], i); } else { fragBuilder->codeAppendf("totalLightColor += %s.z * %s * lightProbability;", lightDirOrPosUniName[i], lightColorUniName[i]); } + + fragBuilder->codeAppendf("totalLightColor += %s;", ambientColorUniName); + fragBuilder->codeAppendf("%s = resultDiffuseColor * vec4(totalLightColor, 1);", + args.fOutputColor); } - fragBuilder->codeAppendf("totalLightColor += %s;", ambientColorUniName); - fragBuilder->codeAppendf("%s = resultDiffuseColor * vec4(totalLightColor, 1);", - args.fOutputColor); } static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, GrProcessorKeyBuilder* b) { const ShadowFP& shadowFP = proc.cast<ShadowFP>(); b->add32(shadowFP.fNumNonAmbLights); - int isPL = 0; + int isPLR = 0; for (int i = 0; i < SkShadowShader::kMaxNonAmbientLights; i++) { - isPL = isPL | ((shadowFP.fIsPointLight[i] ? 1 : 0) << i); + isPLR = isPLR | ((shadowFP.fIsPointLight[i] ? 1 : 0) << i); + isPLR = isPLR | ((shadowFP.fIsRadialLight[i] ? 1 : 0) << (i+4)); } - b->add32(isPL); + b->add32(isPLR); b->add32(shadowFP.fShadowParams.fType); } @@ -528,6 +565,10 @@ public: SkASSERT(i < fNumNonAmbLights); return fIsPointLight[i]; } + bool isRadialLight(int i) const { + SkASSERT(i < fNumNonAmbLights); + return fIsRadialLight[i]; + } const SkVector3& lightDirOrPos(int i) const { SkASSERT(i < fNumNonAmbLights); return fLightDirOrPos[i]; @@ -566,7 +607,8 @@ private: for (int i = 0; i < fNumNonAmbLights; i++) { if (fLightDirOrPos[i] != shadowFP.fLightDirOrPos[i] || fLightColor[i] != shadowFP.fLightColor[i] || - fIsPointLight[i] != shadowFP.fIsPointLight[i]) { + fIsPointLight[i] != shadowFP.fIsPointLight[i] || + fIsRadialLight[i] != shadowFP.fIsRadialLight[i]) { return false; } @@ -582,6 +624,7 @@ private: int fNumNonAmbLights; bool fIsPointLight[SkShadowShader::kMaxNonAmbientLights]; + bool fIsRadialLight[SkShadowShader::kMaxNonAmbientLights]; SkVector3 fLightDirOrPos[SkShadowShader::kMaxNonAmbientLights]; SkColor3f fLightColor[SkShadowShader::kMaxNonAmbientLights]; GrTextureAccess fDepthMapAccess[SkShadowShader::kMaxNonAmbientLights]; |