Если у вас есть доступ к программируемому конвейеру (также известному как шейдеры), вы можете выполнить переход в вершинном шейдере. Я обнаружил, что это работает очень хорошо и не создает артефактов. Вот фрагмент кода GLSL:
#version 150
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
uniform float uNearClipPlane = 1.0;
uniform vec2 uPerspToOrtho = vec2( 0.0 );
in vec4 inPosition;
void main( void )
{
// Calculate view space position.
vec4 view = uViewMatrix * uModelMatrix * inPosition;
// Scale x&y to 'undo' perspective projection.
view.x = mix( view.x, view.x * ( -view.z / uNearClipPlane ), uPerspToOrtho.x );
view.y = mix( view.y, view.y * ( -view.z / uNearClipPlane ), uPerspToOrtho.y );
// Output clip space coordinate.
gl_Position = uProjectionMatrix * view;
}
В коде uPerspToOrtho
— это vec2
(например, float2
), который содержит значение в диапазоне [0..1]. Если установлено значение 0, ваши координаты будут использовать перспективную проекцию (при условии, что ваша проекционная матрица является перспективной). Если установлено значение 1, ваши координаты будут вести себя так, как если бы они были спроецированы матрицей ортогональной проекции. Вы можете сделать это отдельно для осей X и Y.
uNearClipPlane — это расстояние ближней плоскости, значение, которое вы использовали для создания матрицы перспективной проекции.
При преобразовании этого в HLSL вам может понадобиться использовать view.z
вместо -view.z
, но я могу ошибаться.
Я надеюсь, что вы найдете это полезным.
Редактировать: вместо того, чтобы передавать расстояние ближней плоскости отсечения, вы также можете извлечь его из матрицы проекции. Для OpenGL это так:
float zNear = 2.0 * uProjectionMatrix[3][2] / ( 2.0 * uProjectionMatrix[2][2] - 2.0 );
Редактировать 2: вы можете оптимизировать код, одновременно выполняя масштабирование по x и y:
view.xy = mix( view.xy, view.xy * ( -view.z / uNearClipPlane ), uPerspToOrtho.xy );
Чтобы избавиться от деления, вы можете умножить на обратное расстояние в ближней плоскости:
uniform float uInvNearClipPlane; // = 1.0 / zNear
05.07.2018