BreakOut_OpenGL下
BreakOut_OpenGL下
粒子
在游戏中创造一些有趣的效果比如火焰,青烟,烟雾,魔法效果,炮火残渣等
粒子特效由众多粒子组成,每个粒子其实就是总是面向摄像机方向,且大部分区域是透明的纹理的小四边形(sprite),
粒子结构体有生命值,位置,速度,颜色属性,
粒子有额外的shader,使用vec2 offest位置控制粒子运动,它还包含uniform color颜色,让我们可以方便更改粒子颜色,粒子很小,我们会在.vs中直接*缩放10倍
通常会创建粒子生成器管理,它会不断生成新的粒子,旧的粒子会逐渐消亡,并且粒子的颜色可能会随着距离生成点越远颜色越暗(想象火焰)
- init初始化顶点数据,初始化粒子数组
- Update添加新的粒子,更新有效粒子的生命周期(减少),位置和颜色(a分量减少)
- Draw循环每个粒子,调用drawcall
- respawnParticle重置粒子属性,颜色位置随机,速度为父物体速度的0.1(如果速度一致就不会有拖尾效果)
- firstUnusedParticle首个无效粒子的索引(以便用新生成粒子更新它),一般情况即将无效的粒子总在上个无效粒子的右边,如果没有找到,在左边进行线性查找,再否则返回第一个粒子索引
这里的粒子life要设置短一些,否则会出现粒子不会断空情况,因为当vector满了,生命周期长,找不到无效的粒子,就只会更新0索引粒子(仅有一个粒子重置到玩家身上,其余仍以*0.1的速度移动),因此出现断空
这样就可以正常跟随了
顶点数据 vs. uniform
顶点数据通常存放顶点特有(每个顶点都不同)属性(位置,纹理坐标,法线等)
uniform通常存放物体公有(每个顶点都相同)属性(MVP矩阵,光源位置,光源颜色等)
如果将公有属性放到顶点数据,会效率降低(每个顶点都会运行一次着色器)
如果将特有属性放到uniform,很难维护庞大的顶点数据
渲染位置
顶点数据
model矩阵
glm::lookat创建观察矩阵,模拟摄像机位置和旋转,根据传入摄像机位置,目标位置,上向量
glm::ortho(左,右,底,顶,近,远)创建投影矩阵,截取世界坐标系,设置视景体裁剪范围,超出范围的不渲染,最终转换为标准化设备坐标(-1 – 1)
- 这里如果比如bottom大于top,y轴取反,图像延y方向颠倒绘制
framebuffer大小,首次会映射到fb中,再由fb映射到视口
glviewport视口变换,用于关联窗口和裁剪区域,定义了最终渲染结果在窗口中的显示区域(会将裁剪范围转换为视口大小,呈现在窗口,如果视口小于窗口,剩余窗口未使用)
glfwCreateWindow显示在显示器的窗口大小
postprocessing
打算制作三种特效:shake, confuse和chaos
- shake:轻微晃动场景并附加一个微小的模糊效果。
- confuse:反转场景中的颜色并颠倒x轴和y轴。
- chaos: 利用边缘检测卷积核创造有趣的视觉效果,并以圆形旋转动画的形式移动纹理图片,实现“混沌”特效。
这些后处理都需要framebuffer,步骤为:
- 绑定MSFBO(自定义多重采样的帧缓冲(离线渲染)),和往常一样渲染游戏,RBO附件作为(渲染缓冲附件(只读))渲染目标
- 将MSFBO内容Blit位块传输至FBO(自定义帧缓冲中(颜色缓冲附件))
- 解除绑定(绑定回默认的帧缓冲,它负责渲染到屏幕)
- 在post_Processor_shader中使用FBO的颜色缓冲纹理
- 渲染屏幕大小的四边形作为post_Processor_shader的输出
卷积
卷积是一个强有力的数学工具,在CG/CV方面有非常不错的运用,能产生很多效果和输出
数学定义:
\(F(n) = (f * g)(n) = \int_{-\infty}^{\infty} f(\gamma)g(n - \gamma)dr\) 离散定义: \(F(n) = (f * g)(n) = \sum_{\gamma = 0}^{N} f(\gamma)g(n - \gamma)\) 其中: \(n = \gamma + (n - \gamma)\)
f (τ) 表示被积函数,而 g (n − τ) 表示卷积核函数。
f(x) 与 f ( τ )之间还存在着如下关系: \(f(x) = \int_{-\infty}^{\infty} f(\gamma)dr\)
可以形象理解:
post_Processor_shader
shader:接受分支,决定纹理应用哪种后期处理效果
.vs:接受屏幕大小的quad的顶点位置(通常)和自定义帧缓冲的颜色缓冲纹理
.fs:改变颜色输出
抖动:根据时间(时间越长,移动越小)和强度值(强度越大,移动越大)影响quad渲染位置
1
2
3
float strength = 0.01;
gl_Position.x += cos(time * 10) * strength;
gl_Position.y += cos(time * 15) * strength;
展开
颠倒贴图:xy纹理坐标取反
1
TexCoords = vec2(1.0 - texture.x, 1.0 - texture.y);
展开
圆形旋转动画
1
2
3
float strength = 0.3;
vec2 pos = vec2(texture.x + sin(time) * strength, texture.y + cos(time) * strength);
TexCoords = pos;
展开
根据offests(被积偏移量)采样颜色
1
2
for(int i = 0; i < 9; i++)
sample[i] = vec3(texture(scene, TexCoords.st + offsets[i]));
展开
反转颜色
1
color = vec4(1.0 - texture(scene, TexCoords).rgb, 1.0);
展开
边缘检测卷积核edge_kernel
1
2
3
for(int i = 0; i < 9; i++)
color += vec4(sample[i] * edge_kernel[i], 0.0f);
color.a = 1.0f;
展开
模糊卷积核blur_kernel
1
2
3
for(int i = 0; i < 9; i++)
color += vec4(sample[i] * blur_kernel[i], 0.0f);
color.a = 1.0f;
展开
PostProcessor
后期处理管理器
- 构造函数:初始化,两个帧缓冲,quad顶点数据,卷积核以及偏移量
- BeginRender绑定MSFBO(使用额外的帧缓冲,多重采样目的获得更好的效果,通过glRenderbufferStorageMultisample())
- EndRender位块传输到FBO,解除绑定
- Render绘制(bind + drawcall)
修改Game类
在渲染中执行正确的bind
我们只想允许晃动效果持续一小段时间,在碰撞触发时更改uniform变量为true
设计开发一些Gameplay/优化模块
关卡循环:4个关卡循环
窗口大小调整:需要回调帧缓冲,视口两个
砖块根据颜色区分生命值(1,2,3,4),只有当生命值为0才会销毁,否则反弹,并将a分量降低为 当前生命 / 总生命
道具
本节加入道具(Gameplay)模块,任何一个砖块在被销毁时都有一定几率产生一个道具块,这样的道具快会缓慢降落,而且当它与玩家挡板发生接触时,产生对应效果,否则如果超出屏幕销毁
上一节制作了3种特效,通过简单修改unifrom变量就可以启用,我们设置后两种通过道具触发
会有以下6种道具(4种增益道具与2种负面道具):
- Speed: 增加小球20%的速度
- Sticky: 当小球与玩家挡板接触时,小球会保持粘在挡板上的状态直到再次按下空格键,这可以让玩家在释放小球前找到更合适的位置
- Pass-Through: 非实心砖块的碰撞处理被禁用,使小球可以穿过并摧毁多个砖块
- Pad-Size-Increase: 增加玩家挡板50像素的宽度
- Confuse: 短时间内激活confuse后期特效,迷惑玩家
- Chaos: 短时间内激活chaos后期特效,使玩家迷失方向
Powerups
Powerups道具类继承游戏对象,每个道具以字符串的形式定义它的类型 它有持续时间(0表示无限),类型,是否激活的额外属性,定义vector存储上述6种道具,以及一些函数:
- ShouldSpawn给定int,通过随机值,返回bool(每种道具有1/int概率为真)
- SpawnPowerUps根据ShouldSpawn随机生成一个/多个道具,存放在vector(每个道具最多一次)
- isOtherPowerUpActive给定类型,获取是否是激活的
- UpdatePowerUps更新所有当前被激活的道具的属性(位置,持续时间),当Duration结束,会根据道具类型取消当前效果
- ActivatePowerUp会根据道具类型激活道具效果
修改Game类
当销毁砖块,生成道具,与玩家碰撞使用道具,否则销毁 Sticky和Pass-through道具稍微改变了一些原有的游戏逻辑,因此会修改Ball类(增加了两个属性) …… 
音乐音效
IrrKlang是一个可以播放WAV,MP3,OGG和FLAC文件的高级二维和三维(Windows,Mac OS X,Linux)声音引擎和音频库。它还有一些可以自由调整的音频效果,如混响、延迟和失真。 使用非常简单,只需要导入库,初始化声音引擎,并通过play播放音乐,bool控制是否循环
1
2
3
4
#include <irrklang/irrKlang.h>
using namespace irrklang;
ISoundEngine *SoundEngine = createIrrKlangDevice();
SoundEngine->play2D("…….mp3", GL_TRUE);
展开
文本
添加TextRenderer:文本渲染类,Text shaders文本着色器
- 构造:
- Load指定字体文件和字体大小,会加载128个ASCII的字符,将结果(字形纹理(库生成的),大小,垂直偏移,水平偏移)存储在map Characters中
- RenderText,渲染字形,接受字符串,指定位置,大小,颜色,循环每个字符渲染,这里由于orthor中设置(y值取值从顶部到底部递增),需要改变计算垂直方向偏移的方法
FT_Library ,FT_Init_FreeType初始化字体库 FT_Face ,FT_New_Face字体(字符的样式)文件 FT_Set_Pixel_Sizes定义字体大小 FT_Load_Char将其中一个字形(单个字符的具体形状)设置为激活字形
增加gameplay
√正常情况,游戏关卡循环 √左上显示当前关卡,销毁的砖块计数,死亡次数 √按下tab调出重置游戏菜单:暂停游戏:停止update逻辑(游戏对象属性),渲染正常 √按键w s切换开始关卡, √enter重置关卡,会重置所有数据(窗口,音乐) √如果未选择再次按下q,不会发生任何 √字体颜色
————————————-OpenGL 小游戏实战 完结——————————————–



