From 53da5ba619553aa47dfe27c315f7deae389e6b07 Mon Sep 17 00:00:00 2001 From: vjiaoblack Date: Mon, 1 Aug 2016 10:02:31 -0700 Subject: [PATCH] adding new GM to demostrate new shadows BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2118553002 Review-Url: https://codereview.chromium.org/2118553002 --- gm/shadowmaps.cpp | 410 ++++++++++++++++++++ gyp/core.gypi | 2 + src/core/SkShadowShader.cpp | 719 ++++++++++++++++++++++++++++++++++++ src/core/SkShadowShader.h | 32 ++ 4 files changed, 1163 insertions(+) create mode 100644 gm/shadowmaps.cpp create mode 100644 src/core/SkShadowShader.cpp create mode 100644 src/core/SkShadowShader.h diff --git a/gm/shadowmaps.cpp b/gm/shadowmaps.cpp new file mode 100644 index 0000000000..4a0f88ec91 --- /dev/null +++ b/gm/shadowmaps.cpp @@ -0,0 +1,410 @@ +/* + * 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 "gm.h" +#include "SkPaintFilterCanvas.h" +#include "SkPathEffect.h" +#include "SkPictureRecorder.h" +#include "SkShadowShader.h" +#include "SkSurface.h" + +#ifdef SK_EXPERIMENTAL_SHADOWING + +static sk_sp make_shadow_shader(sk_sp povDepth, + sk_sp diffuse, + sk_sp lights) { + + sk_sp povDepthShader = povDepth->makeShader(SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); + + sk_sp diffuseShader = diffuse->makeShader(SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode); + + return SkShadowShader::Make(std::move(povDepthShader), + std::move(diffuseShader), + std::move(lights), + diffuse->width(), diffuse->height()); +} + +static sk_sp make_test_picture(int width, int height) { + SkPictureRecorder recorder; + + // LONG RANGE TODO: eventually add SkBBHFactory (bounding box factory) + SkCanvas* canvas = recorder.beginRecording(SkRect::MakeIWH(width, height)); + + SkASSERT(canvas->getTotalMatrix().isIdentity()); + SkPaint paint; + paint.setColor(SK_ColorGRAY); + + // LONG RANGE TODO: tag occluders + // LONG RANGE TODO: track number of IDs we need (hopefully less than 256) + // and determinate the mapping from z to id + + // universal receiver, "ground" + canvas->drawRect(SkRect::MakeIWH(width, height), paint); + + // TODO: Maybe add the ID here along with the depth + + paint.setColor(0xFFEE8888); + + canvas->translateZ(80); + canvas->drawRect(SkRect::MakeLTRB(200,150,350,300), paint); + + paint.setColor(0xFF88EE88); + + canvas->translateZ(80); + canvas->drawRect(SkRect::MakeLTRB(150,200,300,350), paint); + + paint.setColor(0xFF8888EE); + + canvas->translateZ(80); + canvas->drawRect(SkRect::MakeLTRB(100,100,250,250), paint); + // TODO: Add an assert that Z order matches painter's order + // TODO: think about if the Z-order always matching painting order is too strict + + return recorder.finishRecordingAsPicture(); +} + +namespace skiagm { + +/* We override the onFilter method to draw depths into the canvas + * depending on the current draw depth of the canvas, throwing out + * the actual draw color. + */ +class SkShadowPaintFilterCanvas : public SkPaintFilterCanvas { +public: + + SkShadowPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) { } + + // TODO use a shader instead + bool onFilter(SkTCopyOnFirstWrite* paint, Type type) const override { + if (*paint) { + int z = this->getZ(); + SkASSERT(z <= 0xFF && z >= 0x00); + + SkPaint newPaint; + newPaint.setPathEffect(sk_ref_sp((*paint)->getPathEffect())); + + SkColor color = 0xFF000000; // init color to opaque black + color |= z; // Put the index into the blue component + newPaint.setColor(color); + + *paint->writable() = newPaint; + } + + return true; + } + + void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { + SkTCopyOnFirstWrite filteredPaint(paint); + if (this->onFilter(&filteredPaint, kPicture_Type)) { + // we directly call SkCanvas's onDrawPicture because calling the one + // that INHERITED has (SkPaintFilterCanvas) leads to wrong behavior + this->SkCanvas::onDrawPicture(picture, matrix, filteredPaint); + } + } + + void updateMatrix() { + this->save(); + + // When we use the SkShadowPaintFilterCanvas, we can only render + // one depth map at a time. Thus, we leave it up to the user to + // set SkLights to only contain (or contain at the first position) + // the light they intend to use for the current depth rendering. + + if (fLights->numLights() > 0 && + this->fLights->light(0).type() == SkLights::Light::kDirectional_LightType) { + SkVector3 lightDir = this->fLights->light(0).dir(); + SkScalar x = lightDir.fX * this->getZ(); + SkScalar y = lightDir.fY * this->getZ(); + + this->translate(x, y); + } + + } + + void onDrawPaint(const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPaint(paint); + this->restore(); + } + + void onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPoints(mode, count, pts, paint); + this->restore(); + } + + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawRect(rect, paint); + this->restore(); + } + + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawRRect(rrect, paint); + this->restore(); + } + + void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, + const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawDRRect(outer, inner, paint); + this->restore(); + } + + void onDrawOval(const SkRect& rect, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawOval(rect, paint); + this->restore(); + } + + void onDrawPath(const SkPath& path, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPath(path, paint); + this->restore(); + } + + void onDrawBitmap(const SkBitmap& bm, SkScalar left, SkScalar top, + const SkPaint* paint) override { + this->updateMatrix(); + this->INHERITED::onDrawBitmap(bm, left, top, paint); + this->restore(); + } + + void onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) override { + this->updateMatrix(); + this->INHERITED::onDrawBitmapRect(bm, src, dst, paint, constraint); + this->restore(); + } + + void onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + this->updateMatrix(); + this->INHERITED::onDrawBitmapNine(bm, center, dst, paint); + this->restore(); + } + + void onDrawImage(const SkImage* image, SkScalar left, SkScalar top, + const SkPaint* paint) override { + this->updateMatrix(); + this->INHERITED::onDrawImage(image, left, top, paint); + this->restore(); + } + + void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint constraint) override { + this->updateMatrix(); + this->INHERITED::onDrawImageRect(image, src, dst, paint, constraint); + this->restore(); + } + + void onDrawImageNine(const SkImage* image, const SkIRect& center, + const SkRect& dst, const SkPaint* paint) { + this->updateMatrix(); + this->INHERITED::onDrawImageNine(image, center, dst, paint); + this->restore(); + } + + void onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], + const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawVertices(vmode, vertexCount, vertices, texs, colors, + xmode, indices, indexCount, paint); + this->restore(); + } + + void onDrawPatch(const SkPoint cubics[], const SkColor colors[], const SkPoint texCoords[], + SkXfermode* xmode, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPatch(cubics, colors, texCoords, xmode, paint); + this->restore(); + } + + void onDrawText(const void* text, size_t byteLength, + SkScalar x, SkScalar y, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawText(text, byteLength, x, y, paint); + this->restore(); + } + + void onDrawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPosText(text, byteLength, pos, paint); + this->restore(); + } + + void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], + SkScalar constY, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawPosTextH(text, byteLength, xpos, constY, paint); + this->restore(); + } + + void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, + const SkMatrix* matrix, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawTextOnPath(text, byteLength, path, matrix, paint); + this->restore(); + } + + void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], + const SkRect* cull, const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawTextRSXform(text, byteLength, xform, cull, paint); + this->restore(); + } + + void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) override { + this->updateMatrix(); + this->INHERITED::onDrawTextBlob(blob, x, y, paint); + this->restore(); + } + +private: + typedef SkPaintFilterCanvas INHERITED; +}; + +class ShadowMapsGM : public GM { +public: + ShadowMapsGM() { + this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC)); + } + + void onOnceBeforeDraw() override { + // Create a light set consisting of + // - bluish directional light pointing more right than down + // - reddish directional light pointing more down than right + // - soft white ambient light + + SkLights::Builder builder; + builder.add(SkLights::Light(SkColor3f::Make(0.2f, 0.3f, 0.4f), + SkVector3::Make(0.2f, 0.1f, 1.0f))); + builder.add(SkLights::Light(SkColor3f::Make(0.4f, 0.3f, 0.2f), + SkVector3::Make(0.1f, 0.2f, 1.0f))); + builder.add(SkLights::Light(SkColor3f::Make(0.4f, 0.4f, 0.4f))); + fLights = builder.finish(); + } + +protected: + static const int kWidth = 400; + static const int kHeight = 400; + + SkString onShortName() override { + return SkString("shadowmaps"); + } + + SkISize onISize() override { + return SkISize::Make(kWidth, kHeight); + } + + void onDraw(SkCanvas* canvas) override { + // This picture stores the picture of the scene. + // It's used to generate the depth maps. + sk_sp pic(make_test_picture(kWidth, kHeight)); + + for (int i = 0; i < fLights->numLights(); ++i) { + // skip over ambient lights; they don't cast shadows + if (SkLights::Light::kAmbient_LightType == fLights->light(i).type()) { + continue; + } + // TODO: compute the correct size of the depth map from the light properties + // TODO: maybe add a kDepth_8_SkColorType + + SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + // Create a new surface (that matches the backend of canvas) + // for each shadow map + sk_sp surf(canvas->makeSurface(info)); + + // Wrap another SPFCanvas around the surface + sk_sp depthMapCanvas = + sk_make_sp(surf->getCanvas()); + + // 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(pic); + + fLights->light(i).setShadowMap(surf->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; + builder.add(SkLights::Light(SkColor3f::Make(1.0f, 1.0f, 1.0f), + SkVector3::Make(0.0f, 0.0f, 1.0f))); + sk_sp povLight = builder.finish(); + + SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + // Create a new surface (that matches the backend of canvas) + // to create the povDepthMap + sk_sp surf(canvas->makeSurface(info)); + + // Wrap another SPFCanvas around the surface + sk_sp depthMapCanvas = + sk_make_sp(surf->getCanvas()); + + // set the depth map canvas to have the light as the user's POV + depthMapCanvas->setLights(std::move(povLight)); + + depthMapCanvas->drawPicture(pic); + + povDepthMap = surf->makeImageSnapshot(); + } + + // diffuseMap + { + SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, + kBGRA_8888_SkColorType, + kOpaque_SkAlphaType); + + sk_sp surf(canvas->makeSurface(info)); + surf->getCanvas()->drawPicture(pic); + + diffuseMap = surf->makeImageSnapshot(); + } + + SkPaint paint; + paint.setShader(make_shadow_shader(std::move(povDepthMap), std::move(diffuseMap), fLights)); + + canvas->drawRect(SkRect::MakeIWH(kWidth, kHeight), paint); + } + +private: + sk_sp fLights; + + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM(return new ShadowMapsGM;) +} + +#endif diff --git a/gyp/core.gypi b/gyp/core.gypi index 6306a75df9..fbb2038a2b 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -341,6 +341,8 @@ '<(skia_src_path)/core/SkXfermodeInterpretation.h', '<(skia_src_path)/core/SkYUVPlanesCache.cpp', '<(skia_src_path)/core/SkYUVPlanesCache.h', + '<(skia_src_path)/core/SkShadowShader.cpp', + '<(skia_src_path)/core/SkShadowShader.h', '<(skia_src_path)/image/SkImage.cpp', '<(skia_src_path)/image/SkImage_Generator.cpp', diff --git a/src/core/SkShadowShader.cpp b/src/core/SkShadowShader.cpp new file mode 100644 index 0000000000..c5a99e9e91 --- /dev/null +++ b/src/core/SkShadowShader.cpp @@ -0,0 +1,719 @@ +/* + * 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 "SkReadBuffer.h" +#include "SkShadowShader.h" +#include "SkPoint3.h" + +//////////////////////////////////////////////////////////////////////////// +#ifdef SK_EXPERIMENTAL_SHADOWING + +#define SK_MAX_NON_AMBIENT_LIGHTS 4 + +/** \class SkShadowShaderImpl + This subclass of shader applies shadowing +*/ +class SkShadowShaderImpl : public SkShader { +public: + /** Create a new shadowing shader that shadows + @param to do to do + */ + SkShadowShaderImpl(sk_sp povDepthShader, + sk_sp diffuseShader, + sk_sp lights, + int diffuseWidth, int diffuseHeight) + : fPovDepthShader(std::move(povDepthShader)) + , fDiffuseShader(std::move(diffuseShader)) + , fLights(std::move(lights)) + , fDiffuseWidth(diffuseWidth) + , fDiffuseHeight(diffuseHeight) { } + + bool isOpaque() const override; + +#if SK_SUPPORT_GPU + sk_sp asFragmentProcessor(const AsFPArgs&) const override; +#endif + + class ShadowShaderContext : public SkShader::Context { + public: + // The context takes ownership of the states. It will call their destructors + // but will NOT free the memory. + ShadowShaderContext(const SkShadowShaderImpl&, const ContextRec&, + SkShader::Context* povDepthContext, + SkShader::Context* diffuseContext, + void* heapAllocated); + + ~ShadowShaderContext() override; + + void shadeSpan(int x, int y, SkPMColor[], int count) override; + + uint32_t getFlags() const override { return fFlags; } + + private: + SkShader::Context* fPovDepthContext; + SkShader::Context* fDiffuseContext; + uint32_t fFlags; + + void* fHeapAllocated; + + typedef SkShader::Context INHERITED; + }; + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkShadowShaderImpl) + +protected: + void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec&) const override; + Context* onCreateContext(const ContextRec&, void*) const override; + +private: + sk_sp fPovDepthShader; + sk_sp fDiffuseShader; + sk_sp fLights; + + int fDiffuseWidth; + int fDiffuseHeight; + + friend class SkShadowShader; + + typedef SkShader INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" +#include "SkGrPriv.h" +#include "SkSpecialImage.h" +#include "SkImage_Base.h" +#include "GrContext.h" + +class ShadowFP : public GrFragmentProcessor { +public: + ShadowFP(sk_sp povDepth, + sk_sp diffuse, + sk_sp lights, + int diffuseWidth, int diffuseHeight, + GrContext* context) { + + // fuse all ambient lights into a single one + fAmbientColor.set(0.0f, 0.0f, 0.0f); + + fNumDirLights = 0; // refers to directional lights. + for (int i = 0; i < lights->numLights(); ++i) { + if (SkLights::Light::kAmbient_LightType == lights->light(i).type()) { + fAmbientColor += lights->light(i).color(); + } else if (fNumDirLights < SK_MAX_NON_AMBIENT_LIGHTS){ + fLightColor[fNumDirLights] = lights->light(i).color(); + fLightDir[fNumDirLights] = lights->light(i).dir(); + SkImage_Base* shadowMap = ((SkImage_Base*)lights->light(i).getShadowMap().get()); + + // this sk_sp gets deleted when the ShadowFP is destroyed, and frees the GrTexture* + fTexture[fNumDirLights] = sk_sp(shadowMap->asTextureRef(context, + GrTextureParams::ClampNoFilter(), + SkSourceGammaTreatment::kIgnore)); + fDepthMapAccess[fNumDirLights].reset(fTexture[fNumDirLights].get()); + this->addTextureAccess(&fDepthMapAccess[fNumDirLights]); + + fDepthMapHeight[fNumDirLights] = shadowMap->height(); + fDepthMapWidth[fNumDirLights] = shadowMap->width(); + + fNumDirLights++; + } + } + + fWidth = diffuseWidth; + fHeight = diffuseHeight; + + this->registerChildProcessor(std::move(povDepth)); + this->registerChildProcessor(std::move(diffuse)); + this->initClassID(); + } + + class GLSLShadowFP : public GrGLSLFragmentProcessor { + public: + GLSLShadowFP() { } + + void emitCode(EmitArgs& args) override { + + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + // add uniforms + int32_t numLights = args.fFp.cast().fNumDirLights; + SkASSERT(numLights <= SK_MAX_NON_AMBIENT_LIGHTS); + + const char* lightDirUniName[SK_MAX_NON_AMBIENT_LIGHTS] = {nullptr}; + const char* lightColorUniName[SK_MAX_NON_AMBIENT_LIGHTS] = {nullptr}; + + const char* depthMapWidthUniName[SK_MAX_NON_AMBIENT_LIGHTS] = {nullptr}; + const char* depthMapHeightUniName[SK_MAX_NON_AMBIENT_LIGHTS] = {nullptr}; + + SkString lightDirUniNameBase("lightDir"); + SkString lightColorUniNameBase("lightColor"); + + SkString depthMapWidthUniNameBase("dmapWidth"); + SkString depthMapHeightUniNameBase("dmapHeight"); + + for (int i = 0; i < numLights; i++) { + SkString lightDirUniNameStr(lightDirUniNameBase); + lightDirUniNameStr.appendf("%d", i); + SkString lightColorUniNameStr(lightColorUniNameBase); + lightColorUniNameStr.appendf("%d", i); + + SkString depthMapWidthUniNameStr(depthMapWidthUniNameBase); + depthMapWidthUniNameStr.appendf("%d", i); + SkString depthMapHeightUniNameStr(depthMapHeightUniNameBase); + depthMapHeightUniNameStr.appendf("%d", i); + + fLightDirUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag, + kVec3f_GrSLType, + kDefault_GrSLPrecision, + lightDirUniNameStr.c_str(), + &lightDirUniName[i]); + fLightColorUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag, + kVec3f_GrSLType, + kDefault_GrSLPrecision, + lightColorUniNameStr.c_str(), + &lightColorUniName[i]); + + fDepthMapWidthUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag, + kInt_GrSLType, + kDefault_GrSLPrecision, + depthMapWidthUniNameStr.c_str(), + &depthMapWidthUniName[i]); + fDepthMapHeightUni[i] = uniformHandler->addUniform(kFragment_GrShaderFlag, + kInt_GrSLType, + kDefault_GrSLPrecision, + depthMapHeightUniNameStr.c_str(), + &depthMapHeightUniName[i]); + } + + + 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 povDepth("povDepth"); + this->emitChild(0, nullptr, &povDepth, args); + + SkString diffuseColor("inDiffuseColor"); + this->emitChild(1, nullptr, &diffuseColor, args); + + SkString depthMaps[SK_MAX_NON_AMBIENT_LIGHTS]; + + for (int i = 0; i < numLights; i++) { + SkString povCoord("povCoord"); + povCoord.appendf("%d", i); + + // vMatrixCoord_0_1_Stage0 is the texture sampler coordinates. + // povDepth.b * 255 scales it to 0 - 255, bringing it to world space, + // and the / 400 brings it back to a sampler coordinate, 0 - 1 + // The 400 comes from the shadowmaps GM. + // TODO use real shadowmaps size + SkString offset("offset"); + offset.appendf("%d", i); + + SkString scaleVec("scaleVec"); + scaleVec.appendf("%d", i); + + SkString scaleOffsetVec("scaleOffsetVec"); + scaleOffsetVec.appendf("%d", i); + + fragBuilder->codeAppendf("vec2 %s = vec2(%s) * povDepth.b * 255 / 400;\n", + offset.c_str(), lightDirUniName[i]); + + fragBuilder->codeAppendf("vec2 %s = (vec2(%s, %s) / vec2(%s, %s));\n", + scaleVec.c_str(), + widthUniName, heightUniName, + depthMapWidthUniName[i], depthMapHeightUniName[i]); + + 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; + fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kVec3f_GrSLType, kDefault_GrSLPrecision, + "AmbientColor", &ambientColorUniName); + + 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()); + + for (int i = 0; i < numLights; i++) { + 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;", + totalLightColor.c_str(), + lightDirUniName[i], + lightColorUniName[i]); + fragBuilder->codeAppendf("}"); + } + + fragBuilder->codeAppendf("%s += %s;", + totalLightColor.c_str(), + ambientColorUniName); + + fragBuilder->codeAppendf("resultDiffuseColor *= vec4(%s, 1);", + totalLightColor.c_str()); + + fragBuilder->codeAppendf("%s = resultDiffuseColor;", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, + GrProcessorKeyBuilder* b) { + const ShadowFP& shadowFP = proc.cast(); + b->add32(shadowFP.fNumDirLights); + } + + 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++) { + const SkVector3& lightDir = shadowFP.lightDir(i); + if (lightDir != fLightDir[i]) { + pdman.set3fv(fLightDirUni[i], 1, &lightDir.fX); + fLightDir[i] = lightDir; + } + const SkColor3f& lightColor = shadowFP.lightColor(i); + if (lightColor != fLightColor[i]) { + pdman.set3fv(fLightColorUni[i], 1, &lightColor.fX); + fLightColor[i] = lightColor; + } + + int depthMapWidth = shadowFP.depthMapWidth(i); + if (depthMapWidth != fDepthMapWidth[i]) { + pdman.set1i(fDepthMapWidthUni[i], depthMapWidth); + fDepthMapWidth[i] = depthMapWidth; + } + int depthMapHeight = shadowFP.depthMapHeight(i); + if (depthMapHeight != fDepthMapHeight[i]) { + pdman.set1i(fDepthMapHeightUni[i], depthMapHeight); + fDepthMapHeight[i] = depthMapHeight; + } + } + + int width = shadowFP.width(); + if (width != fWidth) { + pdman.set1i(fWidthUni, width); + fWidth = width; + } + int height = shadowFP.height(); + if (height != fHeight) { + pdman.set1i(fHeightUni, height); + fHeight = height; + } + + const SkColor3f& ambientColor = shadowFP.ambientColor(); + if (ambientColor != fAmbientColor) { + pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX); + fAmbientColor = ambientColor; + } + } + + private: + SkVector3 fLightDir[SK_MAX_NON_AMBIENT_LIGHTS]; + GrGLSLProgramDataManager::UniformHandle fLightDirUni[SK_MAX_NON_AMBIENT_LIGHTS]; + SkColor3f fLightColor[SK_MAX_NON_AMBIENT_LIGHTS]; + GrGLSLProgramDataManager::UniformHandle fLightColorUni[SK_MAX_NON_AMBIENT_LIGHTS]; + + int fDepthMapWidth[SK_MAX_NON_AMBIENT_LIGHTS]; + GrGLSLProgramDataManager::UniformHandle fDepthMapWidthUni[SK_MAX_NON_AMBIENT_LIGHTS]; + int fDepthMapHeight[SK_MAX_NON_AMBIENT_LIGHTS]; + GrGLSLProgramDataManager::UniformHandle fDepthMapHeightUni[SK_MAX_NON_AMBIENT_LIGHTS]; + + int fWidth; + GrGLSLProgramDataManager::UniformHandle fWidthUni; + int fHeight; + GrGLSLProgramDataManager::UniformHandle fHeightUni; + + SkColor3f fAmbientColor; + GrGLSLProgramDataManager::UniformHandle fAmbientColorUni; + + int fNumDirLights; + }; + + void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLShadowFP::GenKey(*this, caps, b); + } + + const char* name() const override { return "shadowFP"; } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + inout->mulByUnknownFourComponents(); + } + int32_t numLights() const { return fNumDirLights; } + const SkColor3f& ambientColor() const { return fAmbientColor; } + const SkVector3& lightDir(int i) const { + SkASSERT(i < fNumDirLights); + return fLightDir[i]; + } + const SkVector3& lightColor(int i) const { + SkASSERT(i < fNumDirLights); + return fLightColor[i]; + } + + int depthMapWidth(int i) const { + SkASSERT(i < fNumDirLights); + return fDepthMapWidth[i]; + } + int depthMapHeight(int i) const { + SkASSERT(i < fNumDirLights); + return fDepthMapHeight[i]; + } + int width() const {return fWidth; } + int height() const {return fHeight; } + +private: + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLShadowFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const ShadowFP& shadowFP = proc.cast(); + if (fAmbientColor != shadowFP.fAmbientColor || fNumDirLights != shadowFP.fNumDirLights) { + return false; + } + + if (fWidth != shadowFP.fWidth || fHeight != shadowFP.fHeight) { + return false; + } + + for (int i = 0; i < fNumDirLights; i++) { + if (fLightDir[i] != shadowFP.fLightDir[i] || + fLightColor[i] != shadowFP.fLightColor[i]) { + return false; + } + + if (fDepthMapWidth[i] != shadowFP.fDepthMapWidth[i] || + fDepthMapHeight[i] != shadowFP.fDepthMapHeight[i]) { + return false; + } + } + + return true; + } + + int fNumDirLights; + + SkVector3 fLightDir[SK_MAX_NON_AMBIENT_LIGHTS]; + SkColor3f fLightColor[SK_MAX_NON_AMBIENT_LIGHTS]; + GrTextureAccess fDepthMapAccess[SK_MAX_NON_AMBIENT_LIGHTS]; + sk_sp fTexture[SK_MAX_NON_AMBIENT_LIGHTS]; + + int fDepthMapWidth[SK_MAX_NON_AMBIENT_LIGHTS]; + int fDepthMapHeight[SK_MAX_NON_AMBIENT_LIGHTS]; + + int fHeight; + int fWidth; + + SkColor3f fAmbientColor; +}; + +//////////////////////////////////////////////////////////////////////////// + +sk_sp SkShadowShaderImpl::asFragmentProcessor(const AsFPArgs& fpargs) const { + + sk_sp povDepthFP = fPovDepthShader->asFragmentProcessor(fpargs); + + sk_sp diffuseFP = fDiffuseShader->asFragmentProcessor(fpargs); + + sk_sp shadowfp = sk_make_sp(std::move(povDepthFP), + std::move(diffuseFP), + std::move(fLights), + fDiffuseWidth, fDiffuseHeight, + fpargs.fContext); + return shadowfp; +} + + +#endif + +//////////////////////////////////////////////////////////////////////////// + +bool SkShadowShaderImpl::isOpaque() const { + return fDiffuseShader->isOpaque(); +} + +SkShadowShaderImpl::ShadowShaderContext::ShadowShaderContext( + const SkShadowShaderImpl& shader, const ContextRec& rec, + SkShader::Context* povDepthContext, + SkShader::Context* diffuseContext, + void* heapAllocated) + : INHERITED(shader, rec) + , fPovDepthContext(povDepthContext) + , fDiffuseContext(diffuseContext) + , fHeapAllocated(heapAllocated) { + bool isOpaque = shader.isOpaque(); + + // update fFlags + uint32_t flags = 0; + if (isOpaque && (255 == this->getPaintAlpha())) { + flags |= kOpaqueAlpha_Flag; + } + + fFlags = flags; +} + +SkShadowShaderImpl::ShadowShaderContext::~ShadowShaderContext() { + // 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. + fPovDepthContext->~Context(); + fDiffuseContext->~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 SkShadowShaderImpl::ShadowShaderContext::shadeSpan(int x, int y, + SkPMColor result[], int count) { + const SkShadowShaderImpl& lightShader = static_cast(fShader); + + SkPMColor diffuse[BUFFER_MAX]; + + do { + int n = SkTMin(count, BUFFER_MAX); + + fPovDepthContext->shadeSpan(x, y, diffuse, n); + fDiffuseContext->shadeSpan(x, y, diffuse, n); + + for (int i = 0; i < n; ++i) { + + SkColor diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); + + SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); + // This is all done in linear unpremul color space (each component 0..255.0f though) + for (int l = 0; l < lightShader.fLights->numLights(); ++l) { + const SkLights::Light& light = lightShader.fLights->light(l); + + if (SkLights::Light::kAmbient_LightType == light.type()) { + accum.fX += light.color().fX * SkColorGetR(diffColor); + accum.fY += light.color().fY * SkColorGetG(diffColor); + accum.fZ += light.color().fZ * SkColorGetB(diffColor); + } else { + // scaling by fZ accounts for lighting direction + accum.fX += light.color().makeScale(light.dir().fZ).fX * SkColorGetR(diffColor); + accum.fY += light.color().makeScale(light.dir().fZ).fY * SkColorGetG(diffColor); + accum.fZ += light.color().makeScale(light.dir().fZ).fZ * SkColorGetB(diffColor); + } + } + + result[i] = convert(accum, SkColorGetA(diffColor)); + } + + result += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////// + +#ifndef SK_IGNORE_TO_STRING +void SkShadowShaderImpl::toString(SkString* str) const { + str->appendf("ShadowShader: ()"); +} +#endif + +sk_sp SkShadowShaderImpl::CreateProc(SkReadBuffer& buf) { + + // Discarding SkShader flattenable params + bool hasLocalMatrix = buf.readBool(); + SkAssertResult(!hasLocalMatrix); + + int numLights = buf.readInt(); + + SkLights::Builder builder; + + for (int l = 0; l < numLights; ++l) { + bool isAmbient = buf.readBool(); + + SkColor3f color; + if (!buf.readScalarArray(&color.fX, 3)) { + return nullptr; + } + + if (isAmbient) { + builder.add(SkLights::Light(color)); + } else { + SkVector3 dir; + if (!buf.readScalarArray(&dir.fX, 3)) { + return nullptr; + } + + sk_sp depthMap; + if (!(depthMap = sk_ref_sp(buf.readImage()))) { + return nullptr; + } + + SkLights::Light light = SkLights::Light(color, dir); + light.setShadowMap(depthMap); + + builder.add(light); + } + } + + sk_sp lights(builder.finish()); + + int diffuseWidth = buf.readInt(); + int diffuseHeight = buf.readInt(); + + sk_sp povDepthShader(buf.readFlattenable()); + sk_sp diffuseShader(buf.readFlattenable()); + + return sk_make_sp(std::move(povDepthShader), + std::move(diffuseShader), + std::move(lights), + diffuseWidth, diffuseHeight); +} + +void SkShadowShaderImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeInt(fLights->numLights()); + + for (int l = 0; l < fLights->numLights(); ++l) { + const SkLights::Light& light = fLights->light(l); + + bool isAmbient = SkLights::Light::kAmbient_LightType == light.type(); + + buf.writeBool(isAmbient); + buf.writeScalarArray(&light.color().fX, 3); + if (!isAmbient) { + buf.writeScalarArray(&light.dir().fX, 3); + } + + buf.writeImage(light.getShadowMap().get()); + } + + buf.writeInt(fDiffuseWidth); + buf.writeInt(fDiffuseHeight); + + buf.writeFlattenable(fPovDepthShader.get()); + buf.writeFlattenable(fDiffuseShader.get()); +} + +size_t SkShadowShaderImpl::onContextSize(const ContextRec& rec) const { + return sizeof(ShadowShaderContext); +} + +SkShader::Context* SkShadowShaderImpl::onCreateContext(const ContextRec& rec, + void* storage) const { + size_t heapRequired = fPovDepthShader->contextSize(rec) + + fDiffuseShader->contextSize(rec); + + void* heapAllocated = sk_malloc_throw(heapRequired); + + void* povDepthContextStorage = heapAllocated; + + SkShader::Context* povDepthContext = + fPovDepthShader->createContext(rec, povDepthContextStorage); + + if (!povDepthContext) { + sk_free(heapAllocated); + return nullptr; + } + + void* diffuseContextStorage = (char*)heapAllocated + fPovDepthShader->contextSize(rec); + + SkShader::Context* diffuseContext = fDiffuseShader->createContext(rec, diffuseContextStorage); + if (!diffuseContext) { + sk_free(heapAllocated); + return nullptr; + } + + return new (storage) ShadowShaderContext(*this, rec, povDepthContext, diffuseContext, + heapAllocated); +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp SkShadowShader::Make(sk_sp povDepthShader, + sk_sp diffuseShader, + sk_sp lights, + int diffuseWidth, int diffuseHeight) { + if (!povDepthShader || !diffuseShader) { + // TODO: Use paint's color in absence of a diffuseShader + // TODO: Use a default implementation of normalSource instead + return nullptr; + } + + return sk_make_sp(std::move(povDepthShader), + std::move(diffuseShader), + std::move(lights), + diffuseWidth, diffuseHeight); +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkShadowShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkShadowShaderImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +/////////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/core/SkShadowShader.h b/src/core/SkShadowShader.h new file mode 100644 index 0000000000..7cbcbca224 --- /dev/null +++ b/src/core/SkShadowShader.h @@ -0,0 +1,32 @@ +/* + * 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 SkShadowShader_DEFINED +#define SkShadowShader_DEFINED + +#ifdef SK_EXPERIMENTAL_SHADOWING + +class SkLights; +class SkShader; + +class SK_API SkShadowShader { +public: + /** This shader combines the diffuse color in 'diffuseShader' with the shadows + * determined by the 'povDepthShader' and the shadow maps stored in each of the + * lights in 'lights' + */ + static sk_sp Make(sk_sp povDepthShader, + sk_sp diffuseShader, + sk_sp lights, + int diffuseWidth, int diffuseHeight); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif +#endif -- 2.34.1