diff options
author | Robert Phillips <robertphillips@google.com> | 2018-07-17 12:30:40 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-07-17 17:24:50 +0000 |
commit | a8cdbd7431b6a27d28db1bc80d7557cedf7e66d0 (patch) | |
tree | 3d866605d2236b31da70535f2a4263c55b01acae /src/shaders/SkLightingShader.cpp | |
parent | 9e0d7e4072e43495a3907bb2bac7824e8e60c368 (diff) |
Restore SkLightingShader and associated classes
This reverts https://skia-review.googlesource.com/c/skia/+/31140 (Remove SkLightingShader and associated classes) and updates the classes to ToT
Change-Id: I3b1df1704cca8907aa00f081a7e93339b65ad4fa
Reviewed-on: https://skia-review.googlesource.com/141545
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
Diffstat (limited to 'src/shaders/SkLightingShader.cpp')
-rw-r--r-- | src/shaders/SkLightingShader.cpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/src/shaders/SkLightingShader.cpp b/src/shaders/SkLightingShader.cpp new file mode 100644 index 0000000000..6fa754a6fa --- /dev/null +++ b/src/shaders/SkLightingShader.cpp @@ -0,0 +1,497 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkArenaAlloc.h" +#include "SkBitmapProcShader.h" +#include "SkBitmapProcState.h" +#include "SkColor.h" +#include "SkColorSpaceXformer.h" +#include "SkEmptyShader.h" +#include "SkLightingShader.h" +#include "SkMathPriv.h" +#include "SkNormalSource.h" +#include "SkPoint3.h" +#include "SkReadBuffer.h" +#include "SkShaderBase.h" +#include "SkUnPreMultiply.h" +#include "SkWriteBuffer.h" + +//////////////////////////////////////////////////////////////////////////// + +/* + SkLightingShader TODOs: + support different light types + support multiple lights + fix non-opaque diffuse textures + + To Test: + A8 diffuse textures + down & upsampled draws +*/ + + + +/** \class SkLightingShaderImpl + This subclass of shader applies lighting. +*/ +class SkLightingShaderImpl : public SkShaderBase { +public: + /** Create a new lighting shader that uses the provided normal map and + lights to light the diffuse bitmap. + @param diffuseShader the shader that provides the diffuse colors + @param normalSource the source of normals for lighting computation + @param lights the lights applied to the geometry + */ + SkLightingShaderImpl(sk_sp<SkShader> diffuseShader, + sk_sp<SkNormalSource> normalSource, + sk_sp<SkLights> lights) + : fDiffuseShader(std::move(diffuseShader)) + , fNormalSource(std::move(normalSource)) + , fLights(std::move(lights)) {} + + bool isOpaque() const override; + +#if SK_SUPPORT_GPU + std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override; +#endif + + class LightingShaderContext : public Context { + public: + // The context takes ownership of the context and provider. It will call their destructors + // and then indirectly free their memory by calling free() on heapAllocated + LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&, + SkShaderBase::Context* diffuseContext, SkNormalSource::Provider*, + void* heapAllocated); + + void shadeSpan(int x, int y, SkPMColor[], int count) override; + + uint32_t getFlags() const override { return fFlags; } + + private: + SkShaderBase::Context* fDiffuseContext; + SkNormalSource::Provider* fNormalProvider; + SkColor fPaintColor; + uint32_t fFlags; + + typedef Context INHERITED; + }; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl) + +protected: + void flatten(SkWriteBuffer&) const override; + Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override; + sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override; + +private: + sk_sp<SkShader> fDiffuseShader; + sk_sp<SkNormalSource> fNormalSource; + sk_sp<SkLights> fLights; + + friend class SkLightingShader; + + typedef SkShaderBase INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +#if SK_SUPPORT_GPU + +#include "GrCoordTransform.h" +#include "GrFragmentProcessor.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" +#include "SkGr.h" + +// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is +// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it +// premul'd. +class LightingFP : public GrFragmentProcessor { +public: + static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> normalFP, + sk_sp<SkLights> lights) { + return std::unique_ptr<GrFragmentProcessor>(new LightingFP(std::move(normalFP), + std::move(lights))); + } + + const char* name() const override { return "LightingFP"; } + + std::unique_ptr<GrFragmentProcessor> clone() const override { + return std::unique_ptr<GrFragmentProcessor>(new LightingFP(*this)); + } + + const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; } + const SkColor3f& ambientColor() const { return fAmbientColor; } + +private: + class GLSLLightingFP : public GrGLSLFragmentProcessor { + public: + GLSLLightingFP() { + fAmbientColor.fX = 0.0f; + } + + void emitCode(EmitArgs& args) override { + + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + const LightingFP& lightingFP = args.fFp.cast<LightingFP>(); + + const char *lightDirsUniName = nullptr; + const char *lightColorsUniName = nullptr; + if (lightingFP.fDirectionalLights.count() != 0) { + fLightDirsUni = uniformHandler->addUniformArray( + kFragment_GrShaderFlag, + kFloat3_GrSLType, + kDefault_GrSLPrecision, + "LightDir", + lightingFP.fDirectionalLights.count(), + &lightDirsUniName); + fLightColorsUni = uniformHandler->addUniformArray( + kFragment_GrShaderFlag, + kFloat3_GrSLType, + kDefault_GrSLPrecision, + "LightColor", + lightingFP.fDirectionalLights.count(), + &lightColorsUniName); + } + + const char* ambientColorUniName = nullptr; + fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, + kFloat3_GrSLType, kDefault_GrSLPrecision, + "AmbientColor", &ambientColorUniName); + + fragBuilder->codeAppendf("float4 diffuseColor = %s;", args.fInputColor); + + SkString dstNormalName("dstNormal"); + this->emitChild(0, &dstNormalName, args); + + fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str()); + + fragBuilder->codeAppend( "float3 result = float3(0.0);"); + + // diffuse light + if (lightingFP.fDirectionalLights.count() != 0) { + fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {", + lightingFP.fDirectionalLights.count()); + // TODO: modulate the contribution from each light based on the shadow map + fragBuilder->codeAppendf(" float NdotL = clamp(dot(normal, %s[i]), 0.0, 1.0);", + lightDirsUniName); + fragBuilder->codeAppendf(" result += %s[i]*diffuseColor.rgb*NdotL;", + lightColorsUniName); + fragBuilder->codeAppend("}"); + } + + // ambient light + fragBuilder->codeAppendf("result += %s * diffuseColor.rgb;", ambientColorUniName); + + // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0) + fragBuilder->codeAppendf("%s = float4(clamp(result.rgb, 0.0, diffuseColor.a), " + "diffuseColor.a);", args.fOutputColor); + } + + static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { + const LightingFP& lightingFP = proc.cast<LightingFP>(); + b->add32(lightingFP.fDirectionalLights.count()); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrFragmentProcessor& proc) override { + const LightingFP& lightingFP = proc.cast<LightingFP>(); + + const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights(); + if (directionalLights != fDirectionalLights) { + SkTArray<SkColor3f> lightDirs(directionalLights.count()); + SkTArray<SkVector3> lightColors(directionalLights.count()); + for (const SkLights::Light& light : directionalLights) { + lightDirs.push_back(light.dir()); + lightColors.push_back(light.color()); + } + + pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX)); + pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX)); + + fDirectionalLights = directionalLights; + } + + const SkColor3f& ambientColor = lightingFP.ambientColor(); + if (ambientColor != fAmbientColor) { + pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX); + fAmbientColor = ambientColor; + } + } + + private: + SkTArray<SkLights::Light> fDirectionalLights; + GrGLSLProgramDataManager::UniformHandle fLightDirsUni; + GrGLSLProgramDataManager::UniformHandle fLightColorsUni; + + SkColor3f fAmbientColor; + GrGLSLProgramDataManager::UniformHandle fAmbientColorUni; + }; + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLLightingFP::GenKey(*this, caps, b); + } + + LightingFP(std::unique_ptr<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights) + : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) { + // fuse all ambient lights into a single one + fAmbientColor = lights->ambientLightColor(); + for (int i = 0; i < lights->numLights(); ++i) { + if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) { + fDirectionalLights.push_back(lights->light(i)); + // TODO get the handle to the shadow map if there is one + } else { + SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP"); + } + } + + this->registerChildProcessor(std::move(normalFP)); + } + + LightingFP(const LightingFP& that) + : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) + , fDirectionalLights(that.fDirectionalLights) + , fAmbientColor(that.fAmbientColor) { + this->registerChildProcessor(that.childProcessor(0).clone()); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const LightingFP& lightingFP = proc.cast<LightingFP>(); + return fDirectionalLights == lightingFP.fDirectionalLights && + fAmbientColor == lightingFP.fAmbientColor; + } + + SkTArray<SkLights::Light> fDirectionalLights; + SkColor3f fAmbientColor; + + typedef GrFragmentProcessor INHERITED; +}; + +//////////////////////////////////////////////////////////////////////////// + +std::unique_ptr<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const GrFPArgs& args) const { + std::unique_ptr<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args)); + if (!normalFP) { + return nullptr; + } + + if (fDiffuseShader) { + std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { + as_SB(fDiffuseShader)->asFragmentProcessor(args), + LightingFP::Make(std::move(normalFP), fLights) + }; + if (!fpPipeline[0] || !fpPipeline[1]) { + return nullptr; + } + + std::unique_ptr<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2); + // FP is wrapped because paint's alpha needs to be applied to output + return GrFragmentProcessor::MulChildByInputAlpha(std::move(innerLightFP)); + } else { + // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP + // expects premul'd color. + return GrFragmentProcessor::PremulInput(LightingFP::Make(std::move(normalFP), fLights)); + } +} + +#endif + +//////////////////////////////////////////////////////////////////////////// + +bool SkLightingShaderImpl::isOpaque() const { + return (fDiffuseShader ? fDiffuseShader->isOpaque() : false); +} + +SkLightingShaderImpl::LightingShaderContext::LightingShaderContext( + const SkLightingShaderImpl& shader, const ContextRec& rec, + SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider, + void* heapAllocated) + : INHERITED(shader, rec) + , fDiffuseContext(diffuseContext) + , fNormalProvider(normalProvider) { + bool isOpaque = shader.isOpaque(); + + // update fFlags + uint32_t flags = 0; + if (isOpaque && (255 == this->getPaintAlpha())) { + flags |= kOpaqueAlpha_Flag; + } + + fPaintColor = rec.fPaint->getColor(); + fFlags = flags; +} + +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 SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, + SkPMColor result[], int count) { + const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader); + + SkPMColor diffuse[BUFFER_MAX]; + SkPoint3 normals[BUFFER_MAX]; + + SkColor diffColor = fPaintColor; + + do { + int n = SkTMin(count, BUFFER_MAX); + + fNormalProvider->fillScanLine(x, y, normals, n); + + if (fDiffuseContext) { + fDiffuseContext->shadeSpan(x, y, diffuse, n); + } + + for (int i = 0; i < n; ++i) { + if (fDiffuseContext) { + diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]); + } + + SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f); + + // Adding ambient light + accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor); + accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor); + accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor); + + // 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); + + SkScalar illuminanceScalingFactor = 1.0f; + + if (SkLights::Light::kDirectional_LightType == light.type()) { + illuminanceScalingFactor = normals[i].dot(light.dir()); + if (illuminanceScalingFactor < 0.0f) { + illuminanceScalingFactor = 0.0f; + } + } + + accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor; + accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor; + accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor; + } + + // convert() premultiplies the accumulate color with alpha + result[i] = convert(accum, SkColorGetA(diffColor)); + } + + result += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { + + // Discarding SkShader flattenable params + bool hasLocalMatrix = buf.readBool(); + SkAssertResult(!hasLocalMatrix); + + sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf); + + sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>()); + + bool hasDiffuse = buf.readBool(); + sk_sp<SkShader> diffuseShader = nullptr; + if (hasDiffuse) { + diffuseShader = buf.readFlattenable<SkShaderBase>(); + } + + return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), + std::move(lights)); +} + +void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + fLights->flatten(buf); + + buf.writeFlattenable(fNormalSource.get()); + buf.writeBool(static_cast<bool>(fDiffuseShader)); + if (fDiffuseShader) { + buf.writeFlattenable(fDiffuseShader.get()); + } +} + +SkShaderBase::Context* SkLightingShaderImpl::onMakeContext( + const ContextRec& rec, SkArenaAlloc* alloc) const +{ + SkShaderBase::Context *diffuseContext = nullptr; + if (fDiffuseShader) { + diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc); + if (!diffuseContext) { + return nullptr; + } + } + + SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc); + if (!normalProvider) { + return nullptr; + } + + return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr); +} + +sk_sp<SkShader> SkLightingShaderImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) const { + sk_sp<SkShader> xformedDiffuseShader = + fDiffuseShader ? xformer->apply(fDiffuseShader.get()) : nullptr; + return SkLightingShader::Make(std::move(xformedDiffuseShader), fNormalSource, + fLights->makeColorSpace(xformer)); +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader, + sk_sp<SkNormalSource> normalSource, + sk_sp<SkLights> lights) { + SkASSERT(lights); + if (!normalSource) { + normalSource = SkNormalSource::MakeFlat(); + } + + return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource), + std::move(lights)); +} + +/////////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +/////////////////////////////////////////////////////////////////////////////// |