(译)LearnOpenGL实际案例Breakout(九):后期处理

英文原文
如果我们能够在Breakout游戏中加一点后期图形特效会更有趣吗?我们能够创建一些模糊晃动效果,反转场景中所有的颜色,实现一些疯狂的顶点偏移和/或做一些能想到的其他的和帧缓存有关的有趣的效果。
在帧缓存教程中我们演示了如何通过后期效果仅仅使用一个简单的贴图来实现有趣的效果。在Breakout中我们准备做一些类似的事情:我们准备从多个样本渲染缓存对象中创建一个帧缓存对象来作为他的颜色附件。所有游戏的渲染代码需要渲染到这个多样本帧缓存然后块传送他的内容到一个不同的有一个贴图附件作为他的颜色缓存的帧缓存。这个贴图内容包含渲染了这个游戏的抗锯齿贴图,我们会将它渲染到一个大应用了0种或者更多后期效果的2D四边形。
所以来总结这些渲染步骤:

  1. 绑定多样本帧缓存
  2. 像普通游戏一样渲染游戏
  3. 块传送多重帧缓存到普通帧缓存
  4. 解绑帧缓存(使用默认帧缓存)
  5. 再后期shader中普通帧缓存使用颜色缓存
  6. 将后期shader输出渲染屏幕尺寸的四边形

后期处理shader允许三类效果:晃动,混淆和混乱。

  • 晃动:带有小模糊的轻微场景晃动
  • 颠倒:不仅仅是反转场景的颜色,还有x和y轴
  • 混乱:用于边缘检测核心来创建有趣的图像并且移动贴图到一个循环样式来产生一个有趣的混乱特效

以上效果看起来如下所示:
这里写图片描述
在一个2D四边形上操作,顶点shader看起来是这样:

