- Полный список
- Треугольник
- Типы треугольников
- Линия
- Типы линий
- Точка
- triangle opengl in android
- 2 Answers 2
- Not the answer you’re looking for? Browse other questions tagged java android opengl-es or ask your own question.
- Related
- Hot Network Questions
- Subscribe to RSS
- Изучаем OpenGL ES2 для Android Урок №2. Создание треугольников
Полный список
— рисуем графические примитивы
Исходники уроков доступны на гитхабе. Скачивайте проект, в нем будем использовать модуль lesson170_primitives.
На прошлом уроке мы разбирались, как передать в шейдеры данные о вершинах и получить в итоге треугольник. Чтобы этот механизм стал более понятен, попробуем развить тему, и создадим несколько примеров по передаче данных о вершинах и построения разных графических примитивов (точка, линия и треугольник) из этих вершин.
Треугольник
Сейчас наше приложение рисует один треугольник. Если мы посмотрим на класс OpenGLRenderer, то в методе prepareData увидим в нем список вершин:
Каждая пара значений – это координаты (x,y) одной вершины. Три пары = три вершины = треугольник.
Далее, в методе onDrawFrame мы используем метод glDrawArrays чтобы нарисовать треугольник.
GL_TRIANGLES — тип примитива, который необходимо нарисовать, треугольник в нашем случае
0 — вершины надо брать из массива начиная с позиции 0, т.е. с самой первой
3 — означает, что для рисования необходимо использовать три вершины
Теперь попробуем нарисовать 4 треугольника. Для этого нам понадобится больше вершин. 4 треугольника, в каждом по три вершины, значит нам нужно 3*4=12 вершин.
Перепишем массив vertices в методе prepareData
Теперь у нас есть 12 вершин из которых можно построить 4 треугольника.
Но видим только один треугольник вместо 4-х. Мы забыли сказать системе, что надо рисовать треугольники используя 12 вершин. Т.е. в метод glDrawArrays мы до сих пор передаем значение 3, а это значит что система возьмет из массива значения, чтобы нарисовать только три вершины.
В методе glDrawArrays укажем 12 вместо 3. Теперь система будет знать, что из массива ей необходимо использовать данные, чтобы сформировать 12 вершин для рисования треугольников, и мы получим 4 треугольника.
Видим два треугольника и прямоугольник. Этот прямоугольник на самом деле состоит из двух треугольников, расположенных впритык друг к другу.
Посмотрите на массив вершин и обратите внимание, что у треугольников 3 и 4 есть общие вершины (0.1f, 0.2f) и (0.5f, 0.8f). Вот по этой стороне они и соединились образовав прямоугольник.
В итоге мы получили 4 треугольника, два из которых выглядят, как один прямоугольник.
Типы треугольников
Чтобы нарисовать треугольник, мы передаем тип GL_TRIANGLES в метод glDrawArrays. Существует еще два типа отрисовки треугольников: GL_TRIANGLE_STRIP и GL_TRIANGLE_FAN.
В чем разница между ними? Смотрим рисунок
GL_TRIANGLES – каждые три переданные вершины образуют треугольник. Т.е.
v0, v1, v2 – первый треугольник
v3, v4, v5 – второй треугольник
GL_TRIANGLE_STRIP – каждый следующий треугольник использует две последние вершины предыдущего
v0, v1, v2 – первый треугольник
v1, v2, v3 – второй треугольник
v2, v3, v4 – третий треугольник
v3, v4, v5 – четвертый треугольник
GL_TRIANGLE_FAN – каждый следующий треугольник использует последнюю вершину предыдущего и самую первую вершину
v0, v1, v2 – первый треугольник
v0, v2, v3 – второй треугольник
v0, v3, v4 – третий треугольник
Рассмотрим эти типы на примерах
Задаем 6 вершин в prepareData:
тип GL_TRIANGLES и 6 вершин в glDrawArrays:
Получаем прямоугольник, составленный из двух треугольников.
Теперь нарисуем тот же прямоугольник, но чуть по-другому
Задаем 4 вершины
тип GL_TRIANGLE_STRIP и 4 вершины
Если у вас среда разработки ругается на константу GL_TRIANGLE_STRIP, то добавьте ее в импорт в начале класса:
И для всех последующих констант делайте аналогично.
Результат тот же, но в этот раз мы использовали 4 вершины, а не 6. Тип треугольника GL_TRIANGLE_STRIP помог немного сэкономить. В данном примере это конечно не особо критично, но, в целом, чем меньше вершин нам приходится передавать, тем выше скорость работы приложения.
Рассмотрим последний тип. Задаем 8 вершин
тип GL_TRIANGLE_FAN и 8 вершин
Получили шестиугольник. Для этого мы указали центральную вершину и вершины-углы, и в режиме GL_TRIANGLE_FAN система нарисовала шестиугольник.
Линия
Переходим к линии. Чтобы нарисовать линию нам необходимо указать две вершины.
Задаем их в массиве:
И перепишем onDrawFrame:
Используем константу GL_LINES и указываем, что надо использовать две вершины.
С одной линией все понятно, попробуем нарисовать три линии.
Не забываем указать в glDrawArrays, что необходимо использовать 6 вершин, и зададим толщину линии = 5.
Нарисованы три линии
Типы линий
Существует три типа отрисовки линий. Разберем их на примерах.
Тип GL_LINES мы уже использовали, он просто берет попарно вершины и рисует линии между ними. Т.е. если у нас есть вершины (v0, v1, v2, v3, v4, v5), то мы получим три линии (v0,v1), (v2,v3) и (v4,v5).
Указываем тип GL_LINES, 6 вершин
Каждая пара вершин образовала линию.
Тип GL_LINE_STRIP рисует линии не попарно, а последовательно между всеми вершинами. Т.е. если у нас есть вершины (v0, v1, v2, v3, v4, v5), то мы получим пять линий (v0,v1), (v1,v2), (v2,v3), (v3,v4) и (v4,v5).
Вершины мы будем использовать те же, а тип поменяем на GL_LINE_STRIP
Линии нарисованы последовательно между всеми вершинами
Тип GL_LINE_LOOP аналогичен GL_LINE_STRIP, только он вдобавок еще рисует линию между первой и последней точкой.
Меняем тип на GL_LINE_LOOP
Результат тот же, что и при GL_LINE_STRIP, плюс есть линия между первой и последней вершинами.
Точка
Осталось рассмотреть точку. Здесь уже нет никаких разных типов, только GL_POINTS.
Укажем его в glDrawArrays:
Вершины оставим те же.
Толщину точки можно задать в вершинном шейдере, используя переменную gl_PointSize.
Нарисованы 6 точек
Ну и напоследок давайте нарисуем сразу несколько разных примитивов, например: 4 треугольника, 2 линии, 3 точки
4 треугольника – это 4 * 3 = 12 вершин
2 линии – это 2 * 2 = 4 вершины
3 точки – это 3 вершины
Итого нам нужно задать 12 + 4 + 3 = 19 вершин
Мы три раза вызываем метод glDrawArrays.
Первый вызов говорит системе о том, что надо нарисовать треугольники и использовать при этом 12 вершин, начиная с первой в массиве (индекс 0).
Второй вызов говорит системе о том, что надо нарисовать линии и использовать при этом 4 вершины, начиная с тринадцатой в массиве (индекс 12). Начинаем с тринадцатой потому, что первые 12 вершин в массиве мы использовали, чтобы задать треугольники, а вершины линий у нас идут начиная с тринадцатой.
Третий вызов говорит системе о том, что надо нарисовать точки и использовать при этом 3 вершины, начиная с семнадцатой в массиве (индекс 16). Начинаем с семнадцатой потому, что первые 12 вершин в массиве мы использовали, чтобы задать треугольники, следующие 4 вершины мы использовали, чтобы задать линии, а вершины точек у нас идут начиная с семнадцатой.
Система нарисовала 4 треугольника (2 их из них образуют прямоугольник), 2 линии и три точки.
Надеюсь, стала понятнее связь между массивом вершин и методом glDrawArrays. Т.е. в glDrawArrays мы указываем какие фигуры рисовать и сколько вершин для этого использовать, а данные по вершинам система берет из массива vertices. Возможно осталось непонятным, например, как система определяет, что для вершины надо брать именно 2 точки из массива vertices. Это мы подробно разберем на следующем уроке и алгоритм передачи данных в шейдеры станет полностью понятным.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
triangle opengl in android
I am new to the opengl so i am using guide of the android developer.I have done all thing that they says but it is showing an error to force to close.
In my log cat file it is showing the following error
my manifest file is as follow
my activity class is as follow
my surface class is as follow
2 Answers 2
The reason you are getting this error is because the device you are testing on doesn’t support OpenGL ES 2.0. You’re probably testing this on the emulator, which doesn’t support that version of OpenGL.
Just try testing it on a real device and you’ll see it will work fine.
This problem occur due to the reason that 3d image is not supported by any android virtual device.If any one want to run the 3d image then he or she should be used any physical device .
Not the answer you’re looking for? Browse other questions tagged java android opengl-es or ask your own question.
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
site design / logo © 2021 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2021.12.3.40888
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
Источник
Изучаем OpenGL ES2 для Android Урок №2. Создание треугольников
Урок №2. Создание треугольников
Основу кода и идеи я черпал отсюда:
1. Сатия Коматинени, Дэйв Маклин, Саид Хашими. Android 3 для профессионалов. Создание приложений для планшетных компьютеров и смартфонов.: Пер. с англ. – М.: ООО «И.Д.Вильямс». 2012 – 1024 с.
2. http://www.learnopengles.com/android-lesson-one-getting-started/
На первом уроке (можно посмотреть здесь https://habrahabr.ru/post/278895/ или здесь albatrossgames.blogspot.com/2016/03/opengl-es-2-android-1-opengl.html#more ) мы с вами научились заливать экран одним цветом с помощью OpenGL ES. Пришла пора рисовать треугольники, а точнее, с помощью треугольников мы нарисуем парусник, который будет циклично двигаться слева направо.
Почему треугольники? Дело в том, что в OpenGL есть только три графических примитива: точка, линия и треугольник. Корпус яхты (трапеция) и море (прямоугольник) нарисованы тоже с помощью треугольников. Как известно, точку в нашем пространстве определяют три координаты (x, y, z). Так как наш рисунок плоский, то у всех точек рисунка одна координата по оси 0z (она перпендикулярна плоскости экрана и идет на нас) будет равна нулю. Для примера я указал координаты по оси 0х и 0у двух крайних точек большого паруса (грота).
В коде определение трех точек грота выглядит так:
Как вы видите, создается массив данных с плавающей запятой. Для каждой точки указывается её координата и цветовая гамма. Первая точка у нас белая, поэтому весовые коэффициенты у красного, зеленого и синего одинаковы и равны 1, а вот две остальные вершины я подсинил для красоты. Обход точек делается против часовой стрелки
Размерность координат в OpenGL условная и будет фактически определяться количеством пикселей экрана устройства по ширине и высоте.
Теперь нужно создать буфер, куда мы перекачаем данные о точках для OpenGL. Связано это с тем, что OpenGL написан на С-подобном языке. Поэтому мы переводим наши данные в другой вид, понятный для OpenGL, выделяя память.
Давайте рассмотрим каждую часть. Во-первых, мы выделили блок машинной памяти, используя ByteBuffer.allocateDirect (); эта память не будет управляться Сборщиком мусора (что важно). Необходимо сказать методу, насколько большой блок памяти должен быть в байтах. Так как наши вершины хранятся в массиве в виде переменных float и занимают 4 байта на каждый float, мы передаем triangle1VerticesData.length * mBytesPerFloat. (Напомню, что private final int mBytesPerFloat = 4;)
Следующая строка говорит байтовому буферу, как он должен организовывать свои байты в машинном коде. Когда дело доходит до значений, которые охватывают несколько байтов, таких как 32-разрядные целые числа, байты можно записывать в разном порядке, например, от наиболее значимого значения до наименее значимого. Это похоже на написание большого числа либо слева направо или справа налево. Нам это всё равно, но важно то, что мы используем один и тот же порядок, что и система. Мы организуем это, вызывая order(ByteOrder.nativeOrder()). Наконец, лучше не иметь дело с отдельными байтами напрямую. Мы хотим работать с floats, поэтому вызываем FloatBuffer (), чтобы получить FloatBuffer, который содержит основные байты. Затем копируем, начиная с нулевой позиции, данные из памяти Dalvik в машинную память, вызывая mTriangle1Vertices.put(triangle1VerticesData).position(0);
Память будет освобождена, когда процесс прекращается, поэтому нам не нужно беспокоиться об этом.
Чтобы понять матрицы, нужно для начала разобраться, как мы «видим» объект в OpenGL.
Представьте, что вы держите в руках фотоаппарат и хотите сфотографировать наш парусник.
Пусть наша камера находится в т.К, эта точка называется точкой обзора. Если в коде мы не укажем точку обзора, камера будет по умолчанию в т.О с координатами (0,0,0).
Посмотрим, как задается положение камеры в коде:
В начале, мы установили цвет фона сине-серый, аналогично, как делали в первом занятии.
Потом разместили камеру, фактически указали координаты т.К. Как вы видите, камера у нас сдвинута по оси 0z на 1,5 единицы (расстояние ОК).
В следующих строчках кода мы указываем координату точки, в которую смотрит камера.
Следующие строчки кода определяют, как ориентирована камера или положение up vector. Из-за неудачных переводов, в разных статьях допущены неточности в этом вопросе. В нашем коде сейчас
Это значит, что камера размещена обычно, так, если бы вы положили её на горизонтальный стол и смотрит на парусник в т.0.
Теперь представьте, что из камеры выходят три вектора, как из т.О. Поставив весовой коэффициент final float upY = 1.0f, мы говорим OpenGL, что вверх будет направлена ось 0У и видим картинку, как в начале статьи.
Но стоит нам поставить вот такие коэффициенты
мы увидим на эмуляторе следующее. Наш парусник будет карабкаться по наклонной вверх.
Камера повернулась на 45 градусов против часовой стрелки, если смотреть на ось 0z. Понятно, что если сделать такую комбинацию
вверх будет смотреть ось 0х камеры и кораблик будет плыть вертикально вверх.
Все наши данные мы передаем методу setLookAtM.
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
Видимый объем камеры
Рассмотрим следующий кусок кода.
Метод onSurfaceChanged позволяет отработать изменение ориентации самого устройства.
Повернем на эмуляторе наш гаджет и видим такую картину
Не очень красиво, но в принципе то, что мы нарисовали.
Следующая строчка кода устанавливает размер экрана. Сначала устанавливаем координаты нижней точки левого угла экрана (0,0), а потом ширину и высоту экрана.
GLES20.glViewport(0, 0, width, height);
Давайте еще раз рассмотрим наш рисунок:
Объем, который видит наша камера, заключен в объеме усеченной пирамиды (A1,B1,C1,D1, A2,B2,C2,D2).
Сначала мы находим отношение ширины к высоте устройства (ratio).
final float ratio = (float) width / height;
Потом задаем координату по 0х левой и правой стороны видимого параллелепипеда (A1,B1,C1,D1).
final float left = -ratio;
final float right = ratio;
Задаем координату по 0у нижней и верхней стороны параллелепипеда (A1,B1,C1,D1).
final float bottom = -1.0f;
final float top = 1.0f;
Расстояние от камеры до передней стороны (КО1)
final float near = 1.0f;
Расстояние от камеры до задней стороны (КО2)
final float far = 10.0f;
Применяем матричное преобразование
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
Есть несколько различных видов матриц, которые мы используем:
1. Матрица модели. Эта матрица используется для размещения модели где-то в «мире». Например, если у вас есть модель автомобиля, и вы захотите расположить её на расстоянии 1000 м на восток, вы будете использовать матрицу модели.
2. Матрица вида. Эта матрица представляет собой камеру. Если мы хотим, посмотреть на нашу машину, которая находится в 1000 м на восток, мы должны передвинуть себя на 1000 м на восток. Или можно остаться неподвижными, а весь остальной мир передвинуть на 1000 м на запад. Чтобы сделать это, мы будем использовать матрицу вида.
3. Матрица проекции. Так как наши экраны являются плоскими, то нам нужно сделать окончательное преобразование в «проект» нашего вида на экране и получить 3D-перспективу. Для этого используют матрицу проекции.
Определение вершинных и фрагментных шейдеров
Даже самые простые рисунки в OpenGL ES 2.0 требуют создания программных сегментов, которые называются шейдерами. Шейдеры формируют ядро OpenGL ES 2.0. Вершины обрабатываются вершинными шейдерами и имеют дело только с точками вершин. Пространства между вершинами обрабатывают с помощью фрагментных шейдеров, они имею дело с каждым пикселем на экране.
Для написания шейдеров используют язык программирования OpenGL Shading Language (GLSL).
Каждый шейдер в основном состоит из ввода, вывода и программы. Сначала мы определяем форму, которая представляет собой комбинированную матрицу, содержащую все наши преобразования. Она постоянна для всех вершин и используется для проецирования их на экран. Затем мы определяем два атрибута для позиции и цвета. Эти атрибуты будут прочитаны из буфера, которое мы определили ранее, они задают положение и цвет каждой вершины. Затем мы определим варьирование (изменение цвета), который интерполирует значения для всего треугольника и передаст его во фрагментный шейдер. Когда дело доходит до фрагментного шейдера, то он будет содержать интерполированное значение для каждого пикселя.
Скажем, мы определили треугольник, у которого вершины будут красная, зеленая и синяя, а его размер будет занимать 10 пикселей на экране. При выполнении фрагментного шейдера, он будет содержать различный изменяющийся цвет для каждого пикселя. В какой-то точке, что изменение будет красным, но на полпути между красным и синим, это может быть пурпурный цвет.
Рассмотрим наш вершинный шейдер
Через униформы (uniform) в шейдеры передаются внешние данные, которые могут быть использованы для расчетов. Униформы могут быть использованы только для чтения. Униформы могут быть переданы как в вершинный, так и в фрагментный шейдеры. В нашем случае униформа одна — это матрица модели-вида-проекции u_MVPMatrix и передается она в вершинный шейдер. Ключевое слово mat4 означает, что это матрица размером 4х4 состоящая из чисел с плавающей точкой. Униформы никак не связаны с конкретной вершиной и являются глобальными константами. Для названия униформ обычно используют префикс u_.
Атрибуты (attribute) — это свойство вершины. У вершины могут быть различные атрибуты. Например, координаты положения в пространстве, координаты вектора нормали, цвет. Кроме того, вы можете передавать в вершинный шейдер какие-либо свои атрибуты. Важно понять, что атрибут-это свойство вершины и поэтому он должен быть задан для каждой вершины. Атрибуты передаются только в вершинный шейдер. Атрибуты доступны вершинному шейдеру только для чтения. Нельзя определять атрибуты во фрагментном шейдере. В дальнейшем для удобства будем обозначать атрибуты с префиксом a_.
Определим в вершинном шейдере три атрибута:
attribute vec4 a_Position;
Переменная a_Position – атрибут вершины, который имеет дело с положением вершины (координатами), это четырехкомпанентный вектор (vec4).
Атрибут цвета вершины
attribute vec4 a_Color;
Атрибут интерполяции цвета
varying vec4 v_Color;
Рассмотрим код функции main подробнее:
v_Color = a_Color;
Передаем информацию о цвете вершин во фрагментный шейдер.
gl_Position = u_MVPMatrix * a_Position;
Трансформируем положение вершин с помощью матрицы и записываем в новую переменную gl_Position.
Системная переменная gl_Position — это четырех-компонентный вектор, определяющий координаты вершины, спроецированные на плоскость экрана. Переменная gl_Position обязательно должна быть определена в вершинном шейдере, иначе на экране мы ничего не увидим.
Приступим к рассмотрению фрагментного шейдера.
Точность по умолчанию устанавливаем среднюю, так как она не нужна нам высокой в случае фрагментного шейдера. В вершинном шейдере точность по умолчанию высокая.
precision mediump float;
Конечная цель фрагментного шейдера — это получение цвета пикселя. Рассчитанный цвет пикселя должен быть обязательно записан в системную переменную gl_FragColor. В нашем простейшем примере мы не вычисляем цвет пикселя во фрагментном шейдере, а просто присваиваем значение цвета v_color, полученного путем интерполяции из цветов вершин:
gl_FragColor = v_color;
Загрузка шейдеров в OpenGL
Связывание вершинного и фрагментного шейдеров вместе в программе
Перед тем как использовать наши вершинный и фрагментный шейдеры, нам нужно связать их вместе в программе. Это то, что соединяет выход вершинного шейдера со входом фрагментного шейдера. Это также то, что позволяет нам передать входные данные из нашей программы и использовать шейдеры, чтобы рисовать наши фигуры.
Мы создаем новый программный объект, и если это удалось, мы затем присоединяем наши шейдеры. Мы хотим передать данные о положении и цвете, как атрибуты, так что нам нужно, чтобы связать эти атрибуты. Затем свяжем шейдеры вместе.
После того как мы успешно связали нашу программу, мы закончим с парой больших задач, теперь мы реально можем использовать её. Первая задача заключается в получении ссылок, поэтому мы можем передавать данные в программу. Тогда мы говорим OpenGL использовать эту программу, когда происходит рисование. Так как мы используем только одну программу в этом уроке, мы можем поставить это в onSurfaceCreated () вместо onDrawFrame ().
Установка перспективной проекции
Наш метод onSurfaceChanged () вызывается по крайней мере один раз, а также всякий раз, когда наша поверхность изменяется. Так как нам надо сбрасывать нашу матрицу проекции всякий раз, когда на экране проецирование изменилась, то onSurfaceChanged () является идеальным местом, чтобы сделать это.
Вывод объектов на экран
Вывод производим в методе onDrawFrame(GL10 glUnused)
Чтобы треугольники двигались по оси 0х, применяем матрицу перемещения и задаем увеличение смещения по х на 0,001 за каждое обновление поверхности. Как только х достигает 1 или правого края экрана, обнуляем его.
Источник