:渲染管線、矩陣變換與性能排查全解析)
簡(jiǎn)介這是一份面向圖形學(xué)求職者與3D開發(fā)者的OpenGL面試題資料圍繞計(jì)算機(jī)圖形學(xué)核心考點(diǎn)整理適合準(zhǔn)備圖形渲染、游戲引擎方向面試的中高級(jí)開發(fā)者查漏補(bǔ)缺。壓縮包內(nèi)共1個(gè)doc文檔約19KB內(nèi)容以問(wèn)答形式覆蓋渲染對(duì)象矩陣、渲染流水線、ZBuffer深度緩沖、Alpha混合公式、3D動(dòng)畫方式、顯卡優(yōu)化、硬件D3D燈光及GF4 MX與TI系列差異等知識(shí)點(diǎn)。文檔還包含四元數(shù)共軛、向量點(diǎn)乘叉乘、旋轉(zhuǎn)矩陣、相機(jī)參數(shù)設(shè)置、LOD、Vertex Shader、陰影判斷、MipMap等幾何與數(shù)學(xué)題目并附有渲染管線流程、骨骼旋轉(zhuǎn)矩陣、圖形引擎架構(gòu)分析等簡(jiǎn)答與分析題。已有1741人學(xué)習(xí)下載可幫助讀者系統(tǒng)梳理OpenGL與D3D面試高頻考點(diǎn)理解矩陣變換、混合與動(dòng)畫實(shí)現(xiàn)原理并借助真題演練檢驗(yàn)圖形學(xué)基礎(chǔ)與引擎設(shè)計(jì)思路。1. 渲染管線與矩陣這場(chǎng) OpenGL 面試到底考什么很多人準(zhǔn)備 OpenGL 面試第一反應(yīng)是背 API結(jié)果一上來(lái)自我介紹完面試官直接甩一句“說(shuō)說(shuō)渲染管線里 w 切分之后發(fā)生了什么”當(dāng)場(chǎng)卡殼。這場(chǎng)面試考的不是你會(huì)不會(huì)調(diào)glDrawArrays而是你對(duì) 3D 圖形學(xué)底層鏈路有沒(méi)有完整的認(rèn)知框架。從檢索到的題目分布看矩陣變換、渲染流水線、ZBuffer、Alpha 混合、光照模型、幾何數(shù)學(xué)、性能分析這幾塊幾乎覆蓋了 80% 以上的面試分值。換句話說(shuō)面試官想確認(rèn)的是給你一個(gè) Model Space 里的三角形你能不能一步步說(shuō)清楚它怎么變成屏幕上的像素中間每一步在算什么、為什么這么算。適合誰(shuí)看準(zhǔn)備圖形學(xué)崗位、引擎開發(fā)、渲染方向或者筆試?yán)锿蝗怀霈F(xiàn)四元數(shù)和矩陣逆的那批人。2. 矩陣與坐標(biāo)系三個(gè)矩陣、四元數(shù)、逆矩陣的連環(huán)追問(wèn)2.1 世界、視圖、投影矩陣到底各自在算什么面試官問(wèn)“渲染對(duì)象時(shí)需要設(shè)置哪三個(gè)矩陣”不是讓你背名字而是想聽你講清楚每個(gè)矩陣把頂點(diǎn)從哪個(gè)空間變到哪個(gè)空間。世界矩陣負(fù)責(zé)把模型空間里的獨(dú)立物體擺到統(tǒng)一的世界坐標(biāo)里組織成一個(gè)完整場(chǎng)景視圖矩陣由虛擬攝像機(jī)決定把世界坐標(biāo)轉(zhuǎn)到觀察空間本質(zhì)上是攝像機(jī)位置和朝向的逆變換投影矩陣把三維場(chǎng)景壓到二維平面上分為正交投影和透視投影透視投影會(huì)產(chǎn)生近大遠(yuǎn)小的效果。這類題通常這么答先點(diǎn)明三個(gè)矩陣的名字和順序再補(bǔ)一句“世界矩陣管擺放視圖矩陣管觀察投影矩陣管成像”最后落到 MVP 變換的乘法順序上。面試官最想聽的是你知道矩陣是左乘還是右乘、列主序還是行主序以及為什么投影之后還要做透視除法。2.2 四元數(shù)共軛、逆、繞軸旋轉(zhuǎn)的真題演練真題里有一道寫出四元數(shù) Q(0.3, 0.7, 0.8, 0.9) 的共軛。四元數(shù)一般表示為 (w, x, y, z)共軛就是虛部取反所以結(jié)果是 (0.3, -0.7, -0.8, -0.9)。另一道是繞軸 (0.2, 0.5, 0.9) 旋轉(zhuǎn) π/4寫出旋轉(zhuǎn)后的四元數(shù)。標(biāo)準(zhǔn)做法是先歸一化軸向量再代入四元數(shù)旋轉(zhuǎn)公式import math def quaternion_from_axis_angle(axis, angle): # 歸一化旋轉(zhuǎn)軸避免縮放影響旋轉(zhuǎn)結(jié)果 length math.sqrt(sum(c * c for c in axis)) ax [c / length for c in axis] half angle / 2.0 w math.cos(half) s math.sin(half) return (w, ax[0] * s, ax[1] * s, ax[2] * s) q quaternion_from_axis_angle([0.2, 0.5, 0.9], math.pi / 4) print(q)這段代碼的關(guān)鍵在于軸必須先歸一化否則旋轉(zhuǎn)角度會(huì)失真half 是把旋轉(zhuǎn)角除以 2因?yàn)樗脑獢?shù)用的是半角公式。面試官追問(wèn)“四元數(shù)的逆有什么意義”你要答單位四元數(shù)的逆等于共軛代表反向旋轉(zhuǎn)四元數(shù)與逆相乘得到單位四元數(shù) (1, 0, 0, 0)數(shù)值上就是無(wú)旋轉(zhuǎn)。矩陣的逆在 3D 中的作用類似用來(lái)把變換反向還原比如從世界空間回到模型空間做拾取計(jì)算。2.3 矩陣轉(zhuǎn)置、旋轉(zhuǎn)矩陣、相機(jī)參數(shù)設(shè)置的筆試題幾何部分??冀o一個(gè) 3×3 矩陣寫轉(zhuǎn)置、繞 X 軸旋轉(zhuǎn)寫旋轉(zhuǎn)矩陣、矩陣相乘寫代碼。繞 X 軸旋轉(zhuǎn) θ 的矩陣是import math def rotation_x(theta): c math.cos(theta) s math.sin(theta) # 繞X軸旋轉(zhuǎn)只影響Y和Z分量X保持不變 return [ [1, 0, 0], [0, c, -s], [0, s, c] ]參數(shù)說(shuō)明theta 是弧度制矩陣第三行第二列是 s第三行第三列是 c符號(hào)別寫反。相機(jī)參數(shù)設(shè)置題通常給 near distance 和 near 與 far 的距離要求寫出投影矩陣參數(shù)。透視投影矩陣?yán)飊ear 和 far 決定深度映射范圍near 太小會(huì)導(dǎo)致深度精度下降far 太大會(huì)讓遠(yuǎn)處物體 z-fighting。這類題面試官想看的是你知不知道 near/far 比值對(duì)深度緩沖精度的影響而不是單純套公式。3. 渲染流水線與光柵化從頂點(diǎn)到像素的每一步3.1 渲染管線順序的真題與易混點(diǎn)真題里有一道選擇題渲染管道流程是哪個(gè)選項(xiàng)給了輸入組裝、頂點(diǎn)、圖元、像素的不同排列。正確答案是輸入組裝 - 頂點(diǎn) - 圖元 - 像素。面試官問(wèn)“一個(gè)在 Model Space 中的三角形經(jīng)過(guò)渲染管線中的哪些計(jì)算步驟最后到屏幕輸出”完整的回答應(yīng)該是局部空間 - 世界空間 - 觀察空間 - 背面消隱 - 光照計(jì)算 - 裁剪 - 投影變換 - 視口變換 - 光柵化。注意這里背面消隱和光照的順序在不同管線里可能略有差異但核心鏈路不能亂。這類題通常這么答先按空間變換順序說(shuō)一遍再補(bǔ)一句“其中頂點(diǎn)著色器負(fù)責(zé)頂點(diǎn)變換和光照?qǐng)D元組裝負(fù)責(zé)把頂點(diǎn)連成三角形光柵化負(fù)責(zé)把三角形變成像素片段像素著色器負(fù)責(zé)最終顏色計(jì)算”。面試官最想聽的是你知道每個(gè)階段在 GPU 的哪個(gè)單元執(zhí)行以及哪些階段是可編程的。3.2 ZBuffer、w 切分、視口變換的作用ZBuffer 是深度緩沖作用是確保多邊形按正確的深度顯示。面試官追問(wèn)“渲染任何物體時(shí)是否都一定要打開 ZBuffer”你要答不一定。對(duì)于兩個(gè)物體以上的場(chǎng)景一般要求打開否則可能出現(xiàn)非正常的立體感觀但如果是純 2D 渲染或者已經(jīng)手動(dòng)排序好的半透明物體可以關(guān)閉 ZBuffer 或者把深度寫入關(guān)掉。w 切分發(fā)生在投影之后把齊次坐標(biāo)除以 w得到歸一化設(shè)備坐標(biāo)。視口變換把歸一化坐標(biāo)映射到屏幕像素坐標(biāo)。提示面試?yán)锶绻粏?wèn)到“為什么半透明物體要最后渲染并且關(guān)閉深度寫入”你要能接上因?yàn)榘胪该餍枰捅澈笠呀?jīng)渲染好的顏色做混合如果先渲染半透明并寫入深度后面的不透明物體就會(huì)被深度測(cè)試擋掉導(dǎo)致混合結(jié)果錯(cuò)誤。3.3 光照計(jì)算與 diffuse 公式光照計(jì)算中 diffuse 的公式是diffuse lightColor * materialDiffuse * max(0, dot(normal, lightDir))。面試官問(wèn)“pix 渲染時(shí)進(jìn)行的光照計(jì)算”或者“渲染管道 pix 時(shí)采用硬件光照不會(huì)進(jìn)行什么”你要注意硬件光照通常只做逐頂點(diǎn)或逐像素的光照計(jì)算陰影計(jì)算一般不在固定管線里自動(dòng)完成。diffuse 計(jì)算的關(guān)鍵是法線和光線方向的點(diǎn)乘點(diǎn)乘結(jié)果為負(fù)說(shuō)明光線在背面直接取 0。def diffuse_light(normal, light_dir, light_color, material_diffuse): # 法線和光線方向都需要?dú)w一化 n normalize(normal) l normalize(light_dir) # 點(diǎn)乘為負(fù)說(shuō)明光在背面取0避免負(fù)顏色 ndotl max(0.0, dot(n, l)) return [light_color[i] * material_diffuse[i] * ndotl for i in range(3)]參數(shù)說(shuō)明normal 是表面法線light_dir 是從表面指向光源的方向light_color 是光源顏色material_diffuse 是材質(zhì)漫反射系數(shù)。這段代碼的易錯(cuò)點(diǎn)是忘記歸一化導(dǎo)致點(diǎn)乘結(jié)果不在 -1 到 1 之間光照強(qiáng)度會(huì)翻車。4. 紋理、混合與動(dòng)畫Alpha、MipMap、骨骼動(dòng)畫的考法4.1 Alpha 混合公式與混合方式Alpha 混合的公式是Color (源顏色 * 源系數(shù)) OP (目標(biāo)顏色 * 目標(biāo)系數(shù))。OP 有加、減、反減、取最小、取最大。面試官問(wèn)“介紹你所有知道的紋理 Alpha 混合方式以及實(shí)際應(yīng)用”你要能說(shuō)出普通半透明用源 alpha 作為源系數(shù)、1 減源 alpha 作為目標(biāo)系數(shù)疊加發(fā)光效果用加法混合陰影或者暗化效果用乘法混合。實(shí)際應(yīng)用中半透明玻璃、火焰、煙霧、UI 淡入淡出都依賴 Alpha 混合。這類題通常這么答先寫公式再舉兩到三種混合方式對(duì)應(yīng)的效果最后補(bǔ)一句“混合順序很重要不透明物體先渲染半透明物體按深度從遠(yuǎn)到近排序渲染”。面試官最想聽的是你知道混合會(huì)破壞深度關(guān)系所以排序和深度寫入策略必須配合。4.2 MipMap 與 LOD 的優(yōu)缺點(diǎn)MipMap 是一組預(yù)先計(jì)算好的縮小版紋理作用是解決遠(yuǎn)處紋理采樣時(shí)的走樣和性能問(wèn)題。LOD 是細(xì)節(jié)層次根據(jù)物體距離或者屏幕占比選擇不同精度的模型。LOD 的優(yōu)點(diǎn)是減少遠(yuǎn)處物體的三角形數(shù)量提升渲染性能缺點(diǎn)是切換時(shí)可能出現(xiàn)視覺(jué)跳變需要做淡入淡出或者 geomorphing。面試官問(wèn)“LOD 是什么優(yōu)缺點(diǎn)”你要能結(jié)合場(chǎng)景說(shuō)大規(guī)模地形和開放世界游戲里 LOD 幾乎是標(biāo)配但 LOD 切換的閾值和過(guò)渡策略需要仔細(xì)調(diào)否則會(huì)出現(xiàn)模型突然變形的血淚經(jīng)驗(yàn)。4.3 骨骼動(dòng)畫、關(guān)鍵幀動(dòng)畫、關(guān)節(jié)動(dòng)畫的對(duì)比3D 動(dòng)畫方式主要有關(guān)節(jié)動(dòng)畫、單一網(wǎng)格模型動(dòng)畫關(guān)鍵幀動(dòng)畫、骨骼動(dòng)畫。關(guān)節(jié)動(dòng)畫把角色分成若干獨(dú)立部分一個(gè)部分對(duì)應(yīng)一個(gè)網(wǎng)格模型部分的動(dòng)畫連接成整體動(dòng)畫角色比較靈活但接縫處容易穿幫。單一網(wǎng)格模型動(dòng)畫由一個(gè)完整網(wǎng)格構(gòu)成在關(guān)鍵幀里記錄頂點(diǎn)位置及改變量然后插值運(yùn)算實(shí)現(xiàn)動(dòng)畫角色動(dòng)畫較真實(shí)但頂點(diǎn)多了之后內(nèi)存和計(jì)算開銷大。骨骼動(dòng)畫集成了以上兩個(gè)方式的優(yōu)點(diǎn)骨骼按角色特點(diǎn)組成層次結(jié)構(gòu)由關(guān)節(jié)相連皮膚作為單一網(wǎng)格蒙在骨骼之外每個(gè)頂點(diǎn)受骨骼影響從而實(shí)現(xiàn)完美動(dòng)畫。面試官問(wèn)“骨骼旋轉(zhuǎn)矩陣”怎么算你要答骨骼的旋轉(zhuǎn)矩陣由關(guān)節(jié)的局部變換逐級(jí)相乘得到最終影響蒙皮頂點(diǎn)的位置。蒙皮計(jì)算里每個(gè)頂點(diǎn)通常受 4 根骨骼影響權(quán)重之和為 1。5. 易錯(cuò)點(diǎn)與失分點(diǎn)這些題一錯(cuò)就直接掛5.1 渲染管線順序?qū)懛村e(cuò)在哪把圖元組裝寫在頂點(diǎn)處理前面或者把像素處理寫在光柵化前面。為什么錯(cuò)管線順序是硬件固定的頂點(diǎn)處理完才能組裝圖元光柵化之后才有像素片段。怎么答對(duì)記住“輸入組裝 - 頂點(diǎn) - 圖元 - 像素”這個(gè)主干再往里填空間變換、裁剪、視口變換。5.2 四元數(shù)共軛和逆搞混錯(cuò)在哪把共軛寫成虛部不變、實(shí)部取反。為什么錯(cuò)四元數(shù)共軛的定義是實(shí)部不變、虛部取反單位四元數(shù)的逆等于共軛但非單位四元數(shù)的逆還要除以模長(zhǎng)平方。怎么答對(duì)先判斷是不是單位四元數(shù)再?zèng)Q定用共軛還是逆。5.3 ZBuffer 關(guān)閉時(shí)機(jī)答錯(cuò)錯(cuò)在哪說(shuō)“任何物體都必須打開 ZBuffer”。為什么錯(cuò)半透明物體渲染時(shí)通常要關(guān)閉深度寫入2D 渲染也可以關(guān)閉。怎么答對(duì)分場(chǎng)景說(shuō)不透明物體開深度測(cè)試和寫入半透明物體開深度測(cè)試但關(guān)深度寫入并且從遠(yuǎn)到近排序。5.4 diffuse 公式忘記取 max(0, dot)錯(cuò)在哪直接寫 dot(normal, lightDir) 不取 0。為什么錯(cuò)點(diǎn)乘為負(fù)時(shí)會(huì)產(chǎn)生負(fù)光照顏色變黑或者出現(xiàn)異常。怎么答對(duì)寫公式時(shí)帶上 max(0, ...)并說(shuō)明這是為了剔除背面光照。5.5 相機(jī) near/far 設(shè)置不合理錯(cuò)在哪near 設(shè)得特別小far 設(shè)得特別大。為什么錯(cuò)深度緩沖精度會(huì)急劇下降遠(yuǎn)處物體出現(xiàn) z-fighting。怎么答對(duì)near 盡量遠(yuǎn)far 盡量近保證場(chǎng)景完整的前提下壓縮深度范圍。6. 答題技巧從分析題到性能排查的押題思路分析題里有一道很典型有一段渲染fps 很低找出原因說(shuō)一下思路。這種題沒(méi)有標(biāo)準(zhǔn)答案但面試官想聽的是你的排查路徑。我自己的習(xí)慣是強(qiáng)制走一遍“從 CPU 到 GPU、從提交到像素”的鏈路。先看 draw call 數(shù)量如果幾千個(gè) draw call大概率是 CPU 提交瓶頸再看三角形數(shù)量如果單幀幾百萬(wàn)三角形可能是頂點(diǎn)處理或者光柵化壓力再看紋理帶寬如果用了大量高分辨率紋理且沒(méi)有 MipMap采樣帶寬會(huì)爆再看 shader 復(fù)雜度逐像素光照、陰影計(jì)算、后處理疊加都會(huì)拉高像素階段耗時(shí)最后看混合和 overdraw半透明物體大面積重疊會(huì)導(dǎo)致像素反復(fù)著色。押題方向上渲染管線順序、MVP 矩陣、ZBuffer、Alpha 混合公式、diffuse 公式、LOD 和 MipMap、骨骼動(dòng)畫原理、四元數(shù)共軛和逆、矩陣轉(zhuǎn)置和旋轉(zhuǎn)矩陣這幾塊幾乎每年都會(huì)換個(gè)問(wèn)法出現(xiàn)。筆試?yán)镞€可能讓你寫矩陣相乘的代碼、寫返回值為 float 輸入?yún)?shù)為 int 的函數(shù)指針、寫 class A 讓 set 合法的定義。函數(shù)指針的寫法是float (*func)(int)set 合法的關(guān)鍵是提供 operator 或者自定義比較器。// 返回值為float輸入?yún)?shù)為int的函數(shù)指針 float (*funcPtr)(int); // 讓setA合法需要定義operator class A { public: int id; bool operator(const A other) const { return id other.id; } };參數(shù)說(shuō)明函數(shù)指針聲明里(*funcPtr)的括號(hào)不能省否則變成返回指針的函數(shù)。set 要求元素可比較operator 必須聲明為 const 成員函數(shù)否則 set 內(nèi)部排序時(shí)會(huì)編譯失敗。線程時(shí)間片到了進(jìn)入什么階段、this 指針?lè)旁谀膫€(gè)寄存器、傳遞常量寄存器的數(shù)據(jù)結(jié)構(gòu)這些屬于操作系統(tǒng)和編譯原理的交叉題。線程時(shí)間片用完進(jìn)入就緒狀態(tài)等待下一次調(diào)度this 指針通常放在 ECX 寄存器常量寄存器傳遞的數(shù)據(jù)結(jié)構(gòu)一般是常量緩沖區(qū)。從那以后我每次遇到“fps 很低”的分析題都強(qiáng)制走一遍 draw call、三角形、紋理、shader、overdraw 這五步先定位瓶頸在 CPU 還是 GPU再往下拆。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取