13、Ogre 2D与合成技术详解

Ogre 2D与合成技术详解

1. 2D在3D API中的意义

在3D API编程指南中讨论二维图形看似有些奇怪,但实际上很多事情在2D中处理会更好或更简单。例如,图形用户界面(GUI)若以二维屏幕呈现,应用程序处理起来更简单,用户理解也更直观。游戏或训练模拟中的应用反馈机制(如平视显示器,即HUD)通常以二维“覆盖层”的形式呈现到3D场景中效果最佳。虽然可以设计全3D用户界面,但即使如此,3D应用中仍有二维框架的用武之地。

2. Ogre中的2D

2.1 覆盖层渲染队列

Ogre中的2D通常存在于“覆盖层”渲染队列中。该队列并无特别之处,只是在其中渲染的所有内容会自动在其他内容之后渲染(即“位于”3D场景之上)。也可以在覆盖层队列中渲染3D对象,比如游戏HUD中目标对象的旋转渲染。

2.2 覆盖层

覆盖层是一个笼统的术语,意思就是它字面所表达的:覆盖层“覆盖”场景中的其他所有内容,如图中左下角的统计窗口和右下角的Ogre标志。统计窗口绘制时有一定透明度,仔细观察可以看到Ogre头部透过统计窗口背景显示出来。

2.3 覆盖层对象类型

Ogre中的覆盖层主要分为两类:
- 受Ogre框架支持的(托管) :可以完全用脚本编写,常用于简单的文本输出或显示。
- 不受支持的(非托管) :涵盖应用程序可能具有的任何类型的GUI或通用UI功能(如游戏中的HUD、UI菜单等)。实际上,非托管覆盖层元素只是在覆盖层渲染队列中渲染的带纹理和着色的四边形,如何绘制、着色和添加纹理完全由开发者决定。

需要注意的是,Ogre本身不提供GUI输入管理或事件生成功能,覆盖层在Ogre中完全是非交互式的,除非开发者提供相应功能使其具有交互性。不过,使用CEGUI UI库来实现交互功能可能是更好的选择,它是一个功能齐全的GUI库,对Ogre覆盖层渲染队列有原生支持。

3. Ogre覆盖层框架

3.1 主要管理对象

多个Ogre API对象协同工作以实现托管覆盖层,整个覆盖层由OverlayManager管理,它是一种可脚本化的标准Ogre资源管理器,类似于合成器、字体、材质和粒子系统资源的管理器。

3.2 覆盖层的主要对象

覆盖层中的主要对象是容器(OverlayContainer)和元素(OverlayElement),其中OverlayElement是PanelOverlayElement、BorderPanelOverlayElement和TextAreaOverlayElement等特定功能类的基类。如果要渲染文本,还需要字体,字体由Font和FontManager类处理,与覆盖层框架是分开的。

3.3 覆盖层布局示例

以下是一个创建覆盖层布局的脚本示例:

TestOverlay
{
    zorder 500
    container Panel(Container1)
    {
        metrics_mode pixels
        left 0
        top 0
        width 40
        height 30
        container Panel(Container2)
        {
            metrics_mode pixels
            left 2
            top 5
            width 37
            height 10
            element TextArea(TextAreaOverlayElement1)
            {
                metrics_mode pixels
                left 15
                top 3
                width 10
                height 7
                font_name StarWars
                char_height 0.05
                colour 1.0 1.0 1.0
                caption TextAreaOverlayElement 1
            }
        }
        container Panel(Container3)
        {
            metrics_mode pixels
            left 2
            top 15
            width 22
            height 14
            element TextArea(TextAreaOverlayElement2)
            {
                metrics_mode relative
                left 0.2
                top 0.2
                width 0.6
                height 0.4
                font_name StarWars
                char_height 0.05
                colour_top 1.0 1.0 1.0
                colour_bottom 0.5 0.5 1.0
                caption TextAreaOverlayElement 2
            }
        }
        container Panel(Container4)
        {
            metrics_mode pixels
            left 25
            top 15
            width 10
            height 14
        }
    }
}

假设图中根容器位于屏幕的(0,0)位置,TextAreaOverlayElement1将位于屏幕的(17,8)位置,因为其父容器位于(2,5),而文本元素相对于其父容器位于(10,3)。

3.4 元素定位指标

