/* * Basic camera class * * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) */ #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE //#define GLM_FORCE_LEFT_HANDED #include #include #include class Camera { private: float fov; float znear, zfar; void updateViewMatrix() { glm::mat4 rotM =glm::transpose(rotationMatrix); glm::mat4 transM; /* rotM = glm::rotate(rotM, glm::radians(rotation.x * (flipY ? -1.0f : 1.0f)), glm::vec3(1.0f, 0.0f, 0.0f)); rotM = glm::rotate(rotM, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); rotM = glm::rotate(rotM, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); */ glm::vec3 translation = position; if (flipY) { translation.y *= -1.0f; } transM = glm::translate(glm::mat4(1.0f), translation); if (type == CameraType::firstperson) { matrices.view = rotM * transM; } else { matrices.view = transM * rotM ; // matrices.view = transM * glm::inverse(glm::transpose(rotationMatrix));//LookAt(position, to, up, false, true);//transM * glm::transpose(glm::inverse(rotationMatrix)); //matrices.view = LookAt(position, to, up, false, true); } //viewPos = glm::vec4(position, 0.0f) * glm::vec4(-1.0f, 1.0f, -1.0f, 1.0f); updated = true; }; public: enum CameraType { lookat, firstperson }; CameraType type = CameraType::lookat; glm::vec3 rotation = glm::vec3(); glm::vec3 position = glm::vec3(); glm::vec4 viewPos = glm::vec4(); glm::vec3 from = glm::vec3(); glm::vec3 to = glm::vec3(); glm::vec3 up = glm::vec3(); glm::mat4 rotationMatrix = glm::mat4(); float rotationSpeed = 1.0f; float movementSpeed = 1.0f; bool updated = false; bool flipY = false; struct { glm::mat4 perspective; glm::mat4 view; } matrices; struct { bool left = false; bool right = false; bool up = false; bool down = false; } keys; bool moving() { return keys.left || keys.right || keys.up || keys.down; } float getNearClip() { return znear; } float getFarClip() { return zfar; } void setTo(glm::vec3 to) { this->to = to; } void setUp(glm::vec3 up) { this->up = up; } /* 方案来源: https://zhuanlan.zhihu.com/p/635801612 */ glm::mat4 GetProjectionMatrixFromClip(float zleft, float zright, float zbottom, float ztop, float znear, float zfar, bool zPositive) { if (zPositive) { float A = -(zfar + znear) / (znear - zfar); float B = 2 * (zfar * znear) / (znear - zfar); glm::mat4 T = glm::mat4( 2 * znear / (zright - zleft), 0, -(zright + zleft) / (zright - zleft), 0, 0, 2 * znear / (ztop - zbottom), -(ztop + zbottom) / (ztop - zbottom), 0, 0, 0, A, B, 0, 0, 1, 0); T = glm::transpose(T); // let's transpose it back to column major return T; } else { float A = (zfar + znear) / (znear - zfar); float B = 2 * (zfar * znear) / (znear - zfar); glm::mat4 T = glm::mat4( 2 * znear / (zright - zleft), 0, (zright + zleft) / (zright - zleft), 0, 0, 2 * znear / (ztop - zbottom), (ztop + zbottom) / (ztop - zbottom), 0, 0, 0, A, B, 0, 0, -1, 0); T = glm::transpose(T); // let's transpose it back to column major return T; } } void setProjectionMatrix(float fx, float fy, float u0, float v0, float znear, float zfar, float viewWidth, float viewHeight, bool zPositive, bool rightHand) { float l = -u0 / fx * znear; float r = (viewWidth - u0) / fx * znear; float t = -v0 / fy * znear; float b = (viewHeight - v0) / fy * znear; glm::mat4 perspective = glm::mat4(); if (rightHand) { if (zPositive) { // right hand system and z positive, usually used in 3d reconstruction/SFM/SLAM perspective = GetProjectionMatrixFromClip(l, r, b, t, znear, zfar, zPositive); } else { // right hand system, z negative, usually used in OpenGL perspective = GetProjectionMatrixFromClip(l, r, -b, -t, znear, zfar, zPositive); } } else { if (zPositive) { // left hand system, z positive perspective = GetProjectionMatrixFromClip(l, r, -b, -t, znear, zfar, zPositive); } else { // left hand system, z negative perspective = GetProjectionMatrixFromClip(l, r, b, t, znear, zfar, zPositive); } } matrices.perspective = perspective; } glm::mat4 LookAt(const glm::vec3& from, const glm::vec3& to, const glm::vec3& up, bool zPositive, bool rightHand) { this->from = from; this->to = to; this->up = up; // we stands on "from" and look at "to" // if zPositive = true, the destination is in the area of the positive half-axis of z, otherwise it is negative // if rightHand = true, the coordinate system is right-handed, otherwise it is left-handed glm::vec3 zAxis = glm::normalize(to - from); glm::vec3 yAxis = glm::normalize(up); glm::vec3 xAxis; if (rightHand) { if (zPositive) { // rightHand = true and zPositive = true, usually used in 3d reconstruction yAxis = -yAxis; } else { // rightHand = true and zPositive = false, usually used in OpenGL zAxis = -zAxis; } } else { // left-hand if (!zPositive) { zAxis = -zAxis; yAxis = -yAxis; } } xAxis = glm::normalize(glm::cross(yAxis, zAxis)); yAxis = glm::normalize(glm::cross(zAxis, xAxis)); // glm matrix is column major, so if you set like this, you will notice that the matrix is transposed when you use it. glm::mat3 R = glm::mat3( xAxis.x, xAxis.y, xAxis.z, yAxis.x, yAxis.y, yAxis.z, zAxis.x, zAxis.y, zAxis.z ); //R = rotationMatrix; R = glm::transpose(R); // Let's transpose it back //R = glm::inverse(R); glm::vec3 t = -R * from; // t = -R * from; //R = glm::transpose(R); /**/ glm::mat4 m = glm::mat4( xAxis.x, xAxis.y, xAxis.z, t.x, yAxis.x, yAxis.y, yAxis.z, t.y, zAxis.x, zAxis.y, zAxis.z, t.z, 0, 0, 0, 1); /* glm::mat4 m = glm::mat4( R[0][0], R[0][1], R[0][2], t.x, R[1][0], R[1][1],R[1][2], t.y, R[2][0], R[2][1],R[2][2], t.z, 0, 0, 0, 1); glm::mat4 m = glm::mat4( 1, 0, 0,0 , 0, 1, 0,0 , 0, 0, 1, 0, 0, 0, 0, 1); */ m = glm::transpose(m); // Let's transpose it back //m = glm::inverse(m); return m; } /* GDC2007方案:Koshy George */ void setPerspective(float fx, float fy, float cx, float cy, float znear, float zfar) { this->znear = znear; this->zfar = zfar; float A = -(zfar + znear) / (znear - zfar); float B = -2 * (zfar * znear) / (znear - zfar); glm::mat4 persepctive = glm::mat4(glm::vec4(fx / cx, 0, 0, 0), glm::vec4(0, fy / cy, 0, 0), glm::vec4(0, 0, A, B), glm::vec4(0, 0, -1, 0)); matrices.perspective = persepctive; } void setPerspective(float fov, float aspect, float znear, float zfar) { this->fov = fov; this->znear = znear; this->zfar = zfar; matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); if (flipY) { matrices.perspective[1][1] *= -1.0f; } }; void updateAspectRatio(float aspect) { matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); if (flipY) { matrices.perspective[1][1] *= -1.0f; } } void setPosition(glm::vec3 position) { this->position = position; updateViewMatrix(); } void setRotation(glm::mat3 rotaionMatrix) { this->rotationMatrix = rotaionMatrix; updateViewMatrix(); } void rotate(glm::vec3 delta) { this->rotation += delta; updateViewMatrix(); } void setTranslation(glm::vec3 translation) { this->position = translation; updateViewMatrix(); }; void translate(glm::vec3 delta) { this->position += delta; updateViewMatrix(); } void setRotationSpeed(float rotationSpeed) { this->rotationSpeed = rotationSpeed; } void setMovementSpeed(float movementSpeed) { this->movementSpeed = movementSpeed; } void update(float deltaTime) { updated = false; if (type == CameraType::firstperson) { if (moving()) { glm::vec3 camFront; camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y)); camFront.y = sin(glm::radians(rotation.x)); camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y)); camFront = glm::normalize(camFront); float moveSpeed = deltaTime * movementSpeed; if (keys.up) position += camFront * moveSpeed; if (keys.down) position -= camFront * moveSpeed; if (keys.left) position -= glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed; if (keys.right) position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed; } } updateViewMatrix(); }; // Update camera passing separate axis data (gamepad) // Returns true if view or position has been changed bool updatePad(glm::vec2 axisLeft, glm::vec2 axisRight, float deltaTime) { bool retVal = false; if (type == CameraType::firstperson) { // Use the common console thumbstick layout // Left = view, right = move const float deadZone = 0.0015f; const float range = 1.0f - deadZone; glm::vec3 camFront; camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y)); camFront.y = sin(glm::radians(rotation.x)); camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y)); camFront = glm::normalize(camFront); float moveSpeed = deltaTime * movementSpeed * 2.0f; float rotSpeed = deltaTime * rotationSpeed * 50.0f; // Move if (fabsf(axisLeft.y) > deadZone) { float pos = (fabsf(axisLeft.y) - deadZone) / range; position -= camFront * pos * ((axisLeft.y < 0.0f) ? -1.0f : 1.0f) * moveSpeed; retVal = true; } if (fabsf(axisLeft.x) > deadZone) { float pos = (fabsf(axisLeft.x) - deadZone) / range; position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * pos * ((axisLeft.x < 0.0f) ? -1.0f : 1.0f) * moveSpeed; retVal = true; } // Rotate if (fabsf(axisRight.x) > deadZone) { float pos = (fabsf(axisRight.x) - deadZone) / range; rotation.y += pos * ((axisRight.x < 0.0f) ? -1.0f : 1.0f) * rotSpeed; retVal = true; } if (fabsf(axisRight.y) > deadZone) { float pos = (fabsf(axisRight.y) - deadZone) / range; rotation.x -= pos * ((axisRight.y < 0.0f) ? -1.0f : 1.0f) * rotSpeed; retVal = true; } } else { // todo: move code from example base class for look-at } if (retVal) { updateViewMatrix(); } return retVal; } };