Android studio canvas треугольник

Изучаем 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. Пришла пора рисовать треугольники, а точнее, с помощью треугольников мы нарисуем парусник, который будет циклично двигаться слева направо.

Android studio canvas треугольник

Почему треугольники? Дело в том, что в OpenGL есть только три графических примитива: точка, линия и треугольник. Корпус яхты (трапеция) и море (прямоугольник) нарисованы тоже с помощью треугольников. Как известно, точку в нашем пространстве определяют три координаты (x, y, z). Так как наш рисунок плоский, то у всех точек рисунка одна координата по оси 0z (она перпендикулярна плоскости экрана и идет на нас) будет равна нулю. Для примера я указал координаты по оси 0х и 0у двух крайних точек большого паруса (грота).

Android studio canvas треугольник

В коде определение трех точек грота выглядит так:

Как вы видите, создается массив данных с плавающей запятой. Для каждой точки указывается её координата и цветовая гамма. Первая точка у нас белая, поэтому весовые коэффициенты у красного, зеленого и синего одинаковы и равны 1, а вот две остальные вершины я подсинил для красоты. Обход точек делается против часовой стрелки
Размерность координат в OpenGL условная и будет фактически определяться количеством пикселей экрана устройства по ширине и высоте.
Теперь нужно создать буфер, куда мы перекачаем данные о точках для OpenGL. Связано это с тем, что OpenGL написан на С-подобном языке. Поэтому мы переводим наши данные в другой вид, понятный для OpenGL, выделяя память.

Android studio canvas треугольник

Давайте рассмотрим каждую часть. Во-первых, мы выделили блок машинной памяти, используя 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.
Представьте, что вы держите в руках фотоаппарат и хотите сфотографировать наш парусник.

Android studio canvas треугольник

Пусть наша камера находится в т.К, эта точка называется точкой обзора. Если в коде мы не укажем точку обзора, камера будет по умолчанию в т.О с координатами (0,0,0).
Посмотрим, как задается положение камеры в коде:

В начале, мы установили цвет фона сине-серый, аналогично, как делали в первом занятии.

Потом разместили камеру, фактически указали координаты т.К. Как вы видите, камера у нас сдвинута по оси 0z на 1,5 единицы (расстояние ОК).

В следующих строчках кода мы указываем координату точки, в которую смотрит камера.

Следующие строчки кода определяют, как ориентирована камера или положение up vector. Из-за неудачных переводов, в разных статьях допущены неточности в этом вопросе. В нашем коде сейчас

Это значит, что камера размещена обычно, так, если бы вы положили её на горизонтальный стол и смотрит на парусник в т.0.

Android studio canvas треугольник
Теперь представьте, что из камеры выходят три вектора, как из т.О. Поставив весовой коэффициент final float upY = 1.0f, мы говорим OpenGL, что вверх будет направлена ось 0У и видим картинку, как в начале статьи.
Но стоит нам поставить вот такие коэффициенты

мы увидим на эмуляторе следующее. Наш парусник будет карабкаться по наклонной вверх.
Android studio canvas треугольник
Камера повернулась на 45 градусов против часовой стрелки, если смотреть на ось 0z. Понятно, что если сделать такую комбинацию

вверх будет смотреть ось 0х камеры и кораблик будет плыть вертикально вверх.
Все наши данные мы передаем методу setLookAtM.
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);

Видимый объем камеры

Рассмотрим следующий кусок кода.

Метод onSurfaceChanged позволяет отработать изменение ориентации самого устройства.
Повернем на эмуляторе наш гаджет и видим такую картину
Android studio canvas треугольник
Не очень красиво, но в принципе то, что мы нарисовали.
Следующая строчка кода устанавливает размер экрана. Сначала устанавливаем координаты нижней точки левого угла экрана (0,0), а потом ширину и высоту экрана.
GLES20.glViewport(0, 0, width, height);
Давайте еще раз рассмотрим наш рисунок:

Android studio canvas треугольник

