CGT_3 - Parametry, textury a výrazy

Tato kapitola pokračuje ve výkladu konceptů jazyka Cg pomocí jednoduchých vertexových a fragmentových programů. Tato kapitola má následující tři části:

3.1 Parametry

C2E1v_green a C2E2f_passthrough z kapitoly 2 jsou velmi základní. Nyní tyto příklady rozšíříme abychom si ukázali další parametry.

3.1.1 Uniformní parametry

Vertexový program C2E1v_green vždy přiřazuje vertexu zelenou barvu. Pokud přejmenujete program C2E1v_green a změníte tu řádku kde se přiřazuje hodnota OUT.color, pak můžete potenciálně vytvořit odlišný vertexový program pro jakoukoli barvu chcete.

Například, změnou příslušné řádky docílíte svůdně růžového stínovacího programu:

	OUT.color = float4(1.0, 0.41, 0.70, 1.0); // svůdně růžová v RGBA

Svět je ale barevný, takže byste asi nechtěli psát Cg program pro každou barvu. Místo toho můžete program zobecnit předáním parametru který říká jakou barvu zrovna chcete.

Vertexový program C3E1v_anyColor v následujícím příkladu poskytuje parametr constantColor kterému vaše aplikace může přiřadit jakoukoli barvu, spíše než jen jednu určitou konstantní barvu.

	struct C3E1v_Output {
		float4 position : POSITION;
		float4 color : COLOR;
	};

	C3E1v_Output C3E1v_anyColor(float2 position : POSITION, uniform float4 constantColor) {
		C3E1v_Output OUT;
		OUT.position = float4(position, 0, 1);
		OUT.color = constantColor; // nějaká RGBA barva
		return OUT;
	}

Rozdíl mezi C3E1v_anyColor a C2E1_green je v definici hlavičky funkce a v hodnotě kerou každý z programů přiřazuje do OUT.color.

Vylepšená definice vypadá takto:

	C3E1v_Output C3E1v_anyColor(float2 position : POSITION, uniform float4 constantColor)

K parametru pozice obsahuje nová hlavička funkce pojmenovaný constantColor který je v programu definován jako typ uniform float4. Jak bylo řečeno dříve, typ float4 je vektorový typ sestávající ze čtyř hodnot s desetinnou čárkou - v tomto případě představuje RGBA barvu. To o čem jsme ale ještě nemluvili je kvalifikátor uniform.

Typový kvalifikátor uniform

Kvalifikátor uniform určuje zdroj počáteční hodnoty proměnné. Když Cg program deklaruje proměnnou jako uniform, říká tím že počáteční hodnota proměnné přichází z prostředí mimo daný Cg program. Toto externí prostředí obsahuje stav vašeho 3D programovacího rozhraní a jiné páry jméno / hodnota vytvořené skrze běhové prostředí Cg.

V případě proměnné constantColor v příkladu C3E1v_anyColor generuje Cg překladač vertexový program, který získá počáteční hodnotu proměnné z registru konstant vertexového procesoru uvnitř GPU.

Za pomoci běhového prostředí Cg může vaše 3D aplikace vložit jméno pro uniformní parametr obsažený v Cg programu - v tomto případě constantColor a použít ho k načtení správné hodnoty pro nějakou uniformní proměnnou do GPU. Detaily o tom jak jsou uniformní parametry specifikovány a načítány se liší profil od profilu, ale běhové prostředí Cg tento proces ulehčuje. Příloha B vysvětluje jak na to.

Náš vertexový program constantColor:

	OUT.color = constantColor; // nějaká RGBA barva

Jakoukoli barvu aplikace přiřadí uniformní proměnné constantColor, takovou Cg program přiřadí výstupnímu vertexu když C3E1v_anyColor transformuje vertex.

Přidání uniformního parametru nám umožňuje zobecnit náš původní příklad tak aby renderoval jakoukoli barvu i když původně uměl renderovat jen zelenou.

Když není kvalifikátor uniform

Když Cg program neobsahuje kvalifikátor uniform v deklaraci proměnné, můžete přiřadit počáteční hodnotu proměnné jedním z následujících způsobů:

Co znamená klíčové slovo uniform v RenderManu oproti Cg

Klíčové slovo uniform bude určitě známé programátorům kteří psali stínovací programy v prostředí RenderMan. Ovšem jeho význam v Cg je odlišný od významu v RenderManu.

V RenderManu uniform modifikátor označuje proměnné, které jsou konstantní pro daný stínovaný povrch, zatímco varying proměnné jsou ty které se mění pro daný stínovaný povrch.

Cg tzto proměnné používá odlišně. V Cg proměnná kvalifikovaná jako uniform získává svou počáteční hodnotu z externího prostředí a kromě tohoto rozdílu v inicializaci je naprost stejná jako jakákoli jiná proměnná. Cg dovoluje všem proměnným měnit svou hodnotu, pokud ovšem nejsou specifikovány s kvalifikátorem const. Narozdíl od RenderManu, Cg nemá klíčové slovo varying.

Kromě sémantického rozdílu v konceptu kvalifikátoru uniform v RenderManu a Cg, proměnné deklarované jako uniform v RenderManu odpovdají proměnným deklarovaným jako uniform v Cg a naopak.

3.1.2 Typový kvalifikátor const

Cg má také kvalifikátor const. Tento kvalifikátor ovlivňuje proměnné stejným způsobem jako kvalifikátor const v C a C++: omezuje způsob jakým může být proměnná ve vašem programu použita. Nemůžete přiřadit hodnotu, nebo ji jinak změnit, proměnné, která je specifikována jako konstanta. Použijte kvalifikátor const k označení proměnné, jejíž hodnota by se neměla nikdy změnit. Cg překladač vygeneruje eror pokud zjistí že jste se pokusili změnit hodnotu proměnné deklarované jako const.

