portaldacalheta.pt
  • Основен
  • Дизайн На Марката
  • Тенденции
  • Инструменти И Уроци
  • Технология
Подвижен

Урок за OpenGL за Android: Изграждане на генератор на комплекти Mandelbrot



OpenGL е мощен интерфейс за различни платформи, който позволява много близък достъп до хардуера на системата в различни програмни среди.

И така, защо трябва да го използвате?



Той осигурява много ниско ниво на обработка на графики както в 2D, така и в 3D. Като цяло това ще избегне всякаква неприятност, която имаме поради интерпретирани езици или езици за програмиране на високо ниво. По-важното обаче е, че предоставя и достъп на хардуерно ниво до ключова характеристика: GPU.



GPU може значително да ускори много приложения, но има много специфична роля в компютъра. GPU ядрата всъщност са по-бавни от CPU ядрата. Ако трябваше да стартираме програма, която е особено серийна, без паралелна активност, тогава тя почти ще стане винаги да бъде по-бавен на ядрото на графичния процесор от ядрото на процесора. Основната разлика е, че GPU поддържа масивна паралелна обработка. Можем да създадем малки програми, наречени шейдъри, които да работят ефективно на стотици ядра наведнъж. Това означава, че можем да поемаме задачи, които иначе са невероятно повтарящи се, и да ги изпълняваме едновременно.



Генератор на OpenGL и Mandelbrot

В тази статия ще създадем просто приложение за Android, което използва OpenGL, за да изобрази съдържанието си на екрана. Преди да започнем, важно е вече да сте запознати с знания за писане на приложения за Android и синтаксис на C-подобен език за програмиране. Целият изходен код на този урок е на разположение на GitHub .



Урок за OpenGL и Android

За да демонстрираме силата на OpenGL, ще напишем относително основно приложение за устройство с Android. Сега OpenGL на Android се разпространява под подмножество, наречено OpenGL за вградени системи (OpenGL ES). По същество можем просто да мислим за това като за съкратена версия на OpenGL, въпреки че основната функционалност, която е необходима, все още ще бъде налична.

Вместо да напишем основно „Hello World“, ще напишем измамно просто приложение: генератор на комплекти Mandelbrot. The Комплект Манделброт е базирана в областта на комплексни числа . Комплексният анализ е красиво обширно поле, така че ще се фокусираме повече върху визуалния резултат, отколкото върху действителната математика зад него.



С OpenGL изграждането на генератор на комплекти Mandelbrot е по-лесно, отколкото си мислите! Tweet

Поддръжка на версията

поддръжка на версия на opengl

Когато правим приложението, искаме да се уверим, че то се разпространява само до онези с подходяща поддръжка на OpenGL. Започнете с деклариране на използването на OpenGL 2.0 във файла на манифеста, между декларацията на манифеста и приложението:



MainActivity

На този етап поддръжката на OpenGL 2.0 е повсеместна. OpenGL 3.0 и 3.1 придобиват съвместимост, но писането и за двете ще остане без приблизително 65% от устройствата , така че вземете решението само ако сте сигурни, че ще ви е необходима допълнителна функционалност. Те могат да бъдат внедрени чрез задаване на версията съответно на „0x000300000“ и „0x000300001“.

Архитектура на приложенията

архитектура на приложението на opengl



Когато правите това приложение на OpenGL на Android, обикновено ще имате три основни класа, които се използват за изчертаване на повърхността: вашият GLSurfaceView, разширение на GLSurfaceView.Renderer , и изпълнение на a MainActivity . Оттам нататък ще създадем различни Модели, които ще капсулират чертежи.

FractalGenerator, наречена GLSurfaceView в този пример по същество просто ще инстанцира вашия public class FractalGenerator extends Activity { private GLSurfaceView mGLView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Create and set GLSurfaceView mGLView = new FractalSurfaceView(this); setContentView(mGLView); } //[...] @Override protected void onPause() { super.onPause(); mGLView.onPause(); } @Override protected void onResume() { super.onResume(); mGLView.onResume(); } } и насочете всички глобални промени по линията. Ето пример, който по същество ще бъде вашият образец:



GLSurfaceView

Това също ще бъде класът, в който ще искате да поставите други модификатори на нивото на активност (като потапящ цял екран ).

