diff --git a/CHANGES.md b/CHANGES.md
index 888bf37ee81..179aa7c095c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -30,20 +30,21 @@ Change Log
 * Added `Globe.showSkirts` to support the ability to hide terrain skirts when viewing terrain from below the surface. [#8489](https://github.com/AnalyticalGraphicsInc/cesium/pull/8489)
 * Fixed `BoundingSphere.projectTo2D` when the bounding sphere’s center is at the origin. [#8482](https://github.com/AnalyticalGraphicsInc/cesium/pull/8482)
 * Added `minificationFilter` and `magnificationFilter` options to `Material` to control texture filtering. [#8473](https://github.com/AnalyticalGraphicsInc/cesium/pull/8473)
-* Update [earcut](https://github.com/mapbox/earcut) to 2.2.1 [#8528](https://github.com/AnalyticalGraphicsInc/cesium/pull/8528)
+* Update [earcut](https://github.com/mapbox/earcut) to 2.2.1. [#8528](https://github.com/AnalyticalGraphicsInc/cesium/pull/8528)
 
 ##### Fixes :wrench:
 * Fixed issue where `RequestScheduler` double-counted image requests made via `createImageBitmap`. [#8162](https://github.com/AnalyticalGraphicsInc/cesium/issues/8162)
 * Fixed a bug where the camera could go underground during mouse navigation. [#8504](https://github.com/AnalyticalGraphicsInc/cesium/pull/8504)
 * Fixed a bug where files with backslashes were not loaded in KMZ files. [#8533](https://github.com/AnalyticalGraphicsInc/cesium/pull/8533)
-* Reduced Cesium bundle size by avoiding unnecessarily importing `Cesium3DTileset` in `Picking.js` [#8532](https://github.com/AnalyticalGraphicsInc/cesium/pull/8532)
+* Reduced Cesium bundle size by avoiding unnecessarily importing `Cesium3DTileset` in `Picking.js`. [#8532](https://github.com/AnalyticalGraphicsInc/cesium/pull/8532)
 * Fixed WebGL warning message about `EXT_float_blend` being implicitly enabled. [#8534](https://github.com/AnalyticalGraphicsInc/cesium/pull/8534)
 * Updated SampleData models to glTF 2.0. [#7802](https://github.com/AnalyticalGraphicsInc/cesium/issues/7802)
-* Fixed a bug where toggling point cloud classification visibility would result in a grey screen on Linux / Nvidia [#8538](https://github.com/AnalyticalGraphicsInc/cesium/pull/8538)
+* Fixed a bug where toggling point cloud classification visibility would result in a grey screen on Linux / Nvidia. [#8538](https://github.com/AnalyticalGraphicsInc/cesium/pull/8538)
 * Fixed a bug where a point in a `PointPrimitiveCollection` is rendered in the middle of the screen instead of being clipped. [#8542](https://github.com/AnalyticalGraphicsInc/cesium/pull/8542)
 * Fixed a crash when deleting and re-creating polylines from CZML. `ReferenceProperty` now returns undefined when the target entity or property does not exist, instead of throwing. [#8544](https://github.com/AnalyticalGraphicsInc/cesium/pull/8544)
 * Fixed a bug where rapidly updating a `PolylineCollection` could result in an instanceIndex is out of range error. [#8546](https://github.com/AnalyticalGraphicsInc/cesium/pull/8546)
 * Fixed a crash that could occur when an entity was deleted while the corresponding `Primitive` was being created asynchronously. [#8569](https://github.com/AnalyticalGraphicsInc/cesium/pull/8569)
+* Fixed a crash when calling `camera.lookAt` with the origin as the target. This could happen when looking at a tileset with the origin as its center. [#8571](https://github.com/AnalyticalGraphicsInc/cesium/pull/8571)
 
 ### 1.65.0 - 2020-01-06
 
diff --git a/Source/Core/Transforms.js b/Source/Core/Transforms.js
index ca9c7c86716..0b775c2f97c 100644
--- a/Source/Core/Transforms.js
+++ b/Source/Core/Transforms.js
@@ -128,8 +128,13 @@ import TimeConstants from './TimeConstants.js';
                 if (!defined(result)) {
                     result = new Matrix4();
                 }
-                // If x and y are zero, assume origin is at a pole, which is a special case.
-                if (CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) && CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14)) {
+                if (Cartesian3.equalsEpsilon(origin, Cartesian3.ZERO, CesiumMath.EPSILON14)) {
+                    // If x, y, and z are zero, use the degenerate local frame, which is a special case
+                    Cartesian3.unpack(degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian);
+                    Cartesian3.unpack(degeneratePositionLocalFrame[secondAxis], 0, scratchSecondCartesian);
+                    Cartesian3.unpack(degeneratePositionLocalFrame[thirdAxis], 0, scratchThirdCartesian);
+                } else if (CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) && CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14)) {
+                    // If x and y are zero, assume origin is at a pole, which is a special case.
                     var sign = CesiumMath.sign(origin.z);
 
                     Cartesian3.unpack(degeneratePositionLocalFrame[firstAxis], 0, scratchFirstCartesian);
diff --git a/Specs/Core/TransformsSpec.js b/Specs/Core/TransformsSpec.js
index 8576a226e7e..4b617118be1 100644
--- a/Specs/Core/TransformsSpec.js
+++ b/Specs/Core/TransformsSpec.js
@@ -69,6 +69,17 @@ describe('Core/Transforms', function() {
         expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
     });
 
+    it('eastNorthUpToFixedFrame works at the origin', function() {
+        var origin = Cartesian3.ZERO;
+        var expectedTranslation = new Cartesian4(0.0, 0.0, 0.0, 1.0);
+
+        var returnedResult = Transforms.eastNorthUpToFixedFrame(origin, Ellipsoid.WGS84);
+        expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(Cartesian4.UNIT_Y); // east
+        expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeX); // north
+        expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // up
+        expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
+    });
+
     it('northEastDownToFixedFrame works without a result parameter', function() {
         var origin = new Cartesian3(1.0, 0.0, 0.0);
         var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0);
@@ -117,6 +128,17 @@ describe('Core/Transforms', function() {
         expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
     });
 
+    it('northEastDownToFixedFrame works at the origin', function() {
+        var origin = Cartesian3.ZERO;
+        var expectedTranslation = new Cartesian4(0.0, 0.0, 0.0, 1.0);
+
+        var returnedResult = Transforms.northEastDownToFixedFrame(origin, Ellipsoid.UNIT_SPHERE);
+        expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(negativeX); // north
+        expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(Cartesian4.UNIT_Y); // east
+        expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(negativeZ); // down
+        expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
+    });
+
     it('northUpEastToFixedFrame works without a result parameter', function() {
         var origin = new Cartesian3(1.0, 0.0, 0.0);
         var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0);
@@ -165,6 +187,17 @@ describe('Core/Transforms', function() {
         expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
     });
 
+    it('northUpEastToFixedFrame works at the origin', function() {
+        var origin = Cartesian3.ZERO;
+        var expectedTranslation = new Cartesian4(0.0, 0.0, 0.0, 1.0);
+
+        var returnedResult = Transforms.northUpEastToFixedFrame(origin, Ellipsoid.UNIT_SPHERE);
+        expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(negativeX); // north
+        expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // up
+        expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_Y); // east
+        expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
+    });
+
     it('northWestUpToFixedFrame works without a result parameter', function() {
         var origin = new Cartesian3(1.0, 0.0, 0.0);
         var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0);
@@ -213,6 +246,17 @@ describe('Core/Transforms', function() {
         expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
     });
 
+    it('northWestUpToFixedFrame works at the origin', function() {
+        var origin = Cartesian3.ZERO;
+        var expectedTranslation = new Cartesian4(0.0, 0.0, 0.0, 1.0);
+
+        var returnedResult = Transforms.northWestUpToFixedFrame(origin, Ellipsoid.UNIT_SPHERE);
+        expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(negativeX); // north
+        expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeY); // west
+        expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // up
+        expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation
+    });
+
     it('normal use of localFrameToFixedFrameGenerator', function() {
         var cartesianTab = [
             new Cartesian3(0.0, 0.0, 1.0),
diff --git a/Specs/Scene/CameraSpec.js b/Specs/Scene/CameraSpec.js
index 0baa4642be5..17ec181357b 100644
--- a/Specs/Scene/CameraSpec.js
+++ b/Specs/Scene/CameraSpec.js
@@ -1499,6 +1499,33 @@ describe('Scene/Camera', function() {
         expect(1.0 - Cartesian3.magnitude(tempCamera.right)).toBeLessThan(CesiumMath.EPSILON14);
     });
 
+    it('lookAt when target is zero', function() {
+        var target = Cartesian3.clone(Cartesian3.ZERO);
+        var offset = new Cartesian3(0.0, -1.0, 0.0);
+
+        var tempCamera = Camera.clone(camera);
+        tempCamera.lookAt(target, offset);
+
+        expect(tempCamera.position).toEqualEpsilon(offset, CesiumMath.EPSILON11);
+        expect(tempCamera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.normalize(offset, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON11);
+        expect(tempCamera.right).toEqualEpsilon(Cartesian3.cross(tempCamera.direction, Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON11);
+        expect(tempCamera.up).toEqualEpsilon(Cartesian3.cross(tempCamera.right, tempCamera.direction, new Cartesian3()), CesiumMath.EPSILON11);
+    });
+
+    it('lookAt when target and camera are zero', function() {
+        var target = Cartesian3.clone(Cartesian3.ZERO);
+        var offset = new Cartesian3(0.0, -1.0, 0.0);
+
+        var tempCamera = Camera.clone(camera);
+        tempCamera.position = Cartesian3.clone(Cartesian3.ZERO);
+        tempCamera.lookAt(target, offset);
+
+        expect(tempCamera.position).toEqualEpsilon(offset, CesiumMath.EPSILON11);
+        expect(tempCamera.direction).toEqualEpsilon(Cartesian3.negate(Cartesian3.normalize(offset, new Cartesian3()), new Cartesian3()), CesiumMath.EPSILON11);
+        expect(tempCamera.right).toEqualEpsilon(Cartesian3.cross(tempCamera.direction, Cartesian3.UNIT_Z, new Cartesian3()), CesiumMath.EPSILON11);
+        expect(tempCamera.up).toEqualEpsilon(Cartesian3.cross(tempCamera.right, tempCamera.direction, new Cartesian3()), CesiumMath.EPSILON11);
+    });
+
     it('lookAt throws with no target parameter', function() {
         expect(function() {
             camera.lookAt(undefined, Cartesian3.ZERO);