Zde jsou nějaké příklady nevhodného použití proměnné deklarované jako const:

	const float pi = 3.14159;
	pi = 0.4; // chyba protože pi je specifikována jako const
	float a = pi++; // implicitní modifikace je také chyba

Typové kvalifikátory const a uniform jsou nezávislé, takže proměnná může být specifikována jako const nebo uniform, nebo jako obojí, či nemusí mít ani jeden z těchto kvalifikátorů.

Proměnné parametry

Už jste viděli příklady proměnných paramterů měnících se pro každý vertex v obou programech C2E1v_green a C3E1v_anyColor. Vstupní sémantika POSITION která následuje parametr position v programech C2E1v_green a C3E1v_anyColor říká že GPU inicializuje každý parametr position vstupní hodnotou pozice každého vertexu zpracovávaného každým z pragramů.

Sémantiky poskytují způsob jak inicializovat parametry Cg programu hodnotami které se mění od vertexu k vertexu (ve vertexových programech) nebo fragment od fragmentu (ve fragmentových programech).

Mírná modifikace programu C3E1v_anyColor, nazvaná C3E2v_varying v následujícím příkladu umožňuje programu produkovat ne jen jednu konstantní barvu, ale barvu a množinu souřadnic textury (použitou pro přístup k texturám) které se mohou lišit pro každý vertex.

	struct C3E2v_Output {
		float4 position : POSITION;
		float4 color : COLOR;
		float2 texCoord : TEXCOORD0;
	};

	C3E2v_Output C3E2v_varying(float2 position : POSITION, float4 color : COLOR, float2 texCoord : TEXCOORD0) {
		C3E2v_Output OUT;
		OUT.position = float4(position, 0, 1);
		OUT.color = color;
		OUT.texCoord = texCoord;
		return OUT;
	}

Prototyp příkladu (hlavička funkce) vertexového programu C3E2v_varying vypadá takto:

	C3E2v_Output C3E2v_varying(float2 position : POSITION, float4 color : COLOR, float2 texCoord : TEXCOORD0)

Příklad C3E2v_varying nahrazuje parametr constantColor deklarovaný jako uniformní parametr v příkladu C3E1v_anyColor dvěma novými proměnnými parametry color a texCoord. Program přiřazuje těmto parametrům také sémantiky COLOR a TEXCOORD0. Tyto dvě sémantiky odpovídají barvě vertexu určené aplikací a souřadnici textury nastavené na nulu.

Místo výstupu obsahující pozici každého vertexu a konstantní barvy, tento nový program transformuje každý vertex a jeho výstupem je pozice každého vertexu, jeho barva a jedna množina souřadnic textury. Používá k tomu následující kód:

	OUT.position = float4(position, 0, 1);
	OUT.color = color;
	OUT.texCoord = texCoord;

Obrázek 3-1 ukazuje výsledek vykreslení našeho původního trojúhelníku použitím vertexového programu C3E2v_varying a fragmentového programu C2E2f_passthrough. Předpokládáme že jste použili OpenGL nebo Direct3D k přiřazení barev jednotlivým vrcholům trojúhleníku, pro vrchní dva vertexy modrou barvu a průhlednou nebo bílou pro dolní vertex. barevná interpolace provedená harwarovou rasterizací plynule stínuje vnitřní fragmenty trojúhelníku. I když jsou souřadnice textury součástí vstupu a výstupu C3E2v_varying vertexového programu, tak fragmentový program C2E2f_passthrough tyto souřadnic ignoruje.

3.2 Vzorkovače textur

Příklad C3E2v_varying předával souřadnice textur pro kadý vertex skrze vertexový program. I když fragmentový program C2E2f_passthrough tyto souřadnice textur ignoruje, další fragmentový program, nazvaný C3E3f_texture, používá souřadnice textur k vzorkování textury.

	struct C3E3f_Output {
		float4 color : COLOR;
	};

	C3E3f_Output C3E3f_texture(float2 texCoord : TEXCOORD0, uniform sampler2D decal) {
		C3E3f_Output OUT;
		OUT.color = tex2D(decal, texCoord);
		return OUT;
	}

Struktura C3E3f_Output je v podstatě ta samá jako struktura C2E2f_Output použítá v programu C2E2f_passthrough v předchozím příkladu fragmetnového programu. Co je nové je deklarace hlavičky programu C3E3f_texture:

	C3E3f_Output C3E3f_texture(float2 texCoord : TEXCOORD0, uniform sampler2D decal)

Fragmentový program C3E3f_texture přijímá množinu interpolovaných souřadnice textury, ale ignoruje interpolovanou barvu. Také přijím uniformní parametr nazvaný decal typu sampler2D.

3.2.1 Vzorkovací objekty

Vzorkovač v Cg znamená externí objekt který Cg může vzorkovat, například textura. Přípona 2D v typu sampler2D znamená že texturou je konvenční dvojrozměrná textura. Následující tabulka pbsahuje seznam dalších vzorkovacích typů podporovaných v Cg které odpovídají různým druhům textur. Některé z nich uvidíte v následujících kapitolách.

Typ vzorkovačeTyp texturyPoužití
sampler1DJednorozměrná textura1D funkce
sampler2DDvourozměrná texturaObtisky, normálové mapy, odleskové mapy, stínové mapy atd.
sampler3DTrojrozměrné texturyObjemová data, funkce pro 3D atenuace
samplerCUBETextura pro krychlové mapyMapy prostředí, normalizační krychlové mapy
samplerRECTTextury s rozměry které nejsou násobky mocnin 2, 2D textury bez mip mapováníVideo, fotografie, dočasná paměť

Souřadnice textury určují kde hledat když se přistupuje k textuře. Obrázek 3-2 ukazuje 2D texturu s vyhledáním barvy texelu pomocí souřadnic textur (0.6, 0.4). Obvykle se sořadnice textury pohybují v rozmezí 0 až 1, ale můžete použít i hodnoty mimo tento rozsah. Nebudeme se tím ale příliš detailně zabývat, protože výsledné chování závisí na tom jak nastavíte svou texturu v OpenGL nebo Direct3D.

Sémantika pro množinu souřadnic textury pojmenované texCoord je TEXCOORD0 a odpovídá množině souřadnic textur v texturovací jednotce 0. jak jméno paramteru vzorkovače napovídá, účelem tohoto fragmetnového programu je použít interpolovanou souřadnici textury fragmentu k přístupu k textuře.

3.2.2 Vzorkování textur

Dalším zajímavým řádkem programu C3E3f_texture je ten na kterém se přistupuje k textuře která tvoří obtisk pomocí interpolovaných souřadnic textury:

	OUT.color = tex2D(decal, texCoord);

Funkce tex2D je součástí standardní knihovny Cg. Je členem rodiny funkcí které přistupujík různým typům vzorkovačů s danou množinou souřadnic textu a navrací vektor jako výsledek. Tento výsledek obsahuje vzorkovaná data na místě které je dáno množinou souřadnic textury ve vzorkovači.

V praxi se to provádí pomocí vyhledávání v textuře. To jak je textura vzorkovaná a filtrovaná záleží na typu textury a parametrech textury které jsou asociované s objektem textury v Cg proměnné. Vlastnosti textury můžete určit pro danou texturu použitím daných příkazů OpenGL nebo Direct3D, v závislosti na vaší volbě 3D programovacího rozhraní. Vaše aplikace pak provede pravděpodobně toto spojení použitím běhového rozhraní Cg.

2D přípona naznačuje že tex2D musí vzorkovat a objekt vzorkovače typu sampler2D. Podobně funkce texCUBE vrací vektor, přijímá vzorkovač typu samplerCUBE jako první argument a vyžaduje trojsložkovou množinu souřadnic textury jako druhý argument.

Základní fragmentové profily (jako je ps_1_1 nebo fp20) omezují vzorkovací funkce textur jako je tex2D a texCUBE na množinu souřadnic textury která odpovídá texturovací jednotce vzorkovače. Aby byl program C3E3f_texture tak jenoduchý jak jen to jde a podporoval všechny fragmentové profily, tak splňuje tato omezení (viz sekce 2.3.1, kde naleznete krátký úvod k profilům).

Pokročilé fragmentové profily (jako ps_2_x, fp20) dovolují vzorkovači být vzorkován použitím množiny suřadnic textury i z jiných texturovacích jednotek, nebo dokonce i ze souřadnic textur vypočítaných ve vašem Cg programu.

3.2.3 Posílání souřadnic textury při vzorkování textury

Vertexový program C3E2v_varying předává pozici každého vertexu, barvu a množinu souřadnic textury resterizeru. Fragmetnový program C3E3f_texture ignoruje interpolovanou barvu, ale vzorkuje obrázek textury pomocí interpolované množiny souřadnic textury. Obrázek 3-3 ukazuje co se stane když spojíte tyto dva Cg programy s texturou která obsahuje obrázek děsivé tváře, a poté renderuje náš jednoduchý trojúhelník s dalšími souřadnicemi textury náležícími každému vertexu.

3.3 Matematické výrazy

Do této chvíle dělaly všechny programy které jsme si ukaázali jen o něco více než to že předávaly parametry, nebo použily parametr ke vzorkování textury. Konvenční neprogramovatelné 3D progamovací rozhraní však mohou dělat to stejné. Cílem těchto příkladů bylo uvést vás do Cg a ukázat vám strukturu jednoduchých Cg programů.

Zajímavější Cg programy provádí výpočty se vstupními parametry použitím operátorů a vestavěných funkcí ze standardní knihovny Cg.

3.3.1 Operátory

Cg podporuje stejné aritmetické, porovnávací a ostatní operátory jako C a C++. To znaméná že sčítání je vyjádřeno znakem +, násobení symbolem * a větší než nebo rovno operátorem >=. Jak už jste mohli vidět v předchozích příkladech, přiřazení se dělá znakem =.

Zde jsou nějaké příklady výrazů v Cg:

	float total = 0.333 * (red + green + blue);
	total += 0.333 * alpha;
	float smaller = (a < b) ? a : b;
	float eitherOption = optionA || option B;
	float allTrue = v[0] && v[1] && v[2];

Cg se liší od C a C++ protože poskytuje vestavěné operace pro aritmetické operace s vektory. Toho můžete v C++ dosáhnout napsáním svých vlastních tříd které používají přetěžování operátorů, ale matematické operace s vektory jsou v Cg standardem.

Následující operátory pracují s vektory v závislosti na typu složek:

*Násobení
/Dělení
-Negace
+Sčítání
-Odčítání

Pokud jsou operandy skalární veličina a vektor, pak je skalární veličina převedena na vektor příslušné velikosti.

Zde jsou nějaké příklady vektorových výrazů v Cg:

	float3 modulatedVector = color * float3(0.2, 0.4, 0.5);
	modulatedColor *= 0.5;
	float3 specular = float3(0.1, 0.0, 0.2);
	modulatedColor += specular;
	negatedColor = -modulatedColor;
	float3 direction = positionA - positionB;
