<p>Given that you already use yaw and pitch angles to describe the camera, the simplest solution is to compute yaw and pitch values for the vector (note: it must be a unit vector).</p>

<p>If you have <code>front</code> vector given, you can compute angles:</p>

<pre><code>pitch = MathHelper.radToDeg(Math.asin(front[1]));
yaw = MathHelper.radToDeg(Math.atan2(front[2], front[1]));
</code></pre>

CC BY-SA 4.0
2018-06-21T20:54:50.043Z
2018-06-21T20:54:50.043Z
joe_chip
<p>I am following <a href="https://learnopengl.com/Getting-started/Camera" rel="nofollow noreferrer">this tutorial</a> on how to implement a first person camera. So far I was able to implement <a href="https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h" rel="nofollow noreferrer">the entire (C++) class</a> in TypeScript and it works as it is supposed to.</p>

<h3>Problem</h3>

<p>What I was not able to implement is a system which takes the camera direction only and initially orients the camera based on it. In the tutorial the author does set the camera direction to <code>(0.0, 0.0, -1.0)</code> but it is instantly overwritten by calling <code>_updateCameraVectors()</code> in the constructor. So as I see it, passing the camera direction to the class will always be ignored. Before looking at the code please note:</p>

<ul>
<li>In the tutorial the author referes to a variable <code>Front</code>, which in my case is called <code>cameraDirection</code></li>
<li>In the tutorial only the view matrix is being calculated by the camera class. I am calculating the view-projection matrix.</li>
</ul>

<h3>Code</h3>

<p>Here are the most important parts:</p>

<p>The constructor initializes all the important stuff, but most importantly calls <code>_updateCameraVectors()</code>.</p>

<pre><code>constructor(gl: WebGL2RenderingContext, program: WebGLProgram, position: vec3, cameraDirection = vec3.fromValues(0.0, 0.0, -1.0),
    yaw = -90.0, pitch = 0.0,
    aspect = gl.canvas.clientWidth / gl.canvas.clientHeight, zNear = 1, zFar = 2000) {
    this.gl = gl;
    this._program = program;
    this._aspect = aspect;
    this._zNear = zNear;
    this._zFar = zFar;
    this._yaw = yaw;
    this._pitch = pitch;
    this._cameraPosition = position;
    this._cameraDirection = cameraDirection;

    this._updateCameraVectors();
    GLHelper.setUniformLocations(this.gl, this._unifroms, this._program);
}
</code></pre>

<p>In <code>_updateCameraVectors()</code> the camera direction is being recalculated based on yaw and pitch values. Then the <code>cameraUp</code> vector is calculated.</p>