#version 330 core
layout(location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
out vec2 TexCoords;
uniform bool  chaos;
uniform bool  confuse;
uniform bool  shake;
uniform float time;
void main()
{
    gl_Position = vec4(vertex.xy, 0.0f, 1.0f);
    vec2 texture = vertex.zw;
    if (chaos)
    {
        float strength = 0.3;
        vec2 pos = vec2(texture.x + sin(time) * strength, texture.y + cos(time) * strength);
        TexCoords = pos;
    }
    else if (confuse)
    {
        TexCoords = vec2(1.0 - texture.x, 1.0 - texture.y);
    }
    else
    {
        TexCoords = texture;
    }
    if (shake)
    {
        float strength = 0.01;
        gl_Position.x += cos(time * 10) * strength;
        gl_Position.y += cos(time * 15) * strength;
    }
}

基于任何一个统一值被设置为true,顶点shader能够有不同的路径。如果混乱或混淆给设置为true则顶点shader将会操作顶点坐标来移动场景环绕(平移贴图坐标到一个循环滚动或是反转贴图坐标)。由于我们设置贴图转换方法为GL_REPEAT,混乱效果将会引起场景在四边形图形部分反射他自己。此外如果形状被设置为true,他将仅仅通过一个小的移动顶点位置环绕。注意,当shake能够和其他效果一起工作时混乱和混淆不必同时为真。
除了顶点位移或者贴图坐标,任何效果被激活时我们将会创建一个强烈的视觉冲击。我们能够在片段shader中完成它:

#version 330 core
in  vec2  TexCoords;
out vec4  color;
uniform sampler2D scene;
uniform vec2      offsets[9];
uniform int       edge_kernel[9];
uniform float     blur_kernel[9];
uniform bool chaos;
uniform bool confuse;
uniform bool shake;
void main()
{
    color = vec4(0.0f);
    vec3 sample[9];
    // sample from texture offsets if using convolution matrix
    if (chaos || shake)
        for (int i = 0; i < 9; i++)
            sample[i] = vec3(texture(scene, TexCoords.st + offsets[i]));
    // process effects
    if (chaos)
    {
        for (int i = 0; i < 9; i++)
            color += vec4(sample[i] * edge_kernel[i], 0.0f);
        color.a = 1.0f;
    }
    else if (confuse)
    {
        color = vec4(1.0 - texture(scene, TexCoords).rgb, 1.0);
    }
    else if (shake)
    {
        for (int i = 0; i < 9; i++)
            color += vec4(sample[i] * blur_kernel[i], 0.0f);
        color.a = 1.0f;
    }
    else
    {
        color = texture(scene, TexCoords);
    }
}

这个长长的shader几乎直接从帧缓存教程的片段shader建立起来并且基于激活的特效类型处理后期特效。然而这次,偏移矩阵和卷积核心被定义为让我们从应用程序设置uniform。有点事我们仅仅需要设置一次,取代在片段shader运行时每帧计算这些矩阵。例如,偏移矩阵被如下配置:

GLfloat offset = 1.0f / 300.0f;
GLfloat offsets[9][2] = {
    { -offset,  offset },  // top-left
    { 0.0f,    offset },  // top-center
    { offset,  offset },  // top-right
    { -offset,  0.0f },  // center-left
    { 0.0f,    0.0f },  // center-center
    { offset,  0.0f },  // center - right
    { -offset, -offset },  // bottom-left
    { 0.0f,   -offset },  // bottom-center
    { offset, -offset }   // bottom-right    
};

glUniform2fv(glGetUniformLocation(shader.ID, “offsets”), 9, (GLfloat*)offsets);
由于所有管理(多样本)帧缓存的概念都已经在之前的教程中被广泛的讨论了,我这次不再深究细节。下面你将发现PostProcessor的类,它被用来管理初始化,读/写片段和渲染一个四边形。你应该能够完全的理解这些代码如果你理解帧缓存和抗锯齿教程。
PostProcessor: header, code.
有趣的是BeginRender和EndRender方法。由于我们能够在将整个游戏场景渲染到帧缓存,我们分别在场景渲染代码的开始和结束时调用BeginRender和EndRender函数。这个类然后进行幕后的帧缓存运行。例如,使用PostProcessor类然后游戏的渲染类看起来像这样:

PostProcessor   *Effects;
void Game::Render()
{
    if (this->State == GAME_ACTIVE)
    {
        Effects->BeginRender();
        // Draw background
        // Draw level
        // Draw player
        // Draw particles   
        // Draw ball
        Effects->EndRender();
        Effects->Render(glfwGetTime());
    }
}

现在无论我们想要在哪里我们都能随意的设置后期处理类的必要的效果特性为true然后他的效果将立即可见。

转载请注明出处:http://blog.csdn.net/ylbs110/article/details/52975810

晃动它

作为这些效果一个(实用的)示例,我们将会模拟球撞击一个固体块。无论在哪里发生了碰撞,通过启用晃动效果一小段时间,它看起来就像发生了更强的碰撞。
我们想要仅仅启用晃动效果一小段时间。我们能够通过在激活的时候调用控制晃动时间的ShakeTime方法创建的参数来运行。无论一个固体在哪里碰撞,我们会重置参数为一个特定的细节:

GLfloat ShakeTime = 0.0f;
void Game::DoCollisions()
{
    for (GameObject &box : this->Levels[this->Level].Bricks)
    {
        if (!box.Destroyed)
        {
            Collision collision = CheckCollision(*Ball, box);
            if (std::get<0>(collision)) // If collision is true
            {
                // Destroy block if not solid
                if (!box.IsSolid)
                    box.Destroyed = GL_TRUE;
                else
                {   // if block is solid, enable shake effect
                    ShakeTime = 0.05f;
                    Effects->Shake = true;
                }
                [...]
            }
        }
    }
    [...]
}

然后在游戏的Update方法中我们减少ShakeTime参数,减到0之后我们禁用晃动特效:

void Game::Update(GLfloat dt)
{
    [...]
    if (ShakeTime > 0.0f)
    {
        ShakeTime -= dt;
        if (ShakeTime <= 0.0f)
            Effects->Shake = false;
    }
}

任何时候我们撞击和一个固体砖块屏幕都会开始晃动并且模糊,在小球撞击一个固体对象时给予玩家一些可视的反馈。
http://learnopengl.com/video/in-practice/breakout/postprocessing_shake.mp4
你能在这里找到游戏类的源码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值