blob: 4614431fc96dbd5bee9ecc3b0a916c3440b0c56a [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkColor.h"
9#include "include/core/SkPoint3.h"
10#include "include/core/SkUnPreMultiply.h"
Ben Wagner729a23f2019-05-17 16:29:34 -040011#include "src/core/SkArenaAlloc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#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"
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040021
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*/
40class SkLightingShaderImpl : public SkShaderBase {
41public:
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 */
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
71 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
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040082protected:
83 void flatten(SkWriteBuffer&) const override;
Mike Reede92aae62018-10-17 10:21:51 -040084#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040085 Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
Mike Reede92aae62018-10-17 10:21:51 -040086#endif
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040087
88private:
Mike Klein4fee3232018-10-18 17:27:16 -040089 SK_FLATTENABLE_HOOKS(SkLightingShaderImpl)
90
Robert Phillipsa8cdbd72018-07-17 12:30:40 -040091 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500104#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"
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400111
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.
115class LightingFP : public GrFragmentProcessor {
116public:
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
123 const char* name() const override { return "LightingFP"; }
124
125 std::unique_ptr<GrFragmentProcessor> clone() const override {
126 return std::unique_ptr<GrFragmentProcessor>(new LightingFP(*this));
127 }
128
129 const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
130 const SkColor3f& ambientColor() const { return fAmbientColor; }
131
132private:
133 class GLSLLightingFP : public GrGLSLFragmentProcessor {
134 public:
135 GLSLLightingFP() {
136 fAmbientColor.fX = 0.0f;
137 }
138
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,
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400151 "LightDir",
152 lightingFP.fDirectionalLights.count(),
153 &lightDirsUniName);
154 fLightColorsUni = uniformHandler->addUniformArray(
155 kFragment_GrShaderFlag,
156 kFloat3_GrSLType,
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400157 "LightColor",
158 lightingFP.fDirectionalLights.count(),
159 &lightColorsUniName);
160 }
161
162 const char* ambientColorUniName = nullptr;
Ethan Nicholas858fecc2019-03-07 13:19:18 -0500163 fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat3_GrSLType,
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400164 "AmbientColor", &ambientColorUniName);
165
Ethan Nicholase1f55022019-02-05 17:17:40 -0500166 fragBuilder->codeAppendf("half4 diffuseColor = %s;", args.fInputColor);
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400167
Ethan Nicholas6ad52892019-05-03 13:13:42 +0000168 SkString dstNormalName("dstNormal");
Ethan Nicholasc6dce5a2019-07-24 16:51:36 -0400169 this->invokeChild(0, &dstNormalName, args);
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400170
Ethan Nicholas6ad52892019-05-03 13:13:42 +0000171 fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str());
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400172
Ethan Nicholase1f55022019-02-05 17:17:40 -0500173 fragBuilder->codeAppend( "half3 result = half3(0.0);");
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400174
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
Ethan Nicholase1f55022019-02-05 17:17:40 -0500180 fragBuilder->codeAppendf(" half NdotL = saturate(half(dot(normal, %s[i])));",
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400181 lightDirsUniName);
Ethan Nicholase1f55022019-02-05 17:17:40 -0500182 fragBuilder->codeAppendf(" result += half3(%s[i])*diffuseColor.rgb*NdotL;",
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400183 lightColorsUniName);
184 fragBuilder->codeAppend("}");
185 }
186
187 // ambient light
Ethan Nicholase1f55022019-02-05 17:17:40 -0500188 fragBuilder->codeAppendf("result += half3(%s) * diffuseColor.rgb;",
189 ambientColorUniName);
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400190
191 // Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
Ethan Nicholase1f55022019-02-05 17:17:40 -0500192 fragBuilder->codeAppendf("%s = half4(clamp(result.rgb, 0.0, diffuseColor.a), "
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400193 "diffuseColor.a);", args.fOutputColor);
194 }
195
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:
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
237 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
238 GLSLLightingFP::GenKey(*this, caps, b);
239 }
240
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
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
264 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
265
266 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
280std::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
309bool SkLightingShaderImpl::isOpaque() const {
310 return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
311}
312
313SkLightingShaderImpl::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
332static 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
357void 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
417sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
418
419 // Discarding SkShader flattenable params
420 bool hasLocalMatrix = buf.readBool();
Robert Phillips75b75262018-07-24 08:23:14 -0400421 if (hasLocalMatrix) {
422 return nullptr;
423 }
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400424
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
439void 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
Mike Reede92aae62018-10-17 10:21:51 -0400451#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400452SkShaderBase::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
Mike Reed011d1662019-02-28 17:19:25 -0500468 // 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?
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400470 return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
471}
Mike Reede92aae62018-10-17 10:21:51 -0400472#endif
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400473
Robert Phillipsa8cdbd72018-07-17 12:30:40 -0400474///////////////////////////////////////////////////////////////////////////////
475
476sk_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
Brian Salomon23356442018-11-30 15:33:19 -0500490void SkLightingShader::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkLightingShaderImpl); }