国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

OpenGL核心技術(shù)之模版測(cè)試

2019-11-09 16:23:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

筆者介紹:姜雪偉,IT公司技術(shù)合伙人,IT高級(jí)講師,CSDN社區(qū)專家,特邀編輯,暢銷書作者,國(guó)家專利發(fā)明人;已出版書籍:《手把手教你架構(gòu)3D游戲引擎》電子工業(yè)出版社和《Unity3D實(shí)戰(zhàn)核心技術(shù)詳解》電子工業(yè)出版社等。

CSDN課程視頻網(wǎng)址:http://edu.csdn.net/lecturer/144

上節(jié)給讀者介紹了深度測(cè)試,本節(jié)介紹一下模版測(cè)試,模版測(cè)試跟深度測(cè)試是不同的,GPU都會(huì)執(zhí)行片段著色器處理,當(dāng)片段著色器處理完片段之后,模板測(cè)試(Stencil Test) 就開始執(zhí)行了,和深度測(cè)試一樣,它能丟棄一些片段。仍然保留下來(lái)的片段進(jìn)入深度測(cè)試階段,深度測(cè)試可能丟棄更多。模板測(cè)試基于另一個(gè)緩沖,這個(gè)緩沖叫做模板緩沖(Stencil Buffer),它是在深度測(cè)試之前執(zhí)行的。

模板緩沖中的模板值(Stencil Value)通常是8位的,因此每個(gè)片段/像素共有256種不同的模板值(譯注:8位就是1字節(jié)大小,因此和char的容量一樣是256個(gè)不同值)。為了能讓讀者更直觀的認(rèn)識(shí)模版測(cè)試,下面通過圖的方式說(shuō)明:

模板緩沖先清空模板緩沖設(shè)置所有片段的模板值為0,然后開啟矩形片段用1填充。場(chǎng)景中的模板值為1的那些片段才會(huì)被渲染(其他的都被丟棄)。模版緩沖要遵守下面的規(guī)則:

開啟模板緩沖寫入。渲染物體,更新模板緩沖。關(guān)閉模板緩沖寫入。渲染(其他)物體,這次基于模板緩沖內(nèi)容丟棄特定片段。

使用模板緩沖我們可以基于場(chǎng)景中已經(jīng)繪制的片段,來(lái)決定是否丟棄特定的片段。

你可以開啟GL_STENCIL_TEST來(lái)開啟模板測(cè)試。接著所有渲染函數(shù)調(diào)用都會(huì)以這樣或那樣的方式影響到模板緩沖。

glEnable(GL_STENCIL_TEST);

要注意的是,像顏色和深度緩沖一樣,在每次循環(huán),你也得清空模板緩沖。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

同時(shí),和深度測(cè)試的glDepthMask函數(shù)一樣,模板緩沖也有一個(gè)相似函數(shù)。glStencilMask允許我們給模板值設(shè)置一個(gè)位遮罩(Bitmask),它與模板值進(jìn)行按位與(AND)運(yùn)算決定緩沖是否可寫。默認(rèn)設(shè)置的位遮罩都是1,這樣就不會(huì)影響輸出,但是如果我們?cè)O(shè)置為0x00,所有寫入深度緩沖最后都是0。這和深度緩沖的glDepthMask(GL_FALSE)很類似:

// 0xFF == 0b11111111//此時(shí),模板值與它進(jìn)行按位與運(yùn)算結(jié)果是模板值,模板緩沖可寫glStencilMask(0xFF); // 0x00 == 0b00000000 == 0//此時(shí),模板值與它進(jìn)行按位與運(yùn)算結(jié)果是0,模板緩沖不可寫glStencilMask(0x00); 大多數(shù)情況你的模板遮罩(stencil mask)寫為0x00或0xFF就行,但是最好知道有一個(gè)選項(xiàng)可以自定義位遮罩。

和深度測(cè)試一樣,模版測(cè)試也有兩個(gè)函數(shù),決定何時(shí)模板測(cè)試通過或失敗以及它怎樣影響模板緩沖。模板測(cè)試的兩個(gè)函數(shù):glStencilFuncglStencilOpvoid glStencilFunc(GLenum func, GLint ref, GLuint mask)函數(shù)有三個(gè)參數(shù):