Един клас по-дълбоко, имаме разширение от setEGLContextClientVersion(int version), което ще действа като наш основен изглед. В този клас ние задаваме версията, настройваме Renderer и контролираме сензорни събития. В нашия конструктор трябва само да зададем версията на OpenGL с public FractalSurfaceView(Context context){ super(context); setEGLContextClientVersion(2); mRenderer = new FractalRenderer(); setRenderer(mRenderer); } а също така създайте и задайте нашия визуализатор:

setRenderMode(int renderMode)

Освен това можем да зададем атрибути като режима на рендиране с RENDERMODE_WHEN_DIRTY . Тъй като генерирането на набор от Mandelbrot може да струва много скъпо, ще използваме requestRender() , който ще изобрази сцената само при инициализация и когато са направени изрични повиквания GLSurfaceView . Още опции за настройки можете да намерите в onTouchEvent(MotionEvent event) ПОЖАР .

След като имаме конструктора, вероятно ще искаме да заменим поне един друг метод: {a, 0, 0, 0, 0, b, 0, 0, 0, 0, c, 0, v_a, v_b, v_c, 1} , който може да се използва за общо въвеждане от потребителя на докосване. Няма да навлизам твърде много в подробности тук, тъй като това не е основният фокус на урока.

Накрая стигаме до нашия Renderer, където ще се случи по-голямата част от работата за осветление или може би промени в сцената. Първо, ще трябва да разгледаме малко как работят и работят матриците в графичния свят.

Бърз урок по линейна алгебра

OpenGL разчита до голяма степен на използването на матрици. Матриците са чудесно компактен начин за представяне на последователности от обобщени промени в координатите . Обикновено те ни позволяват да правим произволни ротации, разширения / контракции и отражения, но с малко финес можем да правим и преводи. По същество всичко това означава, че можете лесно да изпълнявате всякакви разумен промяна, която искате, включително преместване на камера или карате обект да расте. От умножавайки нашите матрици по вектор представяйки нашата координата, ние можем ефективно да създадем новата координатна система.

The Матрица класът, предоставен от OpenGL, дава редица готови начини за изчисляване на матрици, от които ще се нуждаем, но разбирането как работят те е умна идея дори при работа с прости трансформации.

Първо, можем да разгледаме защо ще използваме четиримерни вектори и матрици за справяне с координатите. Това всъщност се връща към идеята да измислим използването на координати, за да можем да правим преводи: докато преводът в 3D пространство е невъзможен само с три измерения, добавянето на четвърто измерение дава възможност.

За да илюстрираме това, можем да използваме много основна матрица с общ мащаб / превод:

мащаб на opengl и матрица за превод

Като важна забележка, OpenGL матриците са колони, така че тази матрица ще бъде записана като onSurfaceCreated(GL10 gl, EGLConfig config), което е перпендикулярно на начина, по който обикновено ще се чете. Това може да бъде рационализирано, като се гарантира, че векторите, които се появяват при умножение като колона, имат същия формат като матриците.

как да се свържете с метамаска от уебсайт

Обратно към кода

Въоръжени с тези познания за матриците, можем да се върнем към проектирането на нашия Renderer. Обикновено в този клас ще създадем матрица, която се формира от произведението на три матрици: Model, View и Projection. Това би било наречено по подходящ начин MVPMatrix. Можете да научите повече за спецификата тук , тъй като ще използваме по-основен набор от трансформации - наборът Mandelbrot е двуизмерен, цял екран модел и всъщност не изисква идеята за камера.

