blob: 83189804ede026aea4ca612c7bc93aeef3748ddc [file] [log] [blame]
Robert Phillipsa8cdbd72018-07-17 12:30:40 -04001/*
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*/
41class SkLightingShaderImpl : public SkShaderBase {
42public:
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 */
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
72 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
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040083protected:
84 void flatten(SkWriteBuffer&) const override;
Mike Reede92aae62018-10-17 10:21:51 -040085#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040086 Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
Mike Reede92aae62018-10-17 10:21:51 -040087#endif
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040088 sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
89
90private:
Mike Klein4fee3232018-10-18 17:27:16 -040091 SK_FLATTENABLE_HOOKS(SkLightingShaderImpl)
92
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040093 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.
117class LightingFP : public GrFragmentProcessor {
118public:
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
125 const char* name() const override { return "LightingFP"; }
126
127 std::unique_ptr<GrFragmentProcessor> clone() const override {
128 return std::unique_ptr<GrFragmentProcessor>(new LightingFP(*this));
129 }
130
131 const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
132 const SkColor3f& ambientColor() const { return fAmbientColor; }
133
134private:
135 class GLSLLightingFP : public GrGLSLFragmentProcessor {
136 public:
137 GLSLLightingFP() {
138 fAmbientColor.fX = 0.0f;
139 }
140
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
Ethan Nicholase1f55022019-02-05 17:17:40 -0500171 fragBuilder->codeAppendf("half4 diffuseColor = %s;", args.fInputColor);
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400172
173 SkString dstNormalName("dstNormal");
174 this->emitChild(0, &dstNormalName, args);
175
176 fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str());
177
Ethan Nicholase1f55022019-02-05 17:17:40 -0500178 fragBuilder->codeAppend( "half3 result = half3(0.0);");
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400179
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
Ethan Nicholase1f55022019-02-05 17:17:40 -0500185 fragBuilder->codeAppendf(" half NdotL = saturate(half(dot(normal, %s[i])));",
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400186 lightDirsUniName);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500187 fragBuilder->codeAppendf(" result += half3(%s[i])*diffuseColor.rgb*NdotL;",
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400188 lightColorsUniName);
189 fragBuilder->codeAppend("}");
190 }
191
192 // ambient light
Ethan Nicholase1f55022019-02-05 17:17:40 -0500193 fragBuilder->codeAppendf("result += half3(%s) * diffuseColor.rgb;",
194 ambientColorUniName);
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400195
196 // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
Ethan Nicholase1f55022019-02-05 17:17:40 -0500197 fragBuilder->codeAppendf("%s = half4(clamp(result.rgb, 0.0, diffuseColor.a), "
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400198 "diffuseColor.a);", args.fOutputColor);
199 }
200
201 static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
202 const LightingFP& lightingFP = proc.cast<LightingFP>();
203 b->add32(lightingFP.fDirectionalLights.count());
204 }
205
206 protected:
207 void onSetData(const GrGLSLProgramDataManager& pdman,
208 const GrFragmentProcessor& proc) override {
209 const LightingFP& lightingFP = proc.cast<LightingFP>();
210
211 const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights();
212 if (directionalLights != fDirectionalLights) {
213 SkTArray<SkColor3f> lightDirs(directionalLights.count());
214 SkTArray<SkVector3> lightColors(directionalLights.count());
215 for (const SkLights::Light& light : directionalLights) {
216 lightDirs.push_back(light.dir());
217 lightColors.push_back(light.color());
218 }
219
220 pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX));
221 pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX));
222
223 fDirectionalLights = directionalLights;
224 }
225
226 const SkColor3f& ambientColor = lightingFP.ambientColor();
227 if (ambientColor != fAmbientColor) {
228 pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
229 fAmbientColor = ambientColor;
230 }
231 }
232
233 private:
234 SkTArray<SkLights::Light> fDirectionalLights;
235 GrGLSLProgramDataManager::UniformHandle fLightDirsUni;
236 GrGLSLProgramDataManager::UniformHandle fLightColorsUni;
237
238 SkColor3f fAmbientColor;
239 GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
240 };
241
242 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
243 GLSLLightingFP::GenKey(*this, caps, b);
244 }
245
246 LightingFP(std::unique_ptr<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights)
247 : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) {
248 // fuse all ambient lights into a single one
249 fAmbientColor = lights->ambientLightColor();
250 for (int i = 0; i < lights->numLights(); ++i) {
251 if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) {
252 fDirectionalLights.push_back(lights->light(i));
253 // TODO get the handle to the shadow map if there is one
254 } else {
255 SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP");
256 }
257 }
258
259 this->registerChildProcessor(std::move(normalFP));
260 }
261
262 LightingFP(const LightingFP& that)
263 : INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag)
264 , fDirectionalLights(that.fDirectionalLights)
265 , fAmbientColor(that.fAmbientColor) {
266 this->registerChildProcessor(that.childProcessor(0).clone());
267 }
268
269 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
270
271 bool onIsEqual(const GrFragmentProcessor& proc) const override {
272 const LightingFP& lightingFP = proc.cast<LightingFP>();
273 return fDirectionalLights == lightingFP.fDirectionalLights &&
274 fAmbientColor == lightingFP.fAmbientColor;
275 }
276
277 SkTArray<SkLights::Light> fDirectionalLights;
278 SkColor3f fAmbientColor;
279
280 typedef GrFragmentProcessor INHERITED;
281};
282
283////////////////////////////////////////////////////////////////////////////
284
285std::unique_ptr<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const GrFPArgs& args) const {
286 std::unique_ptr<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args));
287 if (!normalFP) {
288 return nullptr;
289 }
290
291 if (fDiffuseShader) {
292 std::unique_ptr<GrFragmentProcessor> fpPipeline[] = {
293 as_SB(fDiffuseShader)->asFragmentProcessor(args),
294 LightingFP::Make(std::move(normalFP), fLights)
295 };
296 if (!fpPipeline[0] || !fpPipeline[1]) {
297 return nullptr;
298 }
299
300 std::unique_ptr<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2);
301 // FP is wrapped because paint's alpha needs to be applied to output
302 return GrFragmentProcessor::MulChildByInputAlpha(std::move(innerLightFP));
303 } else {
304 // FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP
305 // expects premul'd color.
306 return GrFragmentProcessor::PremulInput(LightingFP::Make(std::move(normalFP), fLights));
307 }
308}
309
310#endif
311
312////////////////////////////////////////////////////////////////////////////
313
314bool SkLightingShaderImpl::isOpaque() const {
315 return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
316}
317
318SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(
319 const SkLightingShaderImpl& shader, const ContextRec& rec,
320 SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider,
321 void* heapAllocated)
322 : INHERITED(shader, rec)
323 , fDiffuseContext(diffuseContext)
324 , fNormalProvider(normalProvider) {
325 bool isOpaque = shader.isOpaque();
326
327 // update fFlags
328 uint32_t flags = 0;
329 if (isOpaque && (255 == this->getPaintAlpha())) {
330 flags |= kOpaqueAlpha_Flag;
331 }
332
333 fPaintColor = rec.fPaint->getColor();
334 fFlags = flags;
335}
336
337static inline SkPMColor convert(SkColor3f color, U8CPU a) {
338 if (color.fX <= 0.0f) {
339 color.fX = 0.0f;
340 } else if (color.fX >= 255.0f) {
341 color.fX = 255.0f;
342 }
343
344 if (color.fY <= 0.0f) {
345 color.fY = 0.0f;
346 } else if (color.fY >= 255.0f) {
347 color.fY = 255.0f;
348 }
349
350 if (color.fZ <= 0.0f) {
351 color.fZ = 0.0f;
352 } else if (color.fZ >= 255.0f) {
353 color.fZ = 255.0f;
354 }
355
356 return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ);
357}
358
359// larger is better (fewer times we have to loop), but we shouldn't
360// take up too much stack-space (each one here costs 16 bytes)
361#define BUFFER_MAX 16
362void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
363 SkPMColor result[], int count) {
364 const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader);
365
366 SkPMColor diffuse[BUFFER_MAX];
367 SkPoint3 normals[BUFFER_MAX];
368
369 SkColor diffColor = fPaintColor;
370
371 do {
372 int n = SkTMin(count, BUFFER_MAX);
373
374 fNormalProvider->fillScanLine(x, y, normals, n);
375
376 if (fDiffuseContext) {
377 fDiffuseContext->shadeSpan(x, y, diffuse, n);
378 }
379
380 for (int i = 0; i < n; ++i) {
381 if (fDiffuseContext) {
382 diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
383 }
384
385 SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f);
386
387 // Adding ambient light
388 accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor);
389 accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor);
390 accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor);
391
392 // This is all done in linear unpremul color space (each component 0..255.0f though)
393 for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
394 const SkLights::Light& light = lightShader.fLights->light(l);
395
396 SkScalar illuminanceScalingFactor = 1.0f;
397
398 if (SkLights::Light::kDirectional_LightType == light.type()) {
399 illuminanceScalingFactor = normals[i].dot(light.dir());
400 if (illuminanceScalingFactor < 0.0f) {
401 illuminanceScalingFactor = 0.0f;
402 }
403 }
404
405 accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor;
406 accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor;
407 accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor;
408 }
409
410 // convert() premultiplies the accumulate color with alpha
411 result[i] = convert(accum, SkColorGetA(diffColor));
412 }
413
414 result += n;
415 x += n;
416 count -= n;
417 } while (count > 0);
418}
419
420////////////////////////////////////////////////////////////////////////////
421
422sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
423
424 // Discarding SkShader flattenable params
425 bool hasLocalMatrix = buf.readBool();
Robert Phillips75b75262018-07-24 08:23:14 -0400426 if (hasLocalMatrix) {
427 return nullptr;
428 }
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400429
430 sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
431
432 sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>());
433
434 bool hasDiffuse = buf.readBool();
435 sk_sp<SkShader> diffuseShader = nullptr;
436 if (hasDiffuse) {
437 diffuseShader = buf.readFlattenable<SkShaderBase>();
438 }
439
440 return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
441 std::move(lights));
442}
443
444void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
445 this->INHERITED::flatten(buf);
446
447 fLights->flatten(buf);
448
449 buf.writeFlattenable(fNormalSource.get());
450 buf.writeBool(static_cast<bool>(fDiffuseShader));
451 if (fDiffuseShader) {
452 buf.writeFlattenable(fDiffuseShader.get());
453 }
454}
455
Mike Reede92aae62018-10-17 10:21:51 -0400456#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400457SkShaderBase::Context* SkLightingShaderImpl::onMakeContext(
458 const ContextRec& rec, SkArenaAlloc* alloc) const
459{
460 SkShaderBase::Context *diffuseContext = nullptr;
461 if (fDiffuseShader) {
462 diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc);
463 if (!diffuseContext) {
464 return nullptr;
465 }
466 }
467
468 SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc);
469 if (!normalProvider) {
470 return nullptr;
471 }
472
473 return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
474}
Mike Reede92aae62018-10-17 10:21:51 -0400475#endif
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400476
477sk_sp<SkShader> SkLightingShaderImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
478 sk_sp<SkShader> xformedDiffuseShader =
479 fDiffuseShader ? xformer->apply(fDiffuseShader.get()) : nullptr;
480 return SkLightingShader::Make(std::move(xformedDiffuseShader), fNormalSource,
481 fLights->makeColorSpace(xformer));
482}
483
484///////////////////////////////////////////////////////////////////////////////
485
486sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader,
487 sk_sp<SkNormalSource> normalSource,
488 sk_sp<SkLights> lights) {
489 SkASSERT(lights);
490 if (!normalSource) {
491 normalSource = SkNormalSource::MakeFlat();
492 }
493
494 return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
495 std::move(lights));
496}
497
498///////////////////////////////////////////////////////////////////////////////
499
Brian Salomon23356442018-11-30 15:33:19 -0500500void SkLightingShader::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkLightingShaderImpl); }