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 | |
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')
-rw-r--r-- | src/core/SkLights.cpp | 91 | ||||
-rw-r--r-- | src/core/SkNormalFlatSource.cpp | 100 | ||||
-rw-r--r-- | src/core/SkNormalFlatSource.h | 47 | ||||
-rw-r--r-- | src/core/SkNormalMapSource.cpp | 253 | ||||
-rw-r--r-- | src/core/SkNormalMapSource.h | 57 | ||||
-rw-r--r-- | src/core/SkNormalSource.cpp | 22 | ||||
-rw-r--r-- | src/core/SkNormalSource.h | 75 | ||||
-rw-r--r-- | src/gpu/GrProcessor.h | 3 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 4 | ||||
-rw-r--r-- | src/shaders/SkLightingShader.cpp | 497 | ||||
-rw-r--r-- | src/shaders/SkLightingShader.h | 40 |
11 files changed, 1189 insertions, 0 deletions
diff --git a/src/core/SkLights.cpp b/src/core/SkLights.cpp new file mode 100644 index 0000000000..e6c4796506 --- /dev/null +++ b/src/core/SkLights.cpp @@ -0,0 +1,91 @@ + +/* + * 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 "SkColorSpaceXformer.h" +#include "SkLights.h" +#include "SkReadBuffer.h" + +sk_sp<SkLights> SkLights::MakeFromBuffer(SkReadBuffer& buf) { + Builder builder; + + SkColor3f ambColor; + if (!buf.readScalarArray(&ambColor.fX, 3)) { + return nullptr; + } + + builder.setAmbientLightColor(ambColor); + + int numLights = buf.readInt(); + + for (int l = 0; l < numLights; ++l) { + bool isPoint = buf.readBool(); + + SkColor3f color; + if (!buf.readScalarArray(&color.fX, 3)) { + return nullptr; + } + + SkVector3 dirOrPos; + if (!buf.readScalarArray(&dirOrPos.fX, 3)) { + return nullptr; + } + + if (isPoint) { + SkScalar intensity; + intensity = buf.readScalar(); + Light light = Light::MakePoint(color, dirOrPos, intensity); + builder.add(light); + } else { + Light light = Light::MakeDirectional(color, dirOrPos); + builder.add(light); + } + } + + return builder.finish(); +} + +static SkColor3f xform_color(const SkColor3f& color, SkColorSpaceXformer* xformer) { + SkColor origColor = SkColorSetARGB(0xFF, + SkScalarRoundToInt(color.fX), + SkScalarRoundToInt(color.fY), + SkScalarRoundToInt(color.fZ)); + SkColor xformedColor = xformer->apply(origColor); + return SkColor3f::Make(SkIntToScalar(SkGetPackedR32(xformedColor)), + SkIntToScalar(SkGetPackedG32(xformedColor)), + SkIntToScalar(SkGetPackedB32(xformedColor))); +} + +sk_sp<SkLights> SkLights::makeColorSpace(SkColorSpaceXformer* xformer) const { + SkLights::Builder builder; + for (int i = 0; i < this->numLights(); i++) { + Light light(fLights[i].type(), xform_color(fLights[i].color(), xformer), + fLights[i].fDirOrPos, fLights[i].fIntensity); + builder.add(light); + } + builder.setAmbientLightColor(xform_color(fAmbientLightColor, xformer)); + return builder.finish(); +} + +void SkLights::flatten(SkWriteBuffer& buf) const { + buf.writeScalarArray(&this->ambientLightColor().fX, 3); + + buf.writeInt(this->numLights()); + for (int l = 0; l < this->numLights(); ++l) { + const Light& light = this->light(l); + + bool isPoint = Light::kPoint_LightType == light.type(); + + buf.writeBool(isPoint); + buf.writeScalarArray(&light.color().fX, 3); + buf.writeScalarArray(&light.dir().fX, 3); + + if (isPoint) { + buf.writeScalar(light.intensity()); + } + } +} diff --git a/src/core/SkNormalFlatSource.cpp b/src/core/SkNormalFlatSource.cpp new file mode 100644 index 0000000000..f174e185c0 --- /dev/null +++ b/src/core/SkNormalFlatSource.cpp @@ -0,0 +1,100 @@ +/* + * 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 "SkNormalFlatSource.h" + +#include "SkArenaAlloc.h" +#include "SkNormalSource.h" +#include "SkPoint3.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" + +class NormalFlatFP : public GrFragmentProcessor { +public: + static std::unique_ptr<GrFragmentProcessor> Make() { + return std::unique_ptr<GrFragmentProcessor>(new NormalFlatFP()); + } + + const char* name() const override { return "NormalFlatFP"; } + + std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(); } + +private: + class GLSLNormalFlatFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalFlatFP() {} + + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + + fragBuilder->codeAppendf("%s = float4(0, 0, 1, 0);", args.fOutputColor); + } + + private: + void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override {} + }; + + NormalFlatFP() + : INHERITED(kFlatNormalsFP_ClassID, kConstantOutputForConstantInput_OptimizationFlag) { + } + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {} + + GrColor4f constantOutputForConstantInput(GrColor4f) const override { + return GrColor4f(0, 0, 1, 0); + } + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalFlatFP; } + + bool onIsEqual(const GrFragmentProcessor&) const override { return true; } + + typedef GrFragmentProcessor INHERITED; +}; + +std::unique_ptr<GrFragmentProcessor> SkNormalFlatSourceImpl::asFragmentProcessor( + const GrFPArgs&) const { + return NormalFlatFP::Make(); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalFlatSourceImpl::Provider::Provider() {} + +SkNormalFlatSourceImpl::Provider::~Provider() {} + +SkNormalSource::Provider* SkNormalFlatSourceImpl::asProvider(const SkShaderBase::ContextRec &rec, + SkArenaAlloc *alloc) const { + return alloc->make<Provider>(); +} + +void SkNormalFlatSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + for (int i = 0; i < count; i++) { + output[i] = {0.0f, 0.0f, 1.0f}; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalFlatSourceImpl::CreateProc(SkReadBuffer& buf) { + return sk_make_sp<SkNormalFlatSourceImpl>(); +} + +void SkNormalFlatSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFlat() { + return sk_make_sp<SkNormalFlatSourceImpl>(); +} diff --git a/src/core/SkNormalFlatSource.h b/src/core/SkNormalFlatSource.h new file mode 100644 index 0000000000..9303ba10af --- /dev/null +++ b/src/core/SkNormalFlatSource.h @@ -0,0 +1,47 @@ +/* + * 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 SkNormalFlatSource_DEFINED +#define SkNormalFlatSource_DEFINED + +#include "SkNormalSource.h" + +class SK_API SkNormalFlatSourceImpl : public SkNormalSource { +public: + SkNormalFlatSourceImpl(){} + +#if SK_SUPPORT_GPU + std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec, + SkArenaAlloc* alloc) const override; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalFlatSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(); + + ~Provider() override; + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + typedef SkNormalSource::Provider INHERITED; + }; + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + +#endif diff --git a/src/core/SkNormalMapSource.cpp b/src/core/SkNormalMapSource.cpp new file mode 100644 index 0000000000..1b88366690 --- /dev/null +++ b/src/core/SkNormalMapSource.cpp @@ -0,0 +1,253 @@ +/* + * 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 "SkNormalMapSource.h" + +#include "SkArenaAlloc.h" +#include "SkLightingShader.h" +#include "SkMatrix.h" +#include "SkNormalSource.h" +#include "SkPM4f.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" + +#if SK_SUPPORT_GPU +#include "GrCoordTransform.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "SkGr.h" + +class NormalMapFP : public GrFragmentProcessor { +public: + static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> mapFP, + const SkMatrix& invCTM) { + return std::unique_ptr<GrFragmentProcessor>(new NormalMapFP(std::move(mapFP), invCTM)); + } + + const char* name() const override { return "NormalMapFP"; } + + const SkMatrix& invCTM() const { return fInvCTM; } + + std::unique_ptr<GrFragmentProcessor> clone() const override { + return Make(this->childProcessor(0).clone(), fInvCTM); + } + +private: + class GLSLNormalMapFP : public GrGLSLFragmentProcessor { + public: + GLSLNormalMapFP() : fColumnMajorInvCTM22{0.0f} {} + + void emitCode(EmitArgs& args) override { + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + + // add uniform + const char* xformUniName = nullptr; + fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat2x2_GrSLType, + kDefault_GrSLPrecision, "Xform", &xformUniName); + + SkString dstNormalColorName("dstNormalColor"); + this->emitChild(0, &dstNormalColorName, args); + fragBuilder->codeAppendf("float3 normal = normalize(%s.rgb - float3(0.5));", + dstNormalColorName.c_str()); + + // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 + fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); + fragBuilder->codeAppendf(" %s = normalize(float4(0.0, 0.0, normal.z, 0.0));", + args.fOutputColor); + // Else, Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal regardless of any anisotropic scaling that occurs. + // Here, we call 'scaling factor' the number that must divide the transformed X and Y so + // that the normal's length remains equal to 1. + fragBuilder->codeAppend( "} else {"); + fragBuilder->codeAppendf(" float2 transformed = %s * normal.xy;", + xformUniName); + fragBuilder->codeAppend( " float scalingFactorSquared = " + "( (transformed.x * transformed.x) " + "+ (transformed.y * transformed.y) )" + "/(1.0 - (normal.z * normal.z));"); + fragBuilder->codeAppendf(" %s = float4(transformed*inversesqrt(scalingFactorSquared)," + "normal.z, 0.0);", + args.fOutputColor); + fragBuilder->codeAppend( "}"); + } + + static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b) { + b->add32(0x0); + } + + private: + void onSetData(const GrGLSLProgramDataManager& pdman, + const GrFragmentProcessor& proc) override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + + const SkMatrix& invCTM = normalMapFP.invCTM(); + fColumnMajorInvCTM22[0] = invCTM.get(SkMatrix::kMScaleX); + fColumnMajorInvCTM22[1] = invCTM.get(SkMatrix::kMSkewY); + fColumnMajorInvCTM22[2] = invCTM.get(SkMatrix::kMSkewX); + fColumnMajorInvCTM22[3] = invCTM.get(SkMatrix::kMScaleY); + pdman.setMatrix2f(fXformUni, fColumnMajorInvCTM22); + } + + private: + // Upper-right 2x2 corner of the inverse of the CTM in column-major form + float fColumnMajorInvCTM22[4]; + GrGLSLProgramDataManager::UniformHandle fXformUni; + }; + + void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { + GLSLNormalMapFP::GenKey(*this, caps, b); + } + NormalMapFP(std::unique_ptr<GrFragmentProcessor> mapFP, const SkMatrix& invCTM) + : INHERITED(kMappedNormalsFP_ClassID, kNone_OptimizationFlags) + , fInvCTM(invCTM) { + this->registerChildProcessor(std::move(mapFP)); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; } + + bool onIsEqual(const GrFragmentProcessor& proc) const override { + const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>(); + return fInvCTM == normalMapFP.fInvCTM; + } + + SkMatrix fInvCTM; + + typedef GrFragmentProcessor INHERITED; +}; + +std::unique_ptr<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor( + const GrFPArgs& args) const { + std::unique_ptr<GrFragmentProcessor> mapFP = as_SB(fMapShader)->asFragmentProcessor(args); + if (!mapFP) { + return nullptr; + } + + return NormalMapFP::Make(std::move(mapFP), fInvCTM); +} + +#endif // SK_SUPPORT_GPU + +//////////////////////////////////////////////////////////////////////////// + +SkNormalMapSourceImpl::Provider::Provider(const SkNormalMapSourceImpl& source, + SkShaderBase::Context* mapContext) + : fSource(source) + , fMapContext(mapContext) {} + +SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShaderBase::ContextRec &rec, + SkArenaAlloc* alloc) const { + SkMatrix normTotalInv; + if (!this->computeNormTotalInverse(rec, &normTotalInv)) { + return nullptr; + } + + // Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd + SkPaint overridePaint {*(rec.fPaint)}; + overridePaint.setAlpha(0xFF); + SkShaderBase::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix, + rec.fPreferredDstType, rec.fDstColorSpace); + + auto* context = as_SB(fMapShader)->makeContext(overrideRec, alloc); + if (!context) { + return nullptr; + } + + return alloc->make<Provider>(*this, context); +} + +bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShaderBase::ContextRec& rec, + SkMatrix* normTotalInverse) const { + SkMatrix total = SkMatrix::Concat(*rec.fMatrix, fMapShader->getLocalMatrix()); + if (rec.fLocalMatrix) { + total.preConcat(*rec.fLocalMatrix); + } + + return total.invert(normTotalInverse); +} + +#define BUFFER_MAX 16 +void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[], + int count) const { + SkPMColor tmpNormalColors[BUFFER_MAX]; + + do { + int n = SkTMin(count, BUFFER_MAX); + + fMapContext->shadeSpan(x, y, tmpNormalColors, n); + + for (int i = 0; i < n; i++) { + SkPoint3 tempNorm; + + tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f, + SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f); + + tempNorm.normalize(); + + + if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) { + SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY); + + // Normalizing the transformed X and Y, while keeping constant both Z and the + // vector's angle in the XY plane. This maintains the "slope" for the surface while + // appropriately rotating the normal for any anisotropic scaling that occurs. + // Here, we call scaling factor the number that must divide the transformed X and Y + // so that the normal's length remains equal to 1. + SkScalar scalingFactorSquared = + (SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY)) + / (1.0f - SkScalarSquare(tempNorm.fZ)); + SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared)); + + output[i].fX = transformed.fX * invScalingFactor; + output[i].fY = transformed.fY * invScalingFactor; + output[i].fZ = tempNorm.fZ; + } else { + output[i] = {0.0f, 0.0f, tempNorm.fZ}; + output[i].normalize(); + } + + SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f)); + } + + output += n; + x += n; + count -= n; + } while (count > 0); +} + +//////////////////////////////////////////////////////////////////////////////// + +sk_sp<SkFlattenable> SkNormalMapSourceImpl::CreateProc(SkReadBuffer& buf) { + + sk_sp<SkShader> mapShader = buf.readFlattenable<SkShaderBase>(); + + SkMatrix invCTM; + buf.readMatrix(&invCTM); + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(mapShader), invCTM); +} + +void SkNormalMapSourceImpl::flatten(SkWriteBuffer& buf) const { + this->INHERITED::flatten(buf); + + buf.writeFlattenable(fMapShader.get()); + buf.writeMatrix(fInvCTM); +} + +//////////////////////////////////////////////////////////////////////////// + +sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm) { + SkMatrix invCTM; + + if (!ctm.invert(&invCTM) || !map) { + return nullptr; + } + + return sk_make_sp<SkNormalMapSourceImpl>(std::move(map), invCTM); +} diff --git a/src/core/SkNormalMapSource.h b/src/core/SkNormalMapSource.h new file mode 100644 index 0000000000..dfedfd35d5 --- /dev/null +++ b/src/core/SkNormalMapSource.h @@ -0,0 +1,57 @@ +/* + * 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 SkNormalMapSource_DEFINED +#define SkNormalMapSource_DEFINED + +#include "SkNormalSource.h" + +class SkNormalMapSourceImpl : public SkNormalSource { +public: + SkNormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkMatrix& invCTM) + : fMapShader(std::move(mapShader)) + , fInvCTM(invCTM) {} + +#if SK_SUPPORT_GPU + std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override; +#endif + + SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec, + SkArenaAlloc* alloc) const override; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalMapSourceImpl) + +protected: + void flatten(SkWriteBuffer& buf) const override; + + bool computeNormTotalInverse(const SkShaderBase::ContextRec& rec, + SkMatrix* normTotalInverse) const; + +private: + class Provider : public SkNormalSource::Provider { + public: + Provider(const SkNormalMapSourceImpl& source, SkShaderBase::Context* mapContext); + + void fillScanLine(int x, int y, SkPoint3 output[], int count) const override; + + private: + const SkNormalMapSourceImpl& fSource; + SkShaderBase::Context* fMapContext; + + typedef SkNormalSource::Provider INHERITED; + }; + + sk_sp<SkShader> fMapShader; + SkMatrix fInvCTM; // Inverse of the canvas total matrix, used for rotating normals. + + friend class SkNormalSource; + + typedef SkNormalSource INHERITED; +}; + +#endif + diff --git a/src/core/SkNormalSource.cpp b/src/core/SkNormalSource.cpp new file mode 100644 index 0000000000..ad1f5a3e40 --- /dev/null +++ b/src/core/SkNormalSource.cpp @@ -0,0 +1,22 @@ +/* + * 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 "SkNormalFlatSource.h" +#include "SkNormalMapSource.h" +#include "SkNormalSource.h" + +// Generating vtable +SkNormalSource::~SkNormalSource() {} + +//////////////////////////////////////////////////////////////////////////// + +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalMapSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalFlatSourceImpl) +SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END + +//////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h new file mode 100644 index 0000000000..7339890bef --- /dev/null +++ b/src/core/SkNormalSource.h @@ -0,0 +1,75 @@ +/* + * 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 SkNormalSource_DEFINED +#define SkNormalSource_DEFINED + +#include "SkFlattenable.h" +#include "SkShaderBase.h" + +class SkMatrix; +struct SkPoint3; + +#if SK_SUPPORT_GPU +class GrFragmentProcessor; +#endif + +/** Abstract class that generates or reads in normals for use by SkLightingShader. +*/ +class SK_API SkNormalSource : public SkFlattenable { +public: + virtual ~SkNormalSource() override; + +#if SK_SUPPORT_GPU + /** Returns a fragment processor that takes no input and outputs a normal (already rotated) + as its output color. To be used as a child fragment processor. + */ + virtual std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const = 0; +#endif + + class Provider { + public: + virtual ~Provider() {} + + /** Called for each span of the object being drawn on the CPU. Your subclass should set + the appropriate normals that correspond to the specified device coordinates. + */ + virtual void fillScanLine(int x, int y, SkPoint3 output[], int count) const = 0; + }; + + /** Returns an instance of 'Provider' that provides normals for the CPU pipeline. The + necessary data will be initialized in place at 'storage'. + */ + virtual Provider* asProvider(const SkShaderBase::ContextRec&, SkArenaAlloc*) const = 0; + + /** Returns a normal source that provides normals sourced from the the normal map argument. + + @param map a shader that outputs the normal map + @param ctm the current canvas' total matrix, used to rotate normals when necessary. + + nullptr will be returned if 'map' is null + + The normal map is currently assumed to be an 8888 image where the normal at a texel + is retrieved by: + N.x = R-127; + N.y = G-127; + N.z = B-127; + N.normalize(); + The +Z axis is thus encoded in RGB as (127, 127, 255) while the -Z axis is + (127, 127, 0). + */ + static sk_sp<SkNormalSource> MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm); + + /** Returns a normal source that provides straight-up normals only <0, 0, 1>. + */ + static sk_sp<SkNormalSource> MakeFlat(); + + SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource) + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h index 696ed0a96d..038ce16717 100644 --- a/src/gpu/GrProcessor.h +++ b/src/gpu/GrProcessor.h @@ -153,6 +153,9 @@ public: kSwizzleFragmentProcessor_ClassID, kTestFP_ClassID, kTextureGeometryProcessor_ClassID, + kFlatNormalsFP_ClassID, + kMappedNormalsFP_ClassID, + kLightingFP_ClassID, }; virtual ~GrProcessor() = default; diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 22aabc2314..ecb9ccb1ca 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -15,7 +15,9 @@ #include "SkGradientShader.h" #include "SkHighContrastFilter.h" #include "SkLayerDrawLooper.h" +#include "SkLightingShader.h" #include "SkLumaColorFilter.h" +#include "SkNormalSource.h" #include "SkOverdrawColorFilter.h" #include "SkPerlinNoiseShader.h" #include "SkShaderMaskFilter.h" @@ -51,6 +53,8 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // Shader SkPerlinNoiseShader::InitializeFlattenables(); SkGradientShader::InitializeFlattenables(); + SkLightingShader::InitializeFlattenables(); + SkNormalSource::InitializeFlattenables(); // PathEffect SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect) 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 + +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/shaders/SkLightingShader.h b/src/shaders/SkLightingShader.h new file mode 100644 index 0000000000..62c881ccf6 --- /dev/null +++ b/src/shaders/SkLightingShader.h @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkLightingShader_DEFINED +#define SkLightingShader_DEFINED + +#include "SkFlattenablePriv.h" +#include "SkLights.h" +#include "SkShader.h" + +class SkBitmap; +class SkMatrix; +class SkNormalSource; + +class SK_API SkLightingShader { +public: + /** Returns a shader that lights the shape, colored by the diffuseShader, using the + normals from normalSource, with the set of lights provided. + + @param diffuseShader the shader that provides the colors. If nullptr, uses the paint's + color. + @param normalSource the source for the shape's normals. If nullptr, assumes straight + up normals (<0,0,1>). + @param lights the lights applied to the normals + + The lighting equation is currently: + result = (LightColor * dot(Normal, LightDir) + AmbientColor) * DiffuseColor + + */ + static sk_sp<SkShader> Make(sk_sp<SkShader> diffuseShader, sk_sp<SkNormalSource> normalSource, + sk_sp<SkLights> lights); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() +}; + +#endif |