OE官方英文原文:http://docs.osgearth.org/en/latest/developer/shader_composition.html
cpp中的說明:
VirtualProgram支持在osgEarth中進行GLSL着色器合成。 它會在運行時自動將着色器功能組裝為完整的着色器程序。 您可以隨時添加或刪除功能(注入點)。
VirtualProgram(VP)是osg :: StateAttribute。 但是與大多數屬性不同,VP將繼承狀態堆棧中其他VP的屬性。
VirtualProgram最初是由Wojciech Lewandowski完成的VirtualProgram着色器合成工作改編而成,在OSG的osgvirtualprogram示例中可以看到。
Shader Composition
着色器的合成系統
Shader Composition(使用組合着色器的原因)
osgEarth在其幾種渲染模式中使用GLSL着色器。 默認情況下,osgEarth將檢測圖形硬件的功能並自動選擇合適的模式使用。
由於osgEarth依賴着色器,因此作為開發人員,您可能希望自定義渲染或在GLSL中添加自己的效果和功能。 使用着色器的任何人都會遇到相同的挑戰:
- 着色器程序是整體的。 添加新的着色器代碼要求您復制,修改和替換現有代碼,以免失去其功能。
- 使您的更改與原始代碼的着色器的更改保持同步是維護的噩夢。
- 維護着色器main()的多個版本既麻煩又困難。
- 隨着GLSL代碼庫的復雜性增加和添加更多功能,維護可怕的“超級着色器”變得難以管理。
着色器合成(Shader Composition )通過將着色器管道模塊化來解決這些問題。 您可以在程序中的任何位置添加和刪除功能,而無需復制,粘貼或修改其他人的GLSL代碼。
接下來,我們將討論osgEarth的着色器合成框架的結構。
Framework Basics(基礎框架介紹)
組合着色器的框架自動提供main()函數。 您無需編寫它們。 而且,你可以編寫模塊化函數,並告訴框架在哪里執行它們。
下面,我們看看osgEarth創建的main()的偽代碼:
注意: LOCATION_XXXXX 是表示可以在着色器的執行管道中的哪個點插入我們自定義的函數。
// VERTEX SHADER:頂點着色器 void main(void) { vec4 vertex = gl_Vertex; // "LOCATION_VERTEX_MODEL" user functions are called here: 模型操作 model_func_1(vertex); model_func_2(vertex); ... vertex = gl_ModelViewMatrix * vertex; // "LOCATION_VERTEX_VIEW" user functions are called here: 視口操作 view_func_1(vertex); ... vertex = gl_ProjectionMatrix * vertex; // "LOCATION_VERTEX_CLIP" user functions are called last: 裁剪操作 clip_func_1(vertex); ... gl_Position = vertex; } // FRAGMENT SHADER: 片元着色器 void main(void) { vec4 color = gl_Color; ... // "LOCATION_FRAGMENT_COLORING" user functions are called here: 自定義顏色 coloring_func_1(color); ... // "LOCATION_FRAGMENT_LIGHTING" user functions are called here: 光照操作 lighting_func_1(color); ... gl_FragColor = color; }
如上,OE已經做出了指定功能注入點的設計決定。這並不是說它們對所有事物都是完美的,而是說OE相信這種方法使框架易於使用,且不太“低級”。
重要提醒:着色器組合框架此時只支持頂點和片段着色器。它不支持幾何或鑲嵌着色器。OE計划在將來增加這一點。
VirtualProgram
可編程着色器
osgEarth引入了一個新的osg狀態屬性,名為VirtualProgram的運行時着色器合成器。因為VirtualProgram是osg::StateAttribute,你可以將其附加到場景圖中的任何節點上。
使用VirtualProgram的着色器可以在場景樹中作為一個更高層次的渲染存在。
通過這種方式,你可以在osgEarth中添加、組合、重寫每個着色器函數。
在運行時,VirtualProgram將查看當前狀態並組裝一個完整的osg::Program,它使用內置main(),調用VirtualProgram注入所有着色器功能。
Adding Functions
添加函數方案:
從我們前面看到的生成主管道中,osgEarth會調用用戶自定義函數。
這些自定義函數不在osgEarth默認生成的着色器中,但可以作為着色器代碼“注入”到管道中各個位置。
例如,讓我們使用用戶自定義函數創建一個簡單的“模糊”效果:
// haze_vertex: 將放在頂點着色器的view部分 out vec3 v_pos; void setup_haze(inout vec4 vertexView) { v_pos = vertexView.xyz; } // haze_fragment: 將放在片元着色器的lighting部分 in vec3 v_pos; void apply_haze(inout vec4 color) { float dist = clamp( length(v_pos)/10000000.0, 0, 0.75 ); color = mix(color, vec4(0.5, 0.5, 0.5, 1.0), dist); } // C++: oe中加入這兩個函數,將兩個着色器函數放入正確位置 VirtualProgram* vp = VirtualProgram::getOrCreate( stateSet ); vp->setFunction( "setup_haze", haze_vertex, ShaderComp::LOCATION_VERTEX_VIEW); vp->setFunction( "apply_haze", haze_fragment, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
在本例中,函數setup_haze在內置頂點函數之后,從內置頂點着色器main()調用。這個apply_haze函數在內置片段函數之后從核心片元着色器main()調用。
目前OE有六個可插入點,如下:
| Location | Shader Type | Signature |
|---|---|---|
| ShaderComp::LOCATION_VERTEX_MODEL | VERTEX | void func(inout vec4 vertex) |
| ShaderComp::LOCATION_VERTEX_VIEW | VERTEX | void func(inout vec4 vertex) |
| ShaderComp::LOCATION_VERTEX_CLIP | VERTEX | void func(inout vec4 vertex) |
| ShaderComp::LOCATION_FRAGMENT_COLORING | FRAGMENT | void func(inout vec4 color) |
| ShaderComp::LOCATION_FRAGMENT_LIGHTING | FRAGMENT | void func(inout vec4 color) |
| ShaderComp::LOCATION_FRAGMENT_OUTPUT | FRAGMENT | void func(inout vec4 color) |
每個VERTEX定位都可讓您在特定坐標空間中的頂點上進行操作。 您可以更改頂點,但必須將其放在相同的空間中。
頂點定位如下:
| MODEL 模型: | 頂點是幾何中未轉換的原始值。 |
|---|---|
| VIEW 視圖: | 頂點相對於眼點,它位於原點(0,0,0),指向-Z軸。在視圖空間中,原始頂點已被gl_ModelViewMatrix轉換. |
| CLIP 剪輯: | 投影的裁剪空間。剪輯空間位於所有三個軸的[-w.w]范圍內,已將原始頂點通過gl_ModelViewProjectionMatrix轉換. |
片元定位如下:
| COLORING 着色: | 在應用照明之前,解析片段顏色時調用這里的函數。紋理或顏色調整通常發生在這一階段。 |
|---|---|
| LIGHTING 照明: | 這里的功能影響到片元中的光照等算法。例如計算:太陽照明、凹凸貼圖或法線貼圖等。 |
| OUTPUT 輸出: | 這里是設置gl_FragColor的地方。默認情況下,內置片段main()將設置它。但是你可以設置一個輸出着色器來替換這種方式。這樣做的一個典型例子是實現MRT渲染(請參閱osgEarth_mrt示例)。 |
Shader Packages
更多着色器函數庫
Shader組合框架還提供了一個ShaderPackage支持更高級的着色器管理方法:
VirtualProgram Metadata
可編程着色器的元數據
正如我們所看到的,當您向管道中添加一個着色器函數時,可以使用VirtualProgram時,你需要告訴osgEarth要調用的GLSL函數的名稱,以及它在管道中調用的位置,如下所示:
VirtualProgram* vp; .... vp->setFunction( "color_it_red", shaderSource, ShaderComp::LOCATION_FRAGMENT_COLORING );
這很管用。但是,如果函數名或注入位置發生變化,則需要記住使GLSL代碼與所有傳入setFunction()的參數。
這時,ShaderPackage將更容易使用。以下是一個例子:
#version 110 #pragma vp_entryPoint color_it_red #pragma vp_location fragment_coloring #pragma vp_order 1.0 void color_it_red(inout vec4 color) { color.r = 1.0; }
現在不用再調用VirtualProgram::setFunction()函數了,您可以創建一個ShaderPackage,添加您的代碼,並在VirtualProgram中調用Load即可
ShaderPackage package;
package.add( shaderFileName, shaderSource );
package.load( virtualProgram, shaderFileName );
它采用“文件名”,因為着色器可以在外部文件中調用。
這個vp_location取值如下:
- vertex_model
- vertex_view
- vertex_clip
- fragment_coloring
- fragment_lighting
- fragment_output
External GLSL Files
外部GLSL文件
這個ShaderPackage允許你從文件或字符串加載GLSL代碼。當你調用add方法,這個庫將會做:(A)首先使用該文件名查找文件並從該文件加載;(B)如果不存在該文件,則使用源字符串中的代碼。
讓我們來看看這個例子:
ShaderPackage package; package.add( "myshader.frag.glsl", backupSourceCode ); ... package.load( virtualProgram, "myshader.frag.glsl" );
庫將嘗試從GLSL文件加載着色器。它將在OSG_FILE_PATH。如果它找不到文件,它將從軟件包中與該着色器相關聯的備份源代碼中加載着色器。
osgEarth在內部使用這種技術“內聯”儲存着色代碼。這使您可以選擇使用應用程序部署GLSL文件,或者將它們保持在內聯狀態--無論哪種方式,應用程序仍然可以工作。
Include Files
引用文件
這個ShaderPackage如果引用其他文件:你的GLSL代碼只要引用其他文件名就可以調用里面的函數。若要包含其他文件,需要使用自定義#pragma,請執行以下操作:
#pragma include myCode.vertex.glsl
就像在C++中一樣,include引用將直接內聯加載其他文件(或源代碼)。因此,你所引用的文件必須是結構化的。
再次提醒:引用的內容與引用文件必須用同一個 ShaderPackage.
Concepts Specific to osgEarth
特定於osgEarth的可編程着色器概念
盡管可編程着色器框架包含在osgEarth SDK中,但它實際上與地圖渲染無關。
下面,我們將介紹osgEarth對着色器組合所做的一些特別的操作。
Terrain Variables
地形變量
有一些內置的着色器uniforms和variables這是osgEarth地形引擎使用的,也是我們開發者可以使用的。
重要提醒:以前綴 “ OE_ ” 或者 “ osgEarth_ ” 開頭的着色變量請保留給 osgEarth 內部使用。
Uniforms:
oe_tile_key: (vec4) elements 0-2 hold the x, y, and LOD tile key values; element 3 holds the tile’s bounding sphere radius (in meters)
前三個元素包含x、y和LOD的瓦片鍵值;第四個元素 保存瓦片的包圍盒半徑(以米為單位)。
oe_layer_tex: (sampler2D) texture applied to the current layer of the current tile
紋理,適用於當前瓦片的當前層
oe_layer_texc: (vec4) texture coordinates for current tile
當前瓦片的紋理坐標
oe_layer_tilec: (vec4) unit coordinates for the current tile (0..1 in x and y)
當前瓦片的單位坐標(x和y中的0..1)
oe_layer_uid: (int) Unique ID of the active layer
活動層的唯一ID
oe_layer_order: (int) Render order of the active layer
活動層的渲染順序
oe_layer_opacity: (float) Opacity [0..1] of the active layer
活動層的不透明度[0到1]
Vertex attributes:
oe_terrain_attr: (vec4) elements 0-2 hold the unit height vector for a terrain vertex, and element 3 holds the raw terrain elevation value
前三個元素為地形頂點的單位高度向量,最后一個元素為原始地形海拔值。
oe_terrain_attr2: (vec4) element 0 holds the parent tile’s elevation value; elements 1-3 are currently unused.
第一個元素為父級瓷磚的高程值;后三個元素目前未使用.
Shared Image Layers
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////以上是官方文檔原內容的翻譯,下面介紹下目前的OE部分
OE目前支持了幾何着色器
// User function injection points.插入點位置: enum FunctionLocation { // vertex is in model space (equivalent to gl_Vertex).模型空間 LOCATION_VERTEX_MODEL, // vertex is in view(aka eye) coordinates, with the camera at 0,0,0 視口空間 // looking down the -Z axis. LOCATION_VERTEX_VIEW, // vertex is in post-perspective coordinates; [-w..w] along each axis裁剪空間 LOCATION_VERTEX_CLIP, // tessellation control shader; model space LOCATION_TESS_CONTROL, // tessellation evalulation shader; model space LOCATION_TESS_EVALUATION, // geometry shader; inputs are in model space. LOCATION_GEOMETRY, // fragment is being colored. LOCATION_FRAGMENT_COLORING, // fragment is being lit. LOCATION_FRAGMENT_LIGHTING, // fragment output is being assigned. LOCATION_FRAGMENT_OUTPUT, // not defined. LOCATION_UNDEFINED };
