Add lens shift support for the camera and the perspective projection functions

As Blender docs describe it so nicely: Using lens shift is equivalent to rendering an image with a larger FOV and cropping it off-center.
This can be quite useful for architecture renderings, but I guess also has it's use in other situations.
Note: Didn't make the ICameraSceneNode functions pure virtual so users don't have to update their cameras for this
Also some change in serialization - same as in other places by now, do use existing values as defaults values when they are not found instead of resetting them to 0.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6565 dfc29bdd-3216-0410-991c-e03cc46cb475
This commit is contained in:
cutealien 2023-10-19 16:04:43 +00:00
parent 567f8688e2
commit d4f6d8c17b
5 changed files with 65 additions and 27 deletions

@ -1,6 +1,7 @@
-------------------------- --------------------------
Changes in 1.9 (not yet released) Changes in 1.9 (not yet released)
- Add lens shift support for the camera and the perspective projection functions
- TGA loader no longer reduces 24&32 bit TGA's with palettes to 16 bit. Thanks @erlehmann for report: https://irrlicht.sourceforge.io/forum/viewtopic.php?t=52925 - TGA loader no longer reduces 24&32 bit TGA's with palettes to 16 bit. Thanks @erlehmann for report: https://irrlicht.sourceforge.io/forum/viewtopic.php?t=52925
- Fix compile error with OS X 10.10 SDK, bug #463. Thanks @Ryan Schmidt for report and patch. - Fix compile error with OS X 10.10 SDK, bug #463. Thanks @Ryan Schmidt for report and patch.
- Optimize quaternion::rotationFromTo. Thanks @Robert Eisele for patch and proof (https://raw.org/proof/quaternion-from-two-vectors) - Optimize quaternion::rotationFromTo. Thanks @Robert Eisele for patch and proof (https://raw.org/proof/quaternion-from-two-vectors)

@ -120,6 +120,13 @@ namespace scene
/** \return The field of view of the camera in radians. */ /** \return The field of view of the camera in radians. */
virtual f32 getFOV() const =0; virtual f32 getFOV() const =0;
//! Get the horizontal and vertical lens/projection plane shift
/** \return Project plane offset */
virtual core::vector2df getLensShift() const
{
return core::vector2df(0.f, 0.f);
}
//! Sets the value of the near clipping plane. (default: 1.0f) //! Sets the value of the near clipping plane. (default: 1.0f)
/** Also changes projection matrix and resets IsOrthogonal flag. /** Also changes projection matrix and resets IsOrthogonal flag.
\param zn: New z near value. */ \param zn: New z near value. */
@ -140,6 +147,16 @@ namespace scene
\param fovy: New field of view in radians. */ \param fovy: New field of view in radians. */
virtual void setFOV(f32 fovy) =0; virtual void setFOV(f32 fovy) =0;
//! Set the horizontal and vertical lens/projection plane shift
/** Like rendering a larger field of view and then cropping
it off-center. Allows for things like 2-point perspective.
\param shift: Offset by which the projection plane is moved.
If you move by 1 or -1 it will move the center by half a screen.
Positive X go to the left and positive Y go down.
By default it will be 0,0 */
virtual void setLensShift(const core::vector2df& shift)
{}
//! Get the view frustum. //! Get the view frustum.
/** \return The current view frustum. */ /** \return The current view frustum. */
virtual const SViewFrustum* getViewFrustum() const =0; virtual const SViewFrustum* getViewFrustum() const =0;

@ -311,26 +311,28 @@ namespace core
\param zNear: Distance to near plane \param zNear: Distance to near plane
\param zFar: Distance to far plane \param zFar: Distance to far plane
param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style) param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style)
\param zSign: 1 for left-handed projection matrix, -1 for right-handed projection matrix */ \param zSign: 1 for left-handed projection matrix, -1 for right-handed projection matrix
CMatrix4<T>& buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign); \param shiftX: Shift projection plane of camera left/right
\param shiftY: Shift projection plane of camera up/down */
CMatrix4<T>& buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign, T shiftX, T shiftY);
//! Builds a right-handed perspective projection matrix based on a field of view //! Builds a right-handed perspective projection matrix based on a field of view
//\param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style). //\param zClipFromZero: Clipping of z can be projected from 0 to w when true (D3D style) and from -w to w when false (OGL style).
CMatrix4<T>& buildProjectionMatrixPerspectiveFovRH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true); CMatrix4<T>& buildProjectionMatrixPerspectiveFovRH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f);
//! Builds a left-handed perspective projection matrix based on a field of view //! Builds a left-handed perspective projection matrix based on a field of view
CMatrix4<T>& buildProjectionMatrixPerspectiveFovLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true); CMatrix4<T>& buildProjectionMatrixPerspectiveFovLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f);
//! Builds a left-handed perspective projection matrix based on a field of view, with far plane at infinity //! Builds a left-handed perspective projection matrix based on a field of view, with far plane at infinity
CMatrix4<T>& buildProjectionMatrixPerspectiveFovInfinityLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 epsilon=0); CMatrix4<T>& buildProjectionMatrixPerspectiveFovInfinityLH(f32 fieldOfViewRadiansY, f32 aspectRatio, f32 zNear, f32 epsilon=0);
//! Builds a right-handed perspective projection matrix. //! Builds a right-handed perspective projection matrix.
CMatrix4<T>& buildProjectionMatrixPerspectiveRH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true); CMatrix4<T>& buildProjectionMatrixPerspectiveRH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f);
//! Builds a left-handed perspective projection matrix. //! Builds a left-handed perspective projection matrix.
//\param widthOfViewVolume: width of clipped near frustum plane //\param widthOfViewVolume: width of clipped near frustum plane
//\param heightOfViewVolume: height of clipped near frustum plane //\param heightOfViewVolume: height of clipped near frustum plane
CMatrix4<T>& buildProjectionMatrixPerspectiveLH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true); CMatrix4<T>& buildProjectionMatrixPerspectiveLH(f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero=true, f32 shiftX=0.f, f32 shiftY=0.f);
//! Builds a left-handed orthogonal projection matrix. //! Builds a left-handed orthogonal projection matrix.
//\param zClipFromZero: Clipping of z can be projected from 0 to 1 when true (D3D style) and from -1 to 1 when false (OGL style). //\param zClipFromZero: Clipping of z can be projected from 0 to 1 when true (D3D style) and from -1 to 1 when false (OGL style).
@ -1583,7 +1585,7 @@ namespace core
// Builds a perspective projection matrix // Builds a perspective projection matrix
template <class T> template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign) inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFov(T sx, T sy, T zNear, T zFar, bool zClipFromZero, T zSign, T shiftX, T shiftY)
{ {
IRR_DEBUG_BREAK_IF(zNear==zFar); //divide by zero IRR_DEBUG_BREAK_IF(zNear==zFar); //divide by zero
M[0] = sx; M[0] = sx;
@ -1596,8 +1598,8 @@ namespace core
M[6] = 0; M[6] = 0;
M[7] = 0; M[7] = 0;
M[8] = 0; M[8] = shiftX;
M[9] = 0; M[9] = shiftY;
//M[10] below //M[10] below
M[11] = zSign; M[11] = zSign;
@ -1626,23 +1628,23 @@ namespace core
// Builds a right-handed perspective projection matrix based on a field of view // Builds a right-handed perspective projection matrix based on a field of view
template <class T> template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovRH( inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovRH(
f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero) f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY)
{ {
const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5)); const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5));
IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero
const T sx = static_cast<T>(sy / aspectRatio); const T sx = static_cast<T>(sy / aspectRatio);
return buildProjectionMatrixPerspectiveFov(sx, static_cast<T>(sy), zNear, zFar, zClipFromZero, (T)-1); return buildProjectionMatrixPerspectiveFov(sx, static_cast<T>(sy), zNear, zFar, zClipFromZero, (T)-1, shiftX, shiftY);
} }
// Builds a left-handed perspective projection matrix based on a field of view // Builds a left-handed perspective projection matrix based on a field of view
template <class T> template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovLH( inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveFovLH(
f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero) f32 fieldOfViewRadians, f32 aspectRatio, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY)
{ {
const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5)); const f64 sy = reciprocal(tan(fieldOfViewRadians*0.5));
IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero IRR_DEBUG_BREAK_IF(aspectRatio==0.f); //divide by zero
const T sx = static_cast<T>(sy / aspectRatio); const T sx = static_cast<T>(sy / aspectRatio);
return buildProjectionMatrixPerspectiveFov(sx, static_cast<T>(sy), zNear, zFar, zClipFromZero, (T)1); return buildProjectionMatrixPerspectiveFov(sx, static_cast<T>(sy), zNear, zFar, zClipFromZero, (T)1, shiftX, shiftY);
} }
// Builds a left-handed perspective projection matrix based on a field of view, with far plane culling at infinity // Builds a left-handed perspective projection matrix based on a field of view, with far plane culling at infinity
@ -1775,26 +1777,26 @@ namespace core
// Builds a right-handed perspective projection matrix. // Builds a right-handed perspective projection matrix.
template <class T> template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveRH( inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveRH(
f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero) f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY)
{ {
IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero
IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero
const T sx = (T)(2*zNear/widthOfViewVolume); const T sx = (T)(2*zNear/widthOfViewVolume);
const T sy = (T)(2*zNear/heightOfViewVolume); const T sy = (T)(2*zNear/heightOfViewVolume);
return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)-1); return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)-1, shiftX, shiftY);
} }
// Builds a left-handed perspective projection matrix. // Builds a left-handed perspective projection matrix.
template <class T> template <class T>
inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveLH( inline CMatrix4<T>& CMatrix4<T>::buildProjectionMatrixPerspectiveLH(
f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero) f32 widthOfViewVolume, f32 heightOfViewVolume, f32 zNear, f32 zFar, bool zClipFromZero, f32 shiftX, f32 shiftY)
{ {
IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(widthOfViewVolume==0.f); //divide by zero
IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero IRR_DEBUG_BREAK_IF(heightOfViewVolume==0.f); //divide by zero
const T sx = (T)(2*zNear/widthOfViewVolume); const T sx = (T)(2*zNear/widthOfViewVolume);
const T sy = (T)(2*zNear/heightOfViewVolume); const T sy = (T)(2*zNear/heightOfViewVolume);
return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)1); return buildProjectionMatrixPerspectiveFov(sx, sy, zNear, zFar, zClipFromZero, (T)1, shiftX, shiftY);
} }