元素定位可以用像素或归一化屏幕坐标(屏幕左上角为(0.0,0.0),右下角为(1.0,1.0))来完成。在特定元素或容器中,定位和大小必须使用相同的指标,但元素的指标不受其父容器指标的限制。使用相对指标可以使覆盖层与分辨率无关,但如果在覆盖层容器的材质中使用图像,分辨率改变时这些图像可能会被拉伸。

3.5 材质使用

覆盖层元素只是四边形,因此可以像应用程序中的其他几何体一样使用材质。唯一的限制是,在覆盖层元素上使用材质时,光照和深度检查会自动关闭,所以不要在覆盖层元素上使用与场景中普通3D对象相同的材质脚本。不同的覆盖层元素应用材质的方式不同,例如Panel元素将材质视为背景定义,而BorderPanel将材质仅视为中心区域的定义,边框区域的材质通过border_material参数定义。

3.6 元素对齐

容器内元素的对齐可以在两个方向上进行管理:
- 垂直对齐 :使用vert_align指令,可以设置为top、center或bottom。
- 水平对齐 :使用horiz_align指令,可以设置为left、middle或right。默认值为top和left。

一个有效的对齐技巧是在使用像素指标时,对top和/或left位置值使用负值,以将元素固定在屏幕边缘。要实现这一点,需要使用horiz_align right和vert_align bottom对齐操作。

3.7 Z - 顺序

覆盖层中的每个项目都有一个与之关联的“深度”,类似于3D场景中的对象。在二维中,这个深度称为z - 顺序,含义相同:深度较大(z - 顺序较小)的项目将首先绘制,可能会被深度较小(z - 顺序较大)的项目部分或完全遮挡。因此,这个深度决定了覆盖层队列中项目的绘制顺序。2D项目的深度与3D场景中对象的深度在一定程度上是独立的,覆盖层渲染队列中渲染的所有对象都会绘制在屏幕上其他内容的顶部。可以将覆盖层与场景的其他部分进行混合(如示例中的统计覆盖层使用的alpha混合),但只能对整个场景进行混合,而不是场景中的单个对象。如果要创建将3D场景的部分与覆盖层元素相结合的复杂可视化效果,需要将所需的3D对象渲染到纹理上,并将该纹理应用到目标覆盖层元素上。

3.8 字体

在Ogre中,字体本身不是可渲染对象,主要用于为TextAreaOverlayElement对象添加纹理。创建字体有两种方式:
- 使用现有的TrueType字体 :Ogre会在加载时为你生成字形,但会有加载时间的开销。
- 使用自己创建的字体纹理 :需要一些前期时间来创建,但可以使用任意颜色。

需要注意的是,像微软Windows附带的TrueType字体通常不可自由再分发,某些商业字体的商业使用许可费用可能高达数百或数千美元。应用程序可以使用用户系统上安装的字体,但未经付费,大多数TrueType字体不能随应用程序一起分发。例外情况是免费或开源字体,开发者有责任确保随应用程序分发的任何字体不受许可要求或有效专利的限制。

字体纹理是针对特定的屏幕分辨率(每英寸点数)和特定的字体大小(磅)生成的,这些定义可以在以.fontdef结尾的文件中进行脚本编写,例如:

StarWars
{
    type              truetype
    source            solo5.ttf
    size              16
    resolution        96
}

要加载并使用这个字体,可以使用以下代码:

FontPtr font = FontManager::getSingleton().getByName("StarWars");
font->load();

对于图像类型的字体定义,必须在.fontdef文件中为每个字形提供UV映射;对于TrueType类型的字体定义,可以使用antialias_colour参数来抗锯齿颜色(默认值为false)。

3.9 运行时覆盖层示例

当可以通过代码更新覆盖层时,覆盖层最为有用。以下代码将更新上述覆盖层中TextAreaOverlayElement1的标题:

void setOverlayText(const String& newText) {
    OverlayManager *pOverlayManager = OverlayManager::getSingletonPtr();
    OverlayElement* pText = static_cast<OverlayElement*>(
        pOverlayManager->getOverlayElement("TextAreaOverlayElement1"));
    pText->setCaption(newText);
}

这种模式适用于任何覆盖层元素或容器,即获取对象的指针,然后更新其内容。脚本中可用的相同属性也可以通过类似命名的API方法使用。

