@@ -358,10 +358,6 @@ class SamplePointLight3D : public Sample3DView {
358358
359359 bool onChar (SkUnichar uni) override {
360360 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 ;
365361 case ' Z' : fLight .fPos .z += 10 ; return true ;
366362 case ' z' : fLight .fPos .z -= 10 ; return true ;
367363 }
@@ -439,3 +435,193 @@ class SamplePointLight3D : public Sample3DView {
439435 }
440436};
441437DEF_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