Първо, нека настроим класа. Ще трябва да приложим необходими методи за интерфейса на Renderer: onSurfaceChanged(GL10 gl, int width, int height), onDrawFrame(GL10 gl) и public class FractalRenderer implements GLSurfaceView.Renderer { //Provide a tag for logging errors private static final String TAG = 'FractalRenderer'; //Create all models private Fractal mFractal; //Transformation matrices private final float[] mMVPMatrix = new float[16]; //Any other private variables needed for transformations @Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { // Set the background frame color GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //Instantiate all models mFractal = new Fractal(); } @Override public void onDrawFrame(GL10 unused) { //Clear the frame of any color information or depth information GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); //Create a basic scale/translate matrix float[] mMVPMatrix = new float[]{ -1.0f/mZoom, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f/(mZoom*mRatio), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -mX, -mY, 0.0f, 1.0f}; //Pass the draw command down the line to all models, giving access to the transformation matrix mFractal.draw(mMVPMatrix); } @Override public void onSurfaceChanged(GL10 unused, int width, int height) { //Create the viewport as being fullscreen GLES20.glViewport(0, 0, width, height); //Change any projection matrices to reflect changes in screen orientation } //Other public access methods for transformations } . Целият клас в крайна сметка ще изглежда по следния начин:

checkGLError

В предоставения код се използват и два полезни метода, loadShaders и public Fractal() { // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) squareCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(squareCoords); vertexBuffer.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) drawOrder.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(drawOrder); drawListBuffer.position(0); // Prepare shaders int vertexShader = FractalRenderer.loadShader( GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = FractalRenderer.loadShader( GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); // create empty OpenGL Program mProgram = GLES20.glCreateProgram(); // add the vertex shader to program GLES20.glAttachShader(mProgram, vertexShader); // add the fragment shader to program GLES20.glAttachShader(mProgram, fragmentShader); // create OpenGL program executables GLES20.glLinkProgram(mProgram); } за подпомагане при отстраняване на грешки и използване на шейдъри.

Във всичко това продължаваме да предаваме веригата от команди по линията, за да капсулираме различните части на програмата. Най-накрая стигнахме до точката, в която можем да напишем какво всъщност е нашата програма прави , вместо как можем да направим теоретични промени в него. Когато правим това, трябва да направим клас на модел, който съдържа информацията, която трябва да се показва за всеки даден обект в сцената. В сложни 3D сцени това може да е животно или тикъл, но ще направим фрактал като далеч по-опростен 2D пример.

В класовете Model пишем целия клас - няма суперкласове, които трябва да се използват. Необходимо е само да имаме конструктор и някакъв метод за изтегляне, който взема всички параметри.

Това каза, че все още има редица променливи, които ще трябва да имаме, които по същество са примерни. Нека да разгледаме точния конструктор, използван в класа Fractal:

static float squareCoords[] = { -1.0f, 1.0f, 0.0f, // top left -1.0f, -1.0f, 0.0f, // bottom left 1.0f, -1.0f, 0.0f, // bottom right 1.0f, 1.0f, 0.0f }; // top right private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

Доста глътка, нали? За щастие това е част от програмата, която изобщо няма да ви се налага да променяте, освен името на модела. Ако промените подходящо променливите на класа, това трябва да работи добре за основните форми.

За да обсъдим части от това, нека разгледаме някои декларации на променливи:

squareCoords

В (-1,-1) указваме всички координати на квадрата. Обърнете внимание, че всички координати на екрана са представени като решетка с (1,1) в долния ляв ъгъл и drawOrder горе вдясно.

В 0 указваме реда на координатите въз основа на триъгълници, които биха съставили квадрата. По-специално за последователност и скорост, OpenGL използва триъгълници за представяне на всички повърхности. За да направите квадрат, просто изрежете диагонал (в този случай 2 до ByteBuffers), за да получите два триъгълника.

За да добавите и двете към програмата, първо трябва да ги конвертирате в необработен байт буфер, за да свържете директно съдържанието на масива с интерфейса OpenGL. Java съхранява масиви като обекти, които съдържат допълнителна информация, която не е пряко съвместима с базирани на указателя C масиви, които използва реализацията на OpenGL. За да отстраните това, loadShader(int type, String shaderCode) се използват, които съхраняват достъп до суровата памет на масива.

След като въведем данните за върховете и изчертаем реда, трябва да създадем нашите шейдъри.

Шейдъри

Когато се създава модел, трябва да се направят два шейдъра: шейдър Vertex и шейдър Fragment (Pixel). Всички шейдъри са написани на GL Shading Language (GLSL), който е C-базиран език с добавяне на редица вградени функции , модификатори на променливи , примитиви , и вход / изход по подразбиране . В Android те ще бъдат предадени като крайни низове чрез const, един от двата метода на ресурсите в Renderer. Нека първо да разгледаме различните видове квалификации:

  • uniform : Всяка крайна променлива може да бъде декларирана като константа, за да може нейната стойност да се съхранява за лесен достъп. Числа като π могат да бъдат декларирани като константи, ако се използват често в шейдъра. Вероятно компилаторът автоматично ще декларира немодифицирани стойности като константи, в зависимост от изпълнението.
  • varying: Унифицираните променливи са тези, които са обявени за константни за всяко отделно изобразяване. Те се използват по същество като статични аргументи на вашите шейдъри.
  • attribute: Ако променлива е декларирана като варираща и е зададена във шейдър на върхове, тогава тя се линейно интерполира във шейдъра на фрагменти. Това е полезно за създаване на всякакъв вид градиент в цвета и се подразбира за промени в дълбочината.
  • vec2: Атрибутите могат да се възприемат като нестатични аргументи към шейдър. Те означават съвкупността от входове, които са специфични за върховете и ще се появят само във Vertex Shaders.

Освен това трябва да обсъдим два други вида примитиви, които са добавени:

  • vec3, vec4, mat2: Вектори с плаваща запетая с дадена величина.
  • mat3, mat4, x: Матрици с плаваща запетая с дадена величина.

Векторите могат да бъдат достъпни от техните компоненти y, z, w и r или g, b, a и vec3 a. Те също така могат да генерират всеки размер на вектор с множество индекси: за a.xxyz, vec4 връща a a със съответните стойности на mat2 matrix.

Матриците и векторите също могат да бъдат индексирани като масиви и матриците ще върнат вектор само с един компонент. Това означава, че за matrix[0].a, matrix[0][0] е валиден и ще върне vec2 a = vec2(1.0,1.0); vec2 b = a; b.x=2.0; . Когато работите с тях, не забравяйте, че те действат като примитиви, а не като обекти. Например, помислете за следния код:

a=vec2(1.0,1.0)

Това оставя b=vec2(2.0,1.0) и b, което не е това, което човек би очаквал от поведението на обекта, където вторият ред ще даде a указател към private final String vertexShaderCode = 'attribute vec4 vPosition;' + 'void main() {' + ' gl_Position = vPosition;' + '}'; .

В набора Mandelbrot по-голямата част от кода ще бъде във фрагментния шейдър, който е шейдърът, който работи на всеки пиксел. Номинално вершинните шейдъри работят върху всеки връх, включително атрибути, които ще бъдат на база на всеки връх, като промени в цвета или дълбочината. Нека да разгледаме изключително простия върхов шейдър за фрактал:

gl_Position

В това, gl_Position е изходна променлива, дефинирана от OpenGL за запис на координатите на връх. В този случай преминаваме в позиция за всеки връх, към който задаваме vPosition. В повечето приложения бихме умножили MVPMatrix от fragmentShaderCode, преобразувайки нашите върхове, но ние искаме фракталът винаги да е на цял екран. Всички трансформации ще се извършват с локална координатна система.

Фрагментният шейдър ще бъде мястото, където се извършва по-голямата част от работата за генериране на набора. Ще зададем precision highp float; uniform mat4 uMVPMatrix; void main() { //Scale point by input transformation matrix vec2 p = (uMVPMatrix * vec4(gl_PointCoord,0,1)).xy; vec2 c = p; //Set default color to HSV value for black vec3 color=vec3(0.0,0.0,0.0); //Max number of iterations will arbitrarily be defined as 100. Finer detail with more computation will be found for larger values. for(int i=0;i4.0){ //The point, c, is not part of the set, so smoothly color it. colorRegulator increases linearly by 1 for every extra step it takes to break free. float colorRegulator = float(i-1)-log(((log(dot(p,p)))/log(2.0)))/log(2.0); //This is a coloring algorithm I found to be appealing. Written in HSV, many functions will work. color = vec3(0.95 + .012*colorRegulator , 1.0, .2+.4*(1.0+sin(.3*colorRegulator))); break; } } //Change color from HSV to RGB. Algorithm from https://gist.github.com/patriciogonzalezvivo/114c1653de9e3da6e1e3 vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 m = abs(fract(color.xxx + K.xyz) * 6.0 - K.www); gl_FragColor.rgb = color.z * mix(K.xxx, clamp(m - K.xxx, 0.0, 1.0), color.y); gl_FragColor.a=1.0; } към следното:

fract

Голяма част от кода е просто математиката и алгоритъмът за това как работи комплекта. Обърнете внимание на използването на няколко вградени функции: abs, mix, sin, clamp и dot, които всички работят върху вектори или скалари и връщащи вектори или скалари. Освен това, draw се използва, който взема векторни аргументи и връща скалар.

Сега, когато разполагаме с нашите шейдъри, настроени за използване, имаме една последна стъпка, която е да приложим public void draw(float[] mvpMatrix) { // Add program to OpenGL environment GLES20.glUseProgram(mProgram); // get handle to vertex shader's vPosition member mPositionHandle = GLES20.glGetAttribLocation(mProgram, 'vPosition'); mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, 'uMVPMatrix'); //Pass uniform transformation matrix to shader GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); //Add attribute array of vertices GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer( mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); // Draw the square GLES20.glDrawElements( GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); FractalRenderer.checkGlError('Test'); } функция в нашия модел:

uniform

Функцията предава всички аргументи на шейдърите, включително attribute матрица за преобразуване и double позиция.

След като сглобим всички части на програмата, най-накрая можем да я пуснем. При условие, че се работи с подходяща поддръжка на докосване, ще бъдат нарисувани абсолютно хипнотизиращи сцени:

урок на opengl

урок на opengl mandelbrot

урок за генератор на mandelbrot

Точност с плаваща запетая

Ако увеличим малко повече, започваме да забелязваме повреда на изображението:

Това няма абсолютно нищо общо с математиката на комплекта зад него и всичко, свързано с начина, по който числата се съхраняват и обработват в OpenGL. Докато по-скорошната поддръжка за float точност е направена, OpenGL 2.0 не поддържа изобщо нищо повече от precision highp float s. Специално ги определихме като най-прецизните плувки, налични с double в нашия шейдър, но дори това не е достатъчно добро.

За да заобиколите този проблем, единственият начин би бил подражават float s с помощта на две GLSurfaceView s. Този метод всъщност е в рамките на порядъка на действителната точност на вграден, но има доста тежки разходи за скорост. Това ще бъде оставено като упражнение на читателя, ако някой иска да има по-високо ниво на точност.

Заключение

С няколко класа за поддръжка OpenGL може бързо да поддържа рендиране на сложни сцени в реално време. Създаване на оформление, съставено от Renderer, задаване на неговия

 и създаване на модел с шейдъри, всичко завършило с визуализацията на красива математическа структура. Надявам се, че ще намерите толкова голям интерес към разработването на приложение за OpenGL ES!

Ускорете с BERT: NLP оптимизационни модели

Back-End

Ускорете с BERT: NLP оптимизационни модели
Изграждане на REST API за наследени PHP проекти

Изграждане на REST API за наследени PHP проекти

Back-End

Популярни Публикации
Fastlane: Автоматизация на iOS в круиз контрол
Fastlane: Автоматизация на iOS в круиз контрол
Ефективни комуникационни стратегии за дизайнери
Ефективни комуникационни стратегии за дизайнери
Изследване на контролирани алгоритми за машинно обучение
Изследване на контролирани алгоритми за машинно обучение
10-те UX доставки, които топ дизайнерите използват
10-те UX доставки, които топ дизайнерите използват
Тест за използваемост за преобразуване: Спрете да следвате тенденциите. Започнете с данните
Тест за използваемост за преобразуване: Спрете да следвате тенденциите. Започнете с данните
 
Кеширане през пролетта с EhCache анотации
Кеширане през пролетта с EhCache анотации
Разбиване на топ 5 мита за отдалечените работници
Разбиване на топ 5 мита за отдалечените работници
Неформално въведение в DOCX
Неформално въведение в DOCX
Проектиране за интерактивна среда и интелигентни пространства
Проектиране за интерактивна среда и интелигентни пространства
Най-добрите UX инструменти (с инфографика)
Най-добрите UX инструменти (с инфографика)
Популярни Публикации
  • конвертиране на низ към дата javascript
  • най-добрите javascript ресурси, модели на най-добри практики
  • какво измерва ценова еластичност на търсенето
  • пример за единичен тест на c#
  • според гещалт закона за подобието, ние групираме обекти, които изглеждат
  • внезапното осъзнаване на решението на проблема се нарича _____.
Категории
  • Дизайн На Марката
  • Тенденции
  • Инструменти И Уроци
  • Технология
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt