Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 6130d50

Browse files
Nathaniel NifongSkia Commit-Bot
Nathaniel Nifong
authored and
Skia Commit-Bot
committed
Helper function for camera setup
Change-Id: I839fea29c2e56cfc2a4e4557da3197bf211513eb Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299654 Commit-Queue: Nathaniel Nifong <[email protected]> Reviewed-by: Kevin Lubick <[email protected]>
1 parent ac45f49 commit 6130d50

File tree

5 files changed

+69
-37
lines changed

5 files changed

+69
-37
lines changed

modules/canvaskit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
the normal TypedArray version. The TypedArray which it returns is also backed by WASM memory
2222
and when passed into CanvasKit will be used w/o copying the data (just like
2323
`Malloc.toTypedArray`).
24+
- `SkM44.setupCamera` to return a 4x4 matrix which sets up a perspective view from a camera.
2425

2526
### Changed
2627
- In all places where color arrays are accepted (gradient makers, drawAtlas, and MakeSkVertices),

modules/canvaskit/canvaskit/extra.html

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -438,36 +438,6 @@ <h2> Support for extended color spaces </h2>
438438
surface.drawOnce(drawFrame);
439439
}
440440

441-
// Return the inverse of an SkM44. throw an error if it's not invertible
442-
function mustInvert(m) {
443-
const m2 = CanvasKit.SkM44.invert(m);
444-
if (m2 === null) {
445-
throw "Matrix not invertible";
446-
}
447-
return m2;
448-
}
449-
450-
// TODO(nifong): This function is in desperate need of some explanation of what it does
451-
// cam is a object having eye, coa, up, near, far, angle
452-
function saveCamera(canvas, /* rect */ area, /* scalar */ zscale, cam) {
453-
const camera = CanvasKit.SkM44.lookat(cam.eye, cam.coa, cam.up);
454-
const perspective = CanvasKit.SkM44.perspective(cam.near, cam.far, cam.angle);
455-
// Calculate viewport scale. Even through we know these values are all constants in this
456-
// example it might be handy to change the size later.
457-
const center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0];
458-
const viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale];
459-
const viewport = CanvasKit.SkM44.multiply(
460-
CanvasKit.SkM44.translated(center),
461-
CanvasKit.SkM44.scaled(viewScale));
462-
463-
// want "world" to be in our big coordinates (e.g. area), so apply this inverse
464-
// as part of our "camera".
465-
canvas.concat(CanvasKit.SkM44.multiply(viewport, perspective));
466-
canvas.concat(CanvasKit.SkM44.multiply(camera, mustInvert(viewport)));
467-
// Mark the matrix to make it available to the shader by this name.
468-
canvas.markCTM('local_to_world');
469-
}
470-
471441
function Camera3D(canvas, textureImgData, normalImgData, robotoData) {
472442
const surface = CanvasKit.MakeCanvasSurface('camera3d');
473443
if (!surface) {
@@ -618,13 +588,13 @@ <h2> Support for extended color spaces </h2>
618588

619589
function setClickToWorld(canvas, matrix) {
620590
const l2d = canvas.getLocalToDevice();
621-
worldToClick = CanvasKit.SkM44.multiply(mustInvert(matrix), l2d);
622-
clickToWorld = mustInvert(worldToClick);
591+
worldToClick = CanvasKit.SkM44.multiply(CanvasKit.SkM44.mustInvert(matrix), l2d);
592+
clickToWorld = CanvasKit.SkM44.mustInvert(worldToClick);
623593
}
624594

625595
function drawCubeFace(canvas, m, color) {
626596
const trans = new CanvasKit.SkM44.translated([vSphereRadius/2, vSphereRadius/2, 0]);
627-
canvas.concat(CanvasKit.SkM44.multiply(trans, m, mustInvert(trans)));
597+
canvas.concat(CanvasKit.SkM44.multiply(trans, m, CanvasKit.SkM44.mustInvert(trans)));
628598
const znormal = front(canvas.getLocalToDevice());
629599
if (znormal < 0) {
630600
return; // skip faces facing backwards
@@ -645,7 +615,10 @@ <h2> Support for extended color spaces </h2>
645615
canvas.save();
646616
canvas.translate(vSphereCenter[0] - vSphereRadius/2, vSphereCenter[1] - vSphereRadius/2);
647617
// pass surface dimensions as viewport size.
648-
saveCamera(canvas, CanvasKit.LTRBRect(0, 0, vSphereRadius, vSphereRadius), vSphereRadius/2, cam);
618+
canvas.concat(CanvasKit.SkM44.setupCamera(
619+
CanvasKit.LTRBRect(0, 0, vSphereRadius, vSphereRadius), vSphereRadius/2, cam));
620+
// Mark the matrix to make it available to the shader by this name.
621+
canvas.markCTM('local_to_world');
649622
setClickToWorld(canvas, clickM);
650623
for (let f of faces) {
651624
const saveCount = canvas.getSaveCount();
@@ -863,7 +836,8 @@ <h2> Support for extended color spaces </h2>
863836
}
864837
canvas.save();
865838
// Set up 3D view enviroment
866-
saveCamera(canvas, CanvasKit.LTRBRect(0, 0, sizeX, sizeY), halfDim, cam);
839+
canvas.concat(CanvasKit.SkM44.setupCamera(
840+
CanvasKit.LTRBRect(0, 0, sizeX, sizeY), halfDim, cam));
867841

868842
// Rotate the whole paragraph as a unit.
869843
const paraRotPoint = [halfDim, halfDim, 1];

modules/canvaskit/externs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ var CanvasKit = {
339339
SkM44: {
340340
identity: function() {},
341341
invert: function() {},
342+
mustInvert: function() {},
342343
multiply: function() {},
343344
rotatedUnitSinCos: function() {},
344345
rotated: function() {},
@@ -348,6 +349,7 @@ var CanvasKit = {
348349
perspective: function() {},
349350
rc: function() {},
350351
transpose: function() {},
352+
setupCamera: function() {},
351353
},
352354

353355
SkMatrix: {

modules/canvaskit/interface.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,40 @@ CanvasKit.onRuntimeInitialized = function() {
434434
];
435435
}
436436

437+
// Return the inverse of an SkM44. throw an error if it's not invertible
438+
CanvasKit.SkM44.mustInvert = function(m) {
439+
var m2 = CanvasKit.SkM44.invert(m);
440+
if (m2 === null) {
441+
throw "Matrix not invertible";
442+
}
443+
return m2;
444+
}
445+
446+
// returns a matrix that sets up a 3D perspective view from a given camera.
447+
//
448+
// area - a rect describing the viewport. (0, 0, canvas_width, canvas_height) suggested
449+
// zscale - a scalar describing the scale of the z axis. min(width, height)/2 suggested
450+
// cam - an object with the following attributes
451+
// const cam = {
452+
// 'eye' : [0, 0, 1 / Math.tan(Math.PI / 24) - 1], // a 3D point locating the camera
453+
// 'coa' : [0, 0, 0], // center of attention - the 3D point the camera is looking at.
454+
// 'up' : [0, 1, 0], // a unit vector pointing in the camera's up direction, because eye and coa alone leave roll unspecified.
455+
// 'near' : 0.02, // near clipping plane
456+
// 'far' : 4, // far clipping plane
457+
// 'angle': Math.PI / 12, // field of view in radians
458+
// };
459+
CanvasKit.SkM44.setupCamera = function(area, zscale, cam) {
460+
var camera = CanvasKit.SkM44.lookat(cam['eye'], cam['coa'], cam['up']);
461+
var perspective = CanvasKit.SkM44.perspective(cam['near'], cam['far'], cam['angle']);
462+
var center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0];
463+
var viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale];
464+
var viewport = CanvasKit.SkM44.multiply(
465+
CanvasKit.SkM44.translated(center),
466+
CanvasKit.SkM44.scaled(viewScale));
467+
return CanvasKit.SkM44.multiply(
468+
viewport, perspective, camera, CanvasKit.SkM44.mustInvert(viewport));
469+
}
470+
437471
// An SkColorMatrix is a 4x4 color matrix that transforms the 4 color channels
438472
// with a 1x4 matrix that post-translates those 4 channels.
439473
// For example, the following is the layout with the scale (S) and post-transform

modules/canvaskit/tests/matrix.spec.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ describe('CanvasKit\'s Matrix Helpers', () => {
44
await LoadCanvasKit;
55
});
66

7-
const expectArrayCloseTo = (a, b) => {
7+
const expectArrayCloseTo = (a, b, precision) => {
8+
precision = precision || 14 // digits of precision in base 10
89
expect(a.length).toEqual(b.length);
910
for (let i=0; i<a.length; i++) {
10-
expect(a[i]).toBeCloseTo(b[i], 14); // 14 digits of precision in base 10
11+
expect(a[i]).toBeCloseTo(b[i], precision);
1112
}
1213
};
1314

@@ -185,5 +186,25 @@ describe('CanvasKit\'s Matrix Helpers', () => {
185186
CanvasKit.SkM44.multiply(a, b),
186187
CanvasKit.SkM44.identity());
187188
});
189+
190+
it('can create a camera setup matrix', () => {
191+
const camAngle = Math.PI / 12;
192+
const cam = {
193+
'eye' : [0, 0, 1 / Math.tan(camAngle/2) - 1],
194+
'coa' : [0, 0, 0],
195+
'up' : [0, 1, 0],
196+
'near' : 0.02,
197+
'far' : 4,
198+
'angle': camAngle,
199+
};
200+
const mat = CanvasKit.SkM44.setupCamera(CanvasKit.LTRBRect(0, 0, 200, 200), 200, cam);
201+
// these values came from an invocation of setupCamera visually inspected.
202+
const expected = [
203+
7.595754, 0, -0.5, 0,
204+
0, 7.595754, -0.5, 0,
205+
0, 0, 1.010050, -1324.368418,
206+
0, 0, -0.005, 7.595754];
207+
expectArrayCloseTo(mat, expected, 5);
208+
});
188209
}); // describe 4x4
189210
});

0 commit comments

Comments
 (0)