OperátoryPřiřazeníPoužití
( ) [ ] ——> .Zleva do pravaVolání funkce, reference na pole, reference na strukturu, výběr prvku
! ~ ++ -- + - * & (typ)sizeofZprava do levaUnární operátory: negace, inkrement, dekrement, pozitivní hodnota, negativní hodnota, nepřímé adresování (ukazatel), adresa, přetypování
* / %Zleva do pravaNásobení, dělění, zbytek po dělění
+ -Zleva do pravaSčítání, odčítání
<< >>Zleva do pravaOperátory posunu
< <= > >=Zleva do pravaRelační operátory
== !=Zleva do pravaRovnost, nerovnost
&Zleva do pravaBitový AND
^Zleva do pravaBitový exluzivní OR
|Zleva do pravaBitový OR
&&Zleva do pravaLogický AND
||Zleva do pravaLogický OR
?:Zprava do levaPodmíněný výraz
= += -= *= /= %= &= ^= |= <<= >>=Zprava do levaPřiřazení, výrazy přiřazení
,Zprava do levaOperátor čárka

Poznámka

Operátory jsou seřazeny shora dolů, od těch s nejvyšší po ty s nejmenší prioritou. Operátory ve stejném řádku mají tu stejnou přednost.

Předchozí tabulka uvádí kompletní seznam operátorů spolu s jejich prioritou, přiřazením a použitím. Zvlášť vyznačené operátory jsou momentálně vyhrazeny. Momentálně ale žádný Cg program nemůže tytovyhrazené operátory používat protože současný grafický hardware nepodporuje bitové operace s celými čísly.

3.3.2 Číselné datové typy závislé na profilu

Když programujete v C nebo C++ a deklarujete proměnné, vybíráte přitom z několika různě velkých datových celočíselných typů (int, long, short, char) a pár různě velikých datových typů s desetinnou čárkou (float, double).

Vaše CPU poskytuje podporu pro všechny tyto základní datové typy. Avšak GPU obecně nepodporují tolik datových typů, i když s jejich vývojem se dá předpokládat že jich budou podporovat více. Například existující GPU nepodporují ukazatele ve vertexových nebo fragmentových programech.

Plovoucí datové typy

Cg poskytuje tyto plovoucí datové typy - float, half a double. Přístup Cg k definici těchto datových typů je podobný jako v C - jazyk samotný nestanovuje nějakou přesnost. Rozumí se, že half má rozsah a přesnost menší nebo stejnou jako je rozsah a přesnot float a double.

Datový typ half v C nebo C++ neexistuje. Tento nový datový typ uvedený v Cg obsahhuje hodnotu v plovoucí desetinné čárce s poloviční přesností (obvykle 16 bitů) která spotřebuje méně místa a rychleji se s ní počítá než se standardní hodnotou v plovoucí desetinné čárce (obvykle 32 bitů).


CineFX

Architektura nVidia CineFX GPU podporuje hodnoty s poloviční přesností ve fragmetnových programech. Datový typ half je často vhodný pro ukládání mezivýsledků ve fragmentových programech, jako jsou barvy a normalozované vektory. Použitím half místo float lze vaše fragmentové programy urychlit.


Dle návrhu GPU poskytují datové typy které reprezentují kontinuální veličiny, jako třeba barvy a vektory. Nepokskytují (momentálně) datové typy které představují diksrétní veličiny, jako jsou alfanumerické znaky a bitové masky, protože s takovými daty obvykle nepracují.

Kontinuální veličiny nejsou omezeny jen na celočíselné hodnoty. Při programování CPU obvykle programátoři používají k reprezentaci kontinuálních veličin datové typy s plovoucí destinnou čárkou protože tyto mohou reprezentovat zlomky. Kontinuální veličiny zpracovávané GPU, částečně n aúrovni fragmentů, byly omezeny do intervalů jako [0, 1] nebo [-1, +1] spíše než aby podporovaly celý rozsah v desetinné čárky. Například barvy jsou často omezeny na interval [0, 1] a normalizované vektory na interval [-1, +1]. Tyto rozsahově omezené datové typy jsou známé jako typy s "fixní" destinnou čárkou spíše než typy s plovoucí desetinnou čárkou.

I když data s fixní desetinnou čárkou mají omezenou přesnot, mohou reprezentovat kontinuální veličiny. Avšak nemají tentýž rozsah jako datové typy s plovoucí desetinnou čárkou jejichž zápis je podobný vědeckému zápisu. Hodnota v plovoucí desetinné čárce používá k mantise navíc exponent proměnné (což je podobné tomu jak se zpaisují čísla vědeckým zápisem, jako třeba 2.99 x 108), kdežto hodnoty s fixní desetinnou čárkou používají fixní exponent. Například nenormalizovaný vektor nebo dostatečně velké souřadnice textury mohou vyžadovat použití typu s plovoucí desetinnou čárkou aby se předešlo přetečení rozsahu daného fixní desetinou čárkou.

Dnešní GPU zvládají data s plovoucí desetinnou čárkou stejně dobře při vykonávání vertexových i fragmentových programů. Starší programvatelné GPU však používaly plovoucí desetinnou čárku jen při zpracovávání vertexů; pro zpracovávání fragmentů používají jen pevnou desetinnou čárku.

