渲染可以拆分為多個步驟(stage),拆分之后,就可以使用管道(pipeline)來優化執行效率。打個比方,過安檢的時候,至少有三個步驟,把包放在傳送帶上,然后走過去,取回傳送帶上的包。在排隊過安檢的過程中,排在后面的旅客并不需要等前面的旅客完成,只要前面的旅客把包放上傳送帶(完成第一個步驟),后面的旅客就可以接著把包放在傳送帶,這就是管道原理。
Step1 準備頂點數據(vertex specification)。對于現在使用硬件加速的渲染技術,這個步驟是在CPU上完成的。如果要渲染一個三角形,這一步需要幾個信息:三個點的位置和頂點顏色、以及繪制對象:“三角形”。
以下這段代碼是個人隨手而寫,并非真的OpenGL代碼,只是用來描述Step1。
var glState = OpenGLState.getInstance();var arrbuf = new ArrayBuffer();//new a array buffer to store the data//vertex positionarrbuf.push({0, 1});arrbuf.push({-0.5, 0});arrbuf.push({0.5, 1});//vertex colorarrbuf.push({255, 0, 0});arrbuf.push({0, 255, 0});arrbuf.push({0, 0, 255});var vertextArr = new VertexArray(arrbuf);vertextArr.setAttr1(0, 2);//attr1 for positionvertextArr.setAttr1(3, 5);//attr2 for colorglState.vertextArr = vertextArr;var vertextShader = new Shader("your vertex shader code");glState.vertextShader = vertextShader;var fragShader = new Shader("your fragShader");glState.fragShader = fragShader;// other Operation to configure glStateglState.run();// the run call will trigger the gl render stages blowStep2 處理頂點(vertex PRocessing),根據每個頂點的數據(在前一步指定了),以及一些常量(uniform)(在前一步指定了),進行一次分析處理,例如,是否需要對每個頂點的位置做一次位置的變換(transform)(在3D渲染中,變換非常重要)。在可編程(programmable)的渲染管道中,頂點處理可以細分為三個可編程的步驟,第一個小步驟的指令可以vertext shader來指定,第二個小步驟叫tessellation,又可以再細化3個步驟,由2個shader加上一個內置的程序(fixed-function)來控制,第三個步驟由geometry shader來控制,第一個步驟是必須的,后兩個步驟是可選的。
Step3 頂點的后續處理(vertex post-processing),上一個步驟產生的所有頂點信息,會在這里進一步處理。這個步驟是不可以編程的,所以沒有相應的shader,但是可以配置。通過配置,可以實現transform feedback,前一個步驟處理完產生的數據,可以在這一步反饋回CPU。如果不需要繪制到屏幕上,這個步驟可以中斷。如果不中斷,則會進行裁剪(clipping),打個比方,原先的三角形,如果有個點不再可視范圍內,那么這個三角形會被處理成2個三角形,增加2個在可視邊界上的頂點。根據可配置的viewport提供的信息,對頂點的位置投影(projection)到一個平面(window space)上,要么做透視處理(perspective),要么做正交處理(orthographic)。
Step4 對繪制對象的整合(primitive assembly),將前面產生的頂點,根據第一個步驟指定的繪制對象信息,打包成一個個的assembly,然后交給下一個步驟處理。如果第二個步驟中需要進行tessellating,或者需要執行geometry shader,那么在vertex shader之后,也會緊接著完成一個簡單的”繪制對象整合”。
Step5 進行光柵化處理(rasterization),管道里會有一個不可編程的程序稱為rasterizer。所謂光柵化,就是根據頂點的位置信息,加上分辨率信息,對渲染對象所在的一個包圍盒里(包含繪制對象的最小的一個像素矩陣表),凡是判斷到像素格子受到影響,就會生成一個fragment信息。這個fragment信息不一定是整個像素格子。
Step6 可編程的并且可選的(optional)步驟,交由fragment shader來處理。這個步驟的結果是將顏色、深度等信息輸出,也可以輸出自定義信息。當涉及的多重采樣(MSAA),一般情況,shader只會針對每個fragment執行一次。但如果在shader中,使用了gl_SampleID,則shader會針對每個采樣執行一次。
Step7 對fragment shader的輸出進行最后的處理(per-sample processing)。這一步會判斷fragment shader的輸出是否有效,如果無效則忽略,最后會將最終的顏色寫入framebuffer,或者跟framebuffer相應位置的顏色進行混合(blend)。其中,判斷輸出是否有效那些子步驟,在新的OpenGL版本中,可以提前到fragment shader處理之前,這種情況的輸入是來自rasterizer。
參考 [1] Rendering Pipeline Overview
新聞熱點
疑難解答