明暗器组成¶
osgearth在其几种渲染模式中使用glsl明暗器。默认情况下,OSGearth将检测图形硬件的功能,并自动选择要使用的适当模式。
由于osgearth依赖于明暗器,因此作为开发人员,您可能希望自定义渲染或在glsl中添加自己的效果和功能。任何与shaders合作过的人都会遇到同样的挑战:
着色程序是单片的。添加新的明暗器代码需要您复制、修改和替换现有代码,这样就不会丢失其功能。
保持更改与原始代码的着色器的更改同步是维护方面的一个噩梦。
维护多个版本的shader main()s既麻烦又困难。
随着glsl代码库变得越来越复杂,并且添加了更多的功能,维护可怕的“uber shader”变得无法管理。
明暗器组成 通过以下方式解决这些问题 模块化 明暗器管道。您可以添加和删除 功能 在程序中的任何时候,都不会复制、粘贴或破解其他人的glsl代码。
接下来,我们将讨论osgearth的着色合成框架的结构。
框架基础¶
shader composition框架自动提供main()函数。你不需要写它们。相反,您编写模块化函数并告诉框架何时何地执行它们。
下面可以看到osgearth创建的main()函数。这个 LOCATION_*
指示符允许您在明暗器的执行管道中的不同点插入函数。
以下是osgearth内置shaders主电源的伪代码:
// 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;
}
正如您所看到的,我们已经做出了设计决策来指定有意义的函数注入点 most 应用。这并不是说它们对任何事情都是完美的,而是我们相信这种方法使框架易于使用,并且不太“低级”。
重要的 :此时的明暗器组合框架仅支持顶点和片段明暗器。它还不支持几何体或镶嵌着色。我们计划将来再增加这个。
VirtualProgram¶
osgearth引入了一个新的osg状态属性,名为 VirtualProgram
执行运行时明暗器合成。自从 VirtualProgram
是一个 osg::StateAttribute
,可以将一个附加到场景图中的任何节点。属于 VirtualProgram
可以覆盖场景图中较高位置的明暗器。通过这种方式,您可以在osgearth中添加、合并和覆盖各个着色程序函数。
在运行时,A VirtualProgram
将查看当前状态并将 osg::Program
它使用内置的main()并调用通过 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
函数在内置片段函数之后从core fragment shader main()调用。
共有六个注入点,如下所示:
位置 |
明暗器类型 |
签名 |
---|---|---|
shadercomp::location_vertex_model |
VERTEX |
void func(inout vec4顶点) |
shadercomp::位置_顶点_视图 |
VERTEX |
void func(inout vec4顶点) |
shadercomp::location_vertex_clip |
VERTEX |
void func(inout vec4顶点) |
shadercomp::location_fragment_着色 |
FRAGMENT |
void func(输入vec4颜色) |
shadercomp::location_fragment_lighting |
FRAGMENT |
void func(输入vec4颜色) |
shadercomp::location_fragment_输出 |
FRAGMENT |
void func(输入vec4颜色) |
每个顶点位置都允许您在特定的顶点上操作 坐标空间 . 你可以改变顶点,但是你 must 把它放在同一个地方。
- MODEL
顶点是几何体中未转换的原始值。
- VIEW
顶点相对于视点,视点位于原点(0,0,0),并指向-Z轴。在视图空间中,原始顶点已由
gl_ModelViewMatrix
.- CLIP
发布投影剪辑空间。剪辑空间位于 [-w..w] 沿所有三个轴的范围,是通过以下方式转换原始顶点的结果:
gl_ModelViewProjectionMatrix
.
碎片位置如下。
- COLORING
在应用照明之前解析片段颜色时,将调用此处的函数。纹理或颜色调整通常在这个阶段发生。
- LIGHTING
此处的函数会影响应用于片段颜色的照明。这就是太阳光、凹凸贴图或法线贴图通常会出现的地方。
- OUTPUT
这是设置gl_fragcolor的地方。默认情况下,内置fragment main()将为您设置它。但您可以设置输出明暗器以用自己的行为替换此行为。这样做的一个典型原因是实现MRT渲染(请参见osgearth-mrt示例)。
着色程序包¶
前面我们向您展示了如何使用 VirtualProgram
. shader composition框架还提供了 ShaderPackage
它支持更高级的着色管理方法。我们现在就谈谈这些。
虚拟程序元数据¶
如我们所见,当您使用 VirtualProgram
您需要告诉osgearth要调用的glsl函数的名称,以及调用它的管道中的位置,就像这样:
VirtualProgram* vp;
....
vp->setFunction( "color_it_red", shaderSource, ShaderComp::LOCATION_FRAGMENT_COLORING );
那就行了。但如果函数名或注入位置发生更改,则需要记住使glsl代码与 setFunction()
参数。
在同一位置指定这一切会更容易。一 ShaderPackage
你就这么做吧。下面是一个例子:
#version 330
#pragma vp_entryPoint color_it_red
#pragma vp_location fragment_coloring
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
值遵循基于代码的值,如下所示:
vertex_model
vertex_view
vertex_clip
fragment_coloring
fragment_lighting
fragment_output
外部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文件,或者将它们保持在内联状态——应用程序仍然可以以任何方式工作。
包含文件¶
这个 ShaderPackage
支持概念如果 包含文件 . 您的GLSL代码可以 包括 通过引用同一包中的任何其他明暗器的文件名。使用自定义 #pragma
要包含另一个文件:
#pragma include myCode.vertex.glsl
就像C++一样 包括 将直接内联加载另一个文件(或源代码)。因此,您所包含的文件的结构必须像您已经将其放置在include文件中一样。(这意味着它不能拥有自己的 #version
例如,字符串。)
再一次:那个 包括 以及 包括 必须用相同的 ShaderPackage
.