Объем, который видит наша камера, заключен в объеме усеченной пирамиды (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 или правого края экрана, обнуляем его.

Видео:Создание треугольника в CANVASСкачать

Создание треугольника в CANVAS

Полный список

— работаем с Path

На прошлом уроке мы рассмотрели простые фигуры. Но кроме них мы имеем возможность создавать сложные фигуры с помощью объекта Path. Этот объект позволяет нам создать составную фигуру, состоящую из линий, кривых и простых фигур.

Project name: P1431_DrawingPath
Build Target: Android 2.3.3
Application name: DrawingPath
Package name: ru.startandroid.develop.p1431drawingpath
Create Activity: MainActivity

Простые фигуры

Кодим в MainActivity.java:

Android studio canvas треугольник

Метод reset очищает path.

Метод moveTo – ставит «курсор» в указанную точку. Далее рисование пойдет от нее.

lineTo – рисует линию от текущей точки до указанной, следующее рисование пойдет уже от указанной точки

Таким образом мы нарисовали две прямые, получился угол.

Далее перемещаем точку и снова рисуем две линии, и закрываем подфигуру методом close. Методом moveTo мы сообщили, что начали рисовать новую подфигуру и эта точка является начальной, а когда вызываем close – рисуется линия от последней точки до начальной. Т.е. фигура закрывается. Таким образом, нарисовав две линии и вызвав метод close, мы получили треугольник.

Далее методами addRect и addCircle к объекту path добавляем квадрат и круг. Параметры тут стандартные, рассмотрены нами на прошлых уроках, кроме последнего: направления. Здесь есть два варианта: Path.Direction.CW (по часовой) и Path.Direction.CCW (против часовой). Т.е. вы задаете направление рисования линий квадрата или фигуры. Как это можно использовать, рассмотрим чуть позже.

Выводим получившийся path на экран ченым цветом.

Далее работаем с другим Path-объектом: path1. Добавляем в него две пересекающиеся линии. Выводим path1 зеленым цветом. Он у нас получился нарисован поверх path.

Теперь методом addPath добавляем path1 к path. Т.е. к Path можно добавлять не только фигуры и линии, но и Path-объекты. Смещаем итоговый path на 500 вправо и 100 вниз методом offset, меняем цвет на синий и выводим результат.

В хелпе есть еще несколько методов add* для добавления фигур, которые мы прошли в прошлом уроке. С ними все аналогично.

Кривые

Path дает нам возможность рисовать не только прямые, но и кривые линии, а именно квадратичные и кубические кривые Безье. Википедия дает очень хорошие GIF-ки на эту тему.

Перепишем класс DrawView:

Android studio canvas треугольник

Рассмотрим сначала зеленую кривую.

Сначала рисуем черную линию (100,100) – (600,100). Делаем это только для наглядности, чтобы видеть, какой была бы линия, если бы мы из нее кривую не сделали.

Далее нарисуем небольшой круг в точке, которая будет использована для искривления линии. Делаем это тоже только для наглядности, чтобы видеть в каком направлении будет искривлена прямая. Координаты точки заданы в объекте point1.

Теперь рисуем кривую, используя Path. Становимся в точку (100,100) методом moveTo. Метод quadTo рисует кривую из текущей точки (100,100) в точку (600,100) (т.е. те же координаты, что и черной линии). А точка (point1.x, point1.y) позволяет задать изгиб кривой. Проще говоря, кривая будет отклонена в сторону этой точки.

Аналогично рисуем синюю кривую. Сначала черным цветом прямой оригинал. Затем точки отклонения. Затем искривляем. Метод cubicTo рисует кривую из текущей точки (400,400) в точку (1100,400). А точки (point21.x, point21.y) и (point22.x, point22.y) позволяют задать изгиб кривой. Проще говоря, кривая будет отклонена в сторону этих точек.

На получившемся результате видно, что кривые тянутся к точкам, которые показаны кружками. Для зеленой кривой, нарисованной методом quadTo – это одна точка. А метод cubicTo позволил нам задать две такие точки для синей линии.

Также обратите внимание, что при создании объекта Paint я использовал флаг Paint.ANTI_ALIAS_FLAG. Он сглаживает кривые при рисовании. Попробуйте его убрать и сравнить результат.

В качестве задания предлагаю вам вспомнить Урок 102 про касания и сделать приложение, в котором будет нарисована прямая, а касаясь экрана пальцем ее можно будет искривлять в сторону точки касания.

Относительные методы

Методы moveTo, lineTo, quadTo, cubicTo имеют одноименные аналоги, но начинающиеся с буквы r: rMoveTo, rLineTo, rQuadTo, rCubicTo. Отличие r-методов в том, что они используют не абсолютные, а относительные (relative – отсюда и буква r) координаты.

Например, если метод lineTo(100,200) рисовал нам линию от текущей точки в точку (100,200), то rLineTo(100,200) нарисует линию от текущей точки в точку, которая правее текущей на 100 и ниже на 200.

Текст по фигуре

Теперь посмотрим, как можно использовать направление рисования, которое мы задавали в методах addRect и addCircle

Android studio canvas треугольник

Видим четыре текста, которые нарисованы в виде круга. Разберемся, как это сделано.

Добавляем к Path круг методом addCircle, используя направление по часовой — Path.Direction.CW. Далее методом drawTextOnPath рисуем черным цветом текст по контуру path-фигуры. Как видим, текст идет по часовой стрелке. Сам круг при этом не рисуется.

Далее очишаем path и добавляем к нему новый круг, используя направление против часовой Path.Direction.CCW. В нем текст пойдет против часовой стрелки. И синим цветом рисуем и текст и круг.

А теперь рассмотрим параметры drawTextOnPath на зеленой и красной фигурах. Будем использовать тот же path, который нарисовали синим цветом. Только методом offset будем перемещать его на новое место.

У метода drawTextOnPath третий параметр означает длину отступа от старта фигуры. В зеленом круге мы задали этот отступ равным 100. Видно, что по сравнению с синим кругом, текст здесь имеет отступ по окружности от начала.

Четвертый параметр метода drawTextOnPath позволяет указать отступ текста от фигуры. В красном круге мы указали его равным 30. И видим, что текст удален от круга наружу. Если задать отрицательное значение, то текст будет смещен внутрь.

Обратите внимание, что в Path вообще не используется объект Paint. Т.е. Path — это просто фигура. И она ничего не знает про то, какой кистью она будет нарисована. Кисть задается и используется уже непосредственно при рисовании фигуры на канве.

На следующем уроке:

— используем Matrix для геометрических преобразований фигур

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Видео:Start аndroid: Урок 141. Рисование. Доступ к Canvas (2D графика в андроид)Скачать

Start аndroid: Урок 141. Рисование. Доступ к Canvas (2D графика в андроид)

Как нарисовать заполненный треугольник на Android Canvas

У меня есть класс MyView, который расширяет класс представления. MyView должен нарисовать заполненный треугольник. Я нарисовал треугольник, но не могу его заполнить. Это мой метод onDraw ():

вот что я получаю в результате:

Android studio canvas треугольник

Видео:Рисуем в Canvas на Jetpack Compose - Android Studio & KotlinСкачать

Рисуем в Canvas на Jetpack Compose - Android Studio & Kotlin

4 ответов

эти ссылки очень полезные.

Я хотел бы отметить, что вы никогда не должны инициализировать объект из onDraw (), поскольку он вызывается несколько раз и приводит к проблемам производительности.

этот ответ дает немного ясности о том, откуда берутся числа, приведенные в ответе @Egis. (это нарисует перевернутый равносторонний треугольник и написано на Котлине)

функция get height является Теорема Пифагора и всегда будет находить высоту равностороннего треугольника

87% от его длины стороны

суть можно найти здесь, он содержит код для другого направления

📽️ Видео

Android - CanvasСкачать

Android - Canvas

How to draw circle, rectangle and triangle by canvas in Android?Скачать

How to draw circle, rectangle and triangle by canvas in Android?

Android Custom View. Теория. Обзор API и принципов создания ViewСкачать

Android Custom View. Теория. Обзор API и принципов создания View

Рисуем в Canvas (Удаление линий), Jetpack Compose - Android Studio & KotlinСкачать

Рисуем в Canvas (Удаление линий), Jetpack Compose - Android Studio & Kotlin

Работа с объектом canvas в AndroidСкачать

Работа с объектом canvas в Android

Android Canvas простой пример (java)Скачать

Android Canvas простой пример (java)

Android - Canvas drawingСкачать

Android - Canvas drawing

android studio drawon view canvasСкачать

android studio drawon view canvas

Android Canvas - Custom Drawing and Writing.Скачать

Android Canvas - Custom Drawing and Writing.

Create Paint Application Drawing Canvas in Android Studio with JavaСкачать

Create Paint Application Drawing Canvas in Android Studio with Java

010. Школа мобильной разработки – Drawing. Алексей ЩербининСкачать

010. Школа мобильной разработки – Drawing. Алексей Щербинин

Custom UI Component with Jetpack Compose & Canvas | Part #1 - PreviewСкачать

Custom UI Component with Jetpack Compose & Canvas | Part #1 - Preview

How to custom view with canvas - Java - Android TutorialСкачать

How to custom view with canvas - Java - Android Tutorial

Triangle mesh | Canvas & JavaScript | ruСкачать

Triangle mesh | Canvas & JavaScript | ru
Поделиться или сохранить к себе: