Android OpenGL Vykreslování terénu
Terén • Většinou je reprezentovaný pomocí 2D výškové mapy • Na běžných GPU lze výškovou mapu číst přímo ve vertex shaderu, mobilní GPU však tuto funkci nepodporují • Nezbývá než výškovou mapu prostě uložit do pole se souřadnicemi vrcholů
Rozsáhlý Terén • Pro vykreslování rozsáhlého terénu je obvykle potřeba algoritmus level of detail • Kvůli absenci čtení textur vertex shaderem je těžší používat složité LOD techniky • Přehled používaných technik lze nalézt na http://vterrain.org/LOD/Papers/
Mobilní terén • Kvůli jednoduchosti mobilních GPU ve spojením s ne příliš silným CPU (zatím) nelze přenést algoritmy, navržené pro PC • Je třeba se uchýlit ke starším technikám – Vyhnout se aktualizaci vertex bufferů – Vyhnout se složitému napojování dílců
Jednoduchý terén • Terén je uložený jako stejně velké dlaždice o několika různých rozlišeních • Dlaždice jsou již ve formátu vhodném pro kreslení (ne bitmapa, ale vertex array) • Díky předem dané velikosti dlaždic není nutné ukládat index array • Předpokládáme že terén se celý vejde do paměti (v zájmu udržení jednoduchosti, bez ztráty obecnosti algoritmu)
Jednoduchý terén
(jednotlivé dílce terénu)
Navazování dlaždic • a) Neřešíme • b) Uděláme svislé „sukénky“ (filleting, Sun) http://java.sun.com/products/jfc/tsc/articles/jcanyon/
• c) Použijeme „stínové dílce“ (Pouderoux, Marvie: „Adaptive Streaming and Rendering of Large Terrains using Strip Masks“)
Výběr detailu dlaždic • Lze vypočítat zkreslení v závislosti na výšce dlaždice a výšce a vzdálenosti pozorovatele • Nebo lze jednoduše použít vzdálenost od pozorovatele (Losasso, Hoppe: „Geometry clipmaps: Terrain rendering using nested regular grids“)
Výběr detailu dlaždic
(červená = vyšší detail)
Omezení kreslení dlaždic • Známým algoritmem je ořezávání záběrem pohledu (view frustum culling) – Na základě směru pohledu a pozice pozorovatele jde jednoduše otestovat viditelnost dlaždic ještě před kreslením – Výhodné mít dlaždice ve stromové struktuře (např. v octree)
• Pro členité terény lze předpočítat sadu viditelných dlaždic z různých míst (PVS) – Mimo rámec tohoto tutorialu
Ořezávání záběrem pohledu • Pohled má tvar pyramidy • Jednotlivé roviny jdou spočítat z projekční matice • Test zda je dílec terénu před rovinou je primitivní, s výhodou lze využít obalové geometrie jako např. bounding sphere
Texturování • Použijeme jednoduchou opakující se texturu • V pixel shaderu podle výšky vrcholu určíme podíly jednotlivých textur (dole je tráva, nad ní skály, nahoře je sníh) – Pro větší počet vrstev lze použít GL_ARB_texture_array (přítomné na Tegra)
Nebe • Lze použít tzv. skybox, jedná se o texturu nebe, mapovanou na nekonečně velkou krychli – Krychle může být konečně velká, ale pozorovatel musí být uprostřed – Je nutné vypnout depth test glDisable(GL_DEPTH_TEST); glDepthMask(0); // vypne i zápisy
– Lze použít rozšíření GL_ARB_cube_map
obrázek z nvidia.com
Nebe • Pro dynamičtější nebe lze použít tzv. skydome, tedy jakousi polokouli, která zakrývá terén, na ni je možné mapovat pohybující se texturu mraků • Pomocí přepočtu souřadnic v pixel shaderu lze skydome realizovat i s krychlí, nebo dokonce jen s jedním trojúhelníkem obrázek z nvidia.com
Nebe na jeden trojúhelník • Jeden trojúhelník, pokrývající celou obrazovku (případně jeden obdélník) • Vertex shader spočítá směry pohledu v jednotlivých rozích obrazovky • Fragment shader může – Přečíst skybox z cubemap textury – Promítnout směr pohledu na kouli
trojúhelník pokrývající obrazovku
Nebe na jeden trojúhelník • Vertex shader precision highp float; // specifikace přesnosti attribute vec3 v_pos; // pozice – vstup z dat vrcholu uniform mat4 t_modelview_projection_matrix_inverse; // další strana varying vec3 v_view_direction; // výstup – směr pohledu void main() { gl_Position = vec4(v_pos, 1.0); // pozice na obrazovce = pozice vec4 v_worldspace = (t_modelview_projection_matrix_inverse * vec4(v_pos, 1.0)); v_view_direction = v_worldspace.xzy * vec3(1.0, -1.0, 1.0); }
Nebe na jeden trojúhelník • Výpočet matice pro vertex shader Matrix modelview, projection; // vstupní matice – modelview a projection scény Matrix mv_nomove = modelview; mv_nomove[3] = 0; mv_nomove[7] = 0; mv_nomove[11] = 0; mv_nomove[15] = 1; // odstraní z modelview posun (oko bude tím pádem vždy ve středu) Matrix modelview_projection_matrix_inverse = Matrix.Inverse(projection * mv_nomove); // vypočítá součin a inverzi matic
Nebe na jeden trojúhelník • Fragment shader precision highp float; // specifikace přesnosti varying vec3 v_view_direction; // vstup – směr pohledu const vec3 v_sky = vec4(.7 * .9, .9 * .9, 1.0, 0.0), // barva nebe v_tint = vec4(0.5, 0.5, 0.5, 0.0); // zbarvení horizontu void main() { vec3 v_dir = normalize(v_view_direction); // normalizuje směr float f_y = v_dir.y * .5 + .2; // spočítá váhu horizontu float f_tint = (1.0 - f_y) * (1.0 - f_y) * 1.3 - .3; gl_FragColor = v_sky + v_tint * min(f_tint, .6); // míchá barvu }
Výsledek
Konec