Журнал LinuxFormat - перейти на главную

LXF167:Android

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Новая страница: «Категория:Постоянные рубрики '''Android''' '''» Программирование Работа с сенсорным экрано…»)
 
(Тек­сту­ры и 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: На сен­сор­ном эк­ра­не

(thumbnail)
> Тут мы ос­та­но­ви­лись в про­шлой ста­тье (она есть на DVD). Те­перь до­ба­вим тек­сту­ры.
(thumbnail)
Наш эксперт Джуль­ет­та Кемп еще пом­нит вре­ме­на, ко­гда в фо­то­ап­па­ра­тах бы­ла на­стоя­щая плен­ка. Сей­час она де­ла­ет гораздо поболее сним­ков, чем то­гда.

OpenGL в Android по­зво­ля­ет соз­да­вать улуч­шен­ную гра­фи­ку и ис­поль­зо­вать гиб­кий от­кры­тый под­ход к трех­мер­ной гра­фи­ке. В пре­ды­ду­щей ста­тье мы ра­бо­та­ли с фи­гу­ра­ми, цве­та­ми, ка­ме­ра­ми и про­ек­ция­ми эк­ра­на и по­зна­ко­ми­лись с пе­ре­ме­щением фи­гу­ры; те­перь мы про­дол­жим ра­бо­ту с при­ме­ром и бу­дем пе­ре­ме­щать фи­гу­ру по эк­ра­ну в от­вет на ка­сания сен­сор­но­го эк­ра­на, а так­же по­зна­ко­мим­ся с уме­ренно слож­ной, но по­лез­ной тех­но­ло­ги­ей на­ло­жения тек­стур. Как и пре­ж­де, мы поль­зу­ем­ся OpenGL ES2.0, ко­то­рый под­дер­жи­ва­ет­ся в Android 2.2 и вы­ше. Помните, что код нель­зя бу­дет про­ве­рить в эму­ля­то­ре, так как он не ра­бо­та­ет с OpenGL 2, и для от­лад­ки вам по­тре­бу­ет­ся ре­аль­ное уст­рой­ст­во.


В раз­ви­тие пре­ды­ду­щей ста­тьи Джуль­ет­та Кемп пе­ре­хо­дит к тек­сту­рам и сен­сор­но­му эк­ра­ну для пе­ре­ме­щения фи­гур.


Пе­ре­ме­ще­ние и сен­сор­ный эк­ран

Пре­ж­де все­го крат­ко оз­на­ко­мим­ся с пе­ре­ме­ще­ни­ем (объ­ек­та по эк­ра­ну), не ка­са­ясь ра­бо­ты с сен­сор­ным эк­ра­ном. Это про­сто еще од­на опе­ра­ция с мат­ри­цей, 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 и ум­но­жа­ем ее на унифи­ци­ро­ван­ную мат­ри­цу по­сле по­во­ро­та, но до ум­но­жения на про­ек­ци­он­ную мат­ри­цу/мат­ри­цу про­ек­ции ка­ме­ры.

Сно­ва ском­пи­ли­руй­те и за­пусти­те про­грам­му, и теперь куб дол­жен пе­ре­ме­щать­ся в нуж­ном на­прав­лении. О дальней­шем улуч­шении ко­да см. во врез­ке «Начало и окончание движения» на предыдущей странице.

Персональные инструменты
купить
подписаться
Яндекс.Метрика