@@ -358,10 +358,6 @@ class SamplePointLight3D : public Sample3DView {
358
358
359
359
bool onChar (SkUnichar uni) override {
360
360
switch (uni) {
361
- case ' X' : fLight .fPos .x += 10 ; return true ;
362
- case ' x' : fLight .fPos .x -= 10 ; return true ;
363
- case ' Y' : fLight .fPos .y += 10 ; return true ;
364
- case ' y' : fLight .fPos .y -= 10 ; return true ;
365
361
case ' Z' : fLight .fPos .z += 10 ; return true ;
366
362
case ' z' : fLight .fPos .z -= 10 ; return true ;
367
363
}
@@ -439,3 +435,193 @@ class SamplePointLight3D : public Sample3DView {
439
435
}
440
436
};
441
437
DEF_SAMPLE ( return new SamplePointLight3D(); )
438
+
439
+ #include " include/core/SkColorPriv.h"
440
+ #include " include/core/SkSurface.h"
441
+
442
+ static sk_sp<SkImage> make_bump (sk_sp<SkImage> src) {
443
+ src = src->makeRasterImage ();
444
+
445
+ SkPixmap s;
446
+ SkAssertResult (src->peekPixels (&s));
447
+
448
+ SkImageInfo info = SkImageInfo::Make (src->width (), src->height (),
449
+ kR8G8_unorm_SkColorType , kOpaque_SkAlphaType );
450
+
451
+ size_t rb = info.minRowBytes ();
452
+ auto data = SkData::MakeUninitialized (rb * info.height ());
453
+ SkPixmap d = { info, data->writable_data (), rb };
454
+
455
+ const int W = src->width ();
456
+ const int H = src->height ();
457
+
458
+ for (int y = 0 ; y < H; ++y) {
459
+ int y1 = y == H-1 ? 0 : y + 1 ;
460
+ for (int x = 0 ; x < W; ++x) {
461
+ int x1 = x == W-1 ? 0 : x + 1 ;
462
+
463
+ auto lum = [](SkPMColor c) {
464
+ return SkGetPackedR32 (c);
465
+ return (SkGetPackedR32 (c) * 2 + SkGetPackedG32 (c) * 5 + SkGetPackedB32 (c)) >> 3 ;
466
+ };
467
+
468
+ int s00 = lum (*s.addr32 (x, y)),
469
+ s01 = lum (*s.addr32 (x1, y)),
470
+ s10 = lum (*s.addr32 (x, y1 ));
471
+
472
+ auto delta_lum_to_byte = [](int d) {
473
+ SkASSERT (d >= -255 && d <= 255 );
474
+ d >>= 1 ;
475
+ if (d < 0 ) {
476
+ d += 255 ;
477
+ }
478
+ SkASSERT (d >= 0 && d <= 255 );
479
+ return d;
480
+ };
481
+
482
+ int dx = delta_lum_to_byte (s01 - s00);
483
+ int dy = delta_lum_to_byte (s10 - s00);
484
+ *d.writable_addr16 (x, y) = (dx << 8 ) | dy;
485
+ }
486
+ }
487
+ return SkImage::MakeRasterData (info, data, rb);
488
+ }
489
+
490
+ class SampleBump3D : public Sample3DView {
491
+ SkRRect fRR ;
492
+ LightPos fLight = {{200 , 200 , 800 , 1 }, 8 };
493
+
494
+ sk_sp<SkShader> fBmpShader , fImgShader ;
495
+ sk_sp<SkRuntimeEffect> fEffect ;
496
+
497
+ SkM44 fWorldToClick ,
498
+ fClickToWorld ;
499
+
500
+ SkString name () override { return SkString (" bump3d" ); }
501
+
502
+ void onOnceBeforeDraw () override {
503
+ fRR = SkRRect::MakeRectXY ({20 , 20 , 380 , 380 }, 50 , 50 );
504
+ auto img = GetResourceAsImage (" images/brickwork-texture.jpg" );
505
+ fImgShader = img->makeShader (SkMatrix::MakeScale (2 , 2 ));
506
+ fBmpShader = make_bump (img)->makeShader (SkMatrix::MakeScale (2 , 2 ));
507
+
508
+ const char code[] = R"(
509
+ in fragmentProcessor color_map;
510
+ in fragmentProcessor bump_map;
511
+
512
+ uniform float4x4 localToWorld;
513
+ uniform float3 lightPos;
514
+
515
+ float convert_bump_to_delta(float bump) {
516
+ if (bump > 0.5) {
517
+ bump -= 1;
518
+ }
519
+ return bump * 6;
520
+ }
521
+
522
+ void main(float x, float y, inout half4 color) {
523
+ half4 bmp = sample(bump_map);
524
+
525
+ float3 plane_pos = (localToWorld * float4(x, y, 0, 1)).xyz;
526
+
527
+ float3 plane_norm = (localToWorld * float4(
528
+ convert_bump_to_delta(bmp.r),
529
+ convert_bump_to_delta(bmp.g),
530
+ 1, 0)).xyz;
531
+ plane_norm = normalize(plane_norm);
532
+
533
+ float3 light_dir = normalize(lightPos - plane_pos);
534
+ float ambient = 0.4;
535
+ float dp = dot(plane_norm, light_dir);
536
+ float scale = min(ambient + max(dp, 0), 2);
537
+
538
+ color = sample(color_map) * half4(float4(scale, scale, scale, 1));
539
+ }
540
+ )" ;
541
+ auto [effect, error] = SkRuntimeEffect::Make (SkString (code));
542
+ if (!effect) {
543
+ SkDebugf (" runtime error %s\n " , error.c_str ());
544
+ }
545
+ fEffect = effect;
546
+ }
547
+
548
+ bool onChar (SkUnichar uni) override {
549
+ switch (uni) {
550
+ case ' Z' : fLight .fPos .z += 10 ; return true ;
551
+ case ' z' : fLight .fPos .z -= 10 ; return true ;
552
+ }
553
+ return this ->Sample3DView ::onChar (uni);
554
+ }
555
+
556
+ void drawContent (SkCanvas* canvas, const SkMatrix44& m, SkColor color) {
557
+ SkMatrix44 trans;
558
+ trans.setTranslate (200 , 200 , 0 ); // center of the rotation
559
+
560
+ canvas->experimental_concat44 (trans * fRot * m * inv (trans));
561
+
562
+ // wonder if the runtimeeffect can do this reject? (in a setup function)
563
+ if (!front (canvas->experimental_getLocalToDevice ())) {
564
+ return ;
565
+ }
566
+
567
+ struct Uniforms {
568
+ SkM44 fLocalToWorld ;
569
+ SkV3 fLightPos ;
570
+ } uni;
571
+ uni.fLocalToWorld = canvas->experimental_getLocalToWorld ();
572
+ uni.fLightPos = {fLight .fPos .x , fLight .fPos .y , fLight .fPos .z };
573
+ sk_sp<SkData> data = SkData::MakeWithCopy (&uni, sizeof (uni));
574
+ sk_sp<SkShader> children[] = { fImgShader , fBmpShader };
575
+
576
+ SkPaint paint;
577
+ paint.setColor (color);
578
+ paint.setShader (fEffect ->makeShader (data, children, 2 , nullptr , true ));
579
+
580
+ canvas->drawRRect (fRR , paint);
581
+ }
582
+
583
+ void setClickToWorld (SkCanvas* canvas, const SkM44& clickM) {
584
+ auto l2d = canvas->experimental_getLocalToDevice ();
585
+ fWorldToClick = inv (clickM) * l2d;
586
+ fClickToWorld = inv (fWorldToClick );
587
+ }
588
+
589
+ void onDrawContent (SkCanvas* canvas) override {
590
+ if (canvas->getGrContext () == nullptr ) {
591
+ return ;
592
+ }
593
+ SkM44 clickM = canvas->experimental_getLocalToDevice ();
594
+
595
+ canvas->save ();
596
+ canvas->translate (400 , 300 );
597
+
598
+ this ->saveCamera (canvas, {0 , 0 , 400 , 400 }, 200 );
599
+
600
+ this ->setClickToWorld (canvas, clickM);
601
+
602
+ for (auto f : faces) {
603
+ SkAutoCanvasRestore acr (canvas, true );
604
+ this ->drawContent (canvas, f.asM44 (200 ), f.fColor );
605
+ }
606
+
607
+ fLight .draw (canvas);
608
+ canvas->restore ();
609
+ canvas->restore ();
610
+ }
611
+
612
+ Click* onFindClickHandler (SkScalar x, SkScalar y, skui::ModifierKey modi) override {
613
+ auto L = fWorldToClick * fLight .fPos ;
614
+ SkPoint c = project (fClickToWorld , {x, y, L.z /L.w , 1 });
615
+ if (fLight .hitTest (c.fX , c.fY )) {
616
+ return new Click ();
617
+ }
618
+ return nullptr ;
619
+ }
620
+ bool onClick (Click* click) override {
621
+ auto L = fWorldToClick * fLight .fPos ;
622
+ SkPoint c = project (fClickToWorld , {click->fCurr .fX , click->fCurr .fY , L.z /L.w , 1 });
623
+ fLight .update (c.fX , c.fY );
624
+ return true ;
625
+ }
626
+ };
627
+ DEF_SAMPLE ( return new SampleBump3D(); )
0 commit comments