LXF167:Android
Olkol (обсуждение | вклад) (Новая страница: «Категория:Постоянные рубрики '''Android''' '''» Программирование Работа с сенсорным экрано…») |
Olkol (обсуждение | вклад) (→Текстуры и OpenGL: На сенсорном экране) |
||
Строка 6: | Строка 6: | ||
[[Файл:J_kemp_opt.png |100px|left|thumb| '''Наш эксперт''' Джульетта Кемп еще помнит времена, когда в фотоаппаратах была настоящая пленка. Сейчас она делает гораздо поболее снимков, чем тогда.]] | [[Файл:J_kemp_opt.png |100px|left|thumb| '''Наш эксперт''' Джульетта Кемп еще помнит времена, когда в фотоаппаратах была настоящая пленка. Сейчас она делает гораздо поболее снимков, чем тогда.]] | ||
OpenGL в Android позволяет создавать улучшенную графику и использовать гибкий открытый подход к трехмерной графике. В предыдущей статье мы работали с фигурами, цветами, камерами и проекциями экрана и познакомились с перемещением фигуры; теперь мы продолжим работу с примером и будем перемещать фигуру по экрану в ответ на касания сенсорного экрана, а также познакомимся с умеренно сложной, но полезной технологией наложения текстур. Как и прежде, мы пользуемся OpenGL ES2.0, который поддерживается в Android 2.2 и выше. Помните, что код нельзя будет проверить в эмуляторе, так как он не работает с OpenGL 2, и для отладки вам потребуется реальное устройство. | OpenGL в Android позволяет создавать улучшенную графику и использовать гибкий открытый подход к трехмерной графике. В предыдущей статье мы работали с фигурами, цветами, камерами и проекциями экрана и познакомились с перемещением фигуры; теперь мы продолжим работу с примером и будем перемещать фигуру по экрану в ответ на касания сенсорного экрана, а также познакомимся с умеренно сложной, но полезной технологией наложения текстур. Как и прежде, мы пользуемся OpenGL ES2.0, который поддерживается в Android 2.2 и выше. Помните, что код нельзя будет проверить в эмуляторе, так как он не работает с OpenGL 2, и для отладки вам потребуется реальное устройство. | ||
− | |||
+ | {{Врезка|left |Ширина=98%|Заголовок= Преобразования и порядок множителей|Содержание= | ||
Чтобы получить правильный результат при перемещении, как в onSurfaceCreated(), таковое нужно выполнять (то есть домножать на матрицу перемещения iMatrix) после умножения на проекционную матрицу и матрицу проекции камеры. В противном случае оси X и Z меняют направления на противоположные, и куб перемещается не туда, куда вы хотели. Чтобы понять, почему это происходит, нам нужно обратиться к математике умножения матриц, а я не хочу углубляться в детали. | Чтобы получить правильный результат при перемещении, как в onSurfaceCreated(), таковое нужно выполнять (то есть домножать на матрицу перемещения iMatrix) после умножения на проекционную матрицу и матрицу проекции камеры. В противном случае оси X и Z меняют направления на противоположные, и куб перемещается не туда, куда вы хотели. Чтобы понять, почему это происходит, нам нужно обратиться к математике умножения матриц, а я не хочу углубляться в детали. | ||
Строка 15: | Строка 15: | ||
Это может быть удобно при перемещении объектов по экрану. Чтобы это не приводило к проблемам, здесь мы пользуемся отдельными матрицами для поворота и перемещения и внимательно следим за порядком умножения матриц. Мы вернемся к этому позже, начав работу с сенсорным экраном. | Это может быть удобно при перемещении объектов по экрану. Чтобы это не приводило к проблемам, здесь мы пользуемся отдельными матрицами для поворота и перемещения и внимательно следим за порядком умножения матриц. Мы вернемся к этому позже, начав работу с сенсорным экраном. | ||
+ | }} | ||
− | В развитие предыдущей статьи Джульетта Кемп переходит | + | В развитие предыдущей статьи Джульетта Кемп переходит к текстурам и сенсорному экрану для перемещения фигур. |
− | к текстурам и сенсорному экрану для перемещения фигур. | + | |
− | Перемещение и сенсорный экран | + | ===Перемещение и сенсорный экран=== |
Прежде всего кратко ознакомимся с перемещением (объекта по экрану), не касаясь работы с сенсорным экраном. Это просто еще одна операция с матрицей, translateM(). Добавьте следующие две строки после поворота в CISGLRenderer.onSurfaceCreated(): | Прежде всего кратко ознакомимся с перемещением (объекта по экрану), не касаясь работы с сенсорным экраном. Это просто еще одна операция с матрицей, translateM(). Добавьте следующие две строки после поворота в CISGLRenderer.onSurfaceCreated(): | ||
Строка 43: | Строка 43: | ||
Скомпилируйте и запустите программу, и вы увидите, как куб медленно движется по экрану. | Скомпилируйте и запустите программу, и вы увидите, как куб медленно движется по экрану. | ||
+ | События сенсорного экрана | ||
+ | |||
+ | Двигать кубиками – дело хорошее, но интереснее заставить куб перемещаться в контексте события сенсорного экрана. | ||
+ | |||
+ | Напишем код, который при движении пальцем по сенсорному экрану будет перемещать куб вослед. Чтобы происходящее было яснее, стоит закомментировать строку с поворотом на постоянный угол в CISGLRenderer.onDrawFrame() (так что происходит только перемещение) и строку насчет первоначального поворота в CISGLRenderer.onSurfaceCreated() (чтобы объект перемещался по обычным, а не повернутым осям X/Y/Z, о чем сказано ранее). | ||
+ | |||
+ | Для обработки событий сенсорного экрана нужно создать собственный класс SurfaceView, CISSurfaceView: | ||
+ | |||
+ | public class CISSurfaceView extends GLSurfaceView { | ||
+ | |||
+ | private CISGLRenderer renderer; | ||
+ | |||
+ | private float prevX = 0; | ||
+ | |||
+ | private float prevY = 0; | ||
+ | |||
+ | public CISSurfaceView(Context c) { | ||
+ | |||
+ | super(c); | ||
+ | |||
+ | setEGLContextClientVersion(2); | ||
+ | |||
+ | renderer = new CISGLRenderer(this.getContext()); | ||
+ | |||
+ | setRenderer(renderer); | ||
+ | |||
+ | } | ||
+ | |||
+ | public boolean onTouchEvent(MotionEvent e) { | ||
+ | |||
+ | float x = e.getX(); | ||
+ | |||
+ | float y = e.getY(); | ||
+ | |||
+ | float height = getHeight(); | ||
+ | |||
+ | float width = getWidth(); | ||
+ | |||
+ | switch (e.getAction()) { | ||
+ | |||
+ | case MotionEvent.ACTION_MOVE: | ||
+ | |||
+ | float dx = (x - width/2) * -1; | ||
+ | |||
+ | float dy = (y - height/2) * -1; | ||
+ | |||
+ | renderer.diffX = (dx - prevX) / (width/2); | ||
+ | |||
+ | renderer.diffY = (dy - prevY) / (height/2); | ||
+ | |||
+ | prevX = dx; | ||
+ | |||
+ | prevY = dy; | ||
+ | |||
+ | } | ||
+ | |||
+ | return true; | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | Метод конструктора просто перемещает код из основного Занятия в Представление. Это производится в методе onTouchEvent(). Здесь мы получаем значения X и Y из обнаруженного события MotionEvent, затем высоту и ширину экрана. | ||
+ | |||
+ | Сейчас нам интересно только действие ACTION_MOVE, оно регистрируется при перемещении пальца по экрану. Это действие возникает повторно (и очень часто!) до тех пор, пока вы не снимете палец с экрана, так что этот метод будет вызываться много раз (в чем вы убедитесь, если добавите в него строку для записи сообщения в лог). | ||
+ | |||
+ | dx и dy используются для преобразования координат сенсорного экрана X и Y в координаты системы с точкой отсчета в центре экрана. Отсчет координат сенсорного экрана ведется из левого нижнего угла, ось x уходит вверх, а ось y – влево. В OpenGL используется система координат с началом отсчета в центре экрана (ось x по-прежнему уходит вправо, а ось y – вверх). dx и dy содержат расстояние до точки нажатия от центра экрана в пикселях. | ||
+ | |||
+ | Затем мы получаем разницу между предыдущими координатами (prevX и prevY) и текущими координатами (dx and dy) и преобразуем их из пикселей в координаты OpenGL. В OpenGL по умолчанию за 1 берется расстояние от центра до каждого края – очевидно, сенсорный экран имеет (0.5 * высота) пикселей от центра до верхнего края и (0.5 * ширина) пикселей от центра до каждой стороны, поэтому мы используем эти значения для генерирования координат OpenGL. | ||
+ | |||
+ | Наконец, мы передаем эти значения рендереру. Замените статичную строку translateM() в методе onDrawFrame() класса CISGLRenderer следующей: | ||
+ | |||
+ | Matrix.translateM(iMatrix, 0, diffX, diffY, 0); | ||
+ | |||
+ | (также понадобится объявить diffX и diffY как публичные переменные класса). | ||
+ | |||
+ | Скомпилируйте и запустите программу, и вы увидите, как куб перемещается вслед за пальцем. | ||
+ | |||
+ | Исправляем недочеты | ||
+ | |||
+ | Однако на самом деле он перемещается не вслед за пальцем, а по осям, сильно напоминающим ортогональные. Это связано с взаимодействием с проекционной матрицей и матрицей проекции камеры. Есть два способа это исправить. Один из них – изменить направление перемещения: | ||
+ | |||
+ | Matrix.translateM(iMatrix, 0, -diffX, diffY, 0); | ||
+ | |||
+ | Однако лучшим решением будет использовать другую матрицу для перемещения вслед за пальцем и домножать на нее заранее. | ||
+ | |||
+ | Для этой цели мы поместим в наш код следующий дополнительный фрагмент: | ||
+ | |||
+ | private float[] tMatrix = new float[16]; | ||
+ | |||
+ | public void onSurfaceCreated(GL10 gl, EGLConfig config) { | ||
+ | |||
+ | [ ... ] | ||
+ | |||
+ | Matrix.setIdentityM(tMatrix, 0); | ||
+ | |||
+ | } | ||
+ | |||
+ | public void onDrawFrame(GL10 gl) { | ||
+ | |||
+ | Matrix.translateM(tMatrix, 0, diffX, diffY, 0); | ||
+ | |||
+ | Matrix.setIdentityM(uMVPMatrix, 0); | ||
+ | |||
+ | Matrix.multiplyMM(uMVPMatrix, 0, mMatrix, 0, uMVPMatrix, 0); | ||
+ | |||
+ | Matrix.multiplyMM(uMVPMatrix, 0, tMatrix, 0, uMVPMatrix, 0); | ||
+ | |||
+ | Matrix.multiplyMM(uMVPMatrix, 0, vMatrix, 0, uMVPMatrix, 0); | ||
+ | |||
+ | Matrix.multiplyMM(uMVPMatrix, 0, projMatrix, 0, uMVPMatrix, 0); | ||
+ | |||
+ | Matrix.multiplyMM(uMVPMatrix, 0, iMatrix, 0, uMVPMatrix, 0); | ||
+ | |||
+ | [ ... ] | ||
+ | |||
+ | } | ||
+ | |||
+ | Перед выполнением с новой матрицей любых действий важно так установить значения ее элементов, чтобы она сделалась единичной (в противном случае она будет нулевой и останется такой, что бы вы ни делали). | ||
+ | |||
+ | Затем мы используем ее для передачи значений diffX и diffY и умножаем ее на унифицированную матрицу после поворота, но до умножения на проекционную матрицу/матрицу проекции камеры. | ||
+ | |||
+ | Снова скомпилируйте и запустите программу, и теперь куб должен перемещаться в нужном направлении. О дальнейшем улучшении кода см. во врезке «Начало и окончание движения» на предыдущей странице. |
Версия 04:52, 8 ноября 2018
|
|
|
Android » Программирование Работа с сенсорным экраном при помощи библиотек OpenGL
Текстуры и OpenGL: На сенсорном экране
OpenGL в Android позволяет создавать улучшенную графику и использовать гибкий открытый подход к трехмерной графике. В предыдущей статье мы работали с фигурами, цветами, камерами и проекциями экрана и познакомились с перемещением фигуры; теперь мы продолжим работу с примером и будем перемещать фигуру по экрану в ответ на касания сенсорного экрана, а также познакомимся с умеренно сложной, но полезной технологией наложения текстур. Как и прежде, мы пользуемся OpenGL ES2.0, который поддерживается в Android 2.2 и выше. Помните, что код нельзя будет проверить в эмуляторе, так как он не работает с OpenGL 2, и для отладки вам потребуется реальное устройство.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
В развитие предыдущей статьи Джульетта Кемп переходит к текстурам и сенсорному экрану для перемещения фигур.
Перемещение и сенсорный экран
Прежде всего кратко ознакомимся с перемещением (объекта по экрану), не касаясь работы с сенсорным экраном. Это просто еще одна операция с матрицей, translateM(). Добавьте следующие две строки после поворота в CISGLRenderer.onSurfaceCreated():
Matrix.setIdentityM(iMatrix, 0);
Matrix.translateM(iMatrix, 0, 0.2f, 0.2f, 0f);
Также понадобится создать переменную класса iMatrix и добавить в onDrawFrame() строку
Matrix.multiplyMM(uMVPMatrix, 0, tMatrix, 0, uMVPMatrix, 0);
после других операций умножения матриц.
Эти вызовы переместят куб на 0.2 координаты влево и на 0.2 координаты вверх по экрану.
Помните, что порядок множителей при умножении матриц имеет значение! Подробности см. во врезке.
В качестве альтернативного варианта можно немного перемещать куб в каждом кадре, добавив следующую строку в onDrawFrame():
Matrix.translateM(iMatrix, 0, 0.001f, 0.001f, 0);
Скомпилируйте и запустите программу, и вы увидите, как куб медленно движется по экрану. События сенсорного экрана
Двигать кубиками – дело хорошее, но интереснее заставить куб перемещаться в контексте события сенсорного экрана.
Напишем код, который при движении пальцем по сенсорному экрану будет перемещать куб вослед. Чтобы происходящее было яснее, стоит закомментировать строку с поворотом на постоянный угол в CISGLRenderer.onDrawFrame() (так что происходит только перемещение) и строку насчет первоначального поворота в CISGLRenderer.onSurfaceCreated() (чтобы объект перемещался по обычным, а не повернутым осям X/Y/Z, о чем сказано ранее).
Для обработки событий сенсорного экрана нужно создать собственный класс SurfaceView, CISSurfaceView:
public class CISSurfaceView extends GLSurfaceView {
private CISGLRenderer renderer;
private float prevX = 0;
private float prevY = 0;
public CISSurfaceView(Context c) {
super(c);
setEGLContextClientVersion(2);
renderer = new CISGLRenderer(this.getContext());
setRenderer(renderer);
}
public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
float height = getHeight();
float width = getWidth();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = (x - width/2) * -1;
float dy = (y - height/2) * -1;
renderer.diffX = (dx - prevX) / (width/2);
renderer.diffY = (dy - prevY) / (height/2);
prevX = dx;
prevY = dy;
}
return true;
}
}
Метод конструктора просто перемещает код из основного Занятия в Представление. Это производится в методе onTouchEvent(). Здесь мы получаем значения X и Y из обнаруженного события MotionEvent, затем высоту и ширину экрана.
Сейчас нам интересно только действие ACTION_MOVE, оно регистрируется при перемещении пальца по экрану. Это действие возникает повторно (и очень часто!) до тех пор, пока вы не снимете палец с экрана, так что этот метод будет вызываться много раз (в чем вы убедитесь, если добавите в него строку для записи сообщения в лог).
dx и dy используются для преобразования координат сенсорного экрана X и Y в координаты системы с точкой отсчета в центре экрана. Отсчет координат сенсорного экрана ведется из левого нижнего угла, ось x уходит вверх, а ось y – влево. В OpenGL используется система координат с началом отсчета в центре экрана (ось x по-прежнему уходит вправо, а ось y – вверх). dx и dy содержат расстояние до точки нажатия от центра экрана в пикселях.
Затем мы получаем разницу между предыдущими координатами (prevX и prevY) и текущими координатами (dx and dy) и преобразуем их из пикселей в координаты OpenGL. В OpenGL по умолчанию за 1 берется расстояние от центра до каждого края – очевидно, сенсорный экран имеет (0.5 * высота) пикселей от центра до верхнего края и (0.5 * ширина) пикселей от центра до каждой стороны, поэтому мы используем эти значения для генерирования координат OpenGL.
Наконец, мы передаем эти значения рендереру. Замените статичную строку translateM() в методе onDrawFrame() класса CISGLRenderer следующей:
Matrix.translateM(iMatrix, 0, diffX, diffY, 0);
(также понадобится объявить diffX и diffY как публичные переменные класса).
Скомпилируйте и запустите программу, и вы увидите, как куб перемещается вслед за пальцем.
Исправляем недочеты
Однако на самом деле он перемещается не вслед за пальцем, а по осям, сильно напоминающим ортогональные. Это связано с взаимодействием с проекционной матрицей и матрицей проекции камеры. Есть два способа это исправить. Один из них – изменить направление перемещения:
Matrix.translateM(iMatrix, 0, -diffX, diffY, 0);
Однако лучшим решением будет использовать другую матрицу для перемещения вслед за пальцем и домножать на нее заранее.
Для этой цели мы поместим в наш код следующий дополнительный фрагмент:
private float[] tMatrix = new float[16];
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
[ ... ]
Matrix.setIdentityM(tMatrix, 0);
}
public void onDrawFrame(GL10 gl) {
Matrix.translateM(tMatrix, 0, diffX, diffY, 0);
Matrix.setIdentityM(uMVPMatrix, 0);
Matrix.multiplyMM(uMVPMatrix, 0, mMatrix, 0, uMVPMatrix, 0);
Matrix.multiplyMM(uMVPMatrix, 0, tMatrix, 0, uMVPMatrix, 0);
Matrix.multiplyMM(uMVPMatrix, 0, vMatrix, 0, uMVPMatrix, 0);
Matrix.multiplyMM(uMVPMatrix, 0, projMatrix, 0, uMVPMatrix, 0);
Matrix.multiplyMM(uMVPMatrix, 0, iMatrix, 0, uMVPMatrix, 0);
[ ... ]
}
Перед выполнением с новой матрицей любых действий важно так установить значения ее элементов, чтобы она сделалась единичной (в противном случае она будет нулевой и останется такой, что бы вы ни делали).
Затем мы используем ее для передачи значений diffX и diffY и умножаем ее на унифицированную матрицу после поворота, но до умножения на проекционную матрицу/матрицу проекции камеры.
Снова скомпилируйте и запустите программу, и теперь куб должен перемещаться в нужном направлении. О дальнейшем улучшении кода см. во врезке «Начало и окончание движения» на предыдущей странице.