Cg musí umět pracovat s daty s pevnou desetinnou čárkou kvůli podpoře GPU které nepodporují plovoucí desetinnou čárku. To znamená že určité fragmentové profily používají pevnou desetinnou čárku. Následující tabulka obsahuje seznam různých Cg profilů a popisuje jak reprezentují různé datové typy. Pro Cg programátory to znamená že float nemusí být typ s plovoucí desetinnou čárkou ve všech profilech a situacích.

Jméno profiluTypČíselná reprezentace
arbfp1, arbvp1, vs_1_1, vs_2_0, vp20, vp30float, double, half, fixedPlovoucí desetinná čárka
intPlovoucí desetinná čárka oříznutá na celé čísloPlovoucí desetinná čárka pro mapování textur; plovoucí desetinná čárka v rozsahu [-1, +1] pro obarvování fragmentů
fp20float, double, half, int, fixedPlovoucí desetinná čárka pro mapování textur; plovoucí desetinná čárka v rozsahu [-1, +1] pro obarvování fragmentů
ps_1_1, ps_1_2, ps1_3float, double, half, int, fixedPlovoucí desetinná čárka pro mapování textur; pevná desetinná čárka pro obarvování fragmentů v rozsahu závislém na GPU; rozsah závisí na Direct3D implementaci
ps_2_0, ps_2_xfloat, double24 bitová plocoucí desetná čárka (minimum)
intPlovoucí desetinná čárka oříznutá na celočíselný datový typ
half16 bitová plovoucí desetinná čárka(minimum)
fixedZáleží na nastavení překladače
fp30float, doublePlovoucí desetinná čárka
intPlovoucí desetinná čárka oříznutá na celočíselný datový typ
half16 bitová desetinná čárka
fixedPevná desetinná čárka v rozsahu [-2, 2]

Poznámka

Profily fp20 a ps_1_1 zachází s proměnnými při obarvování vertexů jako s čísly s pevnou desetinnou čárkou v roazsahu [-1, +1]. Obarvováním fragmentů myslíme operace prováděné až po výsledcích mapování textur. Pokud chcete opravdové datové typy s plovoucí desetinnou čárkou, použijte profily arbfp1, fp30 nebo vp_2_0, ale mějte na paměti že jsou to pokročilé profily které nejsou podporované staršími GPU.

Poznámka

Architektura CineFX podporuje speciální, vysoce výkonný plovoucí datový typ nazvaný fixed určený pro fragmentové programy. Datový typ fixed má rozsah [-2, +2] (to znamená rozsah od mínus 2 do ne zcela plus 2) v profilu fp30. V ostatních profilech je datový typ fixed tím nejmenším dostupným datovým typem s plovoucí desetinnou čárkou. Přestože překladač Vg (cgc) a běhové prostředí podporují datový typ fixed (a jeho vektorové varianty jako fixed3 a fixed4), tak HLSL překladač (fxc) od Microsoftu jej nepodporuje.

3.3.3 Věstavěné funkce standardní knihovny

Standardní knihovna Cg obsahuje mnoho vestavěných funkcí které zjednodušují programování GPU. V mnoha případech jsou tyto funkce namapovány na nativní GPU instrukce, čímž zvyšují svou efektivitu.

Tyto vestavěné funkce jsou podobné funkcím ve standardní knihovně jazyka C. Standardní knihovna Cg poskytuje praktickou sadu trigonometrických, exponenciálnách, vektorových, maticových a texturovacích funkcí. Neobsahuje však rutiny pro vstup/výstup, práci s řetězci nebo pro práci s pamětí, protože Cg tyto operace nepodporuje (i když vaše aplikace v C nebo C++ by jistě mohla).

Jednu takovou funkci standardní knihovny Cg, tex2D, už jsme použili v příkladu 3-3. V tabulce níže najdete seznam vybraných funkcí které vám standardní knihovna Cg nabízí. Kompletní seznam funkcí standardní knihovny najdete v příloze E.

Protoyp funkcePoužítí dle profiluPopis
abs(x)Ve všech profilechAbsolutní hodnota
cos(x)Vertexové profily, pokročilé fragmentové profilykosinus úhlu v radiánech
cross(v1, v2)Vertexové profily, pokročilé fragmentové profilyVektorový (cross) produkt dvou vektorů
ddx(a), ddy(a)Pokročilé fragmentové profilyAproximace částečných derivátů a vzhledem k x-ovým nebo y-ovým souřadnicím okna
determinant(M)Vertexové profily, pokročilé fragmentové profilyDeterminant matice
dot(a, b)Všechny profily, ale omezně v základních fragmentových profilechskalárový (dot) produkt dvou vektorů
floor(x)Vertexové profily, pokročilé fragmentové profilyNejvětší celé číslo které je menší než x
isnan(x)Pokročilé vertexové a fragmentové profilyVrací true pokud x není číslo (NaN)
lerp(a, b, f)Všechny profilyLineární interpolace mezi a a b, založná na f
log2(x)Vertexové profily, pokročilé fragmentové profilyLogartimus x se základem 2
max(a, b)Všechny profilyMaximum z a a b
mul(M, N), mul(M, v), mul(v, M)Vertexové profily, pokročilé fragmentové profilyNásobení matice maticí, násobení matice vektorem, násobení vektoru maticí
pow(x, y)Vertexové profily, pokročilé fragmentové profilyUmocní x na y
radians(x)Vertexové profily, pokročilé fragmentové profilyPřevod stupňů na radiány
reflect(v, n)Vertexové profily, pokročilé fragmentové profilyOdrazový vektor vstupujícího paprsku v a normálového vektoru n
round(x)Vertexové profily, pokročilé fragmentové profilyZaokrouhlí x na nejbližší celé číslo
rsqrt(x)Vertexové profily, pokročilé fragmentové profilyInverzní odmocnina z x
tex2D(sampler, x)Fragmetnové profily s omezením u základních profilůProhledávání 2D textur
tex3Dproj(sampler, x)Fragmentové profily s omezením u základních profilůProhledávání projektivních 3D textur
texCUBE(sampler, x)Fragmentové profily s omezením u základních profilůProhledávání cube-map textur

