@@ -76,7 +76,6 @@ <h2> 3D perspective transformations </h2>
76
76
RTShaderAPI1 ( CanvasKit ) ;
77
77
78
78
SkpExample ( CanvasKit , skpData ) ;
79
- Camera3D ( CanvasKit ) ;
80
79
} ) ;
81
80
82
81
fetch ( 'https://storage.googleapis.com/skia-cdn/misc/lego_loader.json' ) . then ( ( resp ) => {
@@ -140,6 +139,9 @@ <h2> 3D perspective transformations </h2>
140
139
141
140
const loadDog = fetch ( 'https://storage.googleapis.com/skia-cdn/misc/dog.jpg' ) . then ( ( response ) => response . arrayBuffer ( ) ) ;
142
141
const loadMandrill = fetch ( 'https://storage.googleapis.com/skia-cdn/misc/mandrill_256.png' ) . then ( ( response ) => response . arrayBuffer ( ) ) ;
142
+ const loadBrickTex = fetch ( './brickwork-texture.jpg' ) . then ( ( response ) => response . arrayBuffer ( ) ) ;
143
+ const loadBrickBump = fetch ( './brickwork_normal-map.jpg' ) . then ( ( response ) => response . arrayBuffer ( ) ) ;
144
+ Promise . all ( [ ckLoaded , loadBrickTex , loadBrickBump ] ) . then ( ( results ) => { Camera3D ( ...results ) } ) ;
143
145
144
146
function SkottieExample ( CanvasKit , id , jsonStr , bounds , assets ) {
145
147
if ( ! CanvasKit || ! jsonStr ) {
@@ -544,7 +546,10 @@ <h2> 3D perspective transformations </h2>
544
546
surface . drawOnce ( drawFrame ) ;
545
547
}
546
548
547
- function Camera3D ( canvas ) {
549
+ function Camera3D ( canvas , textureImgData , normalImgData ) {
550
+ if ( ! canvas ) {
551
+
552
+ }
548
553
const surface = CanvasKit . MakeCanvasSurface ( 'camera3d' ) ;
549
554
if ( ! surface ) {
550
555
console . error ( 'Could not make surface' ) ;
@@ -554,12 +559,12 @@ <h2> 3D perspective transformations </h2>
554
559
const sizeX = document . getElementById ( 'camera3d' ) . width ;
555
560
const sizeY = document . getElementById ( 'camera3d' ) . height ;
556
561
557
- var clickToWorld = CanvasKit . SkM44 . identity ( ) ;
558
- var worldToClick = CanvasKit . SkM44 . identity ( ) ;
562
+ let clickToWorld = CanvasKit . SkM44 . identity ( ) ;
563
+ let worldToClick = CanvasKit . SkM44 . identity ( ) ;
559
564
// rotation of the cube shown in the demo
560
- var rotation = CanvasKit . SkM44 . identity ( ) ;
565
+ let rotation = CanvasKit . SkM44 . identity ( ) ;
561
566
// temporary during a click and drag
562
- var clickRotation = CanvasKit . SkM44 . identity ( ) ;
567
+ let clickRotation = CanvasKit . SkM44 . identity ( ) ;
563
568
564
569
// A virtual sphere used for tumbling the object on screen.
565
570
const vSphereCenter = [ sizeX / 2 , sizeY / 2 ] ;
@@ -578,23 +583,82 @@ <h2> 3D perspective transformations </h2>
578
583
const camCOA = [ 0 , 0 , 0 ] ;
579
584
const camUp = [ 0 , 1 , 0 ] ;
580
585
581
- var mouseDown = false ;
582
- var clickDown = [ 0 , 0 ] ; // location of click down
583
- var lastMouse = [ 0 , 0 ] ; // last mouse location
586
+ let mouseDown = false ;
587
+ let clickDown = [ 0 , 0 ] ; // location of click down
588
+ let lastMouse = [ 0 , 0 ] ; // last mouse location
584
589
585
590
// keep spinning after mouse up. Also start spinning on load
586
- var axis = [ 0.4 , 1 , 1 ] ;
587
- var totalSpin = 0 ;
588
- var spinRate = 0.1 ;
589
- var lastRadians = 0 ;
590
- var spinning = setInterval ( keepSpinning , 30 ) ;
591
+ let axis = [ 0.4 , 1 , 1 ] ;
592
+ let totalSpin = 0 ;
593
+ let spinRate = 0.1 ;
594
+ let lastRadians = 0 ;
595
+ let spinning = setInterval ( keepSpinning , 30 ) ;
591
596
592
597
const textPaint = new CanvasKit . SkPaint ( ) ;
593
598
textPaint . setColor ( CanvasKit . BLACK ) ;
594
599
textPaint . setAntiAlias ( true ) ;
595
600
const roboto = CanvasKit . SkFontMgr . RefDefault ( ) . MakeTypefaceFromData ( robotoData ) ;
596
601
const textFont = new CanvasKit . SkFont ( roboto , 30 ) ;
597
602
603
+ const imgscale = CanvasKit . SkMatrix . scaled ( 2 , 2 ) ;
604
+ const textureShader = CanvasKit . MakeImageFromEncoded ( textureImgData ) . makeShader (
605
+ CanvasKit . TileMode . Clamp , CanvasKit . TileMode . Clamp , imgscale ) ;
606
+ const normalShader = CanvasKit . MakeImageFromEncoded ( normalImgData ) . makeShader (
607
+ CanvasKit . TileMode . Clamp , CanvasKit . TileMode . Clamp , imgscale ) ;
608
+ const children = [ textureShader , normalShader ] ;
609
+
610
+ const prog = `
611
+ in fragmentProcessor color_map;
612
+ in fragmentProcessor normal_map;
613
+
614
+ uniform float4x4 localToWorld;
615
+ uniform float4x4 localToWorldAdjInv;
616
+ uniform float3 lightPos;
617
+
618
+ float3 convert_normal_sample(half4 c) {
619
+ float3 n = 2 * c.rgb - 1;
620
+ n.y = -n.y;
621
+ return n;
622
+ }
623
+
624
+ void main(float2 p, inout half4 color) {
625
+ float3 norm = convert_normal_sample(sample(normal_map, p));
626
+ float3 plane_norm = normalize(localToWorld * float4(norm, 0)).xyz;
627
+
628
+ float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
629
+ float3 light_dir = normalize(lightPos - plane_pos);
630
+
631
+ float ambient = 0.2;
632
+ float dp = dot(plane_norm, light_dir);
633
+ float scale = min(ambient + max(dp, 0), 1);
634
+
635
+ color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
636
+ }
637
+ ` ;
638
+ const fact = CanvasKit . SkRuntimeEffect . Make ( prog ) ;
639
+
640
+ // properties of light
641
+ let lightLocation = [ ...vSphereCenter ] ;
642
+ let lightDistance = vSphereRadius ;
643
+ let lightIconRadius = 12 ;
644
+ let draggingLight = false ;
645
+
646
+ function computeLightWorldPos ( ) {
647
+ return CanvasKit . SkVector . add ( CanvasKit . SkVector . mulScalar ( [ ...vSphereCenter , 0 ] , 0.5 ) ,
648
+ CanvasKit . SkVector . mulScalar ( vSphereUnitV3 ( lightLocation ) , lightDistance ) ) ;
649
+ }
650
+
651
+ let lightWorldPos = computeLightWorldPos ( ) ;
652
+
653
+ function drawLight ( canvas ) {
654
+ const paint = new CanvasKit . SkPaint ( ) ;
655
+ paint . setAntiAlias ( true ) ;
656
+ paint . setColor ( CanvasKit . WHITE ) ;
657
+ canvas . drawCircle ( ...lightLocation , lightIconRadius + 2 , paint ) ;
658
+ paint . setColor ( CanvasKit . BLACK ) ;
659
+ canvas . drawCircle ( ...lightLocation , lightIconRadius , paint ) ;
660
+ }
661
+
598
662
// Takes an x and y rotation in radians and a scale and returns a 4x4 matrix used to draw a
599
663
// face of the cube in that orientation.
600
664
function faceM44 ( rx , ry , scale ) {
@@ -668,11 +732,15 @@ <h2> 3D perspective transformations </h2>
668
732
canvas . experimental_concat44 ( CanvasKit . SkM44 . multiply ( trans , m , mustInvert ( trans ) ) ) ;
669
733
const znormal = front ( canvas . experimental_getLocalToDevice ( ) ) ;
670
734
if ( znormal < 0 ) {
671
- return ; // skip faces facing backwards
735
+ return ; // skip faces facing backwards
672
736
}
737
+ const ltw = canvas . experimental_getLocalToWorld ( ) ;
738
+ // shader expects the 4x4 matrices in column major order.
739
+ const uniforms = [ ...CanvasKit . SkM44 . transpose ( ltw ) , ...mustInvert ( ltw ) , ...lightWorldPos ] ;
673
740
const paint = new CanvasKit . SkPaint ( ) ;
674
- paint . setColor ( color ) ;
675
- // TODO replace color with a bump shader
741
+ paint . setAntiAlias ( true ) ;
742
+ const shader = fact . makeShaderWithChildren ( uniforms , true /*=opaque*/ , children ) ;
743
+ paint . setShader ( shader ) ;
676
744
canvas . drawRRect ( rr , paint ) ;
677
745
canvas . drawText ( znormal . toFixed ( 2 ) , faceScale * 0.25 , faceScale * 0.4 , textPaint , textFont ) ;
678
746
}
@@ -705,6 +773,8 @@ <h2> 3D perspective transformations </h2>
705
773
vSphereCenter [ 0 ] , vSphereCenter [ 1 ] + vSphereRadius , paint ) ;
706
774
canvas . drawLine ( vSphereCenter [ 0 ] - vSphereRadius , vSphereCenter [ 1 ] ,
707
775
vSphereCenter [ 0 ] + vSphereRadius , vSphereCenter [ 1 ] , paint ) ;
776
+
777
+ drawLight ( canvas ) ;
708
778
}
709
779
710
780
// convert a 2D point in the circle displayed on screen to a 3D unit vector.
@@ -763,22 +833,40 @@ <h2> 3D perspective transformations </h2>
763
833
764
834
function interact ( e ) {
765
835
const type = e . type ;
836
+ let eventPos = [ e . offsetX , e . offsetY ] ;
766
837
if ( type === 'lostpointercapture' || type === 'pointerup' || type == 'pointerleave' ) {
767
- mouseDown = false ;
768
- if ( spinRate > 0.02 ) {
769
- stopSpinning ( ) ;
770
- spinning = setInterval ( keepSpinning , 30 ) ;
838
+ if ( draggingLight ) {
839
+ draggingLight = false ;
840
+ } else if ( mouseDown ) {
841
+ mouseDown = false ;
842
+ if ( spinRate > 0.02 ) {
843
+ stopSpinning ( ) ;
844
+ spinning = setInterval ( keepSpinning , 30 ) ;
845
+ }
846
+ } else {
847
+ return ;
771
848
}
772
849
return ;
773
850
} else if ( type === 'pointermove' ) {
774
- if ( ! mouseDown ) { return ; }
775
- lastMouse = [ e . offsetX , e . offsetY ] ;
776
- clickRotation = computeVSphereRotation ( clickDown , lastMouse ) ;
851
+ if ( draggingLight ) {
852
+ lightLocation = eventPos ;
853
+ lightWorldPos = computeLightWorldPos ( ) ;
854
+ } else if ( mouseDown ) {
855
+ lastMouse = eventPos ;
856
+ clickRotation = computeVSphereRotation ( clickDown , lastMouse ) ;
857
+ } else {
858
+ return ;
859
+ }
777
860
} else if ( type === 'pointerdown' ) {
861
+ // Are we repositioning the light?
862
+ if ( CanvasKit . SkVector . dist ( eventPos , lightLocation ) < lightIconRadius ) {
863
+ draggingLight = true ;
864
+ return ;
865
+ }
778
866
stopSpinning ( ) ;
779
867
mouseDown = true ;
780
- clickDown = [ e . offsetX , e . offsetY ] ;
781
- lastMouse = clickDown ;
868
+ clickDown = eventPos ;
869
+ lastMouse = eventPos ;
782
870
}
783
871
surface . requestAnimationFrame ( drawFrame ) ;
784
872
} ;
0 commit comments