mirror of
https://github.com/microsoft/DirectXMath
synced 2024-11-21 20:00:12 +00:00
Stereo 3D prototype from 2011
This commit is contained in:
parent
b282f1e624
commit
f89abe64f4
@ -1,73 +1,282 @@
|
||||
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
//// PARTICULAR PURPOSE.
|
||||
////
|
||||
//// Copyright (c) Microsoft Corporation. All rights reserved
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Stereo3DMatrixHelper.h -- SIMD C++ Math helper for Stereo 3D matricies
|
||||
//
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
#include "pch.h"
|
||||
#include "Stereo3DMatrixHelper.h"
|
||||
|
||||
using namespace DirectX;
|
||||
|
||||
StereoParameters CreateDefaultStereoParameters(
|
||||
float viewportWidthInches,
|
||||
float viewportHeightInches,
|
||||
float worldScaleInInches,
|
||||
float stereoExaggeration
|
||||
)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void StereoCreateDefaultParameters
|
||||
(
|
||||
STEREO_PARAMETERS* pStereoParameters
|
||||
)
|
||||
{
|
||||
// The default stereo parameters produced by this method are based on two assumptions:
|
||||
// 1. The viewer's eyes are 24 inches from the display, and
|
||||
// 2. The viewer's eyes are separated by 1.25 inches (interocular distance.)
|
||||
const float DEFAULT_VIEWER_DISTANCE_IN_INCHES = 24.0f;
|
||||
const float DEFAULT_INTEROCULAR_DISTANCE_IN_INCHES = 1.25f;
|
||||
assert( pStereoParameters != nullptr );
|
||||
|
||||
StereoParameters parameters;
|
||||
parameters.viewportWidth = viewportWidthInches / worldScaleInInches;
|
||||
parameters.viewportHeight = viewportHeightInches / worldScaleInInches;
|
||||
parameters.viewerDistance = DEFAULT_VIEWER_DISTANCE_IN_INCHES / worldScaleInInches;
|
||||
parameters.interocularDistance = DEFAULT_INTEROCULAR_DISTANCE_IN_INCHES / worldScaleInInches * stereoExaggeration;
|
||||
// Default assumption is 1920x1200 resolution, a 22" LCD monitor, and a 2' viewing distance
|
||||
pStereoParameters->fViewerDistanceInches = 24.0f;
|
||||
pStereoParameters->fPixelResolutionWidth = 1920.0f;
|
||||
pStereoParameters->fPixelResolutionHeight = 1200.0f;
|
||||
pStereoParameters->fDisplaySizeInches = 22.0f;
|
||||
|
||||
return parameters;
|
||||
pStereoParameters->fStereoSeparationFactor = 1.0f;
|
||||
pStereoParameters->fStereoExaggerationFactor = 1.0f;
|
||||
}
|
||||
|
||||
DirectX::XMMATRIX StereoProjectionFieldOfViewRightHand(
|
||||
const StereoParameters& parameters,
|
||||
float nearZ,
|
||||
float farZ,
|
||||
bool rightChannel
|
||||
)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static inline bool StereoProjectionHelper
|
||||
(
|
||||
const STEREO_PARAMETERS* pStereoParameters,
|
||||
_Out_ float* fVirtualProjection,
|
||||
_Out_ float* zNearWidth,
|
||||
_Out_ float* zNearHeight,
|
||||
float FovAngleY,
|
||||
float AspectHByW,
|
||||
float NearZ
|
||||
)
|
||||
{
|
||||
float yScale = 2.f * parameters.viewerDistance / parameters.viewportHeight;
|
||||
float xScale = 2.f * parameters.viewerDistance / parameters.viewportWidth;
|
||||
// note that most people have difficulty fusing images into 3D
|
||||
// if the separation equals even just the human average. by
|
||||
// reducing the separation (interocular distance) by 1/2, we
|
||||
// guarantee a larger subset of people will see full 3D
|
||||
|
||||
float mFactor = - parameters.interocularDistance / parameters.viewportWidth;
|
||||
// the conservative setting should always be used. the only problem
|
||||
// with the conservative setting is that the 3D effect will be less
|
||||
// impressive on smaller screens (which makes sense, since your eye
|
||||
// cannot be tricked as easily based on the smaller fov). to simulate
|
||||
// the effect of a larger screen, use the liberal settings (debug only)
|
||||
|
||||
if (!rightChannel)
|
||||
// Conservative Settings: * max acuity angle: 0.8f degrees * interoc distance: 1.25 inches
|
||||
|
||||
// Liberal Settings: * max acuity angle: 1.6f degrees * interoc distance: 2.5f inches
|
||||
|
||||
// maximum visual accuity angle allowed is 3.2 degrees for
|
||||
// a physical scene, and 1.6 degrees for a virtual one.
|
||||
// thus we cannot allow an object to appear any closer to
|
||||
// the viewer than 1.6 degrees (divided by two for most
|
||||
// half-angle calculations)
|
||||
|
||||
static const float fMaxStereoDistance = 780; // inches (should be between 10 and 20m)
|
||||
static const float fMaxVisualAcuityAngle = 1.6f * ( XM_PI / 180.0f ); // radians
|
||||
static const float fInterocularDistance = 1.25f; // inches
|
||||
|
||||
bool ComfortableResult = true;
|
||||
float fDisplayHeight, fDisplayWidth, fHalfInterocular, fHalfPixelWidth, fHalfMaximumAcuityAngle, fHalfWidth;
|
||||
float fMaxSeparationAcuityAngle, fMaxSeparationDistance, fRefinedMaxStereoDistance, fFovHalfAngle;
|
||||
float fRefinedMaxSeparationAcuityAngle, fPhysicalZNearDistance, fScalingFactor, fNearZSeparation, fNearZSeparation2;
|
||||
|
||||
fDisplayHeight = pStereoParameters->fDisplaySizeInches / sqrtf( AspectHByW * AspectHByW + 1.0f );
|
||||
fDisplayWidth = fDisplayHeight * AspectHByW;
|
||||
fHalfInterocular = 0.5f * fInterocularDistance * pStereoParameters->fStereoExaggerationFactor;
|
||||
fHalfPixelWidth = fDisplayWidth / pStereoParameters->fPixelResolutionWidth * 0.5f;
|
||||
fHalfMaximumAcuityAngle = fMaxVisualAcuityAngle * 0.5f * pStereoParameters->fStereoExaggerationFactor;
|
||||
fHalfWidth = fDisplayWidth * 0.5f;
|
||||
|
||||
fMaxSeparationAcuityAngle = atanf( fHalfInterocular / fMaxStereoDistance );
|
||||
fMaxSeparationDistance = fHalfPixelWidth / tanf ( fMaxSeparationAcuityAngle );
|
||||
fRefinedMaxStereoDistance = fMaxStereoDistance - fMaxSeparationDistance;
|
||||
fFovHalfAngle = FovAngleY / 2.0f;
|
||||
|
||||
if ( fRefinedMaxStereoDistance < 0.0f || fMaxSeparationDistance > 0.1f * fMaxStereoDistance )
|
||||
{
|
||||
mFactor = -mFactor;
|
||||
// Pixel resolution is too low to offer a comfortable stereo experience
|
||||
ComfortableResult = false;
|
||||
}
|
||||
|
||||
float m22 = farZ / (nearZ - farZ);
|
||||
fRefinedMaxSeparationAcuityAngle = atanf( fHalfInterocular / ( fRefinedMaxStereoDistance ) );
|
||||
fPhysicalZNearDistance = fHalfInterocular / tanf( fHalfMaximumAcuityAngle );
|
||||
fScalingFactor = fHalfMaximumAcuityAngle / atanf( fHalfInterocular / pStereoParameters->fViewerDistanceInches );
|
||||
|
||||
// Construct a stereo perspective projection matrix based on assumptions
|
||||
// about the viewer and specified stereo parameters. Note that compared
|
||||
// to a mono perspective projection matrix, there are two differences:
|
||||
// - a non-zero x:z component (m20)
|
||||
// - a non-zero x:w component (m30)
|
||||
// The values of these two factors affect both the x-offset between the
|
||||
// left and right eyes, as well as the depth at which they converge. The
|
||||
// math used to arrive at these values will often need to change depending
|
||||
// on the content being presented in order to ensure a comfortable viewing
|
||||
// experience. For example, the factors for rendering massive exterior
|
||||
// landscapes will be different than those used for rendering building
|
||||
// interiors. Because of this, developers are encouraged to experiment
|
||||
// with different techniques for generating these values.
|
||||
return XMMATRIX(
|
||||
xScale, 0, 0, 0,
|
||||
0, yScale, 0, 0,
|
||||
mFactor, 0, m22, -1,
|
||||
parameters.viewerDistance * mFactor, 0, nearZ * m22, 0
|
||||
);
|
||||
fNearZSeparation = tanf( fRefinedMaxSeparationAcuityAngle ) * ( fRefinedMaxStereoDistance - fPhysicalZNearDistance );
|
||||
fNearZSeparation2 = fHalfInterocular * ( fRefinedMaxStereoDistance - fPhysicalZNearDistance ) / fRefinedMaxStereoDistance;
|
||||
|
||||
(*zNearHeight) = cosf( fFovHalfAngle ) / sinf( fFovHalfAngle );
|
||||
(*zNearWidth) = (*zNearHeight) / AspectHByW;
|
||||
(*fVirtualProjection) = ( fNearZSeparation * NearZ * (*zNearWidth * 4.0f) ) / ( 2.0f * NearZ );
|
||||
|
||||
return ComfortableResult;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
XMMATRIX StereoProjectionFovLH
|
||||
(
|
||||
const STEREO_PARAMETERS* pStereoParameters,
|
||||
STEREO_CHANNEL Channel,
|
||||
float FovAngleY,
|
||||
float AspectHByW,
|
||||
float NearZ,
|
||||
float FarZ,
|
||||
STEREO_MODE StereoMode
|
||||
)
|
||||
{
|
||||
float fVirtualProjection = 0.0f;
|
||||
float zNearWidth = 0.0f;
|
||||
float zNearHeight = 0.0f;
|
||||
float fInvertedAngle;
|
||||
XMMATRIX patchedProjection, proj;
|
||||
STEREO_PARAMETERS DefaultParameters;
|
||||
|
||||
assert( Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT );
|
||||
assert( StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED );
|
||||
assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f));
|
||||
assert(!XMScalarNearEqual(AspectHByW, 0.0f, 0.00001f));
|
||||
assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
|
||||
|
||||
proj = XMMatrixIdentity();
|
||||
|
||||
if( pStereoParameters == nullptr )
|
||||
{
|
||||
StereoCreateDefaultParameters( &DefaultParameters );
|
||||
pStereoParameters = &DefaultParameters;
|
||||
}
|
||||
|
||||
assert( pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f );
|
||||
assert( pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f );
|
||||
|
||||
StereoProjectionHelper( pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectHByW, NearZ );
|
||||
|
||||
fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias
|
||||
|
||||
//
|
||||
// By applying a translation, we are forcing our cameras to be parallel
|
||||
//
|
||||
|
||||
fInvertedAngle = atanf( fVirtualProjection / ( 2.0f * NearZ ) );
|
||||
|
||||
proj = XMMatrixPerspectiveFovLH( FovAngleY, AspectHByW, NearZ, FarZ );
|
||||
|
||||
if ( Channel == STEREO_CHANNEL_LEFT )
|
||||
{
|
||||
if ( StereoMode > STEREO_MODE_NORMAL )
|
||||
{
|
||||
XMMATRIX rots, trans;
|
||||
rots = XMMatrixRotationY( fInvertedAngle );
|
||||
trans = XMMatrixTranslation( -fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( XMMatrixMultiply( rots, trans ), proj );
|
||||
}
|
||||
else
|
||||
{
|
||||
XMMATRIX trans;
|
||||
trans = XMMatrixTranslation( -fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( trans, proj );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( StereoMode > STEREO_MODE_NORMAL )
|
||||
{
|
||||
XMMATRIX rots, trans;
|
||||
rots = XMMatrixRotationY( -fInvertedAngle );
|
||||
trans = XMMatrixTranslation( fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( XMMatrixMultiply( rots, trans), proj );
|
||||
}
|
||||
else
|
||||
{
|
||||
XMMATRIX trans;
|
||||
trans = XMMatrixTranslation( fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( trans, proj );
|
||||
}
|
||||
}
|
||||
|
||||
return patchedProjection;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
XMMATRIX StereoProjectionFovRH
|
||||
(
|
||||
const STEREO_PARAMETERS* pStereoParameters,
|
||||
STEREO_CHANNEL Channel,
|
||||
float FovAngleY,
|
||||
float AspectHByW,
|
||||
float NearZ,
|
||||
float FarZ,
|
||||
STEREO_MODE StereoMode
|
||||
)
|
||||
{
|
||||
float fVirtualProjection = 0.0f;
|
||||
float zNearWidth = 0.0f;
|
||||
float zNearHeight = 0.0f;
|
||||
float fInvertedAngle;
|
||||
XMMATRIX patchedProjection, proj;
|
||||
STEREO_PARAMETERS DefaultParameters;
|
||||
|
||||
assert( Channel == STEREO_CHANNEL_LEFT || Channel == STEREO_CHANNEL_RIGHT );
|
||||
assert( StereoMode == STEREO_MODE_NORMAL || StereoMode == STEREO_MODE_INVERTED );
|
||||
assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f));
|
||||
assert(!XMScalarNearEqual(AspectHByW, 0.0f, 0.00001f));
|
||||
assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f));
|
||||
|
||||
proj = XMMatrixIdentity();
|
||||
|
||||
if( pStereoParameters == nullptr )
|
||||
{
|
||||
StereoCreateDefaultParameters( &DefaultParameters );
|
||||
pStereoParameters = &DefaultParameters;
|
||||
}
|
||||
|
||||
assert( pStereoParameters->fStereoSeparationFactor >= 0.0f && pStereoParameters->fStereoSeparationFactor <= 1.0f );
|
||||
assert( pStereoParameters->fStereoExaggerationFactor >= 1.0f && pStereoParameters->fStereoExaggerationFactor <= 2.0f );
|
||||
|
||||
StereoProjectionHelper( pStereoParameters, &fVirtualProjection, &zNearWidth, &zNearHeight, FovAngleY, AspectHByW, NearZ );
|
||||
|
||||
fVirtualProjection *= pStereoParameters->fStereoSeparationFactor; // incorporate developer defined bias
|
||||
|
||||
//
|
||||
// By applying a translation, we are forcing our cameras to be parallel
|
||||
//
|
||||
|
||||
fInvertedAngle = atanf( fVirtualProjection / ( 2.0f * NearZ ) );
|
||||
|
||||
proj = XMMatrixPerspectiveFovRH( FovAngleY, AspectHByW, NearZ, FarZ );
|
||||
|
||||
//
|
||||
// By applying a translation, we are forcing our cameras to be parallel
|
||||
//
|
||||
|
||||
if ( Channel == STEREO_CHANNEL_LEFT )
|
||||
{
|
||||
if ( StereoMode > STEREO_MODE_NORMAL )
|
||||
{
|
||||
XMMATRIX rots, trans;
|
||||
rots = XMMatrixRotationY( fInvertedAngle );
|
||||
trans = XMMatrixTranslation( -fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( XMMatrixMultiply( rots, trans ), proj );
|
||||
}
|
||||
else
|
||||
{
|
||||
XMMATRIX trans;
|
||||
trans = XMMatrixTranslation( -fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( trans, proj );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( StereoMode > STEREO_MODE_NORMAL )
|
||||
{
|
||||
XMMATRIX rots, trans;
|
||||
rots = XMMatrixRotationY( -fInvertedAngle );
|
||||
trans = XMMatrixTranslation( fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( XMMatrixMultiply( rots, trans), proj );
|
||||
}
|
||||
else
|
||||
{
|
||||
XMMATRIX trans;
|
||||
trans = XMMatrixTranslation( fVirtualProjection, 0, 0);
|
||||
patchedProjection = XMMatrixMultiply( trans, proj );
|
||||
}
|
||||
}
|
||||
|
||||
return patchedProjection;
|
||||
}
|
||||
|
@ -1,31 +1,65 @@
|
||||
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
//// PARTICULAR PURPOSE.
|
||||
////
|
||||
//// Copyright (c) Microsoft Corporation. All rights reserved
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Stereo3DMatrixHelper.h -- SIMD C++ Math helper for Stereo 3D matrices
|
||||
//
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// Stereo parameters are in the same units as the world.
|
||||
struct StereoParameters
|
||||
#include "DirectXMath.h"
|
||||
|
||||
// Enumeration for stereo channels (left and right).
|
||||
enum STEREO_CHANNEL
|
||||
{
|
||||
float viewportWidth; // viewport width
|
||||
float viewportHeight; // viewport height
|
||||
float viewerDistance; // distance from viewer
|
||||
float interocularDistance; // interocular distance
|
||||
STEREO_CHANNEL_LEFT = 0,
|
||||
STEREO_CHANNEL_RIGHT
|
||||
};
|
||||
|
||||
StereoParameters CreateDefaultStereoParameters(
|
||||
float viewportWidthInches,
|
||||
float viewportHeightInches,
|
||||
float worldScaleInInches,
|
||||
float stereoExaggeration
|
||||
);
|
||||
// Enumeration for stereo mode (normal or inverted).
|
||||
enum STEREO_MODE
|
||||
{
|
||||
STEREO_MODE_NORMAL = 0,
|
||||
STEREO_MODE_INVERTED,
|
||||
};
|
||||
|
||||
DirectX::XMMATRIX StereoProjectionFieldOfViewRightHand(
|
||||
const StereoParameters& parameters,
|
||||
float nearZ,
|
||||
float farZ,
|
||||
bool rightChannel
|
||||
);
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Stereo calibration settings
|
||||
//
|
||||
// * Viewer distance to the display
|
||||
// * Physical display size
|
||||
// * Render resolution
|
||||
//
|
||||
// The stereo separation factor indicates how much separation is between the left and right
|
||||
// eyes. 0 is no separation, 1 is full separation. It defaults to 1.0.
|
||||
//
|
||||
// The debug stereo exaggeration factor indicates how much to increase the interocular spacing and
|
||||
// maximum acuity angle from comfortable defaults. For retail builds, this value should always
|
||||
// be 1.0, but during development, on small screens, this value can be raised to up to 2.0 in
|
||||
// order to exaggerate the 3D effect. Values over 1.0 may cause discomfort on normal sized
|
||||
// displays. It defaults to 1.0.
|
||||
//
|
||||
struct STEREO_PARAMETERS
|
||||
{
|
||||
float fViewerDistanceInches;
|
||||
float fDisplaySizeInches;
|
||||
float fPixelResolutionWidth;
|
||||
float fPixelResolutionHeight;
|
||||
float fStereoSeparationFactor;
|
||||
float fStereoExaggerationFactor;
|
||||
};
|
||||
|
||||
void StereoCreateDefaultParameters( _Out_ STEREO_PARAMETERS* pStereoParameters );
|
||||
|
||||
DirectX::XMMATRIX StereoProjectionFovLH( _In_opt_ const STEREO_PARAMETERS* pStereoParameters, STEREO_CHANNEL Channel,
|
||||
float FovAngleY, float AspectHByW, float NearZ, float FarZ, STEREO_MODE StereoMode );
|
||||
|
||||
DirectX::XMMATRIX StereoProjectionFovRH( _In_opt_ const STEREO_PARAMETERS* pStereoParameters, STEREO_CHANNEL Channel,
|
||||
float FovAngleY, float AspectHByW, float NearZ, float FarZ, STEREO_MODE StereoMode );
|
||||
|
Loading…
Reference in New Issue
Block a user