歡迎訪問我的個(gè)人主頁了解更多關(guān)于OpenGL的知識(shí)
前言
GLSL是OpenGL的著色語言绽诚,在編寫GLSL的過程中疚漆,復(fù)用代碼是一件比較麻煩的事情碉克,GLSL基本是不支持#include
預(yù)編譯指令的尖滚。
目前我已知的OpenGL對于GLSL include的支持僅限于擴(kuò)展ARB_shading_language_include
,如果你的系統(tǒng)支持該OpenGL擴(kuò)展键袱,那么你就可以在GLSL中使用include再菊。不過似乎只有nvidia的OpenGL驅(qū)動(dòng)對它有支持。
復(fù)用GLSL代碼的級別
既然官方的include使用如此艱難宏邮,我們就來自己造輪子吧泽示。首先我們要確定在什么級別復(fù)用代碼。有兩種選擇:
- 程序代碼運(yùn)行時(shí)將可復(fù)用的GLSL代碼塊合并成可運(yùn)行的GLSL代碼蜜氨。
- 程序編譯時(shí)跑一個(gè)腳本械筛,將可復(fù)用的GLSL代碼合并成可運(yùn)行的GLSL文件,然后在運(yùn)行時(shí)使用生成好的GLSL文件记劝。
我之前嘗試過第一種方法变姨,運(yùn)行時(shí)合并GLSL代碼族扰,不過調(diào)試不是很方便厌丑,報(bào)錯(cuò)時(shí)不好定位到哪行,而且運(yùn)行時(shí)生成終究會(huì)消耗一些性能渔呵。所以這一次我打算探索第二條路怒竿。
預(yù)編譯工具m4
在研究第二種方式時(shí),碰巧找到了一個(gè)非常符合解決本次問題的工具m4扩氢。m4說白的就是一個(gè)預(yù)編譯工具耕驰,將寫有宏的文本進(jìn)行預(yù)編譯展開。如果你使用Mac录豺。在終端輸入
m4 --version
如果得到如下朦肘,恭喜你饭弓,你已經(jīng)擁有了此工具。
GNU M4 1.4.6
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Rene' Seindal.
當(dāng)然你也可能是其他版本媒抠。如果沒有出現(xiàn)弟断,你可以使用homebrew安裝。
brew install m4
如果你喜歡使用源碼安裝趴生,可以訪問m4的官網(wǎng)阀趴。
m4的使用示例
我們先來寫一個(gè)例子來感受一下m4的功能。在同一個(gè)目錄下創(chuàng)建2個(gè)文件苍匆,Test.m4和inc.m4刘急。
在inc.m4中輸入下面內(nèi)容。
這里是被包含的內(nèi)容
在Test.m4中我們做兩件事情浸踩,包含inc.m4叔汁,定義一個(gè)變量。
include(`inc.m4')
define(`content', `例子')
這是一個(gè)content
注意检碗,inc.m4左邊不是單引號(hào)攻柠,是`
,右邊是單引號(hào)后裸。content
和 例子
也是一樣瑰钮。include(`inc.m4')
將inc.m4包含到Test.m4中,define(`content', `例子')
將例子
這個(gè)值賦給content
微驶。我們使用m4預(yù)處理一下看看結(jié)果浪谴。在終端運(yùn)行。
m4 Test.m4
結(jié)果如下因苹。
這里是被包含的內(nèi)容
這是一個(gè)例子
將m4運(yùn)用于GLSL
下面是我的一個(gè)GLSL程序結(jié)構(gòu)目錄苟耻,我使用m4將這些復(fù)用代碼合并到一起。
這里我寫了兩個(gè)光照模型扶檐,lambert_phong和lambert_blinn凶杖,它們大部分代碼都是一樣的,只有高光部分代碼不一樣款筑。targets文件夾下是最終被m4處理的文件智蝠。處理完后直接被OpenGL使用。我們看一下其中的一個(gè)文件
frag_lambert_phong.glsl
奈梳。
// defines
include(`fragment shader components/frag_base_header.glsl')
include(`fragment shader components/frag_structs.glsl')
include(`fragment shader components/frag_in_vars.glsl')
include(`fragment shader components/frag_uniform_vars.glsl')
// functions
include(`fragment shader components/frag_normal.glsl')
include(`fragment shader components/frag_light_base.glsl')
include(`fragment shader components/frag_light_lambert.glsl')
include(`fragment shader components/frag_light_phong.glsl')
// light model
include(`fragment shader light model/frag_light_model_lambert_phong.glsl')
其實(shí)我只用到了m4的include功能杈湾,我們將這個(gè)文件和frag_lambert_blinn
對比一下。
// defines
include(`fragment shader components/frag_base_header.glsl')
include(`fragment shader components/frag_structs.glsl')
include(`fragment shader components/frag_in_vars.glsl')
include(`fragment shader components/frag_uniform_vars.glsl')
// functions
include(`fragment shader components/frag_normal.glsl')
include(`fragment shader components/frag_light_base.glsl')
include(`fragment shader components/frag_light_lambert.glsl')
include(`fragment shader components/frag_light_blinn.glsl')
// light model
include(`fragment shader light model/frag_light_model_lambert_blinn.glsl')
只有最后兩個(gè)include不一樣攘须,很好的滿足了我復(fù)用GLSL的需求漆撞。最后我寫了一個(gè)簡單的腳本批量處理targets下的文件。
#!env python
import os
import subprocess
target_files = os.listdir("./targets")
for file in target_files:
print("building " + file + "...")
command = str.format("m4 ./targets/{0} > ../{0}", file)
subprocess.call(command, shell=True)
print(file + " build finish.")
將這個(gè)腳本集成到你的編譯系統(tǒng)中,就可以做到完全自動(dòng)化復(fù)用和預(yù)處理GLSL了浮驳。