英文原文:http://docs.osgearth.org/en/latest/developer/shader_composition.html
osgEarth在其几种渲染模式中使用GLSL着色器。 默认情况下,osgEarth将检测图形硬件的功能并自动选择合适的模式使用。
由于osgEarth依赖着色器,因此作为开发人员,您可能希望自定义渲染或在GLSL中添加自己的效果和功能。 使用着色器的任何人都会遇到相同的挑战:
着色器合成(Shader Composition )通过将着色器管道模块化来解决这些问题。 您可以在程序中的任何位置添加和删除功能,而无需复制,粘贴或修改其他人的GLSL代码。
接下来,我们将讨论osgEarth的着色器合成框架的结构。
组合着色器的框架自动提供main()函数。 您无需编写它们。 而且,你可以编写模块化函数,并告诉框架在哪里执行它们。
下面,你可以看到osgEarth创建的main()函数。
LOCATION_ *指示符允许您在着色器的执行管道中的各个点插入函数。
以下是osgEarth的内置着色器主函数的伪代码:
// 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; }
如您所见,我们已经做出了指定功能注入点的设计决定。最申请。这并不是说它们对所有事物都是完美的,而是说我们相信这种方法使框架易于使用,而不是太“低级”。
重要:着色器组合框架此时只支持顶点和片段着色器。它不支持几何或镶嵌着色器。我们计划在将来增加这一点。
osgEarth引入了一个新的osg状态属性,名为VirtualProgram
执行运行时着色器组合的。自VirtualProgram
是osg::StateAttribute
,您可以将一个附加到场景图中的任何节点。属于VirtualProgram
可以覆盖更高的场景图上的着色器。通过这种方式,您可以在osgEarth中添加、组合和重写单个着色器函数。
在运行时,VirtualProgram
将查看当前状态并组装一个完整的osg::Program
,它使用内置main()s,并调用通过VirtualProgram
.
从我们前面看到的生成的主管道中,osgEarth调用用户函数。这些不存在于osgEarth生成的默认着色器中;相反,它们代表了您作为开发人员可以“注入”到着色器管道中各个位置的代码。
例如,让我们使用用户函数创建一个简单的“模糊”效果:
// haze_vertex: out vec3 v_pos; void setup_haze(inout vec4 vertexView) { v_pos = vertexView.xyz; } // haze_fragment: 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++: 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()调用。
有六个插入点,如下:
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) |
每个顶点位置都允许您在一个特定的顶点上操作。坐标空间。你可以改变顶点,但你必把它放在同一个空间里。
模型: | 顶点是几何中未转换的原始值。 |
---|---|
意见: | 顶点相对于眼点,它位于原点(0,0,0),指向-Z轴。在视图空间中,原始顶点已被gl_ModelViewMatrix . |
剪辑: | 后投影剪辑空间。剪辑空间位于所有三个轴的[-w.w]范围内,是将原始顶点转换为gl_ModelViewProjectionMatrix . |
碎片位置如下。
着色: | 在应用照明之前,在解析片段颜色时调用这里的函数。纹理或颜色调整通常发生在这一阶段。 |
---|---|
照明: | 这里的功能影响到应用于片段颜色的照明。这就是太阳照明、碰撞映射或正常映射等通常发生的地方。 |
产出: | 这里是设置gl_FragColor的地方。默认情况下,内置片段main()将为您设置它。但是您可以设置一个输出着色器来替换您自己的这种行为。这样做的一个典型原因是实现MRT呈现(请参阅osgEarth_mrt示例)。 |
早些时候,我们向您展示了如何使用VirtualProgram
。Shader组合框架还提供了一个ShaderPackage
支持更高级的着色器管理方法。我们现在将讨论其中的一些问题。
正如我们所看到的,当您向管道中添加一个着色器函数时,可以使用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
,添加您的代码,并调用Load在VirtualProgram
:
ShaderPackage package;
package.add( shaderFileName, shaderSource );
package.load( virtualProgram, shaderFileName );
它采用“文件名”,因为着色器可以在外部文件中。但这不是一项要求。阅读更多细节。
这个vp_location
值遵循基于代码的值,如下所示:
这个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文件,或者将它们保持在内联状态--无论哪种方式,应用程序仍然可以工作。
这个ShaderPackage
如果包括文件。您的GLSL代码可以包括通过引用同一包中的任何其他着色器的文件名。使用自定义#pragma
若要包含其他文件,请执行以下操作:
#pragma include myCode.vertex.glsl
就像在C++中一样,包括将直接内联加载其他文件(或源代码)。因此,您所包含的文件必须是结构化的,就好像您已经将其正确地放置在包含文件中一样。)这意味着它不能拥有自己的#version
例如,字符串。)
再次:包容者而包括e必须以相同的方式注册ShaderPackage
.
尽管虚拟程序框架包含在osgEarth SDK中,但它实际上与地图呈现无关。在本节中,我们将介绍osgEarth对着色器组合所做的一些事情。
有一些内置的着色器uniforms
和variables
这是osgEarth地形引擎使用的,也是开发者可以使用的。
重要:以前缀`OO_`或`osgEarth_`开头的着色变量保留给osgEarth内部使用。
制服:
OE_TLAY_KEY: (向量4)元素0-2包含x、y和LOD的平铺键值;元素3保存瓷砖的边界球半径(以米为单位)。 OE_Layer_Tex: (Sampler2D)纹理应用于当前瓷砖的当前层 OE_Layer_texc: (向量4)当前瓷砖的纹理坐标 OE_Layer_Tilec: (向量4)当前瓷砖的单位坐标(x和y中的0..1) OE_Layer_UID: (Int)活动层的唯一ID OE层顺序: (Int)呈现活动层的顺序 OE层不透明度: (浮点)活动层的不透明度[0..1]
顶点属性:
OE地形攻击: (向量4)元素0-2持有地形顶点的单位高度矢量,元素3保存原始地形海拔值。 OE地形吸引2: 元素0保存父母瓷砖的标高值;元素1-3目前未使用.
[原][译]关于osgEarth::VirtualProgram
原文:https://www.cnblogs.com/lyggqm/p/12836601.html