4. 合成器框架

4.1 合成器概述

Ogre合成器框架是一组API和脚本命令,支持在渲染目标级别进行多遍渲染,以帮助创建诸如运动模糊、热 haze、“红外视觉”等视觉效果,或任何可以想象到的逐片段效果或后期处理任务。其核心是一种灵活管理图形渲染管线中一个或多个通道的方法,这些通道在场景的全屏渲染上执行。

4.2 合成器示例

以HDR合成器为例,以下是实现HDR效果的合成器脚本:

compositor HDR
{
    // floating point only for now
    technique
    {
        // Temporary textures
        // Fullsize HDR render target, used as tone mapping source
        texture rt_full target_width target_height PF_FLOAT16_RGB
        // Targets used for luminance evaluation (3x3 downsample, point filtering)
        texture rt_lum0 1 1 PF_FLOAT16_RGB
        texture rt_lum1 4 4 PF_FLOAT16_RGB
        texture rt_lum2 16 16 PF_FLOAT16_RGB
        texture rt_lum3 64 64 PF_FLOAT16_RGB
        texture rt_lum4 128 128 PF_FLOAT16_RGB
        // Bright-pass filtered target (tone mapped)
        texture rt_brightpass 128 128 PF_R8G8B8
        // Bloom filter targets
        texture rt_bloom0 128 128 PF_R8G8B8
        texture rt_bloom1 128 128 PF_R8G8B8
        target rt_full
        {
            // No input, render differently
            input previous
            // Use float target HDR material scheme (unclamped shaders)
            // HDR scheme renders to FP textures, getting around [0..1]
            // limit on oer-channel texture values
            material_scheme HDR
            pass render_scene
            {
            }
        }
        // Downsample the original HDR scene to extract luminance value
        target rt_lum4
        {
            input none
            pass render_quad
            {
                // Downsample using a 2x2 filter and convert to grayscale
                material Ogre/Compositor/HDR/Downsample2x2Luminance
                input 0 rt_full
                identifier 994
            }
        }
        target rt_lum3
        {
            input none
            pass render_quad
            {
                // Downsample using a 3x3 filter
                material Ogre/Compositor/HDR/Downsample3x3
                input 0 rt_lum4
                identifier 993
            }
        }
        target rt_lum2
        {
            input none
            pass render_quad
            {
                // Downsample using a 3x3 filter
                material Ogre/Compositor/HDR/Downsample3x3
                input 0 rt_lum3
                identifier 992
            }
        }
        target rt_lum1
        {
            input none
            pass render_quad
            {
                // Downsample using a 3x3 filter
                material Ogre/Compositor/HDR/Downsample3x3
                input 0 rt_lum2
                identifier 991
            }
        }
        target rt_lum0
        {
            input none
            pass render_quad
            {
                // Downsample using a 3x3 filter
                material Ogre/Compositor/HDR/Downsample3x3
                input 0 rt_lum1
                identifier 990
            }
        }
        target rt_brightpass
        {
            input none
            pass render_quad
            {
                // Downsample using a 3x3 filter, hi-pass and tone map
                material Ogre/Compositor/HDR/Downsample3x3Brightpass
                input 0 rt_full
                input 1 rt_lum0
                identifier 800
            }
        }
        target rt_bloom1
        {
            input none
            pass render_quad
            {
                // Blur horizontally
                material Ogre/Compositor/HDR/GaussianBloom1
                input 0 rt_brightpass
                identifier 701
            }
        }
        target rt_bloom0
        {
            input none
            pass render_quad
            {
                // Blur horizontally
                material Ogre/Compositor/HDR/GaussianBloom0
                input 0 rt_bloom1
                identifier 700
            }
        }
        // Final output combines tone mapping of the original scene, with an
        // exposure setting passed in as a GPU parameter, and an additive bloom
        // effect
        target_output
        {
            input none
            pass render_quad
            {
                material Ogre/Compositor/HDR/ToneMapping
                input 0 rt_full
                input 1 rt_bloom0
                input 2 rt_lum0
            }
        }
    }
}

这个脚本首先定义了九个单独的渲染纹理目标,用于执行HDR渲染演示的渲染通道。每个目标(除第一个外)都忽略渲染目标中的现有内容,第一个通道接收主帧缓冲区的现有内容,该缓冲区包含未处理渲染场景的输出。每个目标运行一个单独的通道,将一个或多个渲染纹理作为源数据进行操作。