@ -201,6 +201,11 @@ f32 CCameraSceneNode::getFOV() const
return Fovy; return Fovy;
} }
core::vector2df CCameraSceneNode::getLensShift() const
{
return LensShift;
}
void CCameraSceneNode::setNearValue(f32 f) void CCameraSceneNode::setNearValue(f32 f)
{ {
@ -231,10 +236,15 @@ void CCameraSceneNode::setFOV(f32 f)
recalculateProjectionMatrix(); recalculateProjectionMatrix();
} }
void CCameraSceneNode::setLensShift(const core::vector2df& shift)
{
LensShift = shift;
recalculateProjectionMatrix();
}
void CCameraSceneNode::recalculateProjectionMatrix() void CCameraSceneNode::recalculateProjectionMatrix()
{ {
ViewArea.getTransform ( video::ETS_PROJECTION ).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar, HasD3DStyleProjectionMatrix); ViewArea.getTransform ( video::ETS_PROJECTION ).buildProjectionMatrixPerspectiveFovLH(Fovy, Aspect, ZNear, ZFar, HasD3DStyleProjectionMatrix, LensShift.X, LensShift.Y);
IsOrthogonal = false; IsOrthogonal = false;
} }
@ -330,6 +340,7 @@ void CCameraSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeR
out->addFloat("Aspect", Aspect); out->addFloat("Aspect", Aspect);
out->addFloat("ZNear", ZNear); out->addFloat("ZNear", ZNear);
out->addFloat("ZFar", ZFar); out->addFloat("ZFar", ZFar);
out->addVector2d("LensShift", LensShift);
out->addBool("Binding", TargetAndRotationAreBound); out->addBool("Binding", TargetAndRotationAreBound);
out->addBool("ReceiveInput", InputReceiverEnabled); out->addBool("ReceiveInput", InputReceiverEnabled);
} }
@ -339,15 +350,15 @@ void CCameraSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttribute
{ {
ICameraSceneNode::deserializeAttributes(in, options); ICameraSceneNode::deserializeAttributes(in, options);
Target = in->getAttributeAsVector3d("Target"); Target = in->getAttributeAsVector3d("Target", Target);
UpVector = in->getAttributeAsVector3d("UpVector"); UpVector = in->getAttributeAsVector3d("UpVector", UpVector);
Fovy = in->getAttributeAsFloat("Fovy"); Fovy = in->getAttributeAsFloat("Fovy", Fovy);
Aspect = in->getAttributeAsFloat("Aspect"); Aspect = in->getAttributeAsFloat("Aspect", Aspect);
ZNear = in->getAttributeAsFloat("ZNear"); ZNear = in->getAttributeAsFloat("ZNear", ZNear);
ZFar = in->getAttributeAsFloat("ZFar"); ZFar = in->getAttributeAsFloat("ZFar", ZFar);
TargetAndRotationAreBound = in->getAttributeAsBool("Binding"); LensShift = in->getAttributeAsVector2d("LensShift", LensShift);
if ( in->findAttribute("ReceiveInput") ) TargetAndRotationAreBound = in->getAttributeAsBool("Binding", TargetAndRotationAreBound);
InputReceiverEnabled = in->getAttributeAsBool("ReceiveInput"); InputReceiverEnabled = in->getAttributeAsBool("ReceiveInput", InputReceiverEnabled);
recalculateProjectionMatrix(); recalculateProjectionMatrix();
recalculateViewArea(); recalculateViewArea();

@ -95,6 +95,9 @@ namespace scene
//! \return Field of view of the camera //! \return Field of view of the camera
virtual f32 getFOV() const IRR_OVERRIDE; virtual f32 getFOV() const IRR_OVERRIDE;
//! Get the horizontal and vertical lens/projection plane shift
virtual core::vector2df getLensShift() const IRR_OVERRIDE;
//! Sets the value of the near clipping plane. (default: 1.0f) //! Sets the value of the near clipping plane. (default: 1.0f)
virtual void setNearValue(f32 zn) IRR_OVERRIDE; virtual void setNearValue(f32 zn) IRR_OVERRIDE;
@ -107,6 +110,9 @@ namespace scene
//! Sets the field of view (Default: PI / 3.5f) //! Sets the field of view (Default: PI / 3.5f)
virtual void setFOV(f32 fovy) IRR_OVERRIDE; virtual void setFOV(f32 fovy) IRR_OVERRIDE;
//! Set the horizontal and vertical lens/projection plane shift
virtual void setLensShift(const core::vector2df& shift) IRR_OVERRIDE;
//! PreRender event //! PreRender event
virtual void OnRegisterSceneNode() IRR_OVERRIDE; virtual void OnRegisterSceneNode() IRR_OVERRIDE;
@ -162,6 +168,7 @@ namespace scene
f32 Aspect; // Aspect ratio. f32 Aspect; // Aspect ratio.
f32 ZNear; // value of the near view-plane. f32 ZNear; // value of the near view-plane.
f32 ZFar; // Z-value of the far view-plane. f32 ZFar; // Z-value of the far view-plane.
core::vector2df LensShift; // For rendering off-center
SViewFrustum ViewArea; SViewFrustum ViewArea;
core::matrix4 Affector; core::matrix4 Affector;