3
\$\begingroup\$

Technically this is a webgl question though I think it's generic enough to get some opengl, erm, perspective on it too...

If I setup an orthographic camera (perspective matrix) like this:

mat4.ortho(output, 0, screenWidth, 0, screenHeight, 0, 1)

And compose it with an identity matrix as the view, and then compose my model matrix with that - everything is fine and I see the items at the correct size on the screen. However, if instead of using an identity matrix as the view I use lookAt via:

mat4.lookAt(output, [0,0,0], [0,0,1], [0,1,0])

I do not see anything. I've tried some other variations like moving the eye parameter from [0,0,0] to [0,0,-1] and increasing the far value of ortho() but nothing worked.

What exactly is the relationship between lookAt and ortho - are they compatible for perspectivexview, if so what settings can I use for lookAt such that it works correctly here?

\$\endgroup\$
4
\$\begingroup\$

When I use the identity view matrix, I can see my model, but when I use LookAt, it is not visible. How can I see my model using LookAt?

The LookAt function takes three parameters:

  • eye, which is where your camera is. in the identity matrix, your camera is at 0, 0, 0
  • target, which is where your camera is pointed at. you can think of the camera's direction vector as target - eye. In the identity matrix, the camera is facing in the direction of the negative Z axis.
  • up, which is which way your camera is rotated. 0, 1, 0 is the default up in the usual coordinate system.

In order to replicate your identity view matrix, just use mat4.lookat(.., [0, 0, 0], [0, 0, -1], [0, 1, 0]).

If the above works, now you can experiment with changing the eye parameter to move the camera around.

If I set up an ortho camera like this: mat4.ortho(output, 0, screenWidth, 0, screenHeight, 0, 1), things go wrong.

Those parameters dont' look right to me. it should be mat4.ortho(output, -scaleX, scaleX, -scaleY, scaleY, 0, farZ) . Depending on your coordinates and aspect ratio, the values of scaleX and scaleY are roughly half the distance between your camera and target to get a similar scale of view to the perspective camera. If you have no ideal, start scaleX and scaleY off at 1. additionally, farZ = 1 is a very short range, you may want to raise it up much higher, especially if you're moving the camera around.

Playground

I've included a basic "rotating camera around a triangle" demo here, the left side is a perspective projection, the right side is an ortho projection. Feel free to play with the distance variable and see how the scale for the ortho changes in proportion to the perspective shrinking as the camera is further away.

cv.width = window.innerWidth;
cv.height = window.innerHeight;
var gl = cv.getContext('webgl');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);

function Mesh(arr) {
  var buf = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
  return buf;
}
var mesh = Mesh([0.0, 1.0, -0,-1.0, -1.0, -0,1.0, -1.0, -0 ])

function shader(type, src) { 
  var s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  return s
}
var program = gl.createProgram();
gl.attachShader(program, shader(gl.VERTEX_SHADER, `
attribute vec3 pos; 
uniform mat4 view;
uniform mat4 projection;
void main() { gl_Position = projection * view * vec4(pos, 1); }
`));
gl.attachShader(program, shader(gl.FRAGMENT_SHADER, "void main() { gl_FragColor = vec4(1); }"));
gl.linkProgram(program);
gl.validateProgram(program);
gl.useProgram(program)
gl.bindBuffer(gl.ARRAY_BUFFER, mesh);
gl.enableVertexAttribArray(program, 0);
function render(viewport, proj_mat, view_mat) { 
    gl.viewport.apply(gl, viewport);
    gl.uniformMatrix4fv(
      gl.getUniformLocation(program, 'projection'), false, proj_mat)
    gl.uniformMatrix4fv(gl.getUniformLocation(program, 'view'), false, view_mat)
    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
}
var view = mat4.create();
var ortho_proj = mat4.create();
var perspective_proj = mat4.create();
var fov = 0.7, distance = 4;
var scale = Math.tan(fov / 2) * distance; // since ortho is distance invariant, scale by (distance from camera to some reference point)

function loop(t) { 
t /= 1000;
mat4.perspective(perspective_proj, fov, 1, 0.1, 1000.0);
mat4.ortho(ortho_proj, -scale, scale, -scale, scale, 0.1, 100);
mat4.lookAt(view, [distance * Math.sin(t),0, distance * Math.cos(t)],[0,0,0],[0,1,0]);

render([0, 0, cv.width / 2, cv.height], perspective_proj, view);
render([cv.width / 2, 0, cv.width / 2, cv.height], ortho_proj, view);
  requestAnimationFrame(loop);
}
loop();
body, html { margin:0px; padding:0px; }
canvas { background: #39e; position:absolute; top:0px; }
h2, h3 { position:absolute; top:0px; z-index: 20; }
.r { right:0px; }
<h2>Perspective</h2>
<h2 class=r>Ortho</h2>
<canvas id=cv> </canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.4.0/gl-matrix.js"></script>


Obsolete Answer

This first answer to a supposed question "what, in the general sense, is the relationship between an ortho and a lookAt matrix?".

There's 3 kinds of matrices that combine to form a full transformation:

the model matrix, which describes the transformation applied to the vertices of your model to get them into world-space,

the view matrix, which describes the transformation to get from your world-space into a camera-relative space (eye coordinates),

the perspective matrix to describe how to transform the camera-space positions into how it appears on your screen (clip-space / screen-space after the perspective correction).

In your case, ortho and perspective are ways to create the perspective matrix, and lookAt creates a view matrix (you could also manually create a view matrix by combining a bunch of mat4.translate/rotate/scales to re-create the camera position).

Your final transform must multiply the perspective * view * model matrices.

\$\endgroup\$
  • \$\begingroup\$ Oh, my mistake - I thought that was implied in my original question but I see that it wasn't. I've now edited to clarify my question in this context, thanks for pointing that out! \$\endgroup\$ – davidkomer Nov 1 '17 at 11:18
  • \$\begingroup\$ @davidkomer I've added an explanation for what I think your question is: (what is the identity lookAt parameters?) See if that is more helpful. \$\endgroup\$ – Jimmy Nov 1 '17 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.