<pre><code>protected _updateCameraVectors(): void {
    const front = vec3.create();
    front[0] = Math.cos(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
    front[1] = Math.sin(MathHelper.degToRad(this._pitch));
    front[2] = Math.sin(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
    vec3.normalize(this._cameraDirection, front);

    const right = vec3.create();
    vec3.cross(right, this._cameraDirection, this._worldUp); // World up is always (0,1,0)
    vec3.normalize(this._cameraRight, right);
    const up = vec3.create();
    vec3.cross(up, this._cameraRight, this._cameraDirection);
    vec3.normalize(this._cameraUp, up);
}
</code></pre>

<p>Based on a <code>requestAnimationFrame</code>-loop the <code>_computeviewProjectionMatrix</code> simply calculates a view-projection matrix which is later used for rendering.</p>

<pre><code>private _computeviewProjectionMatrix(): mat4 {

    this._aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight;

    const cameraTarget = vec3.create();
    vec3.add(cameraTarget, this._cameraPosition, this._cameraDirection); // Here the actual camera target is calculated.

    const view = mat4.create();
    mat4.lookAt(view, this._cameraPosition, cameraTarget, this._cameraUp);

    const projectionMatrix = mat4.create();

    mat4.perspective(projectionMatrix, this._fieldOfViewRadians, this._aspect, this._zNear, this._zFar);

    const viewProjectionMatrix = mat4.create();
    mat4.multiply(viewProjectionMatrix, projectionMatrix, view);

    return viewProjectionMatrix;
}
</code></pre>

<p>The <code>_handleMouseMovement</code> function calls <code>_updateCameraVectors()</code> every time the <code>pitch</code> and <code>yaw</code> values are updated via mouse movement.</p>

<pre><code>private _handleMouseMovement = (event: MouseEvent): void =&gt; {

    if (this._firstMouseMove) {
        this._firstMouseMove = false;
        return;
    }

    let xOffset = event.movementX;
    let yOffset = event.movementY;

    xOffset *= this._sensitivity;
    yOffset *= this._sensitivity;

    this._yaw += xOffset;

    if (!this._inverseY) {
        this._pitch += yOffset;
    } else {
        this._pitch -= yOffset;
    }

    if (this._pitch &gt; 89) {
        this._pitch = 89;
    }
    if (this._pitch &lt; -89) {
        this._pitch = -89;
    }

    this._updateCameraVectors();
}
</code></pre>

<p>Last but not least there is a <code>_moveCamera</code> function which updates the camera position based on WASD input. I will post only a excerpt because it is selfexplonatory.</p>

<pre><code>private _moveCamera(): void {
    // INFO: 'W' - Move forward
    if (this._keyMap[87] === true) {
        const factor = vec3.create();
        vec3.scale(factor, this._cameraDirection, this._cameraSpeed * this._deltaTime);
        vec3.add(this._cameraPosition, this._cameraPosition, factor);
    }

    // ...

}
</code></pre>

<h3>Question</h3>

<p>How would I modify this class to initially "lookAt" a vec3 point passed to the constructor? Note I am not really asking for code only. Suggestion, ideas and any advice would be really helpful.</p>

CC BY-SA 4.0
2018-11-19T00:13:58.173Z
Haven't read through the entire question yet but note that just a view direction is underspecified for the full camera matrix as it does not account for the roll (aka the "up direction" of the camera.
jeremyong
<webgl><camera>
Computing a lookAt matrix by passing a vec3 to a camera object
チーズパン
# Computing a lookAt matrix by passing a vec3 to a camera object

(Asked by: username_0 on 2018-06-20)

I am following this tutorial on how to implement a first person camera. So far I was able to implement the entire (C++) class in TypeScript and it works as it is supposed to.
Problem
What I was not able to implement is a system which takes the camera direction only and initially orients the camera based on it. In the tutorial the author does set the camera direction to (0.0, 0.0, -1.0) but it is instantly overwritten by calling _updateCameraVectors() in the constructor. So as I see it, passing the camera direction to the class will always be ignored. Before looking at the code please note:

In the tutorial the author referes to a variable Front, which in my case is called cameraDirection
In the tutorial only the view matrix is being calculated by the camera class. I am calculating the view-projection matrix.

Code
Here are the most important parts:
The constructor initializes all the important stuff, but most importantly calls _updateCameraVectors().
constructor(gl: WebGL2RenderingContext, program: WebGLProgram, position: vec3, cameraDirection = vec3.fromValues(0.0, 0.0, -1.0),
    yaw = -90.0, pitch = 0.0,
    aspect = gl.canvas.clientWidth / gl.canvas.clientHeight, zNear = 1, zFar = 2000) {
    this.gl = gl;
    this._program = program;
    this._aspect = aspect;
    this._zNear = zNear;
    this._zFar = zFar;
    this._yaw = yaw;
    this._pitch = pitch;
    this._cameraPosition = position;
    this._cameraDirection = cameraDirection;

    this._updateCameraVectors();
    GLHelper.setUniformLocations(this.gl, this._unifroms, this._program);
}

In _updateCameraVectors() the camera direction is being recalculated based on yaw and pitch values. Then the cameraUp vector is calculated.
protected _updateCameraVectors(): void {
    const front = vec3.create();
    front[0] = Math.cos(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
    front[1] = Math.sin(MathHelper.degToRad(this._pitch));
    front[2] = Math.sin(MathHelper.degToRad(this._yaw)) * Math.cos(MathHelper.degToRad(this._pitch));
    vec3.normalize(this._cameraDirection, front);

    const right = vec3.create();
    vec3.cross(right, this._cameraDirection, this._worldUp); // World up is always (0,1,0)
    vec3.normalize(this._cameraRight, right);
    const up = vec3.create();
    vec3.cross(up, this._cameraRight, this._cameraDirection);
    vec3.normalize(this._cameraUp, up);
}

Based on a requestAnimationFrame-loop the _computeviewProjectionMatrix simply calculates a view-projection matrix which is later used for rendering.
private _computeviewProjectionMatrix(): mat4 {

    this._aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight;

    const cameraTarget = vec3.create();
    vec3.add(cameraTarget, this._cameraPosition, this._cameraDirection); // Here the actual camera target is calculated.

    const view = mat4.create();
    mat4.lookAt(view, this._cameraPosition, cameraTarget, this._cameraUp);

    const projectionMatrix = mat4.create();

    mat4.perspective(projectionMatrix, this._fieldOfViewRadians, this._aspect, this._zNear, this._zFar);

    const viewProjectionMatrix = mat4.create();
    mat4.multiply(viewProjectionMatrix, projectionMatrix, view);

    return viewProjectionMatrix;
}

The _handleMouseMovement function calls _updateCameraVectors() every time the pitch and yaw values are updated via mouse movement.
private _handleMouseMovement = (event: MouseEvent): void => {

    if (this._firstMouseMove) {
        this._firstMouseMove = false;
        return;
    }

    let xOffset = event.movementX;
    let yOffset = event.movementY;

    xOffset *= this._sensitivity;
    yOffset *= this._sensitivity;

    this._yaw += xOffset;

    if (!this._inverseY) {
        this._pitch += yOffset;
    } else {
        this._pitch -= yOffset;
    }

    if (this._pitch > 89) {
        this._pitch = 89;
    }
    if (this._pitch < -89) {
        this._pitch = -89;
    }

    this._updateCameraVectors();
}

Last but not least there is a _moveCamera function which updates the camera position based on WASD input. I will post only a excerpt because it is selfexplonatory.
private _moveCamera(): void {
    // INFO: 'W' - Move forward
    if (this._keyMap[87] === true) {
        const factor = vec3.create();
        vec3.scale(factor, this._cameraDirection, this._cameraSpeed * this._deltaTime);
        vec3.add(this._cameraPosition, this._cameraPosition, factor);
    }

    // ...

}

Question
How would I modify this class to initially "lookAt" a vec3 point passed to the constructor? Note I am not really asking for code only. Suggestion, ideas and any advice would be really helpful.

**Tags:** webgl, camera
> **Comments:**
> **username_1**: Haven't read through the entire question yet but note that just a view direction is underspecified for the full camera matrix as it does not account for the roll (aka the "up direction" of the camera.  

---

## 1 answers

### Answer by username_2 on 2018-06-21. Score: -1

Given that you already use yaw and pitch angles to describe the camera, the simplest solution is to compute yaw and pitch values for the vector (note: it must be a unit vector).
If you have front vector given, you can compute angles:
pitch = MathHelper.radToDeg(Math.asin(front[1]));
yaw = MathHelper.radToDeg(Math.atan2(front[2], front[1]));


---