func:設(shè)置模板測(cè)試操作。這個(gè)測(cè)試操作應(yīng)用到已經(jīng)儲(chǔ)存的模板值和glStencilFuncref值上,可用的選項(xiàng)是:GL_NEVERGL_LEQUALGL_GREATERGL_GEQUALGL_EQUALGL_NOTEQUALGL_ALWAYS。它們的語(yǔ)義和深度緩沖的相似。ref:指定模板測(cè)試的引用值。模板緩沖的內(nèi)容會(huì)與這個(gè)值對(duì)比。mask:指定一個(gè)遮罩,在模板測(cè)試對(duì)比引用值和儲(chǔ)存的模板值前,對(duì)它們進(jìn)行按位與(and)操作,初始設(shè)置為1。

在上面簡(jiǎn)單模板的例子里,方程應(yīng)該設(shè)置為:

glStencilFunc(GL_EQUAL, 1, 0xFF)

它會(huì)告訴OpenGL,無(wú)論何時(shí),一個(gè)片段模板值等于(GL_EQUAL)引用值1,片段就能通過測(cè)試被繪制了,否則就會(huì)被丟棄。

但是glStencilFunc只描述了OpenGL對(duì)模板緩沖做什么,而不是描述我們?nèi)绾胃戮彌_。這就需要glStencilOp登場(chǎng)了。

void glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)函數(shù)包含三個(gè)選項(xiàng),我們可以指定每個(gè)選項(xiàng)的動(dòng)作:

sfail: 如果模板測(cè)試失敗將采取的動(dòng)作。dpfail: 如果模板測(cè)試通過,但是深度測(cè)試失敗時(shí)采取的動(dòng)作。dppass: 如果深度測(cè)試和模板測(cè)試都通過,將采取的動(dòng)作。

每個(gè)選項(xiàng)都可以使用下列任何一個(gè)動(dòng)作。

操作	描述GL_KEEP	保持現(xiàn)有的模板值GL_ZERO	將模板值置為0GL_REPLACE	將模板值設(shè)置為用glStencilFunc函數(shù)設(shè)置的ref值GL_INCR	如果模板值不是最大值就將模板值+1GL_INCR_WRAP	與GL_INCR一樣將模板值+1,如果模板值已經(jīng)是最大值則設(shè)為0GL_DECR	如果模板值不是最小值就將模板值-1GL_DECR_WRAP	與GL_DECR一樣將模板值-1,如果模板值已經(jīng)是最小值則設(shè)為最大值

glStencilOp函數(shù)默認(rèn)設(shè)置為 (GL_KEEP, GL_KEEP, GL_KEEP) ,所以任何測(cè)試的任何結(jié)果,模板緩沖都會(huì)保留它的值。默認(rèn)行為不會(huì)更新模板緩沖,所以如果你想寫入模板緩沖的話,你必須像任意選項(xiàng)指定至少一個(gè)不同的動(dòng)作。

使用glStencilFuncglStencilOp,我們就可以指定在什么時(shí)候以及我們打算怎么樣去更新模板緩沖了,我們也可以指定何時(shí)讓測(cè)試通過或不通過。什么時(shí)候片段會(huì)被拋棄。

學(xué)習(xí)了模版以后,如何在開發(fā)中使用,這個(gè)是我們最關(guān)心的,下面展示一個(gè)用模板測(cè)試實(shí)現(xiàn)的一個(gè)特別的和有用的功能,叫做物體輪廓(Object Outlining)。效果如下圖所示:

物體輪廓就像它的名字所描述的那樣,它能夠給每個(gè)(或一個(gè))物體創(chuàng)建一個(gè)有顏色的邊。在策略游戲中當(dāng)你打算選擇一個(gè)單位的時(shí)候它特別有用。給物體加上輪廓的步驟如下:

在繪制物體前,把模板方程設(shè)置為GL_ALWAYS,用1更新物體將被渲染的片段。渲染物體,寫入模板緩沖。關(guān)閉模板寫入和深度測(cè)試。每個(gè)物體放大一點(diǎn)點(diǎn)。使用一個(gè)不同的片段著色器用來(lái)輸出一個(gè)純顏色。再次繪制物體,但只是當(dāng)它們的片段的模板值不為1時(shí)才進(jìn)行。開啟模板寫入和深度測(cè)試。

這個(gè)過程將每個(gè)物體的片段模板緩沖設(shè)置為1,當(dāng)我們繪制邊框的時(shí)候,我們基本上繪制的是放大版本的物體的通過測(cè)試的地方,放大的版本繪制后物體就會(huì)有一個(gè)邊。我們基本會(huì)使用模板緩沖丟棄所有的不是原來(lái)物體的片段的放大的版本內(nèi)容。

我們先來(lái)創(chuàng)建一個(gè)非常基本的片段著色器,它輸出一個(gè)邊框顏色。我們簡(jiǎn)單地設(shè)置一個(gè)固定的顏色值,把這個(gè)著色器命名為shaderSingleColor:

void main(){    outColor = vec4(0.04, 0.28, 0.26, 1.0);}

我們只打算給兩個(gè)箱子加上邊框,所以我們不會(huì)對(duì)地面做什么。這樣我們要先繪制地面,然后再繪制兩個(gè)箱子(同時(shí)寫入模板緩沖),接著我們繪制放大的箱子(同時(shí)丟棄前面已經(jīng)繪制的箱子的那部分片段)。

我們先開啟模板測(cè)試,設(shè)置模板、深度測(cè)試通過或失敗時(shí)才采取動(dòng)作:

glEnable(GL_DEPTH_TEST);glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

如果任何測(cè)試失敗我們都什么也不做,我們簡(jiǎn)單地保持深度緩沖中當(dāng)前所儲(chǔ)存著的值。如果模板測(cè)試和深度測(cè)試都成功了,我們就將儲(chǔ)存著的模板值替換為1,我們要用glStencilFunc來(lái)做這件事。

我們清空模板緩沖為0,為箱子的所有繪制的片段的模板緩沖更新為1,實(shí)現(xiàn)代碼片段如下所示:

glStencilFunc(GL_ALWAYS, 1, 0xFF); //所有片段都要寫入模板緩沖glStencilMask(0xFF); // 設(shè)置模板緩沖為可寫狀態(tài)normalShader.Use();DrawTwoContainers();

使用GL_ALWAYS模板測(cè)試函數(shù),我們確保箱子的每個(gè)片段用模板值1更新模板緩沖。因?yàn)槠慰倳?huì)通過模板測(cè)試,在我們繪制它們的地方,模板緩沖用引用值更新。

現(xiàn)在箱子繪制之處,模板緩沖更新為1了,我們將要繪制放大的箱子,但是這次關(guān)閉模板緩沖的寫入:

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);glStencilMask(0x00); // 禁止修改模板緩沖glDisable(GL_DEPTH_TEST);shaderSingleColor.Use();DrawTwoScaledUpContainers();

我們把模板方程設(shè)置為GL_NOTEQUAL,它保證我們只箱子上不等于1的部分,這樣只繪制前面繪制的箱子外圍的那部分。注意,我們也要關(guān)閉深度測(cè)試,這樣放大的的箱子也就是邊框才不會(huì)被地面覆蓋。

做完之后還要保證再次開啟深度緩沖。

場(chǎng)景中的物體邊框的繪制方法最后看起來(lái)像這樣:

glEnable(GL_DEPTH_TEST);glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);glStencilMask(0x00); // 繪制地板時(shí)確保關(guān)閉模板緩沖的寫入normalShader.Use();DrawFloor()  glStencilFunc(GL_ALWAYS, 1, 0xFF);glStencilMask(0xFF);DrawTwoContainers();glStencilFunc(GL_NOTEQUAL, 1, 0xFF);glStencilMask(0x00);glDisable(GL_DEPTH_TEST);shaderSingleColor.Use();DrawTwoScaledUpContainers();glStencilMask(0xFF);glEnable(GL_DEPTH_TEST);

實(shí)現(xiàn)的效果圖如下所示:

最后把Shader代碼的文件stencil_single_color.frag給讀者展示一下:

#version 330 coreout vec4 outColor;void main(){    outColor = vec4(0.04, 0.28, 0.26, 1.0);}stencil_testing.vs代碼如下所示:

#version 330 corelayout (location = 0) in vec3 position;layout (location = 1) in vec2 texCoords;out vec2 TexCoords;uniform mat4 model;uniform mat4 view;uniform mat4 PRojection;void main(){    gl_Position = projection * view * model * vec4(position, 1.0f);    TexCoords = texCoords;}stencil_testing.frag代碼如下所示:

#version 330 corein vec2 TexCoords;out vec4 color;uniform sampler2D texture1;void main(){                 color = texture(texture1, TexCoords);}接下來(lái)就是通過OpenGL的C++代碼進(jìn)行處理Shader了,下面把核心代碼拿出來(lái)展示:

// 定義視口尺寸    glViewport(0, 0, screenWidth, screenHeight);    // 深度測(cè)試    glEnable(GL_DEPTH_TEST);    glDepthFunc(GL_LESS);    glEnable(GL_STENCIL_TEST);    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);    // 加載Shader    Shader shader("stencil_testing.vs", "stencil_testing.frag");    Shader shaderSingleColor("stencil_testing.vs", "stencil_single_color.frag");下面是對(duì)場(chǎng)景中的物體具體處理:

	// Clear the colorbuffer        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);        // Set uniforms        shaderSingleColor.Use();		        glm::mat4 model;        glm::mat4 view = camera.GetViewMatrix();        glm::mat4 projection = glm::perspective(camera.Zoom, (float)screenWidth/(float)screenHeight, 0.1f, 100.0f);        glUniformMatrix4fv(glGetUniformLocation(shaderSingleColor.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));        glUniformMatrix4fv(glGetUniformLocation(shaderSingleColor.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));        shader.Use();         glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));        // Draw floor as normal, we only care about the containers. The floor should NOT fill the stencil buffer so we set its mask to 0x00        glStencilMask(0x00);        // Floor        glBindVertexArray(planeVAO);        glBindTexture(GL_TEXTURE_2D, floorTexture);        model = glm::mat4();        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));        glDrawArrays(GL_TRIANGLES, 0, 6);		        glBindVertexArray(0);	        // == =============        // 1st. Render pass, draw objects as normal, filling the stencil buffer        glStencilFunc(GL_ALWAYS, 1, 0xFF);        glStencilMask(0xFF);		        // Cubes        glBindVertexArray(cubeVAO);        glBindTexture(GL_TEXTURE_2D, cubeTexture);          model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f));        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));        glDrawArrays(GL_TRIANGLES, 0, 36);        model = glm::mat4();        model = glm::translate(model, glm::vec3(2.0f, 0.0f, 0.0f));        glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));        glDrawArrays(GL_TRIANGLES, 0, 36);        glBindVertexArray(0);	        // == =============        // 2nd. Render pass, now draw slightly scaled versions of the objects, this time disabling stencil writing.        // Because stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are now not drawn, thus only drawing         // the objects' size differences, making it look like borders.        glStencilFunc(GL_NOTEQUAL, 1, 0xFF);        glStencilMask(0x00);        glDisable(GL_DEPTH_TEST);        shaderSingleColor.Use();        GLfloat scale = 1.1;

這樣模版測(cè)試的核心技術(shù)給讀者就介紹完了,希望對(duì)大家有所幫助。。。。。。


上一篇:bzoj3711

下一篇:osx pod

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 柏乡县| 潮安县| 林州市| 治县。| 南丰县| 靖远县| 湾仔区| 乐清市| 绵阳市| 营口市| 错那县| 交城县| 盐亭县| 隆德县| 乌拉特前旗| 金坛市| 乌苏市| 苍溪县| 灵台县| 南木林县| 额尔古纳市| 准格尔旗| 怀仁县| 金山区| 保亭| 梁河县| 汉源县| 陈巴尔虎旗| 林甸县| 讷河市| 偃师市| 泾阳县| 石渠县| 阳春市| 南宫市| 陆河县| 桦南县| 清新县| 南安市| 阜平县| 融水|