1/* 2 * Copyright 2021 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 "modules/skottie/src/effects/Effects.h" 9 10#include "include/effects/SkRuntimeEffect.h" 11#include "include/utils/SkRandom.h" 12#include "modules/skottie/src/Adapter.h" 13#include "modules/skottie/src/SkottieJson.h" 14#include "modules/skottie/src/SkottieValue.h" 15#include "modules/sksg/include/SkSGRenderNode.h" 16 17#include <cmath> 18 19namespace skottie::internal { 20 21#ifdef SK_ENABLE_SKSL 22 23namespace { 24 25// An implementation of the ADBE Fractal Noise effect: 26// 27// - multiple noise sublayers (octaves) are combined using a weighted average 28// - each layer is subject to a (cumulative) transform, filter and post-sampling options 29// 30// Parameters: 31// 32// * Noise Type -- controls noise layer post-sampling filtering 33// (Block, Linear, Soft Linear, Spline) 34// * Fractal Type -- determines a noise layer post-filtering transformation 35// (Basic, Turbulent Smooth, Turbulent Basic, etc) 36// * Transform -- offset/scale/rotate the noise effect (local matrix) 37// 38// * Complexity -- number of sublayers; 39// can be fractional, where the fractional part modulates the last layer 40// * Evolution -- controls noise topology in a gradual manner (can be animated for smooth 41// noise transitions) 42// * Sub Influence -- relative amplitude weight for sublayers (cumulative) 43// 44// * Sub Scaling/Rotation/Offset -- relative scale for sublayers (cumulative) 45// 46// * Invert -- invert noise values 47// 48// * Contrast -- apply a contrast to the noise result 49// 50// * Brightness -- apply a brightness effect to the noise result 51// 52// 53// TODO: 54// - Invert 55// - Contrast/Brightness 56 57static constexpr char gNoiseEffectSkSL[] = 58 "uniform float3x3 u_submatrix;" // sublayer transform 59 60 "uniform float2 u_noise_planes;" // noise planes computed based on evolution params 61 "uniform float u_noise_weight," // noise planes lerp weight 62 "u_octaves," // number of octaves (can be fractional) 63 "u_persistence;" // relative octave weight 64 65 // Hash based on hash13 (https://www.shadertoy.com/view/4djSRW). 66 "float hash(float3 v) {" 67 "v = fract(v*0.1031);" 68 "v += dot(v, v.zxy + 31.32);" 69 "return fract((v.x + v.y)*v.z);" 70 "}" 71 72 // The general idea is to compute a coherent hash for two planes in discretized (x,y,e) space, 73 // and interpolate between them. This yields gradual changes when animating |e| - which is the 74 // desired outcome. 75 "float sample_noise(float2 xy) {" 76 "xy = floor(xy);" 77 78 "float n0 = hash(float3(xy, u_noise_planes.x))," 79 "n1 = hash(float3(xy, u_noise_planes.y));" 80 81 // Note: Ideally we would use 4 samples (-1, 0, 1, 2) and cubic interpolation for 82 // better results -- but that's significantly more expensive than lerp. 83 84 "return mix(n0, n1, u_noise_weight);" 85 "}" 86 87 // filter() placeholder 88 "%s" 89 90 // fractal() placeholder 91 "%s" 92 93 // Generate ceil(u_octaves) noise layers and combine based on persistentce and sublayer xform. 94 "float4 main(vec2 xy) {" 95 "float oct = u_octaves," // initial octave count (this is the effective loop counter) 96 "amp = 1," // initial layer amplitude 97 "wacc = 0," // weight accumulator 98 "n = 0;" // noise accumulator 99 100 // Constant loop counter chosen to be >= ceil(u_octaves). 101 // The logical counter is actually 'oct'. 102 "for (float i = 0; i < %u; ++i) {" 103 // effective layer weight computed to accommodate fixed loop counters 104 // 105 // -- for full octaves: layer amplitude 106 // -- for fractional octave: layer amplitude modulated by fractional part 107 // -- for octaves > ceil(u_octaves): 0 108 // 109 // e.g. for 6 loops and u_octaves = 2.3, this generates the sequence [1,1,.3,0,0] 110 "float w = amp*saturate(oct);" 111 112 "n += w*fractal(filter(xy));" 113 114 "wacc += w;" 115 "amp *= u_persistence;" 116 "oct -= 1;" 117 118 "xy = (u_submatrix*float3(xy,1)).xy;" 119 "}" 120 121 "n /= wacc;" 122 123 // TODO: fractal functions 124 125 "return float4(n,n,n,1);" 126 "}"; 127 128static constexpr char gFilterNearestSkSL[] = 129 "float filter(float2 xy) {" 130 "return sample_noise(xy);" 131 "}"; 132 133static constexpr char gFilterLinearSkSL[] = 134 "float filter(float2 xy) {" 135 "xy -= 0.5;" 136 137 "float n00 = sample_noise(xy + float2(0,0))," 138 "n10 = sample_noise(xy + float2(1,0))," 139 "n01 = sample_noise(xy + float2(0,1))," 140 "n11 = sample_noise(xy + float2(1,1));" 141 142 "float2 t = fract(xy);" 143 144 "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);" 145 "}"; 146 147static constexpr char gFilterSoftLinearSkSL[] = 148 "float filter(float2 xy) {" 149 "xy -= 0.5;" 150 151 "float n00 = sample_noise(xy + float2(0,0))," 152 "n10 = sample_noise(xy + float2(1,0))," 153 "n01 = sample_noise(xy + float2(0,1))," 154 "n11 = sample_noise(xy + float2(1,1));" 155 156 "float2 t = smoothstep(0, 1, fract(xy));" 157 158 "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);" 159 "}"; 160 161static constexpr char gFractalBasicSkSL[] = 162 "float fractal(float n) {" 163 "return n;" 164 "}"; 165 166static constexpr char gFractalTurbulentBasicSkSL[] = 167 "float fractal(float n) {" 168 "return 2*abs(0.5 - n);" 169 "}"; 170 171static constexpr char gFractalTurbulentSmoothSkSL[] = 172 "float fractal(float n) {" 173 "n = 2*abs(0.5 - n);" 174 "return n*n;" 175 "}"; 176 177static constexpr char gFractalTurbulentSharpSkSL[] = 178 "float fractal(float n) {" 179 "return sqrt(2*abs(0.5 - n));" 180 "}"; 181 182enum class NoiseFilter { 183 kNearest, 184 kLinear, 185 kSoftLinear, 186 // TODO: kSpline? 187}; 188 189enum class NoiseFractal { 190 kBasic, 191 kTurbulentBasic, 192 kTurbulentSmooth, 193 kTurbulentSharp, 194}; 195 196sk_sp<SkRuntimeEffect> make_noise_effect(unsigned loops, const char* filter, const char* fractal) { 197 auto result = SkRuntimeEffect::MakeForShader( 198 SkStringPrintf(gNoiseEffectSkSL, filter, fractal, loops), {}); 199 200 if (0 && !result.effect) { 201 printf("!!! %s\n", result.errorText.c_str()); 202 } 203 204 return std::move(result.effect); 205} 206 207template <unsigned LOOPS, NoiseFilter FILTER, NoiseFractal FRACTAL> 208sk_sp<SkRuntimeEffect> noise_effect() { 209 static constexpr char const* gFilters[] = { 210 gFilterNearestSkSL, 211 gFilterLinearSkSL, 212 gFilterSoftLinearSkSL 213 }; 214 215 static constexpr char const* gFractals[] = { 216 gFractalBasicSkSL, 217 gFractalTurbulentBasicSkSL, 218 gFractalTurbulentSmoothSkSL, 219 gFractalTurbulentSharpSkSL 220 }; 221 222 static_assert(static_cast<size_t>(FILTER) < SK_ARRAY_COUNT(gFilters)); 223 static_assert(static_cast<size_t>(FRACTAL) < SK_ARRAY_COUNT(gFractals)); 224 225 static const SkRuntimeEffect* effect = 226 make_noise_effect(LOOPS, 227 gFilters[static_cast<size_t>(FILTER)], 228 gFractals[static_cast<size_t>(FRACTAL)]) 229 .release(); 230 231 SkASSERT(effect); 232 return sk_ref_sp(effect); 233} 234 235class FractalNoiseNode final : public sksg::CustomRenderNode { 236public: 237 explicit FractalNoiseNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {} 238 239 SG_ATTRIBUTE(Matrix , SkMatrix , fMatrix ) 240 SG_ATTRIBUTE(SubMatrix , SkMatrix , fSubMatrix ) 241 242 SG_ATTRIBUTE(NoiseFilter , NoiseFilter , fFilter ) 243 SG_ATTRIBUTE(NoiseFractal , NoiseFractal, fFractal ) 244 SG_ATTRIBUTE(NoisePlanes , SkV2 , fNoisePlanes ) 245 SG_ATTRIBUTE(NoiseWeight , float , fNoiseWeight ) 246 SG_ATTRIBUTE(Octaves , float , fOctaves ) 247 SG_ATTRIBUTE(Persistence , float , fPersistence ) 248 249private: 250 template <NoiseFilter FI, NoiseFractal FR> 251 sk_sp<SkRuntimeEffect> getEffect() const { 252 // Bin the loop counter based on the number of octaves (range: [1..20]). 253 // Low complexities are common, so we maximize resolution for the low end. 254 if (fOctaves > 8) return noise_effect<20, FI, FR>(); 255 if (fOctaves > 4) return noise_effect< 8, FI, FR>(); 256 if (fOctaves > 3) return noise_effect< 4, FI, FR>(); 257 if (fOctaves > 2) return noise_effect< 3, FI, FR>(); 258 if (fOctaves > 1) return noise_effect< 2, FI, FR>(); 259 260 return noise_effect<1, FI, FR>(); 261 } 262 263 template <NoiseFilter FI> 264 sk_sp<SkRuntimeEffect> getEffect() const { 265 switch (fFractal) { 266 case NoiseFractal::kBasic: 267 return this->getEffect<FI, NoiseFractal::kBasic>(); 268 case NoiseFractal::kTurbulentBasic: 269 return this->getEffect<FI, NoiseFractal::kTurbulentBasic>(); 270 case NoiseFractal::kTurbulentSmooth: 271 return this->getEffect<FI, NoiseFractal::kTurbulentSmooth>(); 272 case NoiseFractal::kTurbulentSharp: 273 return this->getEffect<FI, NoiseFractal::kTurbulentSharp>(); 274 } 275 SkUNREACHABLE; 276 } 277 278 sk_sp<SkRuntimeEffect> getEffect() const { 279 switch (fFilter) { 280 case NoiseFilter::kNearest : return this->getEffect<NoiseFilter::kNearest>(); 281 case NoiseFilter::kLinear : return this->getEffect<NoiseFilter::kLinear>(); 282 case NoiseFilter::kSoftLinear: return this->getEffect<NoiseFilter::kSoftLinear>(); 283 } 284 SkUNREACHABLE; 285 } 286 287 sk_sp<SkShader> buildEffectShader() const { 288 SkRuntimeShaderBuilder builder(this->getEffect()); 289 290 builder.uniform("u_noise_planes") = fNoisePlanes; 291 builder.uniform("u_noise_weight") = fNoiseWeight; 292 builder.uniform("u_octaves" ) = fOctaves; 293 builder.uniform("u_persistence" ) = fPersistence; 294 builder.uniform("u_submatrix" ) = std::array<float,9>{ 295 fSubMatrix.rc(0,0), fSubMatrix.rc(1,0), fSubMatrix.rc(2,0), 296 fSubMatrix.rc(0,1), fSubMatrix.rc(1,1), fSubMatrix.rc(2,1), 297 fSubMatrix.rc(0,2), fSubMatrix.rc(1,2), fSubMatrix.rc(2,2), 298 }; 299 300 return builder.makeShader(&fMatrix, false); 301 } 302 303 SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override { 304 const auto& child = this->children()[0]; 305 const auto bounds = child->revalidate(ic, ctm); 306 307 fEffectShader = this->buildEffectShader(); 308 309 return bounds; 310 } 311 312 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override { 313 const auto& bounds = this->bounds(); 314 const auto local_ctx = ScopedRenderContext(canvas, ctx) 315 .setIsolation(bounds, canvas->getTotalMatrix(), true); 316 317 canvas->saveLayer(&bounds, nullptr); 318 this->children()[0]->render(canvas, local_ctx); 319 320 SkPaint effect_paint; 321 effect_paint.setShader(fEffectShader); 322 effect_paint.setBlendMode(SkBlendMode::kSrcIn); 323 324 canvas->drawPaint(effect_paint); 325 } 326 327 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing 328 329 sk_sp<SkShader> fEffectShader; 330 331 SkMatrix fMatrix, 332 fSubMatrix; 333 NoiseFilter fFilter = NoiseFilter::kNearest; 334 NoiseFractal fFractal = NoiseFractal::kBasic; 335 SkV2 fNoisePlanes = {0,0}; 336 float fNoiseWeight = 0, 337 fOctaves = 1, 338 fPersistence = 1; 339 340 using INHERITED = sksg::CustomRenderNode; 341}; 342 343class FractalNoiseAdapter final : public DiscardableAdapterBase<FractalNoiseAdapter, 344 FractalNoiseNode> { 345public: 346 FractalNoiseAdapter(const skjson::ArrayValue& jprops, 347 const AnimationBuilder* abuilder, 348 sk_sp<FractalNoiseNode> node) 349 : INHERITED(std::move(node)) 350 { 351 EffectBinder(jprops, *abuilder, this) 352 .bind( 0, fFractalType ) 353 .bind( 1, fNoiseType ) 354 .bind( 2, fInvert ) 355 .bind( 3, fContrast ) 356 .bind( 4, fBrightness ) 357 // 5 -- overflow 358 // 6 -- transform begin-group 359 .bind( 7, fRotation ) 360 .bind( 8, fUniformScaling ) 361 .bind( 9, fScale ) 362 .bind(10, fScaleWidth ) 363 .bind(11, fScaleHeight ) 364 .bind(12, fOffset ) 365 // 13 -- TODO: perspective offset 366 // 14 -- transform end-group 367 .bind(15, fComplexity ) 368 // 16 -- sub settings begin-group 369 .bind(17, fSubInfluence ) 370 .bind(18, fSubScale ) 371 .bind(19, fSubRotation ) 372 .bind(20, fSubOffset ) 373 // 21 -- center subscale 374 // 22 -- sub settings end-group 375 .bind(23, fEvolution ) 376 // 24 -- evolution options begin-group 377 .bind(25, fCycleEvolution ) 378 .bind(26, fCycleRevolutions) 379 .bind(27, fRandomSeed ) 380 // 28 -- evolution options end-group 381 .bind(29, fOpacity ); 382 // 30 -- TODO: blending mode 383 } 384 385private: 386 std::tuple<SkV2, float> noise() const { 387 // Constant chosen to visually match AE's evolution rate. 388 static constexpr auto kEvolutionScale = 0.25f; 389 390 // Evolution inputs: 391 // 392 // * evolution - main evolution control (degrees) 393 // * cycle evolution - flag controlling whether evolution cycles 394 // * cycle revolutions - number of revolutions after which evolution cycles (period) 395 // * random seed - determines an arbitrary starting plane (evolution offset) 396 // 397 // The shader uses evolution floor/ceil to select two noise planes, and the fractional part 398 // to interpolate between the two -> in order to wrap around smoothly, the cycle/period 399 // must be integral. 400 const float 401 evo_rad = SkDegreesToRadians(fEvolution), 402 rev_rad = std::max(fCycleRevolutions, 1.0f)*SK_FloatPI*2, 403 cycle = fCycleEvolution 404 ? SkScalarRoundToScalar(rev_rad*kEvolutionScale) 405 : SK_ScalarMax, 406 // Adjust scale when cycling to ensure an integral period (post scaling). 407 scale = fCycleEvolution 408 ? cycle/rev_rad 409 : kEvolutionScale, 410 offset = SkRandom(static_cast<uint32_t>(fRandomSeed)).nextRangeU(0, 100), 411 evo = evo_rad*scale, 412 evo_ = std::floor(evo), 413 weight = evo - evo_; 414 415 // We want the GLSL mod() flavor. 416 auto glsl_mod = [](float x, float y) { 417 return x - y*std::floor(x/y); 418 }; 419 420 const SkV2 noise_planes = { 421 glsl_mod(evo_ + 0, cycle) + offset, 422 glsl_mod(evo_ + 1, cycle) + offset, 423 }; 424 425 return std::make_tuple(noise_planes, weight); 426 } 427 428 SkMatrix shaderMatrix() const { 429 static constexpr float kGridSize = 64; 430 431 const auto scale = (SkScalarRoundToInt(fUniformScaling) == 1) 432 ? SkV2{fScale, fScale} 433 : SkV2{fScaleWidth, fScaleHeight}; 434 435 return SkMatrix::Translate(fOffset.x, fOffset.y) 436 * SkMatrix::Scale(SkTPin(scale.x, 1.0f, 10000.0f) * 0.01f, 437 SkTPin(scale.y, 1.0f, 10000.0f) * 0.01f) 438 * SkMatrix::RotateDeg(fRotation) 439 * SkMatrix::Scale(kGridSize, kGridSize); 440 } 441 442 SkMatrix subMatrix() const { 443 const auto scale = 100 / SkTPin(fSubScale, 10.0f, 10000.0f); 444 445 return SkMatrix::Translate(-fSubOffset.x * 0.01f, -fSubOffset.y * 0.01f) 446 * SkMatrix::RotateDeg(-fSubRotation) 447 * SkMatrix::Scale(scale, scale); 448 } 449 450 NoiseFilter noiseFilter() const { 451 switch (SkScalarRoundToInt(fNoiseType)) { 452 case 1: return NoiseFilter::kNearest; 453 case 2: return NoiseFilter::kLinear; 454 default: return NoiseFilter::kSoftLinear; 455 } 456 SkUNREACHABLE; 457 } 458 459 NoiseFractal noiseFractal() const { 460 switch (SkScalarRoundToInt(fFractalType)) { 461 case 1: return NoiseFractal::kBasic; 462 case 3: return NoiseFractal::kTurbulentSmooth; 463 case 4: return NoiseFractal::kTurbulentBasic; 464 default: return NoiseFractal::kTurbulentSharp; 465 } 466 SkUNREACHABLE; 467 } 468 469 void onSync() override { 470 const auto& n = this->node(); 471 472 const auto [noise_planes, noise_weight] = this->noise(); 473 474 n->setOctaves(SkTPin(fComplexity, 1.0f, 20.0f)); 475 n->setPersistence(SkTPin(fSubInfluence * 0.01f, 0.0f, 100.0f)); 476 n->setNoisePlanes(noise_planes); 477 n->setNoiseWeight(noise_weight); 478 n->setNoiseFilter(this->noiseFilter()); 479 n->setNoiseFractal(this->noiseFractal()); 480 n->setMatrix(this->shaderMatrix()); 481 n->setSubMatrix(this->subMatrix()); 482 } 483 484 Vec2Value fOffset = {0,0}, 485 fSubOffset = {0,0}; 486 487 ScalarValue fFractalType = 0, 488 fNoiseType = 0, 489 490 fRotation = 0, 491 fUniformScaling = 0, 492 fScale = 100, // used when uniform scaling is selected 493 fScaleWidth = 100, // used when uniform scaling is not selected 494 fScaleHeight = 100, // ^ 495 496 fComplexity = 1, 497 fSubInfluence = 100, 498 fSubScale = 50, 499 fSubRotation = 0, 500 501 fEvolution = 0, 502 fCycleEvolution = 0, 503 fCycleRevolutions = 0, 504 fRandomSeed = 0, 505 506 fOpacity = 100, // TODO 507 fInvert = 0, // TODO 508 fContrast = 100, // TODO 509 fBrightness = 0; // TODO 510 511 using INHERITED = DiscardableAdapterBase<FractalNoiseAdapter, FractalNoiseNode>; 512}; 513 514} // namespace 515 516#endif // SK_ENABLE_SKSL 517 518sk_sp<sksg::RenderNode> EffectBuilder::attachFractalNoiseEffect( 519 const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const { 520#ifdef SK_ENABLE_SKSL 521 auto fractal_noise = sk_make_sp<FractalNoiseNode>(std::move(layer)); 522 523 return fBuilder->attachDiscardableAdapter<FractalNoiseAdapter>(jprops, fBuilder, 524 std::move(fractal_noise)); 525#else 526 // TODO(skia:12197) 527 return layer; 528#endif 529} 530 531} // namespace skottie::internal 532