Какие преобразования проводит OpenGL с объектными координатами, прежде чем они оказываются оконными координатами?
Перед воспроизведением на экране объектные координаты xo, yo, zo, wo, указываемые в качестве параметров метода Vertex, претерпевают ряд линейных преобразований. Вначале они преобразуются в так называемые координаты наблюдения xe, ye, ze, we. Далее, координаты наблюдения преобразуются в координаты отсечения xc, yc, zc, wc. Координаты отсечения делятся на значение wc, образовав нормализованные координаты устройства xd, yd, zd. Под "устройством" понимается "порт наблюдения" – прямоугольник в окне, с которым связан контекст воспроизведения. В данном случае этим окном является панель panelGL.
По умолчанию матрицы преобразования от объектных координат к координатам наблюдения и от координат наблюдения к координатам отсечения являются единичными. Поэтому, по умолчанию xo = xc, yo = yc, zo = zc, wo = wc. Следовательно, если координата wo = 1, то объектные координаты xo, yo, zo совпадают с нормализованными координатами устройства xd, yd, zd.
Координаты пикселя относительно границ панели xw, yw, zw называются оконными. Оконные координаты xw, yw измеряются в пикселях относительно нижнего левого угла клиентской области окна (в данном случае панели panelGL).
Портом наблюдения (Viewport) называют прямоугольную область, выделенную в оконных координатах, в границах которой воспроизводятся объекты OpenGL. В общем случае порт наблюдения определяется положением своего нижнего левого угла (x, y) в оконных координатах относительно левого нижнего угла окна, шириной w и высотой h, выраженных в пикселях. Поэтому положение центра порта ox, oy относительно левого нижнего угла окна имеет вид ox = x + w/2; oy = y + h/2. Между оконными xw, yw и координатами устройства xd, yd (координаты z будут рассматриваться позже) существуют соотношения вида
xw = (w/2)*xd + ox; yw = (h/2)*yd + oy.
По умолчанию порт наблюдения совпадает со всей клиентской областью окна, то есть, в данном случае, занимает всю поверхность панели panelGL. Поэтому в последних формулах x = 0, y = 0, h - высота клиентской области панели, w – ширина этой области. Отсюда следует, что точка с координатами xo = 0, yo = 0 должна по умолчанию получить оконные координаты xw = ox = w/2, yw = oy = h/2. Поэтому точка изображается в центре панели.
Однако, изменение размеров панели не влечет за собой автоматического изменения размеров порта наблюдения w, h, фигурирующих в формулах. В OpenGL существует специальная команда Viewport, задающая параметры порта наблюдения.
///
/// Устанавливает порт наблюдения.
///
///
/// Определяет левую сторону прямоугольника порта наблюдения
/// по отношению к левой стороне окна.
/// По умолчанию 0.
///
///
/// Определяет нижнюю сторону прямоугольника порта наблюдения
/// по отношению к нижней стороне окна. По умолчанию 0.
///
///
/// Определяет ширину порта наблюдения в пикселях.
/// Когда контекст воспроизведения впервые привязывается
/// к окну значение равно ширине окна.
///
///
/// Определяет высоту порта наблюдения в пикселях.
/// Когда контекст воспроизведения впервые привязывается
/// к окну значение равно высоте окна.
///
[DllImport("OPENGL32.DLL", EntryPoint = "glViewport")]
public static extern void Viewport(int x, int y, int width, int height);
Поместите это описание в класс gl библиотеки GL.
Можно связать изменение размеров панели с размерами порта наблюдения. Для этого на форме f3D выберите панель panelGL и в окне свойств этой панели на странице обработчиков (кнопка с изображением молнии) найдите обработчик события SizeChanged. Этот обработчик будет выполняться при изменении размеров панели panelGL. Поместите в обработчик события SizeChanged панели panelGL код
// Если контекст воспроизведения не установлен, то выход из метода
if (null == port) return;
gl.Viewport(0, 0, panelGL.ClientSize.Width, panelGL.ClientSize.Height);
RenderFrame();
Теперь изменения размеров панели не будет влиять на положение точки, так как порт наблюдения будет автоматически подстраиваться под размеры панели. Порт наблюдения всегда будет совпадать с клиентской областью панели panelGL и точка с объектными координатами 0, 0 окажется в центре панели.
Метод Project
В OpenGL существует команда Project, которая позволяет по объектным координатам вершины определить оконные координаты
///
/// Отображает объектные координат objx, objy, objz
/// на оконные координаты winx, winy, winz при заданных:
/// матрице преобразования от объектных к координатам наблюдения modelMatrix,
/// матрице преобразования projMatrix от координат наблюдения к координатам отсечения
/// и параметрах порта наблюдения viewport
///
[DllImport("GLU32.DLL", EntryPoint = "gluProject")]
public static extern void Project(double objx, double objy, double objz,
double[] modelMatrix, double[] projMatrix, int[] viewport,
out double winx, out double winy, out double winz);
Поместите это описание метода Project в класс gl библиотеки GL.
Для использования этого метода необходимо знать текущую матрицу преобразования от объектных координат к координатам наблюдения (MODELVIEW_MATRIX), от координат наблюдения к координатам отсечения (PROJECTION_MATRIX), и, наконец, параметры порта наблюдения (VIEWPORT). Все эти величины можно получить, используя функцию Get в форме
double[] prMtrx = new double[16], mvMtrx = new double[16];
int[] vp = new int[4];
for (int i = 0; i < 16; i++)
{
prMtrx[i] = gl.Get(gl.PROJECTION_MATRIX, 16)[i];
mvMtrx[i] = gl.Get(gl.MODELVIEW_MATRIX, 16)[i];
if (i < 4) vp[i] = (int)gl.Get(gl.VIEWPORT, 4)[i];
}
Константы, которые использует Get в данном случае, имеют вид
///
/// Аргумент Get требует возврата значения 16 параметров матрицы преобразования
/// от объектных координат к координатам наблюдения, находящейся на верху стека.
///
public const int MODELVIEW_MATRIX = 0x0BA6;
///
/// Аргумент Get требует возврата значения 16 параметров матрицы преобразования
/// от координат наблюдения к координатам отсечения, находящейся на верху стека.
///
public const int PROJECTION_MATRIX = 0x0BA7;
///
/// Аргумент Get требует возврата значения четырех параметров порта наблюдения
/// x, y, width, height.
///
public const int VIEWPORT = 0x0BA2;
Описание констант поместите в класс gl библиотеки GL.
Чтобы наблюдать значения оконных координат вершины, которые возвращает метод Project, добавьте к коду оператора case "One point": код вывода этих значений в строку статуса. Так, что полный код оператора case будет иметь вид
case "One point":
double[] prMtrx = new double[16], mvMtrx = new double[16];
int[] vp = new int[4];
for (int i = 0; i < 16; i++)
{
prMtrx[i] = gl.Get(gl.PROJECTION_MATRIX, 16)[i];
mvMtrx[i] = gl.Get(gl.MODELVIEW_MATRIX, 16)[i];
if (i < 4) vp[i] = (int)gl.Get(gl.VIEWPORT, 4)[i];
}
float Xo, Yo, Zo;
Xo = 0; Yo = 0; Zo = 0;
gl.Begin(gl.POINTS);
gl.Vertex(Xo, Yo);
gl.End();
double Xw, Yw, Zw;
gl.Project(Xo, Yo, Zo, mvMtrx, prMtrx, vp, out Xw, out Yw, out Zw);
stLabel.Text = string.Format(
"Xw={0:f}; Yw={1:f}; panelGL: Width={2}; Height={3}; Центр: Ox={4}; Oy={5}",
Xw, Yw, panelGL.ClientSize.Width, panelGL.ClientSize.Height,
panelGL.ClientSize.Width / 2, panelGL.ClientSize.Height / 2);
break;
В результате в строке статуса должна появиться информация, с помощью которой можно проверить формулы преобразования от объектных координат xo, yo к оконным координатам xw, yw:
xw = (w/2)*xd + ox; yw = (h/2)*yd + oy.
Читатель может отредактировать свой проект, либо посмотреть иллюстрирующее приложение. Ссылка для того, кто продолжает создавать авторское приложение.
Посмотрите и проанализируйте результат, меняя положение вершины на панели panelGL.
Если возникает необходимость сохранить параметры порта наблюдения в стеке, то используется метод PushAttrib с параметром VIEWPORT.
Тест рубежного контроля
-
Какие системы координат используют команды OpenGL?
-
Что такое порт наблюдения?
-
Что такое оконные координаты?
-
Как определены параметры порта наблюдения по умолчанию?
-
Какой метод управляет параметрами порта наблюдения?
-
Где следует вызывать метод, определяющий порт наблюдения?
-
Что делает метод Project?
-
Как определить текущие матрицы преобразований между различными системами координат и параметры порта наблюдения?
Достарыңызбен бөлісу: |