新建網(wǎng)頁 1
圖形管道概述
我們將討論渲染一幅帶有基本光照的單個(gè)圖像的大體過程,這里不考慮動(dòng)畫和全局光照,如陰影和輻射度。
此外,注意這里只從概念上講解通過圖形管道的數(shù)據(jù)流,其順序并不是固定的。實(shí)踐中,我們也許會(huì)為了性能的優(yōu)化而并行或亂序執(zhí)行一些任務(wù)。比如,考慮到不同的渲染API,我們可能首先變換和照明所有頂點(diǎn),然后才進(jìn)一步的處理(進(jìn)行裁剪和剔除),或者會(huì)并行處理二者,也可能在背面剔除之后再進(jìn)行光照會(huì)得到更高效率。
還有一個(gè)我們將不詳細(xì)討論的要點(diǎn),即工作負(fù)擔(dān)如何在CPU與渲染硬件間分配。正確地組織渲染任務(wù),以求得最大的并行效果對(duì)高效渲染是至關(guān)重要的。
考慮上述簡(jiǎn)化,就得到了圖形管道中數(shù)據(jù)流的概況,如下所示:
(1)建立場(chǎng)景:開始渲染之前,需要預(yù)先設(shè)定對(duì)整個(gè)場(chǎng)景有效的一些選項(xiàng)。比如,要建立攝像機(jī)位置,或者更具體些,要選擇進(jìn)行渲染的出發(fā)點(diǎn)---視點(diǎn),渲染的輸出---視圖。還需要設(shè)定光照與霧化選項(xiàng),同時(shí)準(zhǔn)備z緩沖。
(2)可見性檢測(cè):選好了攝像機(jī),就必須檢測(cè)場(chǎng)景中哪些物體是可見的。可見性檢測(cè)對(duì)實(shí)時(shí)渲染極為重要,因?yàn)槲覀儾辉敢饫速M(fèi)時(shí)間去渲染那些根本看不到的東西。
(3)設(shè)置物體級(jí)的渲染狀態(tài):一旦發(fā)現(xiàn)某物體潛在可見,就到了把它實(shí)際繪制出來的時(shí)候。每個(gè)物體的渲染設(shè)置可能是不同的,在渲染該物體的任何片元之前,首先要設(shè)置上述選項(xiàng),最常見的此類選項(xiàng)是紋理映射。
(4)幾何體的生成與提交:接著實(shí)際向API提交幾何體,通常提交的數(shù)據(jù)是種種形式的三角形,或是獨(dú)立的三角形,或是索引三角網(wǎng)格與三角帶。此階段,我們可能會(huì)應(yīng)用LOD,或者漸進(jìn)式生成幾何體。
(5)變換與光照:一旦渲染API得到了三角形數(shù)據(jù),由模型空間向攝像機(jī)空間的頂點(diǎn)坐標(biāo)轉(zhuǎn)換與頂點(diǎn)光照計(jì)算即開始。
(6)背面剔除與裁剪:然后,那些背對(duì)攝像機(jī)的三角形被去除("背面剔除");三角形在視椎外的部分也被去除,稱作裁剪---這可能導(dǎo)致產(chǎn)生多于三個(gè)邊的多邊形。
(7)投影到屏幕空間:在3D裁剪空間中經(jīng)裁剪產(chǎn)生的多邊形,被投影到輸出窗口的2D屏幕空間里。
(8)光柵化:當(dāng)把裁剪后的多邊形轉(zhuǎn)換到屏幕空間后,就到了光柵化階段。光柵化指計(jì)算應(yīng)繪制三角形上的哪些像素的過程,并為接下來的像素著色階段提供合理的插值參數(shù)(如光照和紋理映射坐標(biāo))。
(9)像素著色:最后,在管道的最后階段計(jì)算三角形的色彩,此過程稱作"著色"。接著把這些顏色寫至屏幕,這是可能需要alpha混合與z緩沖。
下面的偽代碼描述了渲染管道,為了達(dá)到概觀的目的,大量細(xì)節(jié)被省去了。同時(shí),由于渲染平臺(tái)和API的不同,實(shí)踐中會(huì)有許多不同的形式。
Listing 15.1: Pseudocode for the graphics pipeline
// First, figure how to view the scene
setupTheCamera();
// Clear the zbuffer
clearZBuffer();
// Setup environmental lighting and fog
setGlobalLightingAndFog();
// Get a list of objects that are potentially visible
potentiallyVisibleObjectList = highLevelVisibilityDetermination(scene);
// Render everything we found to be potentially visible
for (all objects in potentiallyVisibleObjectList)
{
// Perform lower-level VSD using bounding volume test
if (!object.isBoundingVolumeVisible())
continue;
// Fetch or procedurally generate the geometry
triMesh = object.getGeometry()
// Clip and render the faces
for (each triangle in the geometry)
{
// Transform the vertices to clip space, and perform vertex-level lighting
clipSpaceTriangle = transformAndLighting(triangle);
// Is the triangle backfacing?
if (clipSpaceTriangle.isBackFacing()) continue;
// Clip the triangle to the view volume
clippedTriangle = clipToViewVolume(clipSpaceTriangle);
if (clippedTriangle.isEmpty())
continue;
// Project the triangle onto screen space and rasterize
clippedTriangle.projectToScreenSpace();
for (each pixel in the triangle)
{
// Interpolate color, zbuffer value, and texture mapping coords
// Perform zbuffering and alpha test
if (!zbufferTest())
continue;
if (!alphaTest())
continue;
// Shade the pixel.
color = shadePixel();
// Write to the frame buffer and zbuffer
writePixel(color, interpolatedZ);
}
}
}
設(shè)定視圖參數(shù)
渲染場(chǎng)景之前,首先必須建立攝像機(jī)和輸出窗口。即必須決定從哪個(gè)位置進(jìn)行觀察渲染(視點(diǎn)位置、方向、縮放)以及把渲染結(jié)果送到哪里(屏幕上的目標(biāo)矩形區(qū)域)。上述二者中,輸出窗口較為簡(jiǎn)單,故先討論輸出窗口。
指定輸出窗口
我們不一定要把圖像渲染到整個(gè)屏幕。比如,一個(gè)分屏的多人游戲,每個(gè)玩家只占據(jù)顯示屏幕的一部分。輸出窗口即指輸出設(shè)備中圖像將要渲染到的那部分,如圖15.1所示:

窗口位置由左上角像素(winPosx,winPosy)給出,整數(shù)winResx、winResy是以像素為單位的窗口大小,如此定義,使用窗口大小而不是右下角的坐標(biāo),可避免整數(shù)像素坐標(biāo)系帶來一些麻煩。同時(shí)要注意窗口的實(shí)際物體大小和像素大小的區(qū)別。
要知道我們不一定在屏幕上渲染,也許只是將渲染結(jié)果保存到一個(gè)TGA文件里,或是AVI的一幀,也許只是渲染到一個(gè)紋理上---作為主渲染器的一個(gè)子過程而已,因此,名詞"幀緩沖"一般指用來保存我們正渲染圖像的那塊內(nèi)存。
像素縱橫比
不管是渲染到屏幕還是緩沖區(qū),我們必須知道像素的縱橫比。它是像素高對(duì)寬的比值,一般為1("方形"像素),不過并非總是如此。下面給出其計(jì)算公式(公式5.1):

pixPhys指像素物理尺寸。一般來說,度量單位并無關(guān)系,比例才是重要的。devPhys是顯示設(shè)備的物理高與寬比,尺寸可能是英寸、英尺、picas等,但也只有比例才是重要的。比如,標(biāo)準(zhǔn)的桌面顯示器,尺寸各異但卻擁有相同的比值4:3---視區(qū)寬大于高約33%。另一個(gè)常見比例是高清晰電視和DVD上的16:9。整數(shù)devResx和devResy是x、y方向的像素比,如640 x 480指devResx=640,devResy=480。
如前所述,比值為1的方形像素最為常見。如標(biāo)準(zhǔn)桌面顯示器,有4:3的物理縱橫比,而許多常見解析度:320 x 240,640 x 480,800 x 600,1024x 768,1600 x 1200也都是4:3,因此像素是方形的。
注意計(jì)算中未用到窗口的尺寸及位置,這是合理的,窗口性質(zhì)不影響像素的物理屬性。但是,窗口尺寸在視場(chǎng)問題中十分重要,而位置對(duì)攝像機(jī)到屏幕的映射是關(guān)鍵。
視錐
視錐是攝像機(jī)可見的空間體積,看上去像截掉頂部的金字塔,如圖15.2所示:

視錐是由6個(gè)裁剪面圍成的。構(gòu)成視錐的4個(gè)側(cè)面稱為上、左、下、右面,它們對(duì)應(yīng)著輸出窗口的四邊。為防止物體離攝像機(jī)過近,設(shè)置近剪面,從而去除金字塔形的頂端。同理,也設(shè)置了視野的遠(yuǎn)端,因?yàn)樘h(yuǎn)的物體實(shí)際上太小而不可見,故可有效而安全地去掉。
視場(chǎng)與縮放
攝像機(jī)同其他物體一樣有位置和朝向,同時(shí)它還具有"視場(chǎng)"這一額外的屬性。另一名詞"縮放"你也許已經(jīng)很熟悉,直觀上,你早就知道放大和縮小。但拉近時(shí),物體顯大;拉遠(yuǎn)時(shí),物體顯小,這太常見了。
視場(chǎng)是視錐所截的角。實(shí)際上需要兩個(gè)角:分別對(duì)應(yīng)水平視場(chǎng)和垂直視場(chǎng)。這里只在2D中討論其中一個(gè),圖15.3從上方顯示了視錐,精確的展示了水平視場(chǎng)角,坐標(biāo)軸的標(biāo)記用的是攝像機(jī)空間。

縮放表示物體實(shí)際大小和物體在90。視場(chǎng)中顯示大小的比。所以大比值表示放大,小比值表示縮小。比如,2.0的縮放表示物體在屏幕上比用90。視場(chǎng)時(shí)大兩倍。縮放的幾何解釋如圖15.4所示:

應(yīng)用基本三角知識(shí),就能推導(dǎo)出縮放和視場(chǎng)角之間的轉(zhuǎn)換公式:

在3D中,需要兩個(gè)縮放值,一個(gè)水平的,一個(gè)垂直的。可以隨意給值,但如果二者比例不恰當(dāng),圖像便像被拉伸過似的(好比寬銀幕電影在電視上播出)。為了維持恰當(dāng)?shù)谋壤s放要和輸出窗口的尺寸對(duì)應(yīng):

假設(shè)輸出為正常比例,許多渲染引擎允許僅用一個(gè)視場(chǎng)角(或zoom值)設(shè)定攝像機(jī),然后自動(dòng)計(jì)算另一個(gè)。例如,可以指定水平視場(chǎng)角,自動(dòng)計(jì)算垂直視場(chǎng)角,反之亦然;或者指定視場(chǎng)角中較大的一個(gè),自動(dòng)計(jì)算較小的。