4.3 合成器通道参数

合成器目标可以使用多个通道,每个输入参数指定一个纹理单元索引和一个命名的输入纹理。有效的通道参数包括:
- render_quad :渲染到整个渲染目标。
- render_scene :对目标进行正常的场景渲染。
- stencil :执行模板缓冲区设置通道(包括配置模板操作)。
- clear :将目标中的一个或多个缓冲区设置为固定值。

4.4 合成器链

合成器链是指将一个合成器脚本的输出作为另一个在同一视口上启用的合成器的输入。例如,以下代码展示了如何组装一个两部分的合成器链:

// assume vp points to the current Viewport
CompositorManager* cMgr = CompositorManager::getSingletonPtr();
// add Compositor from example script -- will be put first in chain
cMgr->addCompositor(vp, "Bloom", -1);
// enable the Compositor
cMgr->setCompositorEnabled(vp, "Bloom", true);
// do same for Motion Blur Compositor
CompositorInstance* ci = cMgr->addCompositor(vp, "Motion Blur", -1);
cMgr->setCompositorEnabled(vp, "Motion Blur", true);
// make Motion Blur take Bloom's output for target pass 0
ci->getTechnique()->getTargetPass(0)
    ->setInputMode(CompositionTargetPass::IM_PREVIOUS);

可以通过管理addCompositor()的第三个参数来指定合成器在视口中的处理顺序,值为 -1 表示将其放置在列表末尾,也可以提供0到size - 1之间的任何位置,其中size是视口合成器列表的当前长度。

5. 合成器使用注意事项

5.1 合成器通道中渲染场景

在合成器通道中,可以将整个场景再次渲染到渲染目标中。最常见的原因是通过指定起始和结束队列的队列ID来限制在此次场景渲染中调用哪些渲染队列。例如:

pass render_scene {
    first_render_queue 50
    last_render_queue 50
}

这将只渲染“正常”对象队列,忽略天空、背景、世界几何体和覆盖层。默认情况下,第一个和最后一个渲染队列分别为5和95,这将渲染所有内容。同样,也可以设置visibility_mask参数,指示合成器目标仅渲染标志与该参数提供的掩码匹配的对象。

5.2 合成器回调

可以让Ogre在准备为合成器通道设置材质以及即将开始渲染通道时通知应用程序。CompositorInstance::Listener接口提供了notifyMaterialSetup()和notifyMaterialRender()两个方法,回调对象必须实现这两个方法,它们都接受在脚本中为通道指定的ID和相关材质的名称作为参数,这允许开发者在合成器操作期间精细管理材质属性。

5.3 合成器材质和技术

合成器使用的材质脚本遵循与正常场景渲染时使用的材质相同的规则。在pass render_scene块中,可以为每个使用材质的合成器目标指定不同的材质方案等。合成器脚本使用技术,其含义和机制与常规Ogre材质中的技术相同,即合成器技术是实现后期处理效果的“替代方法”,合成器脚本的多个技术会按照它们在脚本中出现的顺序进行优先级评估。合成器技术也可以像材质技术一样使用“回退”机制,在选择“最佳”技术时,首先要确保通道指定的材质至少有一个有效技术,然后检查技术中请求的纹理格式,如果使用了图形硬件不支持的像素格式,合成器框架会先搜索定义的技术中的回退方案,若未找到,才会让硬件将不支持的格式降级为支持的格式。

总之,二维图形和后期处理不仅有趣,还能为应用程序的视觉质量增添独特的魅力,是3D技术无法单独实现的。掌握这些知识后,开发者可以在应用中充分发挥Ogre 3D的强大功能。

6. 合成器技术选择流程

合成器在选择合适的技术时,会遵循一定的流程,以下是使用 mermaid 绘制的流程图,展示了这个过程:

graph TD;
    A[开始] --> B[检查材质是否有有效技术];
    B -- 是 --> C[检查技术中纹理格式];
    B -- 否 --> E[丢弃合成器脚本,抛出异常];
    C -- 所有格式支持 --> F[使用该技术];
    C -- 存在不支持格式 --> D[搜索定义的技术中的回退方案];
    D -- 找到回退方案 --> F[使用回退技术];
    D -- 未找到回退方案 --> G[让硬件降级像素格式请求];
    G -- 有可用组合 --> F[使用降级后可用的技术];
    G -- 无可用组合 --> E[丢弃合成器脚本,抛出异常];

