|
Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки? Спасибо! |
Интерактивные приложения OpenGL
Получение и анализ содержания буфера выбора
Содержание буфера выбора может быть проанализировано после вызова команды смены режима RenderMode при изменении режима на отличный от режима выбора, например, на режим формирования изображения:
C#: GL.RenderMode(RenderingMode.Render); Object Pascal: glRenderMode(GL_RENDER);
Команда RenderMode в качестве результата возвращает количество записей в буфере выбора. Каждая запись буфера выбора имеет следующий формат:
- Первое значение - количество имен объектов в записи. Обозначим это значение как N. Это значение соответствует количеству элементов в стеке имен при попадании примитивов объекта в область выбора, так как содержание стека имен копируется в буфер выбора.
- Второе и третье значения – наименьшее и наибольшее значение буфера глубины для примитивов, попавших в сцену выбора, данного именованного объекта. Значения являются беззнаковыми четырехбайтными целыми числами.
- Начиная с четвертого значения, следуют имена объектов, находящиеся в стеке имен, который копируется в буфер выбора при формировании данной записи. Количество имен определяется первым элементом записи (значение N).
После первой записи в буфере последовательно располагаются остальные записи. Количество записей определяется значением, возвращаемым командой RenderMode при смене режима, и равно количеству именованных объектов, примитивы которых попали в сцену выбора.
Рассмотрим пример использования буфера выбора. Нарисуем в пространстве 4 треугольника и попробуем с помощью буфера выбора определить треугольники, которые находятся под указателем "мыши" в момент нажатия левой кнопки "мыши". Для задания области выбора будем использовать область проекции размером 2*2 пиксела около курсора в момент нажатия кнопки "мыши".
В листинге 10.8 приведено содержание обработчиков событий Paint и MouseDown компонента GLControl на C#.
Rectangle p;
private double[] m = new double[16];
uint[] buf = new uint[128]; // буфер выбора
private void glControl1_Paint(object sender, PaintEventArgs e)
{
// определение текущего режима OpenGL
int[] aMode = new int[1];
GL.GetInteger(GetPName.RenderMode, aMode);
RenderingMode mode = (RenderingMode)aMode[0];
if (mode == RenderingMode.Select)
{
GL.MatrixMode(MatrixMode.Projection);
// сохранение матрицы проекций для формирования изображения
// в стеке матриц и в переменной M
GL.PushMatrix();
GL.GetDouble( GetPName.ProjectionMatrix, m);
// формирование матрицы проекций для режима выбора
GL.LoadIdentity();
int[] vp = new int[4];
GL.GetInteger(GetPName.Viewport, vp);
Tao.OpenGl.Glu.gluPickMatrix(p.X, vp[3] - p.Y, 5, 5, vp);
GL.MultMatrix(m);
GL.MatrixMode(MatrixMode.Modelview);
// инициализация стека имен
GL.InitNames();
GL.PushName(0);
}
// очистка буферов цвета и глубины
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// поворот изображения
GL.LoadIdentity();
GL.Rotate(AngleX, 1.0, 0.0, 0.0);
GL.Rotate(AngleY, 0.0, 1.0, 0.0);
GL.Rotate(AngleZ, 0.0, 0.0, 1.0);
// формирование изображения
if (mode == RenderingMode.Select)
GL.LoadName(1);
GL.Color3(0f, 0f, 1f);
GL.Begin(BeginMode.Triangles);
GL.Vertex3(0,0,0);
GL.Vertex3(-1,-1,0);
GL.Vertex3(-1,1,0);
GL.End();
if (mode == RenderingMode.Select)
GL.LoadName(2);
GL.Color3(1f, 0f, 0);
GL.Begin(BeginMode.Triangles);
GL.Vertex3(0,0,0);
GL.Vertex3(1,1,0);
GL.Vertex3(1,-1,0);
GL.End();
if (mode == RenderingMode.Select)
GL.LoadName(3);
GL.Color3(0f, 1f, 1f);
GL.Begin(BeginMode.Triangles);
GL.Vertex3(0,0,1);
GL.Vertex3(-1,-1,1);
GL.Vertex3(-1,1,1);
GL.End();
if (mode == RenderingMode.Select)
GL.LoadName(4);
GL.Color3(1f, 1f, 0f);
GL.Begin(BeginMode.Triangles);
GL.Vertex3(0,0,1);
GL.Vertex3(1,1,1);
GL.Vertex3(1,-1,1);
GL.End();
// завершение формирования изображения
GL.Flush();
GL.Finish();
if (mode == RenderingMode.Select)
{
int rec_count = GL.RenderMode(RenderingMode.Render);
// восстановление матрицы проекции из стека
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
// вывод содержания буфера выбора
uint obj_count = 0;
uint el = 0;
string s = "";
for (int i = 1; i <= rec_count; ++i)
{
obj_count = buf[el];
s += String.Format("{0}, {1}, {2}", obj_count, buf[el + 1],
buf[el + 2]);
for (int obj=1; obj <= obj_count; ++obj)
s += String.Format(", {0}", buf[el + 2 + obj]);
s += "\n";
el += obj_count + 3;
}
if (rec_count > 0)
MessageBox.Show(s);
}
else
glControl1.SwapBuffers();
}
private void glControl1_MouseDown(object sender, MouseEventArgs e)
{
p.X = e.X;
p.Y = e.Y;
GL.RenderMode(RenderingMode.Select);
glControl1.Invalidate();
}
Листинг
10.8.
Пример определения выбранного объекта сцены с помощью буфера выбора на C#
В обработчике события MouseDown компонента GLControl, который выполняется при нажатии кнопки "мыши", осуществляется сохранение координат указателя в глобальной переменной P типа Rectangle.
В дальнейшем координаты указателя, сохраненные в переменной P, используются в обработчике события Paint. В начале обработчика события Paint определяется режим библиотеки с помощью команды GetInteger. Если активным является режим выбора, то выполняется несколько операций:
- Сохраняется текущая матрица проекции в стеке матриц для её восстановления при переходе в режим формирования изображения.
- Текущая матрица проекций так же сохраняется в переменной M, для того чтобы на её основе сформировать матрицу проекции для режима выбора.
- Формируется матрица проекции для режима выбора и инициализируется стек имен: он очищается c помощью команды InitNames и в него помещается одно значение с помощью команды PushName. При формировании изображения оно будет заменяться с помощью команды LoadName на имя формируемого объекта сцены.
Ниже приведены обработчики событий формы OnPaint и OnMouseDown программы на Object Pascal, которые выполняют действия аналогичные программе на C# ( пример 10.9).
// глобальные переменные
var
SB : Array [0..128] of GLuint;// буфер выбора
P: TPoint;
M : Array [0..3, 0..3] of GLdouble;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Begin
// сохранение координат указателя в переменной P
// и переключение в режим выбора
p.x:=x; p.y:=y;
glRenderMode(GL_SELECT);
InvalidateRect(Handle, nil, False);
end;
procedure TForm1.FormPaint(Sender: TObject);
var
i : GLint;
Count, Mode: GLuint;
vp : Array [0..3] of GLint;
rec_count: integer;
obj, obj_count, el: integer;
s: string;
begin
// определение текущего режима OpenGL
glGetIntegerv(GL_RENDER_MODE, @Mode);
if Mode=GL_SELECT then begin
glMatrixMode(GL_PROJECTION);
// сохранение матрицы проекций для формирования изображения
// в стеке матриц и в переменной M
glPushMatrix;
glGetDoublev(GL_PROJECTION_MATRIX, @M);
// формирование матрицы проекций для режима выбора
glLoadIdentity;
glGetIntegerv(GL_VIEWPORT, @vp);
gluPickMatrix(p.x, vp[3]-p.y, 5, 5, @vp);
glMultMatrixd(@M);
glMatrixMode(GL_MODELVIEW);
// инициализация стека имен
glInitNames;
glPushName(0);
end;
// очистка буфера цвета
glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity;
// поворот сцены вокруг осей координат,
// выполняемый с помощью клавиатуры
glRotatef(AngleX, 1.0, 0.0, 0.0);
glRotatef(AngleY, 0.0, 1.0, 0.0);
glRotatef(AngleZ, 0.0, 0.0, 1.0);
// формирование треугольников
if Mode=GL_SELECT then glLoadName(1);
glColor3f(0,0,1);
glBegin(GL_TRIANGLES);
glVertex3f(0,0,0);
glVertex3f(-1,-1,0);
glVertex3f(-1,1,0);
glEnd;
if Mode=GL_SELECT then glLoadName(2);
glColor3f(1,0,0);
glBegin(GL_TRIANGLES);
glVertex3f(0,0,0);
glVertex3f(1,1,0);
glVertex3f(1,-1,0);
glEnd;
if Mode=GL_SELECT then glLoadName(3);
glColor3f(0,1,1);
glBegin(GL_TRIANGLES);
glVertex3f(0,0,1);
glVertex3f(-1,-1,1);
glVertex3f(-1,1,1);
glEnd;
if Mode=GL_SELECT then glLoadName(4);
glColor3f(1,1,0);
glBegin(GL_TRIANGLES);
glVertex3f(0,0,1);
glVertex3f(1,1,1);
glVertex3f(1,-1,1);
glEnd;
if Mode=GL_SELECT then
begin
rec_count:=glRenderMode(GL_RENDER);
// восстановление матрицы проекции из стека
glMatrixMode(GL_PROJECTION);
glPopMatrix;
glMatrixMode(GL_MODELVIEW);
// вывод содержания буфера выбора
obj_count := 0;
el := 0;
with Form2.Memo1.Lines do begin
Clear;
for i:=1 to rec_count do begin
obj_count := sb[el];
s := IntToStr(obj_count) + ','
+ IntToStr(sb[el + 1]) + ','
+ IntToStr(sb[el + 2]);
for obj := 1 to obj_count do
s := s + ',' + IntToStr(sb[el + 2 + obj]);
el := el + (obj_count + 3);
Add(s);
end;
end;
end
else
SwapBuffers(DC);
end;
Листинг
10.9.
Пример определения выбранного объекта сцены с помощью буфера выбора на Object Pascal
Необходимо так же обратить внимание на содержание буфера выбора. Библиотека помещает информацию в буфер выбора в порядке формирования примитивов. Т. е. она не сортирует их по удалённости от наблюдателя. Поэтому для корректного определения ближайшего к наблюдателю объекта необходимо самостоятельно провести анализ значений буфера глубины (второй и третий элемент записи) и на их основе определить ближайший объект от наблюдателя.
Краткие итоги
Для реализации интерактивных приложений OpenGL можно использовать анализ цвета пикселов выбранной области в отображаемом буфере и во вторичном буфере или режим выбора объектов. Режим выбора объектов по сравнению с анализом цвета пикселов позволяет получить информацию о невидимых объектах в выбранной области. Для анализа цвета пикселов используется команда ReadPixels.
При использовании режима выбора используется команда RenderMode для переключения между режимами формирования изображения и режимом выбора, буфер имен, а так же процедура gluPickMatrix библиотеки GLUT для определения выбранной области в объемной сцене.