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