[原][译]关于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的运行时着色器合成器。因为VirtualProgramosg::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有六个可插入点,如下:

LocationShader TypeSignature
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

地形变量

有一些内置的着色器uniformsvariables这是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

共享图像层

如果你希望一次访问多个图像层。比如:你可能需要一个掩蔽层,以显示土地和水。你可能真的不想绘制这一层,但您想要使用它来调整其他可见层。你可以用 共享图像层。在Map,将图像层标记为共享 (using ImageLayerOptions::shared(),渲染器会将其提供给所有其他层进行二采样。

详情参阅  osgearth_sharedlayer.cpp 的用法示例!

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////以上是官方文档原内容的翻译,下面介绍下目前的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
        };
原文地址:https://www.cnblogs.com/lyggqm/p/12836601.html