Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.29
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
camera.cpp
1#include <Core/Asset/Camera.hpp>
2#include <Core/Math/Math.hpp>
3#include <Engine/Scene/CameraComponent.hpp>
4
5#include <catch2/catch_test_macros.hpp>
6
7using namespace Ra::Core;
8using namespace Ra::Core::Utils;
9using namespace Ra::Core::Asset;
10
11void testProjectUnproject( const Camera& cam, const Vector3& x, const Vector2& ref ) {
12 std::cout << "x" << x.transpose() << " ref " << ref.transpose() << "\n";
13 Vector3 p = cam.projectToScreen( x );
14 std::cout << "p " << p.transpose() << "\n";
15 if ( ref.head<2>().norm() > 0_ra )
16 REQUIRE( p.head<2>().isApprox( ref ) );
17 else
18 REQUIRE( Math::areApproxEqual( p.head<2>().norm(), 0_ra ) );
19
20 Vector3 q = cam.unProjectFromScreen( p );
21 std::cout << "q " << q.transpose() << "\n";
22 if ( x.norm() > 0_ra )
23 REQUIRE( q.isApprox( x ) );
24 else
25 REQUIRE( Math::areApproxEqual( q.norm(), 0_ra ) );
26
27 Vector3 r = cam.unProjectFromScreen( Vector2 { p.head<2>() } );
28 std::cout << "r " << r.transpose() << "\n";
29
30 Vector3 rRef = ( x - cam.getPosition() );
31 std::cout << "rref " << rRef.transpose() << "\n";
32
33 Scalar ratio = cam.getZNear() / cam.getDirection().dot( rRef );
34 rRef *= ratio;
35 rRef += cam.getPosition();
36 std::cout << "rref " << rRef.transpose() << "\n";
37 if ( rRef.norm() > 0_ra )
38 REQUIRE( r.isApprox( rRef ) );
39 else
40 REQUIRE( Math::areApproxEqual( r.norm(), 0_ra ) );
41}
42
43TEST_CASE( "Core/Camera", "[unittests]" ) {
44
45 auto cam = std::make_unique<Camera>( 10, 20 );
46 REQUIRE( cam->getWidth() == 10 );
47 REQUIRE( cam->getHeight() == 20 );
48 REQUIRE( Math::areApproxEqual( cam->getAspect(), 10_ra / 20_ra ) );
49 REQUIRE( Math::areApproxEqual( cam->getZNear(), 0.1_ra ) );
50 REQUIRE( Math::areApproxEqual( cam->getZFar(), 1000_ra ) );
51
52 Vector3 dir { 1, 1, 1 };
53 Vector3 pos { 0, 0, 0 };
54 cam->setPosition( pos );
55 cam->setDirection( dir );
56
57 REQUIRE( dir.normalized().isApprox( cam->getDirection() ) );
58
59 SECTION( "copy" ) {
60 Camera cam2;
61 cam2.setViewport( 200, 300 );
62 REQUIRE( cam2.getWidth() == 200 );
63 REQUIRE( cam2.getHeight() == 300 );
64 REQUIRE( Math::areApproxEqual( cam2.getAspect(), 200_ra / 300_ra ) );
65
66 cam2 = *cam;
67
68 REQUIRE( Math::areApproxEqual( cam2.getZNear(), 0.1_ra ) );
69 REQUIRE( Math::areApproxEqual( cam2.getZFar(), 1000_ra ) );
70 REQUIRE( dir.normalized().isApprox( cam2.getDirection() ) );
71 REQUIRE( cam2.getWidth() == 10 );
72 REQUIRE( cam2.getHeight() == 20 );
73 REQUIRE( Math::areApproxEqual( cam2.getAspect(), 10_ra / 20_ra ) );
74 auto xymag = cam->getXYmag();
75 auto xymag2 = cam2.getXYmag();
76 REQUIRE( Math::areApproxEqual( xymag.first, xymag2.first ) );
77 REQUIRE( Math::areApproxEqual( xymag.second, xymag2.second ) );
78 }
79
80 SECTION( "setters" ) {
81 Ra::Core::Vector3 newDir { 2, 3, 4 };
82 cam->setDirection( newDir );
83 REQUIRE( newDir.normalized().isApprox( cam->getDirection() ) );
84 // it's still an orthonormal basis
85 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getUpVector() ), 0_ra ) );
86 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getRightVector() ), 0_ra ) );
87 REQUIRE( Math::areApproxEqual( cam->getUpVector().dot( cam->getRightVector() ), 0_ra ) );
88
89 Ra::Core::Vector3 newPos { 2, 3, 4 };
90 cam->setPosition( newPos );
91 REQUIRE( newPos.isApprox( cam->getPosition() ) );
92
93 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getUpVector() ), 0_ra ) );
94 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getRightVector() ), 0_ra ) );
95 REQUIRE( Math::areApproxEqual( cam->getUpVector().dot( cam->getRightVector() ), 0_ra ) );
96
97 Ra::Core::Vector3 newUp { 2, 3, 4 };
98 cam->setUpVector( newUp );
99 REQUIRE( newUp.normalized().isApprox( cam->getUpVector() ) );
100
101 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getUpVector() ), 0_ra ) );
102 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getRightVector() ), 0_ra ) );
103 REQUIRE( Math::areApproxEqual( cam->getUpVector().dot( cam->getRightVector() ), 0_ra ) );
104
105 Ra::Core::Vector3 oppositeDir = -cam->getDirection();
106 Ra::Core::Vector3 oldUp = cam->getUpVector();
107
108 cam->setDirection( oppositeDir );
109
110 REQUIRE( oppositeDir.normalized().isApprox( cam->getDirection() ) );
111 // it's still an orthonormal basis
112 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getUpVector() ), 0_ra ) );
113 REQUIRE( Math::areApproxEqual( cam->getDirection().dot( cam->getRightVector() ), 0_ra ) );
114 REQUIRE( Math::areApproxEqual( cam->getUpVector().dot( cam->getRightVector() ), 0_ra ) );
115 // and up do not have changed
116 REQUIRE( oldUp.isApprox( cam->getUpVector() ) );
117
118 cam->setZNear( 10_ra );
119 REQUIRE( Math::areApproxEqual( cam->getZNear(), 10_ra ) );
120 cam->setZFar( 100_ra );
121 REQUIRE( Math::areApproxEqual( cam->getZFar(), 100_ra ) );
122 REQUIRE( cam->getType() == Camera::ProjType::PERSPECTIVE );
123 cam->setType( Camera::ProjType::ORTHOGRAPHIC );
124 REQUIRE( cam->getType() == Camera::ProjType::ORTHOGRAPHIC );
125 cam->updateProjMatrix();
126 // aspect is 10/20, near 10, far 100
127 Ra::Core::Matrix4 ref;
128 ref << 1_ra, 0_ra, 0_ra, 0_ra, 0_ra, 0.5_ra, 0_ra, 0_ra, 0_ra, 0_ra, -2_ra / 90_ra,
129 -110_ra / 90_ra, 0_ra, 0_ra, 0_ra, 1_ra;
130 REQUIRE( cam->getProjMatrix().isApprox( ref ) );
131
132 // reset frame
133 cam->setFrame( Transform::Identity() );
134 REQUIRE( cam->getFrame().isApprox( Transform::Identity() ) );
135 REQUIRE( cam->getViewMatrix().isApprox( Matrix4::Identity() ) );
136
137 // reset proj
138 cam->setProjMatrix( Matrix4::Identity() );
139 REQUIRE( cam->getProjMatrix().isApprox( Matrix4::Identity() ) );
140
141 // and set fov
142 cam->setFOV( Math::PiDiv2 );
143 REQUIRE( Math::areApproxEqual( cam->getFOV(), Math::PiDiv2 ) );
144 cam->setFOV( Math::PiDiv4 );
145 REQUIRE( Math::areApproxEqual( cam->getFOV(), Math::PiDiv4 ) );
146
147 // set x and y mag
148 cam->setXYmag( 1.414_ra, 1.732_ra );
149 auto xymag = cam->getXYmag();
150 REQUIRE( Math::areApproxEqual( xymag.first, 1.414_ra ) );
151 REQUIRE( Math::areApproxEqual( xymag.second, 1.732_ra ) );
152
153 REQUIRE( Math::areApproxEqual( cam->getZoomFactor(), 1_ra ) );
154 cam->setZoomFactor( 2_ra );
155 REQUIRE( Math::areApproxEqual( cam->getZoomFactor(), 2_ra ) );
156 }
157
158 SECTION( "fit aabb" ) {
159 Ra::Core::Aabb aabb { Ra::Core::Vector3 { 3, 3, 3 }, Ra::Core::Vector3 { 6, 6, 6 } };
160 cam->fitZRange( aabb );
161 REQUIRE(
162 ( Math::areApproxEqual( cam->getZNear(), ( cam->getPosition() - aabb.min() ).norm() ) ||
163 cam->getZNear() == cam->m_minZNear ) );
164 REQUIRE( Math::areApproxEqual(
165 cam->getZFar(), ( cam->getPosition() - aabb.max() ).norm(), 16_ra ) );
166 REQUIRE( ( cam->getZNear() > cam->m_minZNear ||
167 Math::areApproxEqual( cam->getZNear(), cam->m_minZNear ) ) );
168 REQUIRE(
169 ( ( cam->getZFar() - cam->getZNear() ) > cam->m_minZRange ||
170 Math::areApproxEqual( ( cam->getZFar() - cam->getZNear() ), cam->m_minZRange ) ) );
171
172 aabb = { Ra::Core::Vector3 { -1, -1, -1 }, Ra::Core::Vector3 { 1, 1, 1 } };
173 cam->fitZRange( aabb );
174 REQUIRE( Math::areApproxEqual( cam->getZNear(), cam->m_minZNear ) );
175 REQUIRE(
176 ( Math::areApproxEqual( cam->getZFar(), ( cam->getPosition() - aabb.max() ).norm() ) ||
177 Math::areApproxEqual( cam->getZFar() - cam->getZNear(), cam->m_minZRange ) ) );
178 REQUIRE( ( cam->getZNear() > cam->m_minZNear ||
179 Math::areApproxEqual( cam->getZNear(), cam->m_minZNear ) ) );
180 REQUIRE(
181 ( ( cam->getZFar() - cam->getZNear() ) > cam->m_minZRange ||
182 Math::areApproxEqual( ( cam->getZFar() - cam->getZNear() ), cam->m_minZRange ) ) );
183
184 aabb = { Ra::Core::Vector3 { -2, -1, 1 }, Ra::Core::Vector3 { -4, -1, 0 } };
185 cam->fitZRange( aabb );
186 REQUIRE( Math::areApproxEqual( cam->getZNear(), cam->m_minZNear ) );
187 REQUIRE( Math::areApproxEqual( cam->getZFar(), cam->getZNear() + cam->m_minZRange ) );
188
189 aabb = { Ra::Core::Vector3 { 0, -3, 0 }, Ra::Core::Vector3 { 1, -2, 1 } };
190 cam->fitZRange( aabb );
191 REQUIRE( Math::areApproxEqual( cam->getZNear(), cam->m_minZNear ) );
192 REQUIRE( Math::areApproxEqual( cam->getZFar(), cam->getZNear() + cam->m_minZRange ) );
193 }
194
195 SECTION( "projection" ) {
196 auto n = 1_ra;
197 auto f = 10_ra;
198 auto fov = Math::Pi / 4_ra;
199 auto scale = std::tan( fov * .5_ra ) * n;
200 auto aspect = 2_ra;
201
202 auto frustum = Camera::frustum( -scale, +scale, -scale / aspect, +scale / aspect, n, f );
203 REQUIRE( frustum.allFinite() );
204 auto perspective = Camera::perspective( aspect, fov, n, f );
205 REQUIRE( perspective.allFinite() );
206
207 REQUIRE( frustum.isApprox( perspective ) );
208
209 Camera projection { 10_ra, 20_ra };
210 projection.setFOV( fov );
211 projection.setZNear( 1_ra );
212 projection.setZFar( 10_ra );
213 projection.setPosition( { 0_ra, 0_ra, 2_ra } );
214 projection.setDirection( { 0_ra, 0_ra, -1_ra } );
215 projection.updateProjMatrix();
216
217 Vector3 x { 0_ra, 0_ra, 0_ra };
218
219 testProjectUnproject( projection, x, Vector2 { 5_ra, 10_ra } );
220 x = { 0_ra, 0_ra, -3_ra };
221 testProjectUnproject( projection, x, Vector2 { 5_ra, 10_ra } );
222 x = { -std::tan( Math::Pi / 8_ra ), 2_ra * std::tan( Math::Pi / 8_ra ), 1_ra };
223 testProjectUnproject( projection, x, Vector2 { 0_ra, 0_ra } );
224 x = { +std::tan( Math::Pi / 8_ra ), -2_ra * std::tan( Math::Pi / 8_ra ), 1_ra };
225 testProjectUnproject( projection, x, Vector2 { 10_ra, 20_ra } );
226 }
227}
Camera class storing the Camera frame and the projection properties The view direction is -z in camer...
Definition Camera.hpp:16
void setViewport(Scalar width, Scalar height)
Change the viewport size. Also compute aspectRatio.
Definition Camera.cpp:50
Scalar getZFar() const
Return the Z Far plane distance from the camera.
Definition Camera.hpp:293
void updateProjMatrix()
Update the projection matrix according to the current parameters.
Definition Camera.cpp:66
static Core::Matrix4 frustum(Scalar l, Scalar r, Scalar b, Scalar t, Scalar n, Scalar f)
Build a projection matrix from the parameters of the view volume.
Definition Camera.cpp:98
Scalar getWidth() const
Return the width of the viewport.
Definition Camera.hpp:311
void setPosition(const Core::Vector3 &position)
Set the position of the camera to position.
Definition Camera.hpp:248
static Core::Matrix4 perspective(Scalar a, Scalar y, Scalar n, Scalar f)
Definition Camera.cpp:84
Scalar getHeight() const
Return the height of the viewport.
Definition Camera.hpp:315
Core::Vector3 unProjectFromScreen(const Core::Vector2 &pix) const
Definition Camera.cpp:198
void setDirection(const Core::Vector3 &direction)
Definition Camera.cpp:32
void setZNear(Scalar zNear)
Set the Z Near plane distance to zNear.
Definition Camera.hpp:288
void setZFar(Scalar zFar)
Set the Z Far plane distance to zFar.
Definition Camera.hpp:297
Core::Vector3 getPosition() const
Return the position.
Definition Camera.hpp:244
void setZoomFactor(const Scalar &zoomFactor)
Set the zoom factor to zoomFactor.
Definition Camera.hpp:306
Core::Vector3 getUpVector() const
Return the up vector.
Definition Camera.hpp:258
Scalar getZNear() const
Return the Z Near plane distance from the camera.
Definition Camera.hpp:284
void setProjMatrix(Core::Matrix4 projMatrix)
Definition Camera.hpp:347
void setUpVector(const Core::Vector3 &upVector)
Definition Camera.hpp:262
Core::Matrix4 getProjMatrix() const
Return the projection matrix.
Definition Camera.hpp:335
void fitZRange(const Core::Aabb &aabb)
Fit the Z-range of the camera to the scene's aabb. This will maximize z-buffer precision.
Definition Camera.cpp:132
void setType(const ProjType &projectionType)
Set the projection type to projectionType.
Definition Camera.hpp:327
Scalar getZoomFactor() const
Return the zoom factor.
Definition Camera.hpp:302
Core::Vector3 projectToScreen(const Core::Vector3 &p) const
Return the screen coordinates + depth of the given point p (in world coordinates).
Definition Camera.cpp:191
void setFrame(const Core::Transform &frame)
Set the frame of the camera to frame.
Definition Camera.hpp:240
Scalar getFOV() const
Definition Camera.hpp:272
ProjType getType() const
Return the projection type.
Definition Camera.hpp:323
Scalar getAspect() const
Return the aspect ratio of the viewport.
Definition Camera.hpp:319
void setFOV(Scalar fov)
Definition Camera.hpp:276
Core::Transform getFrame() const
Definition Camera.hpp:236
void setXYmag(Scalar xmag, Scalar ymag)
Set xmag and ymag for orthographic camera.
Definition Camera.hpp:351
Core::Vector3 getDirection() const
Return the direction the camera is looking at.
Definition Camera.hpp:254
std::pair< Scalar, Scalar > getXYmag() const
Get xmag and ymag for orthographic camera.
Definition Camera.hpp:359
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.
Definition Math.hpp:42
Quaternion scale(const Quaternion &q, const Scalar k)
Returns the quaternion q multipled by a scalar factor of k.
This namespace contains everything "low level", related to data, datastuctures, and computation.
Definition Cage.cpp:5
T ref(T... args)
T tan(T... args)