Přetěžování funkcí

Standardní knihovna Cg "přetěžuje" většinu svých funkcí, takže ta samá funkce funguje pro více datových typů. Stejně jako v C++ funguje přetěžování funkcí v Cg oskytnutím více implementací té samé funkce se steným jménem ale s různým typem parametrů.

Přetěžování může přijít velm vhod. Znamená to že můžete použít funkci, například abs, se skalárním parametrem, dvousložkovým parametrem, třísložkovým parametrem nebo čtyřsložkovým parametrem. V každém z uvedených přápadů Cg "zavolá" příslušnou verzi funkce absolutní hodnoty:

	float4 a4 = float4(0.4, -1.2, 0.3, 0.2);
	float2 b2 = float2(-0.3, 0.9);
	float4 a4abs = abs(a4);
	float2 b2abs = abs(b2);

Ukázka kódu výše volá funkci abs dvakrát. V prvním případě přijímá abs čtyřsložkový vektor. Ve druhém případě přijímá abs dvousložkový vektor. V závislosti na předaných parametrech pak překladač volá příslušnou verzi abs. Rozsáhlé používání přetěžování ve standardní knihovně Cg vás oprošťuje od přemýšlení jakou funkci volat vzhledem k typu vektoru nebo jiného parametru. Cg automaticky vybere příslušnou implementaci funkce kterou potřebujete podle jejího jména.

Přetěžování funkcí není omezeno jen na funkce standardní knihovny Cg. Můžete psát vaše vlastní interní funkce které jsou přetížené.

Ptěžování funkcí v Cg lze aplikovat i na různé implementace te samé funkce pro různé profily. Například pokročilý vertexový profil pro novou GPU může obsahovat speciální instrukce k výpočtu trigonometrických funkcí sinus a kosinus. V základním vertexovém profilu pro starší GPU může tato speciální instrukce chybět. Tento nedostatek můžete vyvážit funkcí která bude dopočítávat sinus nebo kosinus pomocí podporovaných vertexových instrkcí i když s menší přesností. Mohli byste napsat dvě funkce které děají v podstatě totéž ale každá z nich vyžaduje určitý profil.

Podpora přetěžování v závislosti na profilu vám pomůže oddělit omezení jednotlivých profilů ve vašich Cg programech jen na pomocné funkce. Další informace o přetěžování závislém na profilu se můžete dozvědět v Cg Toolkit User's Manual: A Developer's Guide to Programmable Graphics.

Efektivita a přesnost standardní knihovny Cg

Používejte standardni knihovnu Cg k matematickým nebo jiným operacím které podporuje kdykoli je to možné. Funkce standardní knihovny Cg jsou stejně nebo více efektivní a precizní než podobné funkce napsané vámi.

napříkůad funkce dot počítá dot produkt dvou vektorů. Mohli byste tuto funkci napsat sami podobným způsobem jako v ukázce níže:

float myDot(float3 a, float3 b) {
	return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}

Je to stejná matematická formulace jakou používá funkce dot. Avšak funkce dot je namapovaní na speciální instrukci GPU, takže dot produkt získáný pomocí funkce ze standardní knihovny Cg bude velmi pravděpodobně rychlejší a přesnější způsob než použití funkce myDot

Poznámka

Používáním funkcí standardní knihovny Cg instruujete překladač ke generování efektivního a precizního programu pro vaši GPU.

3.3.4 2D Zkroucení

V dalším příkladu zaměstnáte výrazy, operátory a standardní knihovnu Cg. Tento příklad demonstruje jak zkroutit 2D geometrii. Čím dále je vertex od středu okna, tím více náš vertexový program obtočí vertex kolem středu okna.

program C3E4v_twist v příkladu níže používá nísobení skaláru vektorem, sčítání skaláru a jeho násobení, negaci skaláru, funkci length ze standardní knihovny a funkci sincos také ze standardní knihovny.

struct C3E4_Output {
	float4 position : POSITION;
	float4 color : COLOR;
};

C3E4_Ouput C3E4v_twist(float2 position : POSITION, float4 color : COLOR, uniform float twisting) {
	C3E4_Output OUT;
	float angle = twisting * length(position);
	float cosLength, sinLength;
	sincos(angle, sinLength, cosLength);
	OUT.position[0] = cosLength * position[0] + -sinLength * position[1];
	OUT.position[1] = sinLength * position[0] + cosLength * position[1];
	OUT.position[2] = 0;
	OUT.position[3] = 1;
	OUT.color = color;
	return OUT;
}

Program C3E4v_twist přijímá pozici vertexu position a barvu color jako variabilní parametry a uniformní skalár twisting jako faktor zvětšení. Následující obrázek 3-4 ukazuje příklad různé velikosti zkroucení.

Funkce length a sincos ze standardni knihovny

Funkce length má přetížený prototyp, kde SCALAR je jeakýkoli skalární datový typ a VECTOR je stejného datového typu jako skalární datový typ SCALAR s jednou, dvěma, třemi či čtyřmi složkami:

SCALAR length(VECTOR x);

Funkce length standardní knihovny Cg vrací délku skaláru svého jediného vstupního parametru:

	float angle = twisting * length(position);

