TAM Prezentace přednášek Ústav počítačové grafiky a multimédií
Grafika na mobilních zařízeních OpenGL ES 2.0
Motto If Edison had a needle to find in a haystack, he would proceed at once with the diligence of the bee to examine straw after straw until he found the object of his search. I was a sorry witness of such doings, knowing that a little theory and calculation would have saved him ninety per cent of his labor. - Nikola Tesla IVS / Urychlování výpočtů, možnosti paralelizace 3
Obsah: Úvod do OpenGL OpenGL ES on embedded devices Reprezentace objektů ve vertex arrays Vertex a fragment shadery Texturování Framebuffer objekty Doporučená literatura IVS / Urychlování výpočtů, možnosti paralelizace 4
OpenGL Knihovna Open Graphics Library od SGI Dnes udržuje Khronos Group Kreslení pomocí Immediate mode (glvertex3f()) Display lists (glcalllists()) Arrays (gldrawarrays()) Fixed-function pipeline Binding pro mnoho jazyků C /C ++ Java (JOGL) Python (PyGL) Další... IVS / Urychlování výpočtů, možnosti paralelizace 5
OpenGL (ES 1.0) fixed-function pipeline IVS / Urychlování výpočtů, možnosti paralelizace 6
OpenGL programmable pipeline vertex shader tesselation shader geometry shader transform feedback fragment shader IVS / Urychlování výpočtů, možnosti paralelizace 7
OpenGL ES 2.0 pipeline vertex shader tesselation shader geometry shader transform feedback fragment shader IVS / Urychlování výpočtů, možnosti paralelizace 8
OpenGL ES on embedded devices Různé platformy NVIDIA Tegra (ULP GeForce) ARM Android-based devices C /C ++ Java Apple iphone Objective C Java Ubuntu, Win CE C /C ++ IVS / Urychlování výpočtů, možnosti paralelizace 9
OpenGL ES bez zařízení Motivace Vývojářské nástroje, možnost ladění Snadné použití Není potřeba mít cílové mobilní zařízení (zejména velké týmy) Emulace je možná na OpenGL 2.0 hardware See (and use): http://www.stud.fit.vutbr.cz/~xpolok00/proj_glesemu.htm Android emulator (pomalý) IVS / Urychlování výpočtů, možnosti paralelizace 10
Inicializace OpenGL ES Windows CE WGL + nativní okna EGL Ubuntu GLX + Xserver EGL Android android.opengl.glsurfaceview (wrapper EGL) IVS / Urychlování výpočtů, možnosti paralelizace 11
Inicializace OpenGL ES - Android public class MyApp extends Activity { GLSurfaceView mglview; GLSurfaceView.Renderer mrenderer = new MyRenderer(this); protected void oncreate(bundle _instance) { super.oncreate oncreate(_instance); setcontentview(r.layout.main); settitle("opengl ES Hello World"); mglview = (GLSurfaceView)findViewById findviewbyid(r.id.glsv); mglview.seteglcontextclientversion seteglcontextclientversion(2); mglview.setrenderer setrenderer(mrenderer); } protected void onpause() { super.onpause onpause(); mglview.onpause onpause(); } protected void onresume() { super.onresume onresume(); mglview.onresume onresume(); } } IVS / Urychlování výpočtů, možnosti paralelizace 12
Inicializace OpenGL ES - Android public class MyRenderer implements GLSurfaceView.Renderer { private int mwidth = 640, mheight = 480; private MyApp mcontext; } public MyRenderer(MyApp context) { super(); mcontext = context; } public void ondrawframe(gl10 unused) { GLES20.glViewport glviewport(0, 0, mwidth, mheight); GLES20.glClearColor glclearcolor(0.0f, 0.0f, 1.0f, 1.0f); GLES20.glClear glclear(gles20.gl_color_buffer_bit); // GLES20.glDoSomethingRatherInteresting gldosomethingratherinteresting(12345); } public void onsurfacechanged(gl10 gl, int w, int h) { mwidth = w; mheight = h; } public void onsurfacecreated(gl10 gl, EGLConfig cfg) { /* perform init here */ } IVS / Urychlování výpočtů, možnosti paralelizace 13
Inicializace OpenGL ES - Android res.layout.main.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <android android.opengl opengl.glsurfaceview android:layout_height="match_parent" android:layout_width="match_parent" android:id="@+id/glsv glsv"/> </LinearLayout> IVS / Urychlování výpočtů, možnosti paralelizace 14
Inicializace OpenGL ES - Android IVS / Urychlování výpočtů, možnosti paralelizace 15
Kreslení v OpenGL ES Nemůžeme použít immediate mode (glvertex*()) Musíme použít Vertex attributes Vertex arrays Vertex shader Fragment shader IVS / Urychlování výpočtů, možnosti paralelizace 16
Vertex attributes OpenGL už nemá pevně danou sémantiku vrcholových dat Dříve byla pozice, normála, barva, sekundární barva + texcoords Místo toho se používají očíslované atributy glvertexattribpointer(uint attrib_number, // 0 uint num_dimensions, // 3 GLenum data_type, // GL_FLOAT bool is_normalized, // false uint stride, // 3 * sizeof(float) const void *data) // {1, 2, 3} glenablevertexattribarray(uint attrib_number) // 0 V Javě je místo const void* java.nio.buffer IVS / Urychlování výpočtů, možnosti paralelizace 17
Vertex arrays Data vrcholů musí být někde uložená Na lepších GPU použijeme Vertex Buffer Object (VBO) Ten však mobilní zařízení často nemají (sdílená RAM) Nebo použijeme jednoduchý buffer V C /C++ prostě float* V Javě java.nio.floatbuffer (nebo jiný typ) final float[] data = {1, 2, 3}; FloatBuffer buff = ByteBuffer.allocateDirect allocatedirect(data.length * 4). order(byteorder.nativeorder nativeorder()).asfloatbuffer asfloatbuffer(); buff.put put(data); buff.position position(0); GLES20.glVertexAttribPointer glvertexattribpointer(0, 3, GLES20.GL_FLOAT, false, 3 * 4, buff); GLES20.glEnableVertexAttribArray glenablevertexattribarray(0); IVS / Urychlování výpočtů, možnosti paralelizace 18
Vertex arrays Když máme připravená data ve vertex bufferu, můžeme je poslat k vykreslení gldrawarrays(glenum mode, // GL_TRIANGLES uint first, // 0 uint count) // 3 Když chceme kreslit složitější objekty kde se vrcholy opakují, přidáme druhý buffer s indexy (forma komprese) a voláme: gldrawelements(glenum mode, // GL_TRIANGLES uint count, // 3 GLenum data_type, // GL_UNSIGNED_INT const void *indices) // indexy IVS / Urychlování výpočtů, možnosti paralelizace 19
Vertex arrays Vykreslení trojúhelníku y 1 float buff[] = {0,.9f, 0,.9f, -.9f, 0, -.9f, -.9f, 0}; // pozice vrcholů ve 3D glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, buff); // pointer na data vertex atributu 0 (-0.9, -0.9) -1 0 (0, 0.9) (0.9, -0.9) x glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawarrays(gl_triangles, 0, 3); // kreslíme tři vrcholy jako trojúhelník, začínáme nultým IVS / Urychlování výpočtů, možnosti paralelizace 20
Vertex arrays Vykreslení čtverce (-0.9, 0.9) y 1 float buff[] = { +.9f, +.9f, 0, +.9f, -.9f, 0, -.9f, -.9f, 0, -.9f, +.9f, 0}; // pozice vrcholů ve 3D (-0.9, -0.9) -1 0 (0.9, -0.9) (0.9, 0.9) x glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawarrays(gl_quads, 0, 4); // kreslíme 4 vrcholy jako čtverec, začínáme nultým IVS / Urychlování výpočtů, možnosti paralelizace 21
Vertex arrays Vykreslení čtverce (-0.9, 0.9) y 1 float buff[] = { +.9f, +.9f, 0, +.9f, -.9f, 0, -.9f, -.9f, 0, -.9f, +.9f, 0}; // pozice vrcholů ve 3D (-0.9, -0.9) -1 0 (0.9, -0.9) (0.9, 0.9) x glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawarrays(gl_quads, 0, 4); // kreslíme 4 vrcholy jako čtverec, začínáme nultým IVS / Urychlování výpočtů, možnosti paralelizace 22
Vertex arrays Bohužel už nemáme GL_QUADS, GL_QUAD_STRIP a GL_POLYGON Máme GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP a GL_TRIANGLE_FAN 1 3 5 1 2 0 0 3 2 4 5 4 IVS / Urychlování výpočtů, možnosti paralelizace 23
Vertex arrays Vykreslení čtverce (-0.9, 0.9) y 1 float buff[] = { -.9f, +.9f, 0, +.9f, +.9f, 0, 0 (0.9, 0.9) x +.9f, -.9f, 0, +.9f, -.9f, 0, -.9f, -.9f, 0, -.9f, +.9f, 0}; // pozice vrcholů ve 3D glvertexattribpointer(0, 3, (-0.9, -0.9) -1 (0.9, -0.9) GL_FLOAT, false, 3 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawarrays(gl_triangles, 0, 6); // kreslíme 6 vrcholů jako trojúhelníky, začínáme nultým IVS / Urychlování výpočtů, možnosti paralelizace 24
Vertex arrays Vykreslení čtverce (-0.9, 0.9) y 1 float buff[] = { +.9f, +.9f, 0, +.9f, -.9f, 0, -.9f, -.9f, 0, -.9f, +.9f, 0}; // pozice vrcholů ve 3D int indices[] = {3, 0, 1, 1, 2, 3}; (0.9, 0.9) x 0 (-0.9, -0.9) -1 (0.9, -0.9) // permutace vrcholů, tvořící dva trojúhelníky glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawelements(gl_triangles, 6, GL_UNSIGNED_INT, indices); // kreslíme 6 vrcholů které vybíráme ze 4 pomocí pole indices IVS / Urychlování výpočtů, možnosti paralelizace 25
Vertex arrays Vykreslení čtverce (-0.9, 0.9) y 1 float buff[] = { -.9f, -.9f, 0, -.9f, +.9f, 0, +.9f, -.9f, 0, +.9f, +.9f, 0}; // pozice vrcholů ve 3D (-0.9, -0.9) -1 0 (0.9, -0.9) (0.9, 0.9) x glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 gldrawarrays(gl_triangle_strip, 0, 4); // kreslíme 4 vrcholy jako tristrip, začínáme nultým IVS / Urychlování výpočtů, možnosti paralelizace 26
Vertex arrays Vykreslení krychle 4 5 7 6 6 5 4 7 6 4 5 3 2 1 0 3 3 2 0 1 0 1 const float p_box_vertices[] = { -1, -1, -1, +1, -1, -1, +1, -1, +1, -1, -1, +1, -1, +1, -1, +1, +1, -1, +1, +1, +1, -1, +1, +1}; const unsigned int p_box_indices[] = { 0, 1, 3, 2, 6, 1, 5, 0, 4, 3, 7, 6, 4, 5}; IVS / Urychlování výpočtů, možnosti paralelizace 27
Vertex arrays Vykreslení krychle 4 5 7 6 6 5 4 7 6 4 5 3 2 1 0 3 3 2 0 1 0 1 const float p_box_vertices[] = { -1, -1, -1, +1, -1, -1, +1, -1, +1, -1, -1, +1, -1, +1, -1, +1, +1, -1, +1, +1, +1, -1, +1, +1}; const unsigned int p_box_indices[] = { 0, 1, 3, 2, 6, 1, 5, 0, 4, 3, 7, 6, 4, 5}; IVS / Urychlování výpočtů, možnosti paralelizace 28
Vertex Buffer Objekty (VBO) Paměť na GPU pro data vrcholů a indexů Napřed je nutné buffery naplnit (box z předchozí strany) int n_vertex_buffer, n_index_buffer; glgenbuffers(1, &n_vertex_buffer); glbindbuffer(gl_array_buffer, n_vertex_buffer); glbufferdata(gl_array_buffer, sizeof(p_box_vertices), p_box_vertices, GL_STATIC_DRAW); glgenbuffers(1, &n_index_buffer); glbindbuffer(gl_element_array_buffer, n_index_buffer); glbufferdata(gl_element_array_buffer, sizeof(p_box_indices), p_box_indices, GL_STATIC_DRAW); IVS / Urychlování výpočtů, možnosti paralelizace 29
Vertex Buffer Objekty (VBO) Pro nastavení vertex attributů je potřeba buffer nabindovat Adresu dat v bufferu nevíme, použijeme NULL (+ offset, pokud je třeba) Použít pro různé vertex atributy data z více bufferů je dovolené, někdy i žádoucí glbindbuffer(gl_array_buffer, n_vertex_buffer); glenablevertexattribarray(0); glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * sizeof(float), NULL); // NULL ukazuje na začátek VBO // set vertex coordinate attribute glbindbuffer(gl_element_array_buffer, n_index_buffer); gldrawelements(gl_triangle_strip, 14, GL_UNSIGNED_INT, NULL); // NULL ukazuje na začátek VBO s indexy (element array) IVS / Urychlování výpočtů, možnosti paralelizace 30
Vertex Array Objekty (VAO) VAO je objekt, který si pamatuje nastavení vertex atributů Nastavení vyrobíme takto: int n_vao; glgenvertexarrays(1, &n_vao); glbindvertexarray(n_vao); glbindbuffer(gl_array_buffer, n_vertex_buffer); glenablevertexattribarray(0); glvertexattribpointer(0, 3, GL_FLOAT, false, 3 * 4, NULL); glbindbuffer(gl_element_array_buffer, n_index_buffer); glbindvertexarray(0); // zamezíme náhodnému poškození A v okamžiku potřeby si ho už jen vyvoláme: glbindvertexarray(n_vao); gldrawelements(gl_triangle_strip, 14, GL_UNSIGNED_INT, NULL); IVS / Urychlování výpočtů, možnosti paralelizace 31
Vertex shader Krátký program, který říká jak se mají interpretovat vertex atributy Každá instance jeden vertex dostane a jeden taky vyplivne precision highp float; // specifikace přesnosti attribute vec3 v_pos; // atributy - vstup z dat vrcholu varying vec3 v_color; // výstup - barva vrcholu uniform mat4 t_modelview_projection; // parametr shaderu - transformační matice void main() { gl_position = t_modelview_projection * vec4(v_pos, 1.0); // musíme zapsat pozici v_color = v_pos *.5 +.5; // vymyslíme barvu vrcholu pro fragment shader } IVS / Urychlování výpočtů, možnosti paralelizace 32
Fragment shader Krátký program, který říká jak vypočítat z interpolovaných souřadnic barvu Každá instance dostane interpolované atributy pro jeden pixel a na jejich základě může spočítat barvu nebo hloubku, případně pixel zahodit (discard) precision highp float; // specifikace přesnosti varying vec3 v_color; // vstup z vertex shaderu void main() { gl_fragcolor = vec4(v_color, 1.0); // zapíše výstupní barvu } IVS / Urychlování výpočtů, možnosti paralelizace 33
Práce se shadery Na začátku vygenerujeme shader objekty, nahrajeme do nich zdrojový kód shaderů (jako text) a zkompilujeme int p_shader[2]; // vertex a fragment char *p_src[2] = {ReadFile ReadFile( vs.txt ), ReadFile( fs.txt )}; for(int i = 0, n_temp; i < 2; ++ i) { p_shader[i] = glcreateshader((i == 0)? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER); glshadersource(p_shader[i], 1, &p_src[i], NULL); glcompileshader(p_shader[i]); glgetshaderiv(p_shader[i], GL_COMPILE_STATUS, &n_temp); if(n_temp!= GL_TRUE) fail(); // chyba při kompilaci (glgetshaderinfolog glgetshaderinfolog()) free(p_src[i]); //!! } IVS / Urychlování výpočtů, možnosti paralelizace 34
Práce se shadery Vyrobíme program object a shadery k němu připojíme Nastavíme čísla vertex atributů (propojení s proměnnými VS) Slinkujeme program Najdeme čísla parametrů shaderu (uniform) int n_program_object = glcreateprogram(), n_temp; for(int i = 0, n_temp; i < 2; ++ i) glattachshader(n_program_object, p_shader[i]); glbindattriblocation(n_program_object, 0, v_pos ); gllinkprogram(n_program_object); glgetprogramiv(n_program_object, GL_LINK_STATUS, &n_temp); if(n_temp!= GL_TRUE) fail(); // chyba při linkování glgetprograminfolog() int n_mvp_uni = glgetuniformlocation(n_program_object, t_modelview_projection ); IVS / Urychlování výpočtů, možnosti paralelizace 35
Práce se shadery Při použití nastavíme program jako aktivní Nastavíme hodnoty různých parametrů (uniform) Matrix4f t_projection, t_modelview; t_projection.perspective Perspective(90, 4.0f / 3,.01f, 1000); t_modelview.identity Identity(); t_modelview.translate Translate(0, 0, -2); t_modelview.rotatex RotateX(30 * M_PI / 180); t_modelview.rotatey RotateY(10 * M_PI / 180); Matrix4f t_mvp = t_projection * t_camera_matrix; float *p_matrix = &t_mvp[0][0]; // OpenGL už nemá operace s maticemi, musíme počítat sami gluseprogram(n_program_object); gluniformmatrix4fv(n_mvp_uni, 1, GL_FALSE, p_matrix); // aktivujeme program a nastavíme matici IVS / Urychlování výpočtů, možnosti paralelizace 36
Výsledek IVS / Urychlování výpočtů, možnosti paralelizace 37
Textury Textury jsou dvourozměrné rastrové obrázky, jež lze v shaderu číst K texturám přistupujeme pomocí sampleru, je třeba nastavit číslo texturovací jednotky, ve které je textura nastavená OpenGL ES umí i 3D textury (GL_OES_texture_3D) int n_texture; glgentextures(1, &n_texture); glbindtexture(gl_texture_2d, n_texture); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // zapneme mipmapy (trilineární) gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // zapneme bilineární filtr glteximage2d(gl_texture_2d, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, p_bitmap); // specifikujeme obraz glgeneratemipmap(gl_texture_2d); // spočítáme mipmapy IVS / Urychlování výpočtů, možnosti paralelizace 38
Textury Pro texturování tradičním způsobem je potřeba mít souřadnice textur (další vertex atribut) V shaderu ale můžeme souřadnice textury i nějak spočítat float buff[] = {-.9f, -.9f, 0, 0, 0, -.9f, +.9f, 0, 0, 1, +.9f, -.9f, 0, 1, 0, +.9f, +.9f, 0, 1, 1}; // 3D pozice vrcholů + 2D souřadnice textury (5 souřadnic) glvertexattribpointer(0, 3, GL_FLOAT, false, 5 * 4, buff); // pointer na atribut 0 glenablevertexattribarray(0); // povolíme pole atributů 0 glvertexattribpointer(1, 2, GL_FLOAT, false, 5 * 4, buff + 3); // pointer na atribut 1 glenablevertexattribarray(1); // povolíme pole atributů 1 IVS / Urychlování výpočtů, možnosti paralelizace 39
Textury Vertex shader precision highp float; // specifikace přesnosti attribute vec3 v_pos; attribute vec2 v_tex; // atributy - vstup z dat vrcholu varying vec3 v_texcoord; // výstup - souřadnice textury uniform mat4 t_modelview_projection; // parametr shaderu - transformační matice void main() { gl_position = t_modelview_projection * vec4(v_pos, 1.0); // musíme zapsat pozici } v_texcoord = v_tex; // souřadnici textury jen kopírujeme IVS / Urychlování výpočtů, možnosti paralelizace 40
Textury Fragment shader precision highp float; // specifikace přesnosti varying vec2 v_texcoord; // vstup z vertex shaderu uniform sampler2d n_sampler; // sampler pro čtení textury void main() { gl_fragcolor = texture2d(n_sampler, v_texcoord); // přečte texturu a zapíše ji na výstup } int n_sam_uni = glgetuniformlocation(n_program_object, n_sampler ); // najdi n_sampler... gluniform1i(n_sam_uni, 0); // textura je v texturovací jednotce 0 IVS / Urychlování výpočtů, možnosti paralelizace 41
Výsledek IVS / Urychlování výpočtů, možnosti paralelizace 42
Framebuffer objekty (FBO) Umožňují kreslení do textury Různé efekty, Voda, Zrcadla Stínové mapy Framebuffer objekt je kontejner pro framebuffery Renderbuffer objekt (kus paměťi pro uložení rastru) Textura (při kreslení do textury) V OpenGL ES poměrně omezený Jen jeden attachment pro color, musí být obsazený Nejsou k dispozici formáty pro depth textury Dokumentace glgenframebuffers(), glgenrenderbuffers() glbindframebuffer(), glbindrenderbuffer() glrenderbufferstorage() glframebufferrenderbuffer(), glframebuffertexture2d() IVS / Urychlování výpočtů, možnosti paralelizace 43
Framebuffer objekty (FBO) int n_fb, n_rb, n_tex; glgenframebuffers(1, &n_fb); glgenrenderbuffers(1, &n_rb); glbindframebuffer(gl_framebuffer, n_fb); glbindrenderbuffer(gl_renderbuffer, n_rb); glrenderbufferstorage(gl_renderbuffer, GL_DEPTH_COMPONENT16, 256, 256); // renderbuffer pro depth glframebufferrenderbuffer(gl_framebuffer, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, n_rb); // připojí rb glframebuffertexture2d(gl_framebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, n_tex, 0); // připojí texturu int n_status = glcheckframebufferstatus(gl_framebuffer); if(n_status!= GL_FRAMEBUFFER_COMPLETE) fail(); // zpravidla chyba programátora glviewport(0, 0, 256, 256); // potom je třeba vrátit původní // kreslení do textury glframebuffertexture2d(gl_framebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); // uvolnění textury pro kreslení IVS / Urychlování výpočtů, možnosti paralelizace 44
Literatura NVIDIA OpenGL SDK www.fit.vutbr.cz/~ipolok/gles/ www.fit.vutbr.cz/~ipolok/gl/ NeHe IVS / Urychlování výpočtů, možnosti paralelizace 45
The End Questions?