LearnOpenGL - 渲染技巧

渲染技巧


©shuan9999

1. 隱藏⾯消除((Hidden surface elimination)

  • 先看如下圖的問題:
    在這裏插入圖片描述
  • 出現這種問題的原因在於我們將其背面也繪製出來了,正常來說我們是看不到也不需要看到背面的,所以先看第一種解決辦法:
  • 油畫算法

    • 先繪製場景中的離觀察者較遠的物體,再繪製較近的物體
    • 例如下⾯的圖例: 先繪製紅⾊部分,再繪製⻩⾊部分,最後再繪製灰⾊部分,即可解決隱藏⾯消除的問題
    • 在這裏插入圖片描述
    • 弊端:
      • 使⽤油畫算法,只要將場景按照物理距離觀察者的距離遠近排序,由遠及近的繪製即可。那麼會出現什麼問題? 如果三個三⻆形是疊加的情況,油畫算法將⽆法處理。
      • 在這裏插入圖片描述
看到了油畫算法的弊端後,我們再來看看第二種解決辦法:
  • 正背⾯剔除(Face Culling)

    • 3D圖形裏,對於一個立方體我們最多能看到的只有3面,而那另外的看不到的3面我們是不用去繪製的,而且當我們以某種方式去放棄繪製這些看不見的數據時,OpenGL的渲染性能可以提高50%以上。
    • 但是要如何知道哪裏是背面哪裏是正面呢?這就是上一篇說道的三角形的環繞方向。OpenGL 可以做到檢查所有正⾯朝向觀察者的⾯,並渲染它們。從⽽丟棄背⾯朝向的⾯,這樣可以節約⽚元着⾊器的性能
    • 總之,正⾯和背⾯是有三⻆形的頂點定義順序和觀察者⽅向共同決定的。隨着觀察者的⻆度⽅向的改變,正⾯背⾯也會跟着改變

      開啓表⾯剔除(默認背⾯剔除) :
      void glEnable(GL_CULL_FACE);
      關閉表⾯剔除(默認背⾯剔除) :
      void glDisable(GL_CULL_FACE);
      選擇剔除那個⾯(正⾯/背⾯) :
      void glCullFace(GLenum mode);
      mode參數爲: GL_FRONT,GL_BACK,GL_FRONT_AND_BACK ,默認GL_BACK
      指定繞序那個爲正⾯:
      void glFrontFace(GLenum mode);
      mode參數爲: GL_CW,GL_CCW,默認值:GL_CCW
      例如,剔除正⾯實現(1) :
      glCullFace(GL_BACK);
      glFrontFace(GL_CW);
      例如,剔除正⾯實現(2) :
      glCullFace(GL_FRONT)

2. 深度

深度就是該像素點在3D世界中距離攝像機的距離,Z值
  • 深度緩衝區
    • 深度緩存區,就是⼀塊內存區域,專⻔存儲着每個像素點(繪製在屏幕上的)深度值。深度值(Z值)越⼤,則離攝像機就越遠
  • 爲什麼需要深度緩衝區?
    • 在不使⽤深度測試的時候,如果我們先繪製⼀個距離⽐較近的物理,再繪製距離較遠的物理,則距離遠的位圖因爲後繪製,會把距離近的物體覆蓋掉.。有了深度緩衝區後,繪製物體的順序就不那麼重要了。實際上,只要存在深度緩衝區,OpenGL都會把像素的深度值寫⼊到緩衝區中。除⾮調⽤
      glDepthMask(GL_FALSE)來禁⽌寫⼊
  • 深度測試
    • 深度緩衝區(DepthBuffer)和顏⾊緩存區(ColorBuffer)是對應的。顏⾊緩存區存儲像素的顏⾊信息,⽽深度緩衝區存儲像素的深度信息。在決定是否繪製⼀個物體表⾯時,⾸先要將表⾯對應的像素的深度值與當前深度緩衝區中的值進⾏⽐較。如果⼤於深度緩衝區中的值,則丟棄這部分。否則利⽤這個像素對應的深度值和顏⾊值。分別更新深度緩衝區和顏⾊緩存區。這個過程稱爲”深度測試”

開啓深度測試:
glEnable(GL_DEPTH_TEST);
在繪製場景前,清除顏⾊緩存區、深度緩衝區:
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清除深度緩衝區默認值爲1.0,表示最⼤的深度值,深度值的範圍爲(0,1)之間。值越⼩表示越靠近觀察者,值越⼤表示越遠離觀察者
指定深度測試判斷模式:
void glDepthFunc(GLEnum mode);
在這裏插入圖片描述
打開/阻斷 深度緩存區寫⼊:
void glDepthMask(GLBool value);
value : GL_TURE 開啓深度緩衝區寫⼊; GL_FALSE 關閉深度緩衝區寫⼊

  • ZFighting閃爍問題
    • 爲什麼會出現 ZFighting 閃爍問題?
    • 因爲開啓深度測試後,OpenGL就不會再去繪製模型被遮擋的部分。這樣實現的顯示更加真實。但是由於深度緩衝區精度的限制對於深度相差⾮常⼩的情況下。(例如在同⼀平⾯上進⾏2次 制),OpenGL 就可能出現不能正確判斷兩者的深度值,會導致深度測試的結果不可預測。顯示出來的
      現象時交錯閃爍。的前⾯2個畫⾯,交錯出現。
      在這裏插入圖片描述
  • 解決⽅法
    • 第⼀步: 啓⽤ Polygon Offset
    • 讓深度值之間產⽣間隔。如果2個圖形之間有間隔,是不是意味着就不會產⽣⼲涉。可以理
      解爲在執⾏深度測試前將⽴⽅體的深度值做⼀些細微的增加。於是就能將重疊的2個圖形深度值之
      前有所區分

      //啓⽤Polygon Offset ⽅式
      glEnable(GL_POLYGON_OFFSET_FILL)
      參數列表:
      GL_POLYGON_OFFSET_POINT 對應光柵化模式: GL_POINT
      GL_POLYGON_OFFSET_LINE 對應光柵化模式: GL_LINE
      GL_POLYGON_OFFSET_FILL 對應光柵化模式: GL_FILL

    • 第⼆步: 指定偏移量
    • 通過glPolygonOffset 來指定glPolygonOffset 需要2個參數: factor ,units
    • 每個Fragment 的深度值都會增加如下所示的偏移量:

      Offset = ( m * factor ) + ( r * units);
      m : 多邊形的深度的斜率的最⼤值,理解⼀個多邊形越是與近裁剪⾯平⾏,m 就越接近於0
      r : 能產⽣於窗⼝座標系的深度值中可分辨的差異最⼩值,r 是由具體OpenGL 平臺指定的⼀個常量

    • ⼀個⼤於0的Offset 會把模型推到離你(攝像機)更遠的位置,相應的⼀個⼩於0的Offset會把模型拉近
    • ⼀般⽽⾔,只需要將-1.0和-1這樣簡單賦值給glPolygonOffset基本可以滿⾜需求

      void glPolygonOffset(Glfloat factor,Glfloat units);
      應⽤到⽚段上總偏移計算⽅程式:
      Depth Offset = (DZ * factor) + (r * units);
      DZ:深度值(Z值)
      r:使得深度緩衝區產⽣變化的最⼩值
      負值,將使得z值距離我們更近,⽽正值,將使得z值距離我們更遠,

    • 第三步: 關閉Polygon Offset

      glDisable(GL_POLYGON_OFFSET_FILL)

3. 裁剪

  • 在OpenGL中提⾼渲染性能的⼀種⽅式,只刷新屏幕上發⽣變化的部分,OpenGL 允許將要進⾏渲染的窗⼝只去指定⼀個裁剪框。
  • ⽤於渲染時限制繪製區域,通過此技術可以在屏幕(幀緩衝)指定⼀個矩形區域。啓⽤剪裁
    測試之後,不在此矩形區域內的⽚元被丟棄,只有在此矩形區域內的⽚元纔有可能進⼊幀緩衝。因此實
    際達到的效果就是在屏幕上開闢了⼀個⼩窗⼝,可以再其中進⾏指定內容的繪製

    //1 開啓裁剪測試
    glEnable(GL_SCISSOR_TEST);
    //2.關閉裁剪測試
    glDisable(GL_SCISSOR_TEST);
    //3.指定裁剪窗⼝
    void glScissor(Glint x,Glint y,GLSize width,GLSize height);
    x,y:指定裁剪框左下⻆位置;
    width , height:指定裁剪尺⼨

  • 理解窗⼝,視⼝,裁剪區域
    • 窗口: 就是顯示界面
    • 視口: 就是窗口中用來顯示圖形的一塊矩形區域,它可以和窗口等大,也可以比窗口大或者小。只有繪製在視口區域中的圖形才能被顯示,如果圖形有一部分超出了視口區域,那麼那一部分是看不到的。通過glViewport()函數設置。
    • 裁剪區域:就是視口矩形區域的最小最大x座標(left,right)和最小最大y座標(bottom,top),而不是窗口的最小最大x座標和y座標。通過glOrtho()函數設置,這個函數還需指定最近最遠z座標,形成一個立體的裁剪區域。

4. 混合

  • 我們把OpenGL 渲染時會把顏色值存在顏色緩存區中,每個片段的深度值也是放在深度緩衝區。當深度緩衝區被關閉時,新的顏色將簡單的覆蓋原來顏色緩存區存在的顏色值,當深度緩衝區再次打開時,新的顏色片段只是當它們比原來的值更接近鄰近的裁剪平面纔會替換原來的顏色片段。

    glEnable(GL_BlEND);

  • 目標顏色:已經存儲在顏色緩存區的顏色值

  • 源顏色:作爲當前渲染命令結果進入顏色緩存區的顏色值

  • 當混合功能被啓動時,源顏色和目標顏色的組合方式是混合方程式控制的。在默認情況下,混合方程式如下所示:

    Cf = (Cs * S) + (Cd * D)
    Cf :最終計算參數的顏色
    Cs : 源顏色
    Cd :目標顏色
    S:源混合因子
    D:目標混合因子
    設置混合因子,需要用到glBlendFun函數
    glBlendFunc(GLenum S,GLenum D);
    在這裏插入圖片描述
    表中R、G、B、A 分別代表 紅、綠、藍、alpha。
    表中下標S、D,分別代表源、目標
    表中C 代表常量顏色(默認黑色)

  • 下面通過一個常⻅的混合函數組合來說明問題:

    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    如果顏色緩存區已經有一種顏色紅色(1.0f,0.0f,0.0f,0.0f),這個是目標顏色Cd,如果在這上面用一種alpha爲0.6的藍色(0.0f,0.0f,1.0f,0.6f)
    Cd (目標顏色) = (1.0f,0.0f,0.0f,0.0f);
    Cs (源顏色) = (0.0f,0.0f,1.0f,0.6f);
    S = 源alpha值 = 0.6f
    D = 1 - 源alpha值= 1- 0.6f = 0.4f
    方程式Cf = (Cs * S) + (Cd * D)
    等價於 = (Blue * 0.6f) + (Red * 0.4f)

  • 最終顏色是以原先的紅色(目標顏色)與 後來的藍色(源顏色)進行組合。源顏色的alpha值越高,添加的藍色顏色成分越高,目標顏色所保留的成分就會越少。
    混合函數經常用於實現在其他一些不透明的物體前面繪製一個透明物體的效果。

  • 實際上遠不止上面介紹的一種混合方程式,我們可以從5個不同的方程式中進行選擇

    選擇混合方程式的函數:
    glbBlendEquation(GLenum mode);
    在這裏插入圖片描述

  • 除了能使用glBlendFunc 來設置混合因子,還可以有更靈活的選擇。

    void glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB ,GLenum srcAlpha,GLenum dstAlpha);
    srcRGB: 源顏色的混合因子
    dstRGB: 目標顏色的混合因子
    srcAlpha: 源顏色的Alpha因子
    dstAlpha: 目標顏色的Alpha因子

    • glBlendFunc指定源和目標 RGBA值的混合函數,但是glBlendFuncSeparate函數則允許爲RGB和Alpha 成分單獨指定混合函數。
    • 在混合因子表中,GL_CONSTANT_COLOR,GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA,GL_ONE_MINUS_CONSTANT值允許混合方程式中引入一個常量混合顏色。
    • 常量混合顏色,默認初始化爲黑色(0.0f,0.0f,0.0f,1.0f),但是還是可以修改這個常量混合顏色。

      void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章