Program vypořítává úhel v radiánech, což je parametr twisting vynásobený délkou vstupní pozice. Poté funkce sincos standardní knihovny vypočítá sinus a kosinus tohoto úhlu.

Funkce sincos má násldující přetážený prototyp, kde SCALAR je jakýkoli skalární datový typ:

void sincos(SCALAR angle, out SCALAR s, out SCALAR c);

Když funkce sincos navrací, Cg aktualizuje parametry volání s a c hodnotami sinus a kosinus parametru angle (za předpokladu že je v radiánech).

Předávání parametrů výsledkem

Kvalifikátor out říká, že když funkce navrací hodnotu, musí Cg přiřadit výslednou hodnotu formálního parametru kvalifikovaného klíčoým slovem out odovídajícímu parametru volání. Počáteční hodnota parametru kvalifikovaného out není definovaná. Tato volací konvence je známá jako předávání parametrů výsledkem volání (nebo vykopírování).

Jazyk C nemá žádnou podobnou konvenci pro předávání parametrů. C++ umožňuje mít parametr odkazující se na funkci (označený předponou & před formálními parametry), ale to je konvence nazývaná předávání parametrů odkazem, ne výsledkem jak je tomu v Cg.

Cg nabízí také klíčová slova in a inout. kvalifikátor typu in říká Cg aby se parametr předal hodnotou. Parametr volající funkce inicializuje odpovídající formální parametr volané funkce. Když funkce s parametry kvalifikovanými pomocí in navrací, Cg ignoruje hodnoty těchto parametrů, pokud nejsou také kvalifikovány pomocí out.

Jazyk C používá k předávání parametrů kopírování hodnoty pro všechny parametry. C++ používá to stejné , kromě těch přdávaných odkazem.

Kvalifikátor typu inout (nebo kvalifkátory in a out pro jednotlivé parametry) kombinuje volání hodnotou s voláním výsledkem (také jinak známé jako volání hodnotou výsledku nebo vkopírování a vykopírování).

Kvalifikátor in je volitelný protože pokud nepoužijete kvalifikátor in, out nebo inout, použije se kvalifikátor in.

Můžete použít parametry kvalifikované out nebo inout a přitom navracet konveční návratovou hodnotu.

Otáčení vertexů

Poté co program spočítá sinus a kosinus úhlu otočení vertexu, provede se transformace. Následující rovnice vyjadřuje 2D rotaci.

Následující ukázka kódu implementuje tuto rovnici. V kapitole 4 se dozvíte jak vyjádřit tento typ matice stručněji a efektivněji, ale prozatím použijeme tuto přímočarou implementaci:

	OUT.position[0] = cosLength * position[0] + -sinLength * position[1];
	OUT.position[1] = sinLength * position[0] + cosLength * position[1];

Důležitost teselace ve vertexových programech

Program C3E4v_twist pracuje tak že otáčí vertexy kolem středu obrázku. Jak se zvětšuje velikost otočení, potřebuje objekt více vertexů - lepší teselaci - k vytvoření efektivní rotace.

Obecně pokud program vykonává nelineární výpočty jako jsou trigonometrické funkce v našem příkladu, je potřeba lepší teselace k dosažení věrohodných výsledků. Je tomu tak proto že rasterizer při vytváření fragmentů počítá nové hodnoty vertexů lineárně. Pokud nedochází k dostatečné teselaci, pak lze to pozorovat na použité geometrii. Obrázek 3-5 ukazuje jak teselace přispívá k výslednému vzhledu v příkladu C3E4v_twist.

3.3.5 Zdvojené vidění

Teď si demonstrujeme jak zkombinovat vertexový a fragmentový program abychom dosáhli efektu "zdvojeného vidění". Podstatou je vykreslit tu samou texturu dvakrát, pokaždé s trochu odlišnými souřadnicemi, a pak tyto dvě textury prolnout.

Vertexový program C3E5v_twoTextures v příkladu 3-5 posouvá souřadnice textury pomocí dvou různých odsazení aby vytvořil dvě lehce odlišné sady souřadnic. Fragmentový progam pak přistupuje k obrázku textury na těchto dvou odlišných pozicích a prolíná jej. Obrázek 3-6 ukazuje výsledek renderování a požadovaná vstupní data.

void C3E5v_twoTextures(float2 position : POSITION, float2 texCoord : TEXCOORD0,
		out float4 oPosition : POSITION, out float2 leftTexCoord : TEXCOORD0, out float2 rightTexCoord : TEXCOORD1, 
		uniform float2 leftSeparation, uniform float2 rightSeparation) {
	oPosition = float4(position, 0, 1);
	leftTexCoord = texCoord + leftSeparation;
	rightTexCoord = texCoord + rightSeparation;
}

Vertexový program pro zdvojené vidění

Program C3E5v_twoTextures v příkladu 3-5 pracuje s pozicí vertexu. Program dvakrát vrací původní souřadnici textury, jednou posunutou uniformním parametrem leftSeparation a poté posunutou uniformním parametrem rightSeparation.

	oPosition = float4(position, 0, 1);
	leftTexCoord = texCoord + leftSeparation;
	rightTexCoord = texCoord + rightSeparation;

out parametry vs. výstupní struktury

Na příkladu C3E5v_twoTextures lze také vidět odlišný přístup k výstupním parametrům. Narozdíl od všech našich předchozích příkladů které navacely výstupní strukturu, příklad C3E5v_twoTextures nic nevrací; návratová hodnota funkce je void. Místo toho používá out parametry, které jsou součástí prototypu funkce, aby určil které z parametrů jsou těmi výstupními. Je zcela na vás zda použijete out parametry nebo výstupní strukturu pro předání parametrů z funkce. Není v tom žádný rozdíl, dokonce tyto dva přístupy můžete kombinovat.

