OGL(教程35) 延遲渲染1

2021-08-29 04:49:46 字數 4709 閱讀 6700

從17節開始,到現在對燈光的處理就是所謂的向前渲染或者向前著色。這是最直接的方式,我們在頂點著色器中對物體的各個頂點做變換(對法線進行變化,把位置變換到裁剪空間),然後在fs中對畫素進行光照計算。對於每個物件的每個畫素只能呼叫一次fs,我們需要對fs提供所有的光源資訊,然後在計算這個畫素的光照的時候全部考慮進去。這種方式簡單,但是也有自身的缺點。如果場景高度複雜(比如當今流行的遊戲中),它有大量的物體,還有大量的深度複雜性(螢幕上的乙個畫素會被多個物體覆蓋),這樣對此畫素的計算會浪費很多cpu。比如,如果深度複雜度為4,那就意味著對3個畫素的計算是浪費掉的,因為只有最上面的乙個畫素才最終有效。我們可以對物體進行排序,但是對複雜的物體,其效果不是很理想。

向前渲染的另外乙個問題是,在很多燈的情況下也會遇到問題。在這種情況下,光源對於很小的區域有效果,否則會承受不住壓力。但是我們的fs計算,是針對所有燈的,即使它離畫素很遠。你可以嘗試計算畫素和光源的位置,但是會額外增加計算量,還會增加fs的分支。向前渲染的fs部分,對多盞燈不能勝任。

延遲渲染是針對上面的問題,被當今很多遊戲所採用的流行方案。延遲渲染的乙個核心是,把幾何計算(位置和法線計算)和光照計算解耦。並不是把所有的物體都一股腦的處理,從頂點緩衝到最終的幀緩衝,我們把它分為兩個主要的階段。第乙個階段,我們執行vs,但是我們不把處理的屬性傳給fs以備光照計算使用,我們把它傳入到g-buffer中。gbuffer從邏輯上講是一組2d貼圖,每個貼圖對應乙個頂點屬性。我們分離屬性,然後利用opengl所謂的mrt(多個渲染目標技術)一次繪製所有的屬性到多張貼圖。由於我們在fs中寫入屬性,那麼在gbuffer中的最終結果是被光柵化插值之後的頂點屬性。我們把這個階段叫做幾何階段。每個物件都在這個階段處理。由於深度測試,當幾何階段完成之後,出現在gbuffer中的,都是離攝像機最近的的點的差值之後的屬性。這就意味著所有無關的畫素深度測試失敗,然後被丟棄,只有留在gbuffer中的畫素才是最終會被計算光照的畫素。下面是典型的gbuffer的一幀圖:

在第二個階段,稱之為光照階段,我們遍歷gbuffer中的畫素,我們從不同的貼圖取樣畫素的屬性,然後像之前我們處理光照的方式計算光照。由於留下的畫素都是離攝像機最近的點,所以我們對乙個畫素只會進行一次光照計算。

我們怎麼遍歷gbuffer中的畫素呢?最簡單的方式是渲染乙個和螢幕大小相同的四邊形。但是還有更好的方法。之前說過,由於光源的影響範圍有限,我們假設很多畫素與它無關。當乙個光源的對畫素的影響足夠小,最好忽略這個光源對其的影響,這也是從效能考慮。在向前渲染中沒有有效的方式處理。但是在延遲渲染中,我們可以計算光源影響的乙個球體(對於點光源來說是這樣,對於聚光燈,我們使用cone角度)。球體代表了光源影響的區域,在球體之外,我們忽略光源的影響。我們可以用乙個粗略的球體模型,它只有很少的幾個面,來表現那個點的光源所影響的範圍。vs只做位置的變換,把位置變換到裁剪空間。fs只會對相關的畫素進行處理,並對相關的畫素做光照計算。有些人會計算出最小的包含球體的四邊形,這個計算是從光源視角計算。渲染這個四邊形不耗時,因為只有兩個三角形而已。這個方法可有效限制計算畫素的個數。

我們將分為三步介紹延遲渲染,分為三個章節進行。

1、本節我們將會使用mrt技術填充gbuffer。我們會輸出到gbuffer到螢幕。

2、下一節,我們將會增加光照通道,在延遲渲染中看看光照如何計算。

3、最後一節,我們將會學習模板緩衝,來阻止距離較遠的點光源。

**注釋:

source walkthru

(gbuffer.h:28)

class gbuffer

; gbuffer();

~gbuffer();

bool init(unsigned int windowwidth, unsigned int windowheight);

void bindforwriting();

void bindforreading();

private:

gluint m_fbo;

gluint m_textures[gbuffer_num_textures];

gluint m_depthtexture;

};

gbuffer包含所有延遲渲染階段需要的貼圖。我們有針對針對頂點屬性的貼圖,也有稱當深度緩衝的貼圖。我們之所以需要深度緩衝貼圖,因為我們將會在fbo中封裝所有的貼圖,所以預設的深度緩衝沒有用。fbo已經在23節講述,這裡不再提及。