这个流程图清晰地展示了合成器在选择技术时的决策过程。首先会检查材质是否有有效技术,如果没有则直接丢弃合成器脚本。若有有效技术,会进一步检查技术中请求的纹理格式,若所有格式都被硬件支持,则直接使用该技术;若存在不支持的格式,会先搜索回退方案,若找到则使用回退技术,若未找到则让硬件降级像素格式请求,最后根据是否有可用组合来决定是使用降级后可用的技术还是丢弃合成器脚本。

7. 合成器使用总结表格

为了更清晰地总结合成器的使用要点,以下是一个表格:
| 使用方面 | 详细说明 |
| — | — |
| 渲染场景 | 可在合成器通道中再次渲染场景,通过指定队列 ID 限制渲染队列,默认队列范围是 5 - 95,也可设置 visibility_mask 参数筛选对象。 |
| 回调机制 | 利用 CompositorInstance::Listener 接口的 notifyMaterialSetup() 和 notifyMaterialRender() 方法,在设置材质和开始渲染通道时接收通知,精细管理材质属性。 |
| 材质和技术 | 材质脚本遵循正常场景渲染规则,可在 pass render_scene 块中指定不同材质方案。合成器技术是实现后期处理效果的替代方法,支持回退机制,按脚本顺序评估优先级。 |
| 技术选择 | 先确保材质有有效技术,再检查纹理格式,优先搜索回退方案,必要时让硬件降级格式。 |

8. 覆盖层与合成器的综合应用示例

在实际应用中,覆盖层和合成器可以结合使用,以实现更丰富的视觉效果。例如,在一个游戏场景中,我们可以使用覆盖层显示游戏的 HUD 信息,同时使用合成器添加一些特效,如运动模糊或 HDR 效果。

以下是一个简单的伪代码示例,展示了如何在代码中同时使用覆盖层和合成器:

// 初始化覆盖层
OverlayManager *overlayManager = OverlayManager::getSingletonPtr();
Overlay *hudOverlay = overlayManager->create("GameHUD");
// 添加覆盖层元素,如文本区域显示分数等
OverlayElement *scoreText = overlayManager->createOverlayElement("TextArea", "ScoreText");
scoreText->setCaption("Score: 0");
hudOverlay->add2D(scoreText);
hudOverlay->show();

// 初始化合成器
CompositorManager *compositorManager = CompositorManager::getSingletonPtr();
Viewport *viewport = // 获取当前视口
compositorManager->addCompositor(viewport, "MotionBlur", -1);
compositorManager->setCompositorEnabled(viewport, "MotionBlur", true);

// 在游戏循环中更新覆盖层信息
while (gameRunning) {
    // 更新分数
    int newScore = // 获取新分数
    scoreText->setCaption("Score: " + std::to_string(newScore));

    // 其他游戏逻辑
    // ...
}

在这个示例中,我们首先创建了一个覆盖层来显示游戏的 HUD 信息,然后添加了一个合成器来实现运动模糊效果。在游戏循环中,我们不断更新覆盖层上的分数信息,同时进行其他游戏逻辑的处理。

9. 总结

通过对 Ogre 2D 与合成技术的详细介绍,我们了解到在 3D 编程中,2D 图形和合成技术有着重要的作用。覆盖层可以用于创建直观的用户界面和显示信息,而合成器则可以实现各种复杂的视觉效果,如运动模糊、HDR 等。

在使用覆盖层时,需要注意元素的定位、材质的使用、对齐方式以及字体的选择和管理。而在使用合成器时,要了解其多遍渲染的原理、通道参数的设置、合成器链的组装以及技术选择的规则。

掌握这些技术后,开发者可以为应用程序添加独特的视觉效果,提升用户体验。无论是游戏开发、模拟训练还是其他图形应用,Ogre 2D 与合成技术都能为开发者提供强大的工具,帮助他们实现创意和想法。希望通过本文的介绍,读者能够更好地理解和应用这些技术,在图形编程领域取得更好的成果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值