diff options
author | 2015-08-19 10:35:14 -0700 | |
---|---|---|
committer | 2015-08-19 10:35:14 -0700 | |
commit | 45b59ed6e4e231814dbdb9f707b3d2a7ee50de84 (patch) | |
tree | 3adbdd8f81dd894932b61af82c45cdaadfbdceed /src | |
parent | 95b96d649547c6b89ae0eca0f88f965d90c531a5 (diff) |
Update SkLightingShader to support rotation
This also:
makes the SkLightingShader handle normal maps where the rects aren't aligned between the diffuse and normal maps.
adds a light aggregating class (Lights) to SkLightingShader (along with a Builder nested class).
Split out of https://codereview.chromium.org/1261433009/ (Add SkCanvas::drawLitAtlas call)
Review URL: https://codereview.chromium.org/1291783003
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkLight.h | 56 | ||||
-rw-r--r-- | src/core/SkLightingShader.cpp (renamed from src/effects/SkLightingShader.cpp) | 424 | ||||
-rw-r--r-- | src/core/SkLightingShader.h (renamed from src/effects/SkLightingShader.h) | 56 | ||||
-rw-r--r-- | src/core/SkPoint3.cpp (renamed from src/effects/SkPoint3.cpp) | 0 | ||||
-rw-r--r-- | src/core/SkPoint3.h | 124 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_chromium.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 2 |
7 files changed, 518 insertions, 146 deletions
diff --git a/src/core/SkLight.h b/src/core/SkLight.h new file mode 100644 index 0000000000..4a6e149c76 --- /dev/null +++ b/src/core/SkLight.h @@ -0,0 +1,56 @@ + +/* + * 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 SkLight_DEFINED +#define SkLight_DEFINED + +#include "SkPoint3.h" + +class SK_API SkLight { +public: + enum LightType { + kAmbient_LightType, // only 'fColor' is used + kDirectional_LightType + }; + + SkLight() : fType(kAmbient_LightType) { + fColor.set(0.0f, 0.0f, 0.0f); + fDirection.set(0.0f, 0.0f, 1.0f); + } + + SkLight(const SkColor3f& color) + : fType(kAmbient_LightType) + , fColor(color) { + fDirection.set(0.0f, 0.0f, 1.0f); + } + + SkLight(const SkColor3f& color, const SkVector3& dir) + : fType(kDirectional_LightType) + , fColor(color) + , fDirection(dir) { + if (!fDirection.normalize()) { + fDirection.set(0.0f, 0.0f, 1.0f); + } + } + + LightType type() const { return fType; } + const SkColor3f& color() const { return fColor; } + const SkVector3& dir() const { + SkASSERT(kAmbient_LightType != fType); + return fDirection; + } + +private: + LightType fType; + SkColor3f fColor; // linear (unpremul) color. Range is 0..1 in each channel. + SkVector3 fDirection; // direction towards the light (+Z is out of the screen). + // If degenerate, it will be replaced with (0, 0, 1). +}; + + +#endif diff --git a/src/effects/SkLightingShader.cpp b/src/core/SkLightingShader.cpp index d441d9ba9f..ce5c0ddb25 100644 --- a/src/effects/SkLightingShader.cpp +++ b/src/core/SkLightingShader.cpp @@ -12,6 +12,7 @@ #include "SkErrorInternals.h" #include "SkLightingShader.h" #include "SkMathPriv.h" +#include "SkPoint3.h" #include "SkReadBuffer.h" #include "SkWriteBuffer.h" @@ -40,24 +41,33 @@ class SK_API SkLightingShaderImpl : public SkShader { public: - /** Create a new lighting shader that use the provided normal map, light - and ambient color to light the diffuse bitmap. - @param diffuse the diffuse bitmap - @param normal the normal map - @param light the light applied to the normal map - @param ambient the linear (unpremul) ambient light color + /** Create a new lighting shader that uses the provided normal map and + lights to light the diffuse bitmap. + @param diffuse the diffuse bitmap + @param normal the normal map + @param lights the lights applied to the normal map + @param invNormRotation rotation applied to the normal map's normals + @param diffLocalM the local matrix for the diffuse coordinates + @param normLocalM the local matrix for the normal coordinates */ SkLightingShaderImpl(const SkBitmap& diffuse, const SkBitmap& normal, - const SkLightingShader::Light& light, - const SkColor3f& ambient, const SkMatrix* localMatrix) - : INHERITED(localMatrix) + const SkLightingShader::Lights* lights, + const SkVector& invNormRotation, + const SkMatrix* diffLocalM, const SkMatrix* normLocalM) + : INHERITED(diffLocalM) , fDiffuseMap(diffuse) , fNormalMap(normal) - , fLight(light) - , fAmbientColor(ambient) { - if (!fLight.fDirection.normalize()) { - fLight.fDirection = SkPoint3::Make(0.0f, 0.0f, 1.0f); + , fLights(SkRef(lights)) + , fInvNormRotation(invNormRotation) { + + if (normLocalM) { + fNormLocalMatrix = *normLocalM; + } else { + fNormLocalMatrix.reset(); } + // Pre-cache so future calls to fNormLocalMatrix.getType() are threadsafe. + (void)fNormLocalMatrix.getType(); + } bool isOpaque() const override; @@ -94,12 +104,16 @@ public: protected: void flatten(SkWriteBuffer&) const override; Context* onCreateContext(const ContextRec&, void*) const override; + bool computeNormTotalInverse(const ContextRec& rec, SkMatrix* normTotalInverse) const; private: - SkBitmap fDiffuseMap; - SkBitmap fNormalMap; - SkLightingShader::Light fLight; - SkColor3f fAmbientColor; // linear (unpremul) color. Range is 0..1/channel. + SkBitmap fDiffuseMap; + SkBitmap fNormalMap; + + SkAutoTUnref<const SkLightingShader::Lights> fLights; + + SkMatrix fNormLocalMatrix; + SkVector fInvNormRotation; friend class SkLightingShader; @@ -119,19 +133,32 @@ private: class LightingFP : public GrFragmentProcessor { public: - LightingFP(GrTexture* diffuse, GrTexture* normal, const SkMatrix& matrix, - const SkVector3& lightDir, const SkColor3f& lightColor, - const SkColor3f& ambientColor) - : fDeviceTransform(kDevice_GrCoordSet, matrix) - , fDiffuseTextureAccess(diffuse) - , fNormalTextureAccess(normal) - , fLightDir(lightDir) - , fLightColor(lightColor) - , fAmbientColor(ambientColor) { - this->addCoordTransform(&fDeviceTransform); + LightingFP(GrProcessorDataManager* pdm, GrTexture* diffuse, GrTexture* normal, + const SkMatrix& diffMatrix, const SkMatrix& normMatrix, + const GrTextureParams& diffParams, const GrTextureParams& normParams, + const SkLightingShader::Lights* lights, const SkVector& invNormRotation) + : fDiffDeviceTransform(kLocal_GrCoordSet, diffMatrix, diffuse, diffParams.filterMode()) + , fNormDeviceTransform(kLocal_GrCoordSet, normMatrix, normal, normParams.filterMode()) + , fDiffuseTextureAccess(diffuse, diffParams) + , fNormalTextureAccess(normal, normParams) + , fInvNormRotation(invNormRotation) { + this->addCoordTransform(&fDiffDeviceTransform); + this->addCoordTransform(&fNormDeviceTransform); this->addTextureAccess(&fDiffuseTextureAccess); this->addTextureAccess(&fNormalTextureAccess); + // fuse all ambient lights into a single one + fAmbientColor.set(0.0f, 0.0f, 0.0f); + for (int i = 0; i < lights->numLights(); ++i) { + if (SkLight::kAmbient_LightType == lights->light(i).type()) { + fAmbientColor += lights->light(i).color(); + } else { + // TODO: handle more than one of these + fLightColor = lights->light(i).color(); + fLightDir = lights->light(i).dir(); + } + } + this->initClassID<LightingFP>(); } @@ -141,6 +168,7 @@ public: fLightDir.fX = 10000.0f; fLightColor.fX = 0.0f; fAmbientColor.fX = 0.0f; + fInvNormRotation.fX = 0.0f; } void emitCode(EmitArgs& args) override { @@ -163,6 +191,11 @@ public: kVec3f_GrSLType, kDefault_GrSLPrecision, "AmbientColor", &ambientColorUniName); + const char* xformUniName = NULL; + fXformUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, + kVec2f_GrSLType, kDefault_GrSLPrecision, + "Xform", &xformUniName); + fpb->codeAppend("vec4 diffuseColor = "); fpb->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], args.fCoords[0].c_str(), @@ -171,13 +204,20 @@ public: fpb->codeAppend("vec4 normalColor = "); fpb->appendTextureLookup(args.fSamplers[1], - args.fCoords[0].c_str(), - args.fCoords[0].getType()); + args.fCoords[1].c_str(), + args.fCoords[1].getType()); fpb->codeAppend(";"); - fpb->codeAppend("vec3 normal = normalize(normalColor.rgb - vec3(0.5));"); - fpb->codeAppendf("vec3 lightDir = normalize(%s);", lightDirUniName); - fpb->codeAppend("float NdotL = dot(normal, lightDir);"); + fpb->codeAppend("vec3 normal = normalColor.rgb - vec3(0.5);"); + + fpb->codeAppendf("mat3 m = mat3(%s.x, -%s.y, 0.0, %s.y, %s.x, 0.0, 0.0, 0.0, 1.0);", + xformUniName, xformUniName, xformUniName, xformUniName); + + // TODO: inverse map the light direction vectors in the vertex shader rather than + // transforming all the normals here! + fpb->codeAppend("normal = normalize(m*normal);"); + + fpb->codeAppendf("float NdotL = clamp(dot(normal, %s), 0.0, 1.0);", lightDirUniName); // diffuse light fpb->codeAppendf("vec3 result = %s*diffuseColor.rgb*NdotL;", lightColorUniName); // ambient light @@ -213,6 +253,12 @@ public: pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX); fAmbientColor = ambientColor; } + + const SkVector& invNormRotation = lightingFP.invNormRotation(); + if (invNormRotation != fInvNormRotation) { + pdman.set2fv(fXformUni, 1, &invNormRotation.fX); + fInvNormRotation = invNormRotation; + } } private: @@ -224,6 +270,9 @@ public: SkColor3f fAmbientColor; GrGLProgramDataManager::UniformHandle fAmbientColorUni; + + SkVector fInvNormRotation; + GrGLProgramDataManager::UniformHandle fXformUni; }; void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { @@ -239,106 +288,116 @@ public: const SkVector3& lightDir() const { return fLightDir; } const SkColor3f& lightColor() const { return fLightColor; } const SkColor3f& ambientColor() const { return fAmbientColor; } + const SkVector& invNormRotation() const { return fInvNormRotation; } private: GrGLFragmentProcessor* onCreateGLInstance() const override { return SkNEW(LightingGLFP); } bool onIsEqual(const GrFragmentProcessor& proc) const override { const LightingFP& lightingFP = proc.cast<LightingFP>(); - return fDeviceTransform == lightingFP.fDeviceTransform && + return fDiffDeviceTransform == lightingFP.fDiffDeviceTransform && + fNormDeviceTransform == lightingFP.fNormDeviceTransform && fDiffuseTextureAccess == lightingFP.fDiffuseTextureAccess && fNormalTextureAccess == lightingFP.fNormalTextureAccess && fLightDir == lightingFP.fLightDir && fLightColor == lightingFP.fLightColor && - fAmbientColor == lightingFP.fAmbientColor; + fAmbientColor == lightingFP.fAmbientColor && + fInvNormRotation == lightingFP.fInvNormRotation; } - GrCoordTransform fDeviceTransform; + GrCoordTransform fDiffDeviceTransform; + GrCoordTransform fNormDeviceTransform; GrTextureAccess fDiffuseTextureAccess; GrTextureAccess fNormalTextureAccess; SkVector3 fLightDir; SkColor3f fLightColor; SkColor3f fAmbientColor; + + SkVector fInvNormRotation; }; //////////////////////////////////////////////////////////////////////////// -bool SkLightingShaderImpl::asFragmentProcessor(GrContext* context, const SkPaint& paint, - const SkMatrix& viewM, const SkMatrix* localMatrix, - GrColor* color, GrProcessorDataManager*, - GrFragmentProcessor** fp) const { - // we assume diffuse and normal maps have same width and height - // TODO: support different sizes - SkASSERT(fDiffuseMap.width() == fNormalMap.width() && - fDiffuseMap.height() == fNormalMap.height()); - SkMatrix matrix; - matrix.setIDiv(fDiffuseMap.width(), fDiffuseMap.height()); +static bool make_mat(const SkBitmap& bm, + const SkMatrix& localMatrix1, + const SkMatrix* localMatrix2, + SkMatrix* result) { + + result->setIDiv(bm.width(), bm.height()); SkMatrix lmInverse; - if (!this->getLocalMatrix().invert(&lmInverse)) { + if (!localMatrix1.invert(&lmInverse)) { return false; } - if (localMatrix) { + if (localMatrix2) { SkMatrix inv; - if (!localMatrix->invert(&inv)) { + if (!localMatrix2->invert(&inv)) { return false; } lmInverse.postConcat(inv); } - matrix.preConcat(lmInverse); - - // Must set wrap and filter on the sampler before requesting a texture. In two places below - // we check the matrix scale factors to determine how to interpret the filter quality setting. - // This completely ignores the complexity of the drawVertices case where explicit local coords - // are provided by the caller. - GrTextureParams::FilterMode textureFilterMode = GrTextureParams::kBilerp_FilterMode; - switch (paint.getFilterQuality()) { - case kNone_SkFilterQuality: - textureFilterMode = GrTextureParams::kNone_FilterMode; - break; - case kLow_SkFilterQuality: - textureFilterMode = GrTextureParams::kBilerp_FilterMode; - break; - case kMedium_SkFilterQuality:{ - SkMatrix matrix; - matrix.setConcat(viewM, this->getLocalMatrix()); - if (matrix.getMinScale() < SK_Scalar1) { - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - } else { - // Don't trigger MIP level generation unnecessarily. - textureFilterMode = GrTextureParams::kBilerp_FilterMode; - } - break; + result->preConcat(lmInverse); + + return true; +} + +bool SkLightingShaderImpl::asFragmentProcessor(GrContext* context, const SkPaint& paint, + const SkMatrix& viewM, const SkMatrix* localMatrix, + GrColor* color, GrProcessorDataManager* pdm, + GrFragmentProcessor** fp) const { + // we assume diffuse and normal maps have same width and height + // TODO: support different sizes + SkASSERT(fDiffuseMap.width() == fNormalMap.width() && + fDiffuseMap.height() == fNormalMap.height()); + SkMatrix diffM, normM; + + if (!make_mat(fDiffuseMap, this->getLocalMatrix(), localMatrix, &diffM)) { + return false; } - case kHigh_SkFilterQuality: - default: - SkErrorInternals::SetError(kInvalidPaint_SkError, - "Sorry, I don't understand the filtering " - "mode you asked for. Falling back to " - "MIPMaps."); - textureFilterMode = GrTextureParams::kMipMap_FilterMode; - break; + if (!make_mat(fNormalMap, fNormLocalMatrix, localMatrix, &normM)) { + return false; } + bool doBicubic; + GrTextureParams::FilterMode diffFilterMode = GrSkFilterQualityToGrFilterMode( + SkTMin(paint.getFilterQuality(), kMedium_SkFilterQuality), + viewM, + this->getLocalMatrix(), + &doBicubic); + SkASSERT(!doBicubic); + + GrTextureParams::FilterMode normFilterMode = GrSkFilterQualityToGrFilterMode( + SkTMin(paint.getFilterQuality(), kMedium_SkFilterQuality), + viewM, + fNormLocalMatrix, + &doBicubic); + SkASSERT(!doBicubic); + // TODO: support other tile modes - GrTextureParams params(kClamp_TileMode, textureFilterMode); - SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, fDiffuseMap, ¶ms)); + GrTextureParams diffParams(kClamp_TileMode, diffFilterMode); + SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, + fDiffuseMap, &diffParams)); if (!diffuseTexture) { SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } - SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, fNormalMap, ¶ms)); + GrTextureParams normParams(kClamp_TileMode, normFilterMode); + SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, + fNormalMap, &normParams)); if (!normalTexture) { SkErrorInternals::SetError(kInternalError_SkError, "Couldn't convert bitmap to texture."); return false; } - *fp = SkNEW_ARGS(LightingFP, (diffuseTexture, normalTexture, matrix, - fLight.fDirection, fLight.fColor, fAmbientColor)); + + *fp = SkNEW_ARGS(LightingFP, (pdm, diffuseTexture, normalTexture, + diffM, normM, diffParams, normParams, fLights, + fInvNormRotation)); + *color = GrColorPackA4(paint.getAlpha()); return true; } @@ -391,27 +450,38 @@ SkLightingShaderImpl::LightingShaderContext::~LightingShaderContext() { fNormalState->~SkBitmapProcState(); } -static inline int light(SkScalar light, int diff, SkScalar NdotL, SkScalar ambient) { - SkScalar color = light * diff * NdotL + 255 * ambient; - if (color <= 0.0f) { - return 0; - } else if (color >= 255.0f) { - return 255; - } else { - return (int) color; - } +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 could here costs 16 bytes) +// take up too much stack-space (each one here costs 16 bytes) #define TMP_COUNT 16 void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader); - SkPMColor tmpColor[TMP_COUNT], tmpColor2[TMP_COUNT]; - SkPMColor tmpNormal[TMP_COUNT], tmpNormal2[TMP_COUNT]; + uint32_t tmpColor[TMP_COUNT], tmpNormal[TMP_COUNT]; + SkPMColor tmpColor2[2*TMP_COUNT], tmpNormal2[2*TMP_COUNT]; SkBitmapProcState::MatrixProc diffMProc = fDiffuseState->getMatrixProc(); SkBitmapProcState::SampleProc32 diffSProc = fDiffuseState->getSampleProc32(); @@ -419,17 +489,19 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, SkBitmapProcState::MatrixProc normalMProc = fNormalState->getMatrixProc(); SkBitmapProcState::SampleProc32 normalSProc = fNormalState->getSampleProc32(); + int diffMax = fDiffuseState->maxCountForBufferSize(sizeof(tmpColor[0]) * TMP_COUNT); + int normMax = fNormalState->maxCountForBufferSize(sizeof(tmpNormal[0]) * TMP_COUNT); + int max = SkTMin(diffMax, normMax); + SkASSERT(fDiffuseState->fPixmap.addr()); SkASSERT(fNormalState->fPixmap.addr()); - SkPoint3 norm; - SkScalar NdotL; - int r, g, b; + SkPoint3 norm, xformedNorm; do { int n = count; - if (n > TMP_COUNT) { - n = TMP_COUNT; + if (n > max) { + n = max; } diffMProc(*fDiffuseState, tmpColor, n, x, y); @@ -445,18 +517,34 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, SkIntToScalar(SkGetPackedB32(tmpNormal2[i]))-127.0f); norm.normalize(); + xformedNorm.fX = lightShader.fInvNormRotation.fX * norm.fX + + lightShader.fInvNormRotation.fY * norm.fY; + xformedNorm.fY = lightShader.fInvNormRotation.fX * norm.fX - + lightShader.fInvNormRotation.fY * norm.fY; + xformedNorm.fZ = norm.fZ; + SkColor diffColor = SkUnPreMultiply::PMColorToColor(tmpColor2[i]); - NdotL = norm.dot(lightShader.fLight.fDirection); - // This is all done in linear unpremul color space - r = light(lightShader.fLight.fColor.fX, SkColorGetR(diffColor), NdotL, - lightShader.fAmbientColor.fX); - g = light(lightShader.fLight.fColor.fY, SkColorGetG(diffColor), NdotL, - lightShader.fAmbientColor.fY); - b = light(lightShader.fLight.fColor.fZ, SkColorGetB(diffColor), NdotL, - lightShader.fAmbientColor.fZ); + 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 SkLight& light = lightShader.fLights->light(l); + + if (SkLight::kAmbient_LightType == light.type()) { + accum += light.color().makeScale(255.0f); + } else { + SkScalar NdotL = xformedNorm.dot(light.dir()); + if (NdotL < 0.0f) { + NdotL = 0.0f; + } + + accum.fX += light.color().fX * SkColorGetR(diffColor) * NdotL; + accum.fY += light.color().fY * SkColorGetG(diffColor) * NdotL; + accum.fZ += light.color().fZ * SkColorGetB(diffColor) * NdotL; + } + } - result[i] = SkPreMultiplyARGB(SkColorGetA(diffColor), r, g, b); + result[i] = convert(accum, SkColorGetA(diffColor)); } result += n; @@ -474,8 +562,21 @@ void SkLightingShaderImpl::toString(SkString* str) const { #endif SkFlattenable* SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { - SkMatrix localMatrix; - buf.readMatrix(&localMatrix); + SkMatrix diffLocalM; + bool hasDiffLocalM = buf.readBool(); + if (hasDiffLocalM) { + buf.readMatrix(&diffLocalM); + } else { + diffLocalM.reset(); + } + + SkMatrix normLocalM; + bool hasNormLocalM = buf.readBool(); + if (hasNormLocalM) { + buf.readMatrix(&normLocalM); + } else { + normLocalM.reset(); + } SkBitmap diffuse; if (!buf.readBitmap(&diffuse)) { @@ -489,38 +590,84 @@ SkFlattenable* SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { } normal.setImmutable(); - SkLightingShader::Light light; - if (!buf.readScalarArray(&light.fDirection.fX, 3)) { - return NULL; - } - if (!buf.readScalarArray(&light.fColor.fX, 3)) { - return NULL; - } + int numLights = buf.readInt(); - SkColor3f ambient; - if (!buf.readScalarArray(&ambient.fX, 3)) { - return NULL; + SkLightingShader::Lights::Builder builder; + + for (int l = 0; l < numLights; ++l) { + bool isAmbient = buf.readBool(); + + SkColor3f color; + if (!buf.readScalarArray(&color.fX, 3)) { + return NULL; + } + + if (isAmbient) { + builder.add(SkLight(color)); + } else { + SkVector3 dir; + if (!buf.readScalarArray(&dir.fX, 3)) { + return NULL; + } + builder.add(SkLight(color, dir)); + } } - return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, light, ambient, &localMatrix)); + SkAutoTUnref<const SkLightingShader::Lights> lights(builder.finish()); + + return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, lights, + SkVector::Make(1.0f, 0.0f), + &diffLocalM, &normLocalM)); } void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { - buf.writeMatrix(this->getLocalMatrix()); + this->INHERITED::flatten(buf); + + bool hasNormLocalM = !fNormLocalMatrix.isIdentity(); + buf.writeBool(hasNormLocalM); + if (hasNormLocalM) { + buf.writeMatrix(fNormLocalMatrix); + } buf.writeBitmap(fDiffuseMap); buf.writeBitmap(fNormalMap); - buf.writeScalarArray(&fLight.fDirection.fX, 3); - buf.writeScalarArray(&fLight.fColor.fX, 3); - buf.writeScalarArray(&fAmbientColor.fX, 3); + + buf.writeInt(fLights->numLights()); + for (int l = 0; l < fLights->numLights(); ++l) { + const SkLight& light = fLights->light(l); + + bool isAmbient = SkLight::kAmbient_LightType == light.type(); + + buf.writeBool(isAmbient); + buf.writeScalarArray(&light.color().fX, 3); + if (!isAmbient) { + buf.writeScalarArray(&light.dir().fX, 3); + } + } +} + +bool SkLightingShaderImpl::computeNormTotalInverse(const ContextRec& rec, + SkMatrix* normTotalInverse) const { + SkMatrix total; + total.setConcat(*rec.fMatrix, fNormLocalMatrix); + + const SkMatrix* m = &total; + if (rec.fLocalMatrix) { + total.setConcat(*m, *rec.fLocalMatrix); + m = &total; + } + return m->invert(normTotalInverse); } SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, void* storage) const { - SkMatrix totalInverse; - // Do this first, so we know the matrix can be inverted. - if (!this->computeTotalInverse(rec, &totalInverse)) { + SkMatrix diffTotalInv; + // computeTotalInverse was called in SkShader::createContext so we know it will succeed + SkAssertResult(this->computeTotalInverse(rec, &diffTotalInv)); + + SkMatrix normTotalInv; + if (!this->computeNormTotalInverse(rec, &normTotalInv)) { return NULL; } @@ -531,7 +678,7 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, diffuseState->fTileModeX = SkShader::kClamp_TileMode; diffuseState->fTileModeY = SkShader::kClamp_TileMode; diffuseState->fOrigBitmap = fDiffuseMap; - if (!diffuseState->chooseProcs(totalInverse, *rec.fPaint)) { + if (!diffuseState->chooseProcs(diffTotalInv, *rec.fPaint)) { diffuseState->~SkBitmapProcState(); return NULL; } @@ -543,7 +690,7 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, normalState->fTileModeX = SkShader::kClamp_TileMode; normalState->fTileModeY = SkShader::kClamp_TileMode; normalState->fOrigBitmap = fNormalMap; - if (!normalState->chooseProcs(totalInverse, *rec.fPaint)) { + if (!normalState->chooseProcs(normTotalInv, *rec.fPaint)) { diffuseState->~SkBitmapProcState(); normalState->~SkBitmapProcState(); return NULL; @@ -566,9 +713,9 @@ static bool bitmap_is_too_big(const SkBitmap& bm) { } SkShader* SkLightingShader::Create(const SkBitmap& diffuse, const SkBitmap& normal, - const SkLightingShader::Light& light, - const SkColor3f& ambient, - const SkMatrix* localMatrix) { + const Lights* lights, + const SkVector& invNormRotation, + const SkMatrix* diffLocalM, const SkMatrix* normLocalM) { if (diffuse.isNull() || bitmap_is_too_big(diffuse) || normal.isNull() || bitmap_is_too_big(normal) || diffuse.width() != normal.width() || @@ -576,7 +723,10 @@ SkShader* SkLightingShader::Create(const SkBitmap& diffuse, const SkBitmap& norm return nullptr; } - return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, light, ambient, localMatrix)); + SkASSERT(SkScalarNearlyEqual(invNormRotation.lengthSqd(), SK_Scalar1)); + + return SkNEW_ARGS(SkLightingShaderImpl, (diffuse, normal, lights, + invNormRotation, diffLocalM, normLocalM)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkLightingShader.h b/src/core/SkLightingShader.h index 64d41a291e..499e358238 100644 --- a/src/effects/SkLightingShader.h +++ b/src/core/SkLightingShader.h @@ -10,15 +10,57 @@ #ifndef SkLightingShader_DEFINED #define SkLightingShader_DEFINED -#include "SkPoint3.h" +#include "SkFlattenable.h" +#include "SkLight.h" #include "SkShader.h" +#include "SkTDArray.h" + +class SkBitmap; +class SkMatrix; class SK_API SkLightingShader { public: - struct Light { - SkVector3 fDirection; // direction towards the light (+Z is out of the screen). - // If degenerate, it will be replaced with (0, 0, 1). - SkColor3f fColor; // linear (unpremul) color. Range is 0..1 in each channel. + class Lights : public SkRefCnt { + public: + class Builder { + public: + Builder(const SkLight lights[], int numLights) + : fLights(SkNEW_ARGS(Lights, (lights, numLights))) { + } + + Builder() : fLights(SkNEW(Lights)) { } + + // TODO: limit the number of lights here or just ignore those + // above some maximum? + void add(const SkLight& light) { + if (fLights) { + *fLights->fLights.push() = light; + } + } + + const Lights* finish() { + return fLights.detach(); + } + + private: + SkAutoTUnref<Lights> fLights; + }; + + int numLights() const { + return fLights.count(); + } + + const SkLight& light(int index) const { + return fLights[index]; + } + + private: + Lights() {} + Lights(const SkLight lights[], int numLights) : fLights(lights, numLights) {} + + SkTDArray<SkLight> fLights; + + typedef SkRefCnt INHERITED; }; /** Returns a shader that lights the diffuse and normal maps with a single light. @@ -50,8 +92,8 @@ public: (127, 127, 0). */ static SkShader* Create(const SkBitmap& diffuse, const SkBitmap& normal, - const SkLightingShader::Light& light, const SkColor3f& ambient, - const SkMatrix* localMatrix); + const Lights* lights, const SkVector& invNormRotation, + const SkMatrix* diffLocalMatrix, const SkMatrix* normLocalMatrix); SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/src/effects/SkPoint3.cpp b/src/core/SkPoint3.cpp index 3b5586b067..3b5586b067 100644 --- a/src/effects/SkPoint3.cpp +++ b/src/core/SkPoint3.cpp diff --git a/src/core/SkPoint3.h b/src/core/SkPoint3.h new file mode 100644 index 0000000000..af24a8df58 --- /dev/null +++ b/src/core/SkPoint3.h @@ -0,0 +1,124 @@ +/* + * 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 SkPoint3_DEFINED +#define SkPoint3_DEFINED + +#include "SkScalar.h" + +struct SK_API SkPoint3 { + SkScalar fX, fY, fZ; + + static SkPoint3 Make(SkScalar x, SkScalar y, SkScalar z) { + SkPoint3 pt; + pt.set(x, y, z); + return pt; + } + + SkScalar x() const { return fX; } + SkScalar y() const { return fY; } + SkScalar z() const { return fZ; } + + void set(SkScalar x, SkScalar y, SkScalar z) { fX = x; fY = y; fZ = z; } + + friend bool operator==(const SkPoint3& a, const SkPoint3& b) { + return a.fX == b.fX && a.fY == b.fY && a.fZ == b.fZ; + } + + friend bool operator!=(const SkPoint3& a, const SkPoint3& b) { + return !(a == b); + } + + /** Returns the Euclidian distance from (0,0,0) to (x,y,z) + */ + static SkScalar Length(SkScalar x, SkScalar y, SkScalar z); + + /** Return the Euclidian distance from (0,0,0) to the point + */ + SkScalar length() const { return SkPoint3::Length(fX, fY, fZ); } + + /** Set the point (vector) to be unit-length in the same direction as it + already points. If the point has a degenerate length (i.e., nearly 0) + then set it to (0,0,0) and return false; otherwise return true. + */ + bool normalize(); + + /** Return a new point whose X, Y and Z coordinates are scaled. + */ + SkPoint3 makeScale(SkScalar scale) const { + SkPoint3 p; + p.set(scale * fX, scale * fY, scale * fZ); + return p; + } + + /** Scale the point's coordinates by scale. + */ + void scale(SkScalar value) { + fX *= value; + fY *= value; + fZ *= value; + } + + /** Return a new point whose X, Y and Z coordinates are the negative of the + original point's + */ + SkPoint3 operator-() const { + SkPoint3 neg; + neg.fX = -fX; + neg.fY = -fY; + neg.fZ = -fZ; + return neg; + } + + /** Returns a new point whose coordinates are the difference between + a and b (i.e., a - b) + */ + friend SkPoint3 operator-(const SkPoint3& a, const SkPoint3& b) { + SkPoint3 v; + v.set(a.fX - b.fX, a.fY - b.fY, a.fZ - b.fZ); + return v; + } + + /** Returns a new point whose coordinates are the sum of a and b (a + b) + */ + friend SkPoint3 operator+(const SkPoint3& a, const SkPoint3& b) { + SkPoint3 v; + v.set(a.fX + b.fX, a.fY + b.fY, a.fZ + b.fZ); + return v; + } + + /** Add v's coordinates to the point's + */ + void operator+=(const SkPoint3& v) { + fX += v.fX; + fY += v.fY; + fZ += v.fZ; + } + + /** Subtract v's coordinates from the point's + */ + void operator-=(const SkPoint3& v) { + fX -= v.fX; + fY -= v.fY; + fZ -= v.fZ; + } + + /** Returns the dot product of a and b, treating them as 3D vectors + */ + static SkScalar DotProduct(const SkPoint3& a, const SkPoint3& b) { + return a.fX * b.fX + a.fY * b.fY + a.fZ * b.fZ; + } + + SkScalar dot(const SkPoint3& vec) const { + return DotProduct(*this, vec); + } +}; + +typedef SkPoint3 SkVector3; +typedef SkPoint3 SkColor3f; + +#endif diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp index 3aecbd5471..04ca841e5f 100644 --- a/src/ports/SkGlobalInitialization_chromium.cpp +++ b/src/ports/SkGlobalInitialization_chromium.cpp @@ -41,7 +41,7 @@ #include "SkLayerRasterizer.h" #include "SkLerpXfermode.h" #include "SkLightingImageFilter.h" -#include "../effects/SkLightingShader.h" +#include "SkLightingShader.h" #include "SkLocalMatrixShader.h" #include "SkLumaColorFilter.h" #include "SkMagnifierImageFilter.h" diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 47b00b96f1..0d05c1dcb7 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -37,7 +37,7 @@ #include "SkLayerRasterizer.h" #include "SkLerpXfermode.h" #include "SkLightingImageFilter.h" -#include "../effects/SkLightingShader.h" +#include "SkLightingShader.h" #include "SkLocalMatrixShader.h" #include "SkLumaColorFilter.h" #include "SkMagnifierImageFilter.h" |