gbuffer類有兩個方法,將會在執行時反覆呼叫。bindforwriting()繫結貼圖到目標,這個發生在幾何階段。bindforreading()繫結fbo作為輸入,所以它的結果可以輸出到螢幕。

(gbuffer.cpp:48)

bool gbuffer::init(unsigned int windowwidth, unsigned int windowheight)

// depth

glbindtexture(gl_texture_2d, m_depthtexture);

glteximage2d(gl_texture_2d, 0, gl_depth_component32f, windowwidth, windowheight, 0, gl_depth_component, gl_float,

null);

glframebuffertexture2d(gl_framebuffer, gl_depth_attachment, gl_texture_2d, m_depthtexture, 0);

glenum drawbuffers = ;

gldrawbuffers(array_size_in_elements(drawbuffers), drawbuffers);

glenum status = glcheckframebufferstatus(gl_framebuffer);

if (status != gl_framebuffer_complete)

// restore default fbo

glbindframebuffer(gl_draw_framebuffer, 0);

return true;

}

這個就是我們如何初始化gbuffer的。我們首先建立了fbo物件、還有各種貼圖,還有深度緩衝。頂點屬性貼圖在迴圈中進行初始化,迴圈體中做的是:

1、建立貼圖的儲存區域(沒有初始化它)

2、繫結貼圖到fbo作為目標

初始化深度貼圖被顯示呼叫,因為他有不同的格式,它被繫結到不同的fbo物件。

為了做mrt,我們需要寫入所有的貼圖。我們通過提供乙個陣列的附著地點給gldrawbuffers()函式。這個陣列允許我們有一些靈活性,如果我們把gl_color_attachment6 作為第乙個索引,fs寫入的第乙個輸出變數,此變數會繫結到gl_color_attachment6。本節我們不考慮效率,只是乙個接乙個的繫結。

最終,我們檢測fbo的狀態,確保所有的東西都是正確的額,然後重置預設的fbo(未來的變換不會影響到gbuffer)。此時gbuffer已經可以被使用了。

(tutorial35.cpp:105)

virtual void renderscenecb()

我們現在從上往下看執行過程。上面的函式主迴圈函式。它做的並不多。它處理一些全域性的事務,比如幀率計算,還有展示,攝像機的更新等等。主要的工作室執行幾何階段,接著是光照階段。如之前提到的,本節只處理幾何階段。

(tutorial35.cpp:122)

void dsgeometrypass()

我們開始幾何階段,是通過開啟合適的計算,然後設定gbuffer物件以備寫入。這樣之後,我們清除gbuffer。現在所有的東西都準備好了,我們開始變換,然後是渲染網格。在真實的遊戲中,我們會乙個接乙個渲染多個網格。最終我們會得到頂點的各個屬性。

(tutorial35.cpp:141)

void dslightpass()

(geometry_pass.vs)

#version 330

layout (location = 0) in vec3 position;

layout (location = 1) in vec2 texcoord;

layout (location = 2) in vec3 normal;

uniform mat4 gwvp;

uniform mat4 gworld;

out vec2 texcoord0;

out vec3 normal0;

out vec3 worldpos0;

void main()

#version 330

in vec2 texcoord0;

in vec3 normal0;

in vec3 worldpos0;

layout (location = 0) out vec3 worldposout;

layout (location = 1) out vec3 diffuseout;

layout (location = 2) out vec3 normalout;

layout (location = 3) out vec3 texcoordout;

uniform sampler2d gcolormap;

void main()

OGL(教程22) 模型載入

背景知識 我們不能手動的建立複雜的模型。正如你所想象的,為每個頂點指定位置和其他的屬性,並且要在縮放的時候保持正確是不現實的。乙個盒子 金字塔或者是簡單的表面是ok的,但是對於人臉這種複雜的網格結構就不可行了。現實的世界或者是商業的應用,處理的網格模型都是通過設計師設計的模型,他們使用blender...

延遲渲染 Deferred Rendering

在計算機圖形學中,延遲渲染 deferred rendering 即延遲著色 deferred shading 是將著色計算延遲到深度測試之後進行處理的一種渲染方法。延遲著色技術的最大的優勢就是將光源的數目和場景中物體的數目在複雜度層面上完全分開,能夠在渲染擁有成百上千光源的場景的同時依然保持很高的...

shader 延遲渲染

正向渲染 forward rendering 或稱正向著色 forward shading 是渲染物體的一種非常直接的方式,在場景中我們根據所有光源照亮乙個物體,之後再渲染下乙個物體,以此 類推。傳統的正向渲染思路是,先進行著色,再進行深度測試。其的主要缺點就是光照計算跟場景 複雜度和光源個數有很大...