(原創)OGRE主要渲染流程簡介
OGRE主要渲染流程簡介
轉載請註明出處
很早以前就想寫一些關於OGRE的文章了,一直沒機會。
理解一個渲染引擎,我覺得最重要的是先抓住了它的主架構,它的主線,渲染流程,不然的話,一個引擎幾萬行,甚至幾十萬行的程式碼,光是開啟solution就能嚇你一跳了,OGRE也有十幾萬行的程式碼量,我一開始看它的時候也是無從下手,感覺程式碼太多了,不知道從哪開始看好,這個
這篇短文也是對OGRE的主要渲染流程的一個介紹,可能對一些class不會太多地去介紹具體的實現細節。我所用的程式碼都是取自於OGRE的最新的CVS版本。
讀者最好對OGRE有一定的瞭解,至少得看懂它的example,不然可能一些東西理解起來比較困難。對D3D,OPENGL有一定了解更好。
如果你看過D3D SDK中帶的例子,你一定知道一個比較簡單的3D程式要執行起來,至少都會涉及以下的幾部分:
首先是資料的來源,包括頂點資料,紋理資料等,這些資料可以從檔案中讀取,也可以在程式執行時生成。
接下來,我們會建立頂點緩衝區把頂點儲存起來,建立texture物件來表示texture,對頂點組成的物體設定它在世界座標系下的座標,設定攝像機的位置,視點,設定viewport的位置和大小,然後就可以在渲染迴圈中開始呼叫渲染操作了,經過了front buffer和back buffer的交換,我們就能在螢幕上看到3D圖形了,虛擬碼如下:
setupVertexBuffer
setWorldTransform
setCamera
setProjectionTransform
setViewport
beginFrame
setTexture
drawObject
endFrame
以下就是渲染一個物體的主要步驟,在我看來,這就是3D程式的主線,同樣道理,無論你多複雜的渲染引擎,都得實現上述的這些步驟,其他的一些效果如陰影,光照等,都是附著在這條主線上的,所以,如果你能在你所研究的渲染引擎上也清晰地看到這條主線,可能對你深入地研究它會大有幫助,下面,我們就一起來找到OGRE中的這條主線。
OGRE的渲染迴圈都是起源於Root::renderOneFrame,這個函式在OGRE自帶的example中是不會顯式呼叫的,因為example都呼叫了Root::startRendering,由startRendering來呼叫renderOneFrame,如果你用OGRE來寫真正的遊戲,或者編輯器,你可能就需要在的訊息主迴圈中呼叫renderOneFrame了,顧名思義,這個函式就是對整個OGRE進行一幀的更新,包括動畫,渲染狀態的改變,渲染api的呼叫等,在這個函式中,會包括了我們上述虛擬碼的幾乎全部內容,所以是本文的重點所在。
進入renderOneFrame,可以看到頭尾兩個fire函式,這種函式在OGRE中經常出現,一般都是fire…start和fire…end一起出現的,在這些函式中,可能會處理一些使用者自定義的操作,如_fireFrameStarted就會對所以的frameListener進行處理,這些fire函式可以暫時不用理會,繼續看_updateAllRenderTargets,在這個函式中,會委派當前所用的renderer對所有創建出來的render target進行update,render target也就是渲染的目的地,一般會有兩種,一種是render texture,一種是render buffer,接著進入RenderSystem::_updateAllRenderTargets,可以看到在render system中,對創建出來的render target是用RenderTargetPriorityMap來儲存的,以便按照一定的順序來對render target進行update,因為在渲染物體到render buffer時,一般會用到之前渲染好的render texture,所以render texture形式的render target需要在render buffer之前進行更新。
進入render target的update,可以看到,它仍然把update操作繼續傳遞下去,呼叫所有掛在這個render target上的viewport的update。
Viewport其實就是定義了render target上的一塊要進行更新的區域,所以一個render target是可以掛多個viewport的,以實現多人對戰時分屏,或者是畫中畫等效果,可以把OGRE中的viewport看成是儲存camera和rendertarget這兩者的組合,把viewport中所定義的camera所看到的場景內容渲染到viewport所定義的render target的區域裡。
Viewport還有一個重要資訊是ZOrder,可以看到RenderTarget中的ViewportList帶有一個比較函式,所以在RenderTarget::update中,ZOrder越小的,越先被渲染,所以,如果兩個viewport所定義的區域互相重疊了,而且ZOrder又不一樣,最終的效果就是ZOrder小的viewport的內容會被ZOrder大的viewport的內容所覆蓋。
繼續進入Viewport::update,就像前面所說,它呼叫它所引用的camera來渲染整個場景,而在Camera::_renderScene中,是呼叫SceneManager::_renderScene(Camera* camera, Viewport* vp, bool includeOverlays)。SceneManager::_renderScene裡就是具體的渲染流程了。從函式名稱還有引數也可以看出來,這個函式的作用就是利用所指定的camera和viewport,來把場景中的內容渲染到viewport所指定的render target的某塊區域中。根據camera,我們可以定出view matrix,projection matrix,還可以進行視錐剔除,只渲染看得見的物體。注意,我們這裡只看標準的SceneManager的方法,不看BspSceneManager派生類的方法,而且,我們會拋開跟主線無關的內容,如對shadow的setup,骨骼動畫的播放,shader引數的傳遞等,因為我們只注重渲染的主流程。
在SceneManager::_renderScene中所應看的第一個重要函式是_updateSceneGraph,OGRE對場景的組織是通過節點樹來組織的,一個節點,你可以看成是空間中的某些變換的組合,如位置,縮放,旋轉等,這些變換,會作用到掛接在這些節點上的具體的物體的資訊,也就是說,節點儲存了world transform,對具體的物體,如一個人,在空間中的定位,都是通過操作節點來完成的。同時節點還儲存了一個世界座標的AABB,這個AABB能容納所有它所掛接的物體的大小,主要是用於視錐裁減的,如果當前攝像機看不見某個節點的AABB,那麼說明攝像機看不見節點所掛接的所有物體,所以在渲染時可以對這個節點視而不見。
_updateSceneGraph的內部處理比較繁瑣,我們只需知道,經過了_updateSceneGraph,場景節點樹中的每個節點都經過了更新,包括位置,縮放,和方位,還有節點的包圍盒。
繼續回到SceneManager::_renderScene,接下來要看的是setViewport,它會呼叫具體的renderer的setviewport的操作,設定viewport中所掛接的render target為當前所要渲染的目標,viewport中的區域為當前所要渲染的目標中的區域。
接下來要碰到OGRE渲染流程中的一個重要的概念,Render Queue。這個東西實在內容比較多,還是以後有機會單獨提出來說吧,你可以簡單把它想成是一個容器,裡面的元素就是renderable,每個renderable可以看成是每次呼叫drawprimitive函式所渲染的物體,可以是一個模型,也可以是模型的一部分。在RenderQueue中,它會按材質來分組這些renderable,還會對renderable進行排序。
在每一次呼叫SceneManager::_renderScene時,都會呼叫SceneManager::prepareRenderQueue來清理RenderQueue,然後再呼叫SceneManager::__findVisibleObjects來把當前攝像機所能看見的物體都加入到RenderQueue中。
SceneManager::__findVisibleObjects是一個遞迴的處理過程,它從場景的根節點開始,先檢查攝像機是否能看見這個節點的包圍盒(包圍盒在_updateSceneGraph時已經計算好了),如果看不見,那麼這個節點,還有它的子節點都不用管了。如果能看見,再檢測掛在這個節點上的所有MovableObject,如果當前所檢測的MovableObject是可見的,就會呼叫它的_updateRenderQueue方法,一般在這個方法裡就可以把和這個MovableObject相關的renderable送入RenderQueue了。
這裡要說說MovableObject,MovableObject主要是用於表示場景中離散的物體,如Entity,顧名思義,能移動的物體,不過它的“能移動”這個能力是要通過SceneNode來實現的,所以MovableObject來能顯示出來,首先得先掛接在某個場景節點上,通過場景節點來定位。你可以控制MovableObject的一些屬性,如某個MovableObject是否要顯示,是否要隱藏,都可以通過MovableObject::setVisible方法來實現。
檢測完該節點上的MovableObject之後,就繼續呼叫所有子節點的_findVisibleObjects方法,一直遞迴下去。這樣,就能把場景中所有要渲染的renderable所加入到RenderQueue中了。
至此,我們就擁有了要渲染的物體的資訊了,接下來就是對這些物體進行渲染了,你會發現跟D3D或OpenGL的程式碼很類似的呼叫:
mDestRenderSystem->clearFrameBuffer
mDestRenderSystem->_beginFrame
mDestRenderSystem->_setProjectionMatrix
mDestRenderSystem->_setViewMatrix
_renderVisibleObjects();
mDestRenderSystem->_endFrame();
這些api的作用和D3D中的類似呼叫的作用都差不多,這裡再說一下_renderVisibleObjects(),在這個函式中,會對RenderQueue中的每個renderable進行渲染,用的是visitor模式來遍歷操作每個renderable,最終在SceneManager::renderSingleObject中取出每個renderable所儲存的頂點,索引,世界矩陣等資訊,來進行渲染。這其中還包括了查詢離該renderable最近的光源等操作,比較複雜。
到這裡,SceneManager::_renderScene的流程基本走完了,也就是說,OGRE一幀中的渲染流程差不多也結束了,你應該也發現,這個流程跟你用D3D寫一個簡單程式的流程基本是一樣的,在這個流程的基礎上,再去看具體的實現,如怎麼樣設定紋理,怎麼樣呼叫你熟悉的D3D或OpenGL的API來渲染物體,應該會簡單得多。
對OGRE的渲染流程的大概介紹到這裡也結束了,很多細節都沒涉及,以後有機會再寫吧。
相關推薦
(原創)OGRE主要渲染流程簡介
OGRE主要渲染流程簡介 謝偉亮 [email protected] 轉
netlink監聽網路變化程式碼(轉載)+流程分析(原創+轉載)+資料結構以及相關巨集的解析(原創)
一.netlink監聽網路變化程式碼(Linux下使用NetLink 監聽網路變化) #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h>
(原創)CRC計算流程分析(RefIn,Init,RefOut,XorOut)
CRC的計算流程 以此CRC計算程式為例, 1、輸入:原資料是ASCII碼的“A”,對應二進位制資料“0100 0001” 2、選擇校驗方式,以CRC-4/ITU為例,多項式為x4+x+1對應二進位制:10011 3、引數Info 1)Name:CRC校驗演
(四)4 請求-響應流程
python現在我們開發了一個簡單的Flask小程序,知道用戶在瀏覽器中輸入一個URL對應到哪個視圖函數進行處理。那麽問題來了,怎麽去進行處理一些比較復雜的業務邏輯呢? 一、程序上下文 1、整個app範圍,也就是說是全局的(程序級別)的上下文 怎麽理解這個上下文呢?就是在所有的這個請求當中,我們共享的是同一
(原創)Maven+Spring+CXF+Tomcat7 簡單例子實現webservice
produces per back targe xsd lean listener ans 控制 這個例子需要建三個Maven項目,其中一個為父項目,另外兩個為子項目 首先,建立父項目testParent,選擇quickstart: 輸入項目名稱和模塊名稱,然後創建:
Linux設備驅動程序(一)設備驅動程序簡介
包括 收集 字符設備 調度器 計算機 啟動 驅動程序 str 單個 機制or策略: 驅動提供機制(what),而不是提供策略(how); 內核功能劃分: 根據內核完成任務的不同,可分為如下幾個部分: 1. 進程管理 負責進程的的創建和銷毀,並
走進Struts2(一) — Struts2的執行流程及其工作原理
管理 npr clean 核心部分 由於 nco 方式 中間 con Struts2是一套很優秀的Web應用框架,實現優雅、功能強大、使用簡潔。能夠說是Struts2是一款很成熟的MVC架構。 在我們學習Struts2時,最好是先學習它的執行流程、核心概念。從中
(原創)虛幻3--控制臺命令參數--1
pan use 控制臺命令 span find space -1 -- pac SEEKFREELOADING:Only use cooked data.(只使用cooked數據,參數:" -seekfreeloading"); FOV [number] 改變視點角度; S
CentOS 6.4 添加永久靜態路由所有方法匯總(原創)
eth targe 失效 linux 2.0 href nbsp 無法自動 flag 轉摘,原文章地址:http://blog.sina.com.cn/s/blog_828e50020101ern5.html查看路由的命令route -n CentOS添加永久靜態路由
WP集成七牛雲存儲(原創)
keyword 原來 所有 上傳 pdf 節點 cname int ima 借助:七牛鏡像存儲 WordPress 插件 https://wordpress.org/plugins/wpjam-qiniu/ 安裝本插件1.4.5及以上版本,請先安裝並激活WPJAM
HTML學習筆記 CSS學習選擇器 第五節 (原創)
ext spa family 如果 styles ctype css gre utf <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <
HTML學習筆記 CSS樣式 第六節 (原創)
Y軸 重復 eight -i tac 圖片 500px itl idt <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title&
HTML學習筆記基礎表格 第二節 (原創)
utf 空心圓 無序列表 har ble 學習 oot order 有序 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title
HTML學習筆記 CSS塊元素加偽類選擇器 第三節 (原創)
筆記 solid oct 元素 是否 選擇器 size set 區域 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title&g
HTML學習筆記 css定位浮動及瀑布流 第十三節 (原創)
oct adding styles gin ima alt eight div height <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8">
HTML學習筆記 w3sCss盒子模型應用 第十一節 (原創)
.com foo margin images href ack har htm com <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> &l
HTML學習筆記 CSS學習選擇器案例 第五節 (原創) 參考使用表
樣式 back ack aid head 派生選擇器 char logs pan <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">
HTML學習筆記 基礎標簽及css引用案例 第一節 (原創)參考使用表
set utf har del 文件 定義 .com eight head <!DOCTYPE html><!--頭文件 不是標簽 也沒有結束,這是聲明該文件為HTML5--><html lang="en"><!--表示網頁文字以什
HTML學習筆記 CSS背景樣式案例 第六節 (原創) 參考使用表
ext jpg class 設置 link eight text spa .com <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">
HTML學習筆記 css定位浮動及瀑布流案例 第十三節 (原創) 參考使用表
20px 筆記 lis containe use cor ack 100% set #fd { width: 100px; height: 150px; background-color: forestgreen; float: left;