• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkColor.h"
9 #include "include/core/SkPoint3.h"
10 #include "include/core/SkUnPreMultiply.h"
11 #include "src/core/SkArenaAlloc.h"
12 #include "src/core/SkBitmapProcState.h"
13 #include "src/core/SkMathPriv.h"
14 #include "src/core/SkNormalSource.h"
15 #include "src/core/SkReadBuffer.h"
16 #include "src/core/SkWriteBuffer.h"
17 #include "src/shaders/SkBitmapProcShader.h"
18 #include "src/shaders/SkEmptyShader.h"
19 #include "src/shaders/SkLightingShader.h"
20 #include "src/shaders/SkShaderBase.h"
21 
22 ////////////////////////////////////////////////////////////////////////////
23 
24 /*
25    SkLightingShader TODOs:
26         support different light types
27         support multiple lights
28         fix non-opaque diffuse textures
29 
30     To Test:
31         A8 diffuse textures
32         down & upsampled draws
33 */
34 
35 
36 
37 /** \class SkLightingShaderImpl
38     This subclass of shader applies lighting.
39 */
40 class SkLightingShaderImpl : public SkShaderBase {
41 public:
42     /** Create a new lighting shader that uses the provided normal map and
43         lights to light the diffuse bitmap.
44         @param diffuseShader     the shader that provides the diffuse colors
45         @param normalSource      the source of normals for lighting computation
46         @param lights            the lights applied to the geometry
47     */
SkLightingShaderImpl(sk_sp<SkShader> diffuseShader,sk_sp<SkNormalSource> normalSource,sk_sp<SkLights> lights)48     SkLightingShaderImpl(sk_sp<SkShader> diffuseShader,
49                          sk_sp<SkNormalSource> normalSource,
50                          sk_sp<SkLights> lights)
51         : fDiffuseShader(std::move(diffuseShader))
52         , fNormalSource(std::move(normalSource))
53         , fLights(std::move(lights)) {}
54 
55     bool isOpaque() const override;
56 
57 #if SK_SUPPORT_GPU
58     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
59 #endif
60 
61     class LightingShaderContext : public Context {
62     public:
63         // The context takes ownership of the context and provider. It will call their destructors
64         // and then indirectly free their memory by calling free() on heapAllocated
65         LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&,
66                               SkShaderBase::Context* diffuseContext, SkNormalSource::Provider*,
67                               void* heapAllocated);
68 
69         void shadeSpan(int x, int y, SkPMColor[], int count) override;
70 
getFlags() const71         uint32_t getFlags() const override { return fFlags; }
72 
73     private:
74         SkShaderBase::Context*    fDiffuseContext;
75         SkNormalSource::Provider* fNormalProvider;
76         SkColor                   fPaintColor;
77         uint32_t                  fFlags;
78 
79         typedef Context INHERITED;
80     };
81 
82 protected:
83     void flatten(SkWriteBuffer&) const override;
84 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
85     Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
86 #endif
87 
88 private:
89     SK_FLATTENABLE_HOOKS(SkLightingShaderImpl)
90 
91     sk_sp<SkShader> fDiffuseShader;
92     sk_sp<SkNormalSource> fNormalSource;
93     sk_sp<SkLights> fLights;
94 
95     friend class SkLightingShader;
96 
97     typedef SkShaderBase INHERITED;
98 };
99 
100 ////////////////////////////////////////////////////////////////////////////
101 
102 #if SK_SUPPORT_GPU
103 
104 #include "src/gpu/GrCoordTransform.h"
105 #include "src/gpu/GrFragmentProcessor.h"
106 #include "src/gpu/SkGr.h"
107 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
108 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
109 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
110 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
111 
112 // This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is
113 // handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it
114 // premul'd.
115 class LightingFP : public GrFragmentProcessor {
116 public:
Make(std::unique_ptr<GrFragmentProcessor> normalFP,sk_sp<SkLights> lights)117     static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> normalFP,
118                                                      sk_sp<SkLights> lights) {
119         return std::unique_ptr<GrFragmentProcessor>(new LightingFP(std::move(normalFP),
120                                                                    std::move(lights)));
121     }
122 
name() const123     const char* name() const override { return "LightingFP"; }
124 
clone() const125     std::unique_ptr<GrFragmentProcessor> clone() const override {
126         return std::unique_ptr<GrFragmentProcessor>(new LightingFP(*this));
127     }
128 
directionalLights() const129     const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
ambientColor() const130     const SkColor3f& ambientColor() const { return fAmbientColor; }
131 
132 private:
133     class GLSLLightingFP : public GrGLSLFragmentProcessor {
134     public:
GLSLLightingFP()135         GLSLLightingFP() {
136             fAmbientColor.fX = 0.0f;
137         }
138 
emitCode(EmitArgs & args)139         void emitCode(EmitArgs& args) override {
140 
141             GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
142             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
143             const LightingFP& lightingFP = args.fFp.cast<LightingFP>();
144 
145             const char *lightDirsUniName = nullptr;
146             const char *lightColorsUniName = nullptr;
147             if (lightingFP.fDirectionalLights.count() != 0) {
148                 fLightDirsUni = uniformHandler->addUniformArray(
149                         kFragment_GrShaderFlag,
150                         kFloat3_GrSLType,
151                         "LightDir",
152                         lightingFP.fDirectionalLights.count(),
153                         &lightDirsUniName);
154                 fLightColorsUni = uniformHandler->addUniformArray(
155                         kFragment_GrShaderFlag,
156                         kFloat3_GrSLType,
157                         "LightColor",
158                         lightingFP.fDirectionalLights.count(),
159                         &lightColorsUniName);
160             }
161 
162             const char* ambientColorUniName = nullptr;
163             fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat3_GrSLType,
164                                                           "AmbientColor", &ambientColorUniName);
165 
166             fragBuilder->codeAppendf("half4 diffuseColor = %s;", args.fInputColor);
167 
168             SkString dstNormalName("dstNormal");
169             this->invokeChild(0, &dstNormalName, args);
170 
171             fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str());
172 
173             fragBuilder->codeAppend( "half3 result = half3(0.0);");
174 
175             // diffuse light
176             if (lightingFP.fDirectionalLights.count() != 0) {
177                 fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {",
178                                          lightingFP.fDirectionalLights.count());
179                 // TODO: modulate the contribution from each light based on the shadow map
180                 fragBuilder->codeAppendf("    half NdotL = saturate(half(dot(normal, %s[i])));",
181                                          lightDirsUniName);
182                 fragBuilder->codeAppendf("    result += half3(%s[i])*diffuseColor.rgb*NdotL;",
183                                          lightColorsUniName);
184                 fragBuilder->codeAppend("}");
185             }
186 
187             // ambient light
188             fragBuilder->codeAppendf("result += half3(%s) * diffuseColor.rgb;",
189                                      ambientColorUniName);
190 
191             // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
192             fragBuilder->codeAppendf("%s = half4(clamp(result.rgb, 0.0, diffuseColor.a), "
193                                                "diffuseColor.a);", args.fOutputColor);
194         }
195 
GenKey(const GrProcessor & proc,const GrShaderCaps &,GrProcessorKeyBuilder * b)196         static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
197             const LightingFP& lightingFP = proc.cast<LightingFP>();
198             b->add32(lightingFP.fDirectionalLights.count());
199         }
200 
201     protected:
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & proc)202         void onSetData(const GrGLSLProgramDataManager& pdman,
203                        const GrFragmentProcessor& proc) override {
204             const LightingFP& lightingFP = proc.cast<LightingFP>();
205 
206             const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights();
207             if (directionalLights != fDirectionalLights) {
208                 SkTArray<SkColor3f> lightDirs(directionalLights.count());
209                 SkTArray<SkVector3> lightColors(directionalLights.count());
210                 for (const SkLights::Light& light : directionalLights) {
211                     lightDirs.push_back(light.dir());
212                     lightColors.push_back(light.color());
213                 }
214 
215                 pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX));
216                 pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX));
217 
218                 fDirectionalLights = directionalLights;
219             }
220 
221             const SkColor3f& ambientColor = lightingFP.ambientColor();
222             if (ambientColor != fAmbientColor) {
223                 pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
224                 fAmbientColor = ambientColor;
225             }
226         }
227 
228     private:
229         SkTArray<SkLights::Light> fDirectionalLights;
230         GrGLSLProgramDataManager::UniformHandle fLightDirsUni;
231         GrGLSLProgramDataManager::UniformHandle fLightColorsUni;
232 
233         SkColor3f fAmbientColor;
234         GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
235     };
236 
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const237     void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
238         GLSLLightingFP::GenKey(*this, caps, b);
239     }
240 
LightingFP(std::unique_ptr<GrFragmentProcessor> normalFP,sk_sp<SkLights> lights)241     LightingFP(std::unique_ptr<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights)
242             : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) {
243         // fuse all ambient lights into a single one
244         fAmbientColor = lights->ambientLightColor();
245         for (int i = 0; i < lights->numLights(); ++i) {
246             if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) {
247                 fDirectionalLights.push_back(lights->light(i));
248                 // TODO get the handle to the shadow map if there is one
249             } else {
250                 SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP");
251             }
252         }
253 
254         this->registerChildProcessor(std::move(normalFP));
255     }
256 
LightingFP(const LightingFP & that)257     LightingFP(const LightingFP& that)
258             : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag)
259             , fDirectionalLights(that.fDirectionalLights)
260             , fAmbientColor(that.fAmbientColor) {
261         this->registerChildProcessor(that.childProcessor(0).clone());
262     }
263 
onCreateGLSLInstance() const264     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
265 
onIsEqual(const GrFragmentProcessor & proc) const266     bool onIsEqual(const GrFragmentProcessor& proc) const override {
267         const LightingFP& lightingFP = proc.cast<LightingFP>();
268         return fDirectionalLights == lightingFP.fDirectionalLights &&
269                fAmbientColor == lightingFP.fAmbientColor;
270     }
271 
272     SkTArray<SkLights::Light> fDirectionalLights;
273     SkColor3f                 fAmbientColor;
274 
275     typedef GrFragmentProcessor INHERITED;
276 };
277 
278 ////////////////////////////////////////////////////////////////////////////
279 
asFragmentProcessor(const GrFPArgs & args) const280 std::unique_ptr<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const GrFPArgs& args) const {
281     std::unique_ptr<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args));
282     if (!normalFP) {
283         return nullptr;
284     }
285 
286     if (fDiffuseShader) {
287         std::unique_ptr<GrFragmentProcessor> fpPipeline[] = {
288             as_SB(fDiffuseShader)->asFragmentProcessor(args),
289             LightingFP::Make(std::move(normalFP), fLights)
290         };
291         if (!fpPipeline[0] || !fpPipeline[1]) {
292             return nullptr;
293         }
294 
295         std::unique_ptr<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2);
296         // FP is wrapped because paint's alpha needs to be applied to output
297         return GrFragmentProcessor::MulChildByInputAlpha(std::move(innerLightFP));
298     } else {
299         // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP
300         // expects premul'd color.
301         return GrFragmentProcessor::PremulInput(LightingFP::Make(std::move(normalFP), fLights));
302     }
303 }
304 
305 #endif
306 
307 ////////////////////////////////////////////////////////////////////////////
308 
isOpaque() const309 bool SkLightingShaderImpl::isOpaque() const {
310     return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
311 }
312 
LightingShaderContext(const SkLightingShaderImpl & shader,const ContextRec & rec,SkShaderBase::Context * diffuseContext,SkNormalSource::Provider * normalProvider,void * heapAllocated)313 SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(
314         const SkLightingShaderImpl& shader, const ContextRec& rec,
315         SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider,
316         void* heapAllocated)
317     : INHERITED(shader, rec)
318     , fDiffuseContext(diffuseContext)
319     , fNormalProvider(normalProvider) {
320     bool isOpaque = shader.isOpaque();
321 
322     // update fFlags
323     uint32_t flags = 0;
324     if (isOpaque && (255 == this->getPaintAlpha())) {
325         flags |= kOpaqueAlpha_Flag;
326     }
327 
328     fPaintColor = rec.fPaint->getColor();
329     fFlags = flags;
330 }
331 
convert(SkColor3f color,U8CPU a)332 static inline SkPMColor convert(SkColor3f color, U8CPU a) {
333     if (color.fX <= 0.0f) {
334         color.fX = 0.0f;
335     } else if (color.fX >= 255.0f) {
336         color.fX = 255.0f;
337     }
338 
339     if (color.fY <= 0.0f) {
340         color.fY = 0.0f;
341     } else if (color.fY >= 255.0f) {
342         color.fY = 255.0f;
343     }
344 
345     if (color.fZ <= 0.0f) {
346         color.fZ = 0.0f;
347     } else if (color.fZ >= 255.0f) {
348         color.fZ = 255.0f;
349     }
350 
351     return SkPreMultiplyARGB(a, (int) color.fX,  (int) color.fY, (int) color.fZ);
352 }
353 
354 // larger is better (fewer times we have to loop), but we shouldn't
355 // take up too much stack-space (each one here costs 16 bytes)
356 #define BUFFER_MAX 16
shadeSpan(int x,int y,SkPMColor result[],int count)357 void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
358                                                             SkPMColor result[], int count) {
359     const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader);
360 
361     SkPMColor diffuse[BUFFER_MAX];
362     SkPoint3 normals[BUFFER_MAX];
363 
364     SkColor diffColor = fPaintColor;
365 
366     do {
367         int n = SkTMin(count, BUFFER_MAX);
368 
369         fNormalProvider->fillScanLine(x, y, normals, n);
370 
371         if (fDiffuseContext) {
372             fDiffuseContext->shadeSpan(x, y, diffuse, n);
373         }
374 
375         for (int i = 0; i < n; ++i) {
376             if (fDiffuseContext) {
377                 diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
378             }
379 
380             SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f);
381 
382             // Adding ambient light
383             accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor);
384             accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor);
385             accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor);
386 
387             // This is all done in linear unpremul color space (each component 0..255.0f though)
388             for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
389                 const SkLights::Light& light = lightShader.fLights->light(l);
390 
391                 SkScalar illuminanceScalingFactor = 1.0f;
392 
393                 if (SkLights::Light::kDirectional_LightType == light.type()) {
394                     illuminanceScalingFactor = normals[i].dot(light.dir());
395                     if (illuminanceScalingFactor < 0.0f) {
396                         illuminanceScalingFactor = 0.0f;
397                     }
398                 }
399 
400                 accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor;
401                 accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor;
402                 accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor;
403             }
404 
405             // convert() premultiplies the accumulate color with alpha
406             result[i] = convert(accum, SkColorGetA(diffColor));
407         }
408 
409         result += n;
410         x += n;
411         count -= n;
412     } while (count > 0);
413 }
414 
415 ////////////////////////////////////////////////////////////////////////////
416 
CreateProc(SkReadBuffer & buf)417 sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
418 
419     // Discarding SkShader flattenable params
420     bool hasLocalMatrix = buf.readBool();
421     if (hasLocalMatrix) {
422         return nullptr;
423     }
424 
425     sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
426 
427     sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>());
428 
429     bool hasDiffuse = buf.readBool();
430     sk_sp<SkShader> diffuseShader = nullptr;
431     if (hasDiffuse) {
432         diffuseShader = buf.readFlattenable<SkShaderBase>();
433     }
434 
435     return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
436                                             std::move(lights));
437 }
438 
flatten(SkWriteBuffer & buf) const439 void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
440     this->INHERITED::flatten(buf);
441 
442     fLights->flatten(buf);
443 
444     buf.writeFlattenable(fNormalSource.get());
445     buf.writeBool(static_cast<bool>(fDiffuseShader));
446     if (fDiffuseShader) {
447         buf.writeFlattenable(fDiffuseShader.get());
448     }
449 }
450 
451 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const452 SkShaderBase::Context* SkLightingShaderImpl::onMakeContext(
453     const ContextRec& rec, SkArenaAlloc* alloc) const
454 {
455     SkShaderBase::Context *diffuseContext = nullptr;
456     if (fDiffuseShader) {
457         diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc);
458         if (!diffuseContext) {
459             return nullptr;
460         }
461     }
462 
463     SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc);
464     if (!normalProvider) {
465         return nullptr;
466     }
467 
468     // The diffuse shader can inspect the rec and make its decision about rec's colorspace.
469     // What about the lighting shader? Is lighting sensitive to the rec's (device) colorspace?
470     return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
471 }
472 #endif
473 
474 ///////////////////////////////////////////////////////////////////////////////
475 
Make(sk_sp<SkShader> diffuseShader,sk_sp<SkNormalSource> normalSource,sk_sp<SkLights> lights)476 sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader,
477                                        sk_sp<SkNormalSource> normalSource,
478                                        sk_sp<SkLights> lights) {
479     SkASSERT(lights);
480     if (!normalSource) {
481         normalSource = SkNormalSource::MakeFlat();
482     }
483 
484     return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
485                                             std::move(lights));
486 }
487 
488 ///////////////////////////////////////////////////////////////////////////////
489 
RegisterFlattenables()490 void SkLightingShader::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkLightingShaderImpl); }
491