{"id":246,"date":"2021-10-10T16:45:39","date_gmt":"2021-10-10T16:45:39","guid":{"rendered":"https:\/\/www.spaceflint.com\/?p=246"},"modified":"2025-04-21T05:58:15","modified_gmt":"2025-04-21T05:58:15","slug":"opengl-tips","status":"publish","type":"post","link":"https:\/\/www.spaceflint.com\/?p=246","title":{"rendered":"OpenGL Tips"},"content":{"rendered":"<h2>Camera<\/h2>\n<p>Some informational pages about implementing a camera:<\/p>\n<p><a href=\"https:\/\/gamedev.stackexchange.com\/questions\/136174\">Stack Exchange &#8211; I&#8217;m rotating an object on two axes, so why does it keep twisting around the third axis?<\/a><\/p>\n<p><a href=\"https:\/\/learnopengl.com\/Getting-started\/Camera\">LearnOpenGL &#8211; Camera<\/a><\/p>\n<p><a href=\"https:\/\/www.3dgep.com\/understanding-the-view-matrix\/\">3D Game Engine Programming &#8211; Understanding the View Matrix<\/a><\/p>\n<p><a href=\"https:\/\/www.scratchapixel.com\/lessons\/mathematics-physics-for-computer-graphics\/lookat-function\">Placing a Camera: the LookAt Function<\/a><\/p>\n<p>These generally suggest accumulating yaw and pitch, clamping pitch, and calculating right, up, and forward vectors to create a <strong>LookAt<\/strong> view matrix.<\/p>\n<p>Here we suggest a related approach, wherein per-frame yaw and pitch values are taken as direction vectors in camera space, to calculate a forward vector for <strong>LookAt<\/strong> in world space, while maintaining a consistent up vector.<\/p>\n<p>The result is a camera that reacts consistently to input, regardless of the orientation of the camera.  In other words, when the camera is looking at an object, rotating the camera left will always make the object on the screen appear to move to the right, regardless if the camera is above, or behind, or in any other relation to the object.<\/p>\n<p>The gist of it, as the code below shows, is to transform the vector (<strong>yaw<\/strong>, <strong>pitch<\/strong>, 1, 0) by the inverse of the view matrix.  (The view matrix transforms world coordinates to camera space; its inverse transforms camera space to world coordinates.)  This produces the desired forward vector in world space.<\/p>\n<p>The fourth column of that inverted matrix is the position of the camera (assuming a column-major matrix), and the second column is the camera up vector, both in world space.  Taken together, the camera position, and forward and up vectors are used to construct a new <strong>LookAt<\/strong> view matrix for rendering.<\/p>\n<pre><pre class=\"brush: csharp; gutter: false; title: ; notranslate\" title=\"\">\n\npublic class Camera\n{\n\n\/\/\n\/\/ this float&#x5B;16] array can be passed\n\/\/ as a shader uniform, e.g.:\n\/\/\n\/\/ uniform mat4 ViewMatrix;\n\/\/ gl_Position = ProjectionMatrix\n\/\/             * ViewMatrix\n\/\/             * vec4(aPos, 1.);\n\/\/\n\/\/ use your standard method to overwrite this with\n\/\/ a constructed look-at matrix, whenever the camera\n\/\/ has to be set explicitly, e.g. during scene set-up.\n\/\/\n\npublic readonly float&#x5B;] ViewMatrix = Matrix.Identity();\n\n\/\/\n\/\/ per-frame Update method.\n\/\/\n\/\/ yRotation - negative values rotate left,\n\/\/             positive values rotate right\n\/\/\n\/\/ xRotation - negative values rotate down,\n\/\/             positive values rotate up\n\/\/\n\/\/ zMovement - negative values move backward,\n\/\/             positive values move forward\n\/\/\n\/\/ (the above assumes a left-hand coordinate system\n\/\/ where positive X goes right, positive Y goes up,\n\/\/ and positive Z goes forward into the screen.)\n\/\/\n\/\/ consider pre-multiplying these values by\n\/\/ deltaTime, before using them in this method\n\/\/\n\npublic void Update (\n    float yRotation,    \/\/ yaw left\/right\n    float xRotation,    \/\/ pitch up\/down\n    float zMovement)    \/\/ forward\/back\n{\n    \/\/ the inverse of the view matrix (i.e. world\n    \/\/ to camera) is a matrix that transforms from\n    \/\/ camera to world space\n    var inv = Matrix.Inverse(ViewMatrix);\n\n    \/\/ using rotation input in a vector:\n    \/\/      (yaw, pitch, 1, 0)\n    \/\/ and rotating it into camera space.\n    \/\/ note that w == 0, so translation is\n    \/\/ not applied\n    var vector =\n        new float&#x5B;] { yRotation, xRotation, 1f, 0f };\n    Matrix.TransformVector(vector, inv);\n    var (x, y, z) = (vector&#x5B;0], vector&#x5B;1], vector&#x5B;2]);\n\n    \/\/ the rotated vector is the new forward direction,\n    \/\/ relative to the current camera position\/origin.\n    \/\/ the fourth column of the (inverted view) matrix\n    \/\/ is the translation vector from world to camera,\n    \/\/ i.e., the current camera position\/origin\n    var factor = zMovement\n               \/ MathF.Sqrt(x * x + y * y + z * z);\n    \/\/ array indices are for a column-major matrix:\n    var positionX = inv&#x5B;12] + x * factor;\n    var positionY = inv&#x5B;13] + y * factor;\n    var positionZ = inv&#x5B;14] + z * factor;\n\n    \/\/ finally, use the new position and forward\n    \/\/ direction, and the old up vector, to\n    \/\/ calculate a new view matrix\n    Matrix.SetLookAtMatrix(ViewMatrix,\n                   positionX, positionY, positionZ,\n                   \/\/ camera target (forward vector)\n                   positionX - x,\n                   positionY - y,\n                   positionZ - z,\n                   \/\/ up vector is the second column\n                   \/\/ of the inverted view matrix\n                   inv&#x5B;4], inv&#x5B;5], inv&#x5B;6]);\n}\n\n\/\/\n\/\/ Methods used:\n\/\/\n\/\/ Matrix.Identity returns a float&#x5B;16] identity matrix.\n\/\/\n\/\/ Matrix.Inverse returns a float&#x5B;16] inverse matrix for\n\/\/ some input float&#x5B;16] matrix.\n\/\/\n\/\/ Matrix.TransformVector multiplies float&#x5B;4] vector V\n\/\/ by float&#x5B;16] matrix M, in-place, i.e.:  V = M * V\n\/\/\n\/\/ Matrix.SetLookAtMatrix(M,Position, Target, UpVector)\n\/\/ overwrites float&#x5B;16] matrix M with a look-at matrix\n\/\/ constructed from position, target, and up vector.\n\/\/\n\/\/ Matrix are column-major as expected by OpenGL.\n\/\/\n\/\/ Coordinate system is left-handed with positive X\n\/\/ is right, positive Y is up, positive Z is forward.\n\/\/\n}\n\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Camera Some informational pages about implementing a camera: Stack Exchange &#8211; I&#8217;m rotating an object on two axes, so why does it keep twisting around the third axis? LearnOpenGL &#8211; Camera 3D Game Engine Programming &#8211; Understanding the View Matrix Placing a Camera: the LookAt Function These generally suggest accumulating yaw and pitch, clamping pitch, &hellip; <a href=\"https:\/\/www.spaceflint.com\/?p=246\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">OpenGL Tips<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-246","post","type-post","status-publish","format-standard","hentry","category-dev-tools"],"_links":{"self":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/246","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=246"}],"version-history":[{"count":9,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/246\/revisions"}],"predecessor-version":[{"id":256,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=\/wp\/v2\/posts\/246\/revisions\/256"}],"wp:attachment":[{"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=246"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=246"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.spaceflint.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=246"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}