Skip to content

Instantly share code, notes, and snippets.

@AKosmachyov
Last active April 29, 2024 22:36
Show Gist options
  • Save AKosmachyov/916f397246abe00e53da028502cd08e1 to your computer and use it in GitHub Desktop.
Save AKosmachyov/916f397246abe00e53da028502cd08e1 to your computer and use it in GitHub Desktop.
3D notes

3D Function Description

dot - скалярное произведение wiki, Khan Academy

$\vec{a} \cdot \vec{b} = | \vec{a} | | \vec{b} | \cos(\theta)$

Where

  • $| \vec{a} |, | \vec{b} |$ - magnitudes/lengths
  • $\theta$ - is the angle between $\vec{a}$ and $\vec{b}$

Результат dot для 2 нормализированных векторов, отображает их сонаправленность:

  • 1 - повернуты в одну сторону;
  • 0 - перпендикулярны;
  • -1 - повернуты в разные стороны.
function dot( v ) {
  return this.x * v.x + this.y * v.y + this.z * v.z;
}

cross - векторное произведение wiki

Результат cross для 2 нормализированных векторов, третий вектор перпендикулярный первому и второму.

function crossVectors( a, b ) {
  const ax = a.x, ay = a.y, az = a.z;
  const bx = b.x, by = b.y, bz = b.z;

  this.x = ay * bz - az * by;
  this.y = az * bx - ax * bz;
  this.z = ax * by - ay * bx;

  return this;
}

World to Local / Local to World

vector.applyMatrix4 matrixWorld.multiplyMatrices

function worldToLocal( vector ) {
  return vector.applyMatrix4( this.matrixWorld.clone().invert() );
}
function localToWorld( vector ) {
  return vector.applyMatrix4( this.matrixWorld );
}

function updateMatrixWorld( force ) {
  if ( this.parent === null ) {
    this.matrixWorld.copy( this.matrix );
  } else {
    this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
  }
}

Rotation

X-pitch, Y-yaw, Z-roll

// Rotate object during touch
const cameraPosition = new Vector3();
const object = Object3D();
const objectWorldPosition = object.getWorldPosition(new Vector3());
const objectWorldQuaternion = object.getWorldQuaternion(new Quaternion());

// Touch down
const pointStart = new Vector3();
const axis = 'X';
const objectQuaternionStart = object.quaternion.clone();

// Touch move
const pointEnd = new Vector3();
const offset = new Vector3().copy(pointEnd).sub(pointStart);
const ROTATION_SPEED = 10 / objectWorldPosition.distanceTo(cameraPosition);

const unit = {
    X: new Vector3(1, 0, 0),
    Y: new Vector3(0, 1, 0),
    Z: new Vector3(0, 0, 1)
};

const rotationAxis = unit[axis].clone();
const rotationAxisWorld = rotationAxis.clone().applyQuaternion(objectWorldQuaternion);
const rotationAngle = offset.dot(rotationAxisWorld.cross(this.eye).normalize()) * ROTATION_SPEED;

const quaternion = new Quaternion().setFromAxisAngle(rotationAxis, rotationAngle);
object.quaternion.copy(objectQuaternionStart);
object.quaternion.multiply(quaternion).normalize();

Matrix

A(m x s) * B(s x n) = C(m x n)

[1]             [1*2 1*4 1*1]   [2 4  1]
[4] * [2 4 1] = [4*2 4*4 4*1] = [8 16 4] 
[3]             [3*2 3*4 3*1]   [6 12 3]

          [1]
[2 4 1] * [4] = [2*1 + 4*4 + 1*3] = [21]
          [3]

        [3 4]   
[1 2] * [5 6] = [1*3+2*5 1*4+2*6] = [13 16]


[2 -3 1]   [-7 5]   [2*(-7)+(-3)*2+1*4 2*5+(-3)*(-1)+1*3]   [-16 16]
[5 4 -2] * [2 -1] = [5*(-7)+4*2+(-2)*4 5*5+4*(-1)+(-2*3)] = [-35 15] 
           [4  3]

Ray calculation from the camera to the point

if let projection = cameraPOV.camera?.projectionTransform {
    let pointInClipSpace = simd_float4(
        Float(touchPoint.x / viewSize.width) * 2 - 1.0,
        1.0 - Float(touchPoint.y / viewSize.height) * 2,
        0.5,
        1
    )
    let projectionInverse = simd_inverse(simd_float4x4(projection))
    let pointInCameraSpace = simd_mul(projectionInverse, pointInClipSpace)
    var pointInWorldSpace = simd_mul(cameraPOV.simdWorldTransform, pointInCameraSpace)
    pointInWorldSpace = pointInWorldSpace / pointInWorldSpace.w // Homogeneous divide
    
    let rayOrigin = cameraPOV.simdWorldPosition
    let touchPointWorld = simd_float3(pointInWorldSpace.x, pointInWorldSpace.y, pointInWorldSpace.z)
    let rayDirection = simd_normalize(touchPointWorld - rayOrigin)
    let rayQuery = ARRaycastQuery(origin: rayOrigin, direction: rayDirection, allowing: .estimatedPlane, alignment: .any)
    
    let result = sceneView.session.raycast(rayQuery)
}

Literature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment