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