Ve zbyztku této knihy používáme out parametr, protože se tak můžeme vyhnout nutnosti definovat výstupní strukturu. Abychom rozlišili mezi vstupními a výstupními parametry které bz jinak měli stejný název, přidali jsme k out parametrům předponu o, například parametry position a oPosition.

void C3E6f_twoTextures(float2 leftTexCoord : TEXCOORD0, float2 rightTexCoord : TEXCOORD1,
		out float4 color : COLOR, uniform sampler2D decal) {
	float4 leftColor = tex2D(decal, leftTexCoord);
	float4 rightColor = tex2D(decal, rightTexCoord);
	color = lerp(leftColro, rightColor, 0.5);
}

V příkladu 3-5 jakožto i v následujících příkladech budeme uvádět parametry v pořadí vstupní, výstupní a uniformní parametry. Tento styl je sice náročnější na formátování kódu, ale používáme ho aby byly příklady čitelnější, zvlášť pokud obsahují mnoho parametrů.

Fragmentový program zdvojeného vidění upravený pro pokročilé fragmentové profily

Fragmentový program C3E6f_twoTextures z příkladu 3-6 přijímá dvě sady posunutých souřadnic textury vypočátané programem C3E5v_twoTextures a použije je k vykreslení dvou obrázků textury, jak je vidět na obrázku 3-6.

	float4 leftColor = tex2D(decal, leftTeCoord);
	float4 rightColor = tex2D(decal, rightTexCoord);

Poté program spočítá průměr dvou barevných vzorků:

	color = lerp(leftColor, rightColor, 0.5);

Funkce lerp počítá váženou lineární interpolaci dvou vektorů o stejné velikosti. Zkratka lerp znamená "LinEární inteRPolace". Funkce má svou přetíženou verzi kde VECTOR je jedno, dvou, tří nebo čtyřsložkový vektor a TYPE je skalár nebo vektor se stejným počtem složek a typu prvku jako VECTOR:

VECTOR lerp(VECTOR a, VECTOR b, TYPE weight);

Funkce lerp používá tuto rovnici:

	result = (1 - weight) * a + weight * b

Váha s hodnotou 0.5 vytváří uniformní průměr. její hodnota není omezena jen na rozsah 0 až 1.

Fragmentový program C3E6f_twoTextures se ale bohužel nepřeloží pro základní fragmentové profily jako je fp20 a ps_1_1 (proč se dozvíte za chvíli). Naopak bezproblémů se přeloží pro pokročilé fragmentové profily fp30 a ps_2_0.

Fragmentový program zdvojeného vidění upravený pro základní fragmentové profily

Příklad C3E6f_twoTextures používá dvě sady souřadnic textury, 0 a 1 pro přístup k texturovací jednotce 0. Z tohoto důvodu program nelze přeložit pro základní fragmentové profily. Tyto profily mohou použít pouze jednu sadu souřadnic na texturovací jednotku kvůli omezení GPU třetí generace a starších.

Program C3E6f_twoTextures lze lehce modifikovat aby šel přeložit pro základní i pokročilé profily. Verze C3E7f_twoTextures v příkladu 3-7 obsahuje nezbytné úpravy.

void C3E7f_twoTextures(float2 leftTexCoord : TEXCOORD0, float2 rightTexCoord : TEXCOORD1,
	out float4 color : COLOR, uniform sampler2D decal0, uniform sampler2D decal1) {
	float4 leftColor = tex2D(decal0, leftTexCoord);
	float4 rightColor = tex2D(decal1, rightTexCoord);
	color = lerp(leftColor, rightColor, 0.5);
}

Upravený program potřebuje dvě texturovací jednotky:

. . .
	uniform sampler2D decal0,
	uniform sampler2D decal1
. . .

Výkon těchto dvou programů je srovnatený. Tento příklad ukazuje že jednodušší Cg programy - ty které nejsou příliš komplikované - lze často snadno přepsat tak aby běžely na starších GPU, které podporují základní vertexové a fragmentové profily a stejně tak i na nových GPU které podporují pokrořilé profily.

3.4 Cvičení

  1. Zodpovězte: Rozeberte jak je možné že funkce sincos ze standardní knihovny vrací sinus i kosinus úhlu? (Nápověda: trigonometrické podobnosti)
  2. Zodpovězte: Vysvětlete vlastními slovy proč zvýšená teselace na obrázku 3-5 přispívá k lepšímu vzhledu zkrouceného trojúhelníku?
  3. Zkuste sami: Upravte příklad C3E4v_twist tak, aby středem otáčení byl nějaký 2D bod definovaný jako parametr typu uniform float2.
  4. Zkuste sami: Upravte programy C3E5v_twoTextures a C3E7f_twoTextures tak abyste dosáhli čtyřnásobného vidění. Ujistěte se že program funguje pro základní i pokročilé profily s předpokladem že vaše GPU podporuje čtyři texturovací jednotky.
  5. Zkuste sami: Upravte příklad C3E5v_twoTextures tak, aby místo out parametru navracel výstupní strukturu. Zkuste také upravit některý z dřívějších příkladů, třeba C3E4v_twist tak, aby používal out parametry místo výstupní sturktury. Co z toho preferujete?

3.5 Další iteratura

O 2x2 maticích se můžete dočíst více v knize The Geometry Toolbox for Graphics and Modeling (A. K. Peters, 1998), by Gerald Farin and Dianne Hansford.

The Cg Tutorial, Nejjednodušší programy | Dr3dweRkZ | The Cg Tutorial, Transformace