[原][译]关于osgEarth::VirtualProgram说明
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 };