@@ -76,7 +76,6 @@ <h2> 3D perspective transformations </h2>
7676 RTShaderAPI1 ( CanvasKit ) ;
7777
7878 SkpExample ( CanvasKit , skpData ) ;
79- Camera3D ( CanvasKit ) ;
8079 } ) ;
8180
8281 fetch ( 'https://storage.googleapis.com/skia-cdn/misc/lego_loader.json' ) . then ( ( resp ) => {
@@ -140,6 +139,9 @@ <h2> 3D perspective transformations </h2>
140139
141140 const loadDog = fetch ( 'https://storage.googleapis.com/skia-cdn/misc/dog.jpg' ) . then ( ( response ) => response . arrayBuffer ( ) ) ;
142141 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 ) } ) ;
143145
144146 function SkottieExample ( CanvasKit , id , jsonStr , bounds , assets ) {
145147 if ( ! CanvasKit || ! jsonStr ) {
@@ -544,7 +546,10 @@ <h2> 3D perspective transformations </h2>
544546 surface . drawOnce ( drawFrame ) ;
545547 }
546548
547- function Camera3D ( canvas ) {
549+ function Camera3D ( canvas , textureImgData , normalImgData ) {
550+ if ( ! canvas ) {
551+
552+ }
548553 const surface = CanvasKit . MakeCanvasSurface ( 'camera3d' ) ;
549554 if ( ! surface ) {
550555 console . error ( 'Could not make surface' ) ;
@@ -554,12 +559,12 @@ <h2> 3D perspective transformations </h2>
554559 const sizeX = document . getElementById ( 'camera3d' ) . width ;
555560 const sizeY = document . getElementById ( 'camera3d' ) . height ;
556561
557- var clickToWorld = CanvasKit . SkM44 . identity ( ) ;
558- var worldToClick = CanvasKit . SkM44 . identity ( ) ;
562+ let clickToWorld = CanvasKit . SkM44 . identity ( ) ;
563+ let worldToClick = CanvasKit . SkM44 . identity ( ) ;
559564 // rotation of the cube shown in the demo
560- var rotation = CanvasKit . SkM44 . identity ( ) ;
565+ let rotation = CanvasKit . SkM44 . identity ( ) ;
561566 // temporary during a click and drag
562- var clickRotation = CanvasKit . SkM44 . identity ( ) ;
567+ let clickRotation = CanvasKit . SkM44 . identity ( ) ;
563568
564569 // A virtual sphere used for tumbling the object on screen.
565570 const vSphereCenter = [ sizeX / 2 , sizeY / 2 ] ;
@@ -578,23 +583,82 @@ <h2> 3D perspective transformations </h2>
578583 const camCOA = [ 0 , 0 , 0 ] ;
579584 const camUp = [ 0 , 1 , 0 ] ;
580585
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
584589
585590 // 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 ) ;
591596
592597 const textPaint = new CanvasKit . SkPaint ( ) ;
593598 textPaint . setColor ( CanvasKit . BLACK ) ;
594599 textPaint . setAntiAlias ( true ) ;
595600 const roboto = CanvasKit . SkFontMgr . RefDefault ( ) . MakeTypefaceFromData ( robotoData ) ;
596601 const textFont = new CanvasKit . SkFont ( roboto , 30 ) ;
597602
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+
598662 // Takes an x and y rotation in radians and a scale and returns a 4x4 matrix used to draw a
599663 // face of the cube in that orientation.
600664 function faceM44 ( rx , ry , scale ) {
@@ -668,11 +732,15 @@ <h2> 3D perspective transformations </h2>
668732 canvas . experimental_concat44 ( CanvasKit . SkM44 . multiply ( trans , m , mustInvert ( trans ) ) ) ;
669733 const znormal = front ( canvas . experimental_getLocalToDevice ( ) ) ;
670734 if ( znormal < 0 ) {
671- return ; // skip faces facing backwards
735+ return ; // skip faces facing backwards
672736 }
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 ] ;
673740 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 ) ;
676744 canvas . drawRRect ( rr , paint ) ;
677745 canvas . drawText ( znormal . toFixed ( 2 ) , faceScale * 0.25 , faceScale * 0.4 , textPaint , textFont ) ;
678746 }
@@ -705,6 +773,8 @@ <h2> 3D perspective transformations </h2>
705773 vSphereCenter [ 0 ] , vSphereCenter [ 1 ] + vSphereRadius , paint ) ;
706774 canvas . drawLine ( vSphereCenter [ 0 ] - vSphereRadius , vSphereCenter [ 1 ] ,
707775 vSphereCenter [ 0 ] + vSphereRadius , vSphereCenter [ 1 ] , paint ) ;
776+
777+ drawLight ( canvas ) ;
708778 }
709779
710780 // convert a 2D point in the circle displayed on screen to a 3D unit vector.
@@ -763,22 +833,40 @@ <h2> 3D perspective transformations </h2>
763833
764834 function interact ( e ) {
765835 const type = e . type ;
836+ let eventPos = [ e . offsetX , e . offsetY ] ;
766837 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 ;
771848 }
772849 return ;
773850 } 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+ }
777860 } else if ( type === 'pointerdown' ) {
861+ // Are we repositioning the light?
862+ if ( CanvasKit . SkVector . dist ( eventPos , lightLocation ) < lightIconRadius ) {
863+ draggingLight = true ;
864+ return ;
865+ }
778866 stopSpinning ( ) ;
779867 mouseDown = true ;
780- clickDown = [ e . offsetX , e . offsetY ] ;
781- lastMouse = clickDown ;
868+ clickDown = eventPos ;
869+ lastMouse = eventPos ;
782870 }
783871 surface . requestAnimationFrame ( drawFrame ) ;
784872 } ;
0 commit comments