OpenGL制作炫酷字符雨屏保程序

  • 前言
  • OpenGL3.3+制作屏保程序
  • 将制作的程序运用到屏保
  • 效果展示

前言

之前早就想做个类似《黑客帝国》的数字雨屏保程序了,这次趁着有点时间做个三维效果的数字雨,就是有距离感而不单单是二维的数字雨效果。此屏保程序是居家旅行必备的装逼神器,→_→炫酷的字符流,营造出一种神秘感。
本工程项目及发布版本的scr屏保程序和exe程序在github可下载:
https://github.com/ZeusYang/Breakout/tree/master/MatrixSaver

OpenGL3.3+制作屏保程序

工具

OpenGL3.3+图形库、glm数学矩阵库、glfw创建窗口。

字符纹理

数字雨中的字符可借用freetype实现,也可用一张纹理图片,这张纹理图片将需要显示的字符全部合成一张,然后通过改变纹理坐标来实现字符的变化。本人采用后者的方法,用freetype实现的话需要不断切换纹理,非常耗费效率;而用纹理图片的话,由于纹理的分辨率导致放大产生的锯齿是它的缺点。本人采用的纹理如下所示:

窗口

本人采用glfw创建全屏的窗口,当然你要用win32也可以。glfw创建全屏窗口的代码如下,更多的细节请看源代码:

    //全屏显示GLFWmonitor *pMonitor = glfwGetPrimaryMonitor();const GLFWvidmode * mode = glfwGetVideoMode(pMonitor);SCREEN_WIDTH = mode->width;SCREEN_HEIGHT = mode->height;GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Matrix", pMonitor, nullptr);if (window == NULL) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}

数字字符链

数字雨中每一条字符链我们采用链表实现,在字符雨下落的过程中,头部不断产生新的字符,而尾部则不断地删除消失,总的长度保持不变。单个字符的属性有位置、颜色、字符种类这三个属性,其中字符种类我们通过纹理坐标来指定每个字符,通过改变纹理坐标就能够索引到不同的字符。

#pragma once
#include <list>
#include <glm/glm.hpp>struct Symbol {//单个字符glm::vec4 Pos;//位置、大小glm::vec4 Color;//颜色glm::vec4 Coord;//纹理坐标的移动
};class CharList{
public:CharList(float zn, float zf, float ap, float fy);~CharList() = default;void Move(float dt);//移动bool IsOufScreen();//判断是否飘出屏幕外std::list<Symbol> necklace;glm::vec3 pos;//列尾位置glm::vec3 vel;//速度方向int num;//字符数量private://单个字符大小、近平面、远平面、宽高比、视锥范围夹角float size, znear, zfar, aspect, fovy;void RndPos();//随机位置inline float Rand0_1() {//0-1的随机数return (double)rand() / (double)RAND_MAX;}
};
#include "CharList.h"CharList::CharList(float zn, float zf, float ap, float fy):size(1.0f), znear(zn), zfar(zf), aspect(ap), fovy(fy){//字符链的字符数量num = 15 + rand() % 10;RndPos();//随机位置glm::vec3 tp = pos;float alpha = 1.0f;for (auto x = 0; x < num; ++x) {//生成num个字符Symbol tmp;tmp.Color = glm::vec4(0.0, 1.0, 0.5, alpha);tmp.Pos = glm::vec4(tp.x, tp.y, tp.z, size);//随机纹理tmp.Coord.x = rand() % 11;tmp.Coord.y = rand() % 6;necklace.push_back(tmp);tp.y += size;alpha -= 1.0f / num;}//头部的颜色为白色necklace.front().Color = glm::vec4(0.8, 0.8, 0.8, 1.0);//速度vel = glm::vec3(0.0, -0.1, 0.022);
}void CharList::Move(float dt) {//头部插入,删除尾部Symbol tmp;tmp.Color = glm::vec4(0.8, 0.8, 0.8, 1.0);tmp.Pos = glm::vec4(necklace.front().Pos.x, necklace.front().Pos.y, necklace.front().Pos.z, size);tmp.Pos.y -= size;tmp.Coord.x = rand() % 11;tmp.Coord.y = rand() % 6;necklace.front().Color = glm::vec4(0.0, 1.0, 0.5, 1.0f);necklace.push_front(tmp);necklace.pop_back();//修改alpha值float alpha = 1.0f;for (auto &e : necklace) {e.Color.w = alpha;alpha -= 1.0f / num;e.Pos.z += vel.z;}pos = necklace.front().Pos;
}bool CharList::IsOufScreen() {//判断是否飘出屏幕double range = tan(glm::radians(fovy))*pos.z;float length = num * size;if (pos.z >= -znear || pos.z <= -this->zfar || pos.y < (range - length)){return true;}return false;
}void CharList::RndPos() {//随机位置生成double range;pos.z = -(Rand0_1()*(zfar - znear) + znear);range = tan(glm::radians(fovy))*pos.z;pos.y = -range;pos.x = Rand0_1()*range * 2 * aspect - range*aspect;
}

渲染精灵

一个字符就是一个图片,简单地写一个渲染循环一个个地draw是一种方法,但是这种方法效率极低,drawcall次数很大,特别是数量非常大的时候。为此,我采用了OpenGL3.3+的实例化特性。

class SpriteRenderer{
public:SpriteRenderer(Shader &shader, float untiX, float unitY);~SpriteRenderer();//实例化void SetInstance(const std::vector<CharList*> &target);//绘制精灵,除了纹理,其余参数都没用到void DrawSprite(Texture2D &texture, glm::vec2 position = glm::vec2(0, 0),glm::vec2 size = glm::vec2(10, 10), GLfloat rotate = 0.0f,glm::vec3 color = glm::vec3(1.0f));private:int amount;//实例化的个数Shader shader;GLuint quadVAO;GLuint instanceVBO;void initRenderData(float untiX, float unitY);
};
#include "SpriteRenderer.h"
#include <iostream>SpriteRenderer::SpriteRenderer(Shader &shader, float unitX, float unitY):shader(shader), amount(0)
{this->initRenderData(unitX, unitY);
}SpriteRenderer::~SpriteRenderer() {glDeleteVertexArrays(1, &this->quadVAO);glDeleteBuffers(1, &this->instanceVBO);
}void SpriteRenderer::initRenderData(float unitX,float unitY)
{// 配置 VAO/VBOGLuint VBO;GLfloat vertices[] = {// 位置               // 纹理-0.5f, 0.5f,0.0f, 0.000f,  unitX,0.5f,-0.5f,0.0f,  unitY, 0.000f,-0.5f,-0.5f,0.0f, 0.000f, 0.000f,-0.5f, 0.5f,0.0f, 0.000f,  unitX,0.5f, 0.5f,0.0f,  unitY,  unitX,0.5f,-0.5f,0.0f,  unitY, 0.000f};glGenVertexArrays(1, &this->quadVAO);glGenBuffers(1, &instanceVBO);glGenBuffers(1, &VBO);glBindVertexArray(this->quadVAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);
}void SpriteRenderer::SetInstance(const std::vector<CharList*> &target) {//获取实例矩阵std::vector<glm::mat4> instance;amount = target.size()*target[0]->necklace.size();instance.reserve(amount);for (auto it = target.begin(); it != target.end(); ++it) {for (auto th = (*it)->necklace.begin(); th != (*it)->necklace.end(); ++th) {glm::mat4 tmp(1.0f);tmp[0] = th->Pos;tmp[1] = th->Color;tmp[2] = th->Coord;tmp[3] = glm::vec4(1.0f);instance.push_back(tmp);}}GLsizei vec4Size = sizeof(glm::vec4);glBindVertexArray(quadVAO);glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);glBufferData(GL_ARRAY_BUFFER, instance.size() * sizeof(glm::mat4), &instance[0], GL_DYNAMIC_DRAW);glEnableVertexAttribArray(2);glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)0);glEnableVertexAttribArray(3);glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(vec4Size));glEnableVertexAttribArray(4);glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(2 * vec4Size));glEnableVertexAttribArray(5);glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(3 * vec4Size));glVertexAttribDivisor(2, 1);glVertexAttribDivisor(3, 1);glVertexAttribDivisor(4, 1);glVertexAttribDivisor(5, 1);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);
}void SpriteRenderer::DrawSprite(Texture2D &texture, glm::vec2 position,glm::vec2 size, GLfloat rotate, glm::vec3 color){this->shader.Use();//绑定纹理glActiveTexture(GL_TEXTURE0);texture.Bind();glBindVertexArray(this->quadVAO);glDrawArraysInstanced(GL_TRIANGLES, 0, 6, amount);glBindVertexArray(0);
}

渲染循环

初始化:

//初始化
void Saver::Init() {// 加载着色器std::string vShader = "#version 330 core\n""layout(location = 0) in vec3 vertex;\n""layout(location = 1) in vec2 texcoord;\n""layout(location = 2) in mat4 instanceMatrix;\n""out vec2 TexCoords;\n""out vec4 Color;\n""uniform mat4 projection;\n""void main()\n""{\n""   //位置\n""   vec3 pos = vertex + instanceMatrix[0].xyz;\n""   pos *= instanceMatrix[0].w;\n""   gl_Position = projection * vec4(pos, 1.0f);\n""   //颜色\n""   Color = instanceMatrix[1];\n""   TexCoords.x = texcoord.x + 0.091f*instanceMatrix[2].x;\n""   TexCoords.y = texcoord.y + 0.167f*instanceMatrix[2].y;\n""}\n";std::string fShader ="#version 330 core\n""in vec2 TexCoords;\n""in vec4 Color;\n""out vec4 color;\n""uniform sampler2D image;\n""void main() {\n""   vec4 tex = texture(image, TexCoords);\n""   color.xyz = Color.xyz;\n""   color.w = (1.0 - tex.x)*Color.w;\n""}\n";ResourceManager::LoadShaderFromString(vShader,fShader,  "sprite");/*ResourceManager::LoadShader("Sprite.vert","Sprite.frag", nullptr, "sprite");*/// 加载纹理ResourceManager::LoadTexture("C:/Windows/number.png", GL_TRUE, "number");// 配置着色器glm::mat4 projection = glm::perspective(glm::radians(fovy), aspect, znear, zfar);ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);//50个字符链for (auto x = 0; x < 200; ++x) {matrix.push_back(new CharList(znear, zfar, aspect, fovy));}//渲染精灵,采用实例化渲染sprite = std::make_shared<SpriteRenderer>(ResourceManager::GetShader("sprite"), 0.167f, 0.091f);
}

更新:

//更新
void Saver::Update(GLfloat dt) {timeCounter += dt;//移动if (timeCounter >= 0.10f) {for (auto it = matrix.begin(); it != matrix.end(); ++it) {(*it)->Move(dt);}timeCounter = 0;//检查是否飘出屏幕外for (auto it = matrix.begin(); it != matrix.end(); ++it) {if ((*it)->IsOufScreen()) {CharList* tmp = *it;*it = new CharList(znear, zfar, aspect, fovy);delete tmp;}}}//传入sprite实例化sprite->SetInstance(matrix);
}

渲染:

//渲染
void Saver::Render() {sprite->DrawSprite(ResourceManager::GetTexture("number"));
}

将制作的程序运用到屏保

我们编译发布版的可执行程序是exe文件,然后我们修改exe程序的后缀exe变成scr,windows操作系统自动将scr程序识别为屏幕保护程序。
对于我编译的屏保程序,按照如下的步骤即可:
1、将MatrixSaver.scr和number.png放到C:/Windows目录下
2、在相应的屏保选择选项中选择MatrixSaver作为屏保程序
例:在win8.1下,右键桌面空白部分,选择“个性化”->屏幕保护程序->在对话框的下拉框中选择MatrixSaver,然后“确定”。

效果展示

尽情享受视觉的盛宴吧!(好中二,好羞耻)

本工程项目及发布版本的scr屏保程序和exe程序在github可下载:
https://github.com/ZeusYang/Breakout/tree/master/MatrixSaver

[OpenGL] OpenGL制作三维字符雨屏保程序相关推荐

  1. 学以致用——Java源码——使用随机几何图形制作屏保程序(Screen Saver with Shapes Using the Java 2D API)

    程序功能: 使用随机输出的几何图形作为屏保程序,用户可随时指定屏幕上要显示的图形元素的数量. 运行示例: 源码: 1. 实体类 import java.awt.Graphics; import jav ...

  2. Linux下屏保程序

    http://www.openswc.com/forum.php?mod=viewthread&tid=560 利用一些便捷的小软件把自己的数码相片做成绚烂多彩的屏保,放在自己的电脑上该是多么 ...

  3. 用C++编写一个个性化的屏保程序,零基础小白可收藏学习!

    C++编写一个个性化的屏保程序,这个屏幕保护程序模仿自一个著名杀毒软件中的待机画面,图标动态变化的屏保程序,进入屏保状态后,程序自动获取一些windows中所安装程序的ICO图标,随机显示一些图标,并 ...

  4. 自定义设置一个屏保程序

    用C语言写一个简单的窗口程序,目的是生成一个可视化的图形窗口,需要用到EasyX库,可在文章末尾的网盘链接中下载.该程序退出需左击鼠标,否则无法退出. #include<stdio.h> ...

  5. python写背单词软件_python实现屏保程序(适用于背单词)

    今天要给大家分享的是一款自己写的屏保程序,大学大家最头疼的就是四六级的考试了,上次考试做阅读的时候,情不自禁的发呆,想着如果我能在电脑上写一个屏保程序,那么就可以天天记单词了! 开始 首先:我们使用的 ...

  6. From .1:从屏保到Win平台开发 - 一个可运行的C#屏保程序

    有一天在微信里看见了微软的Fluent Design系统(腾讯翻译为浸流设计系统)界面设计的一张图片,觉得非常好看,顿时想,这么好看的界面,如果能是一个屏保该有多好.这样的话,用户坐在电脑前也不会忍心 ...

  7. Python实战:利用Tkinter实现屏保程序

    利用Tkinter实现彩球碰撞屏保 一.架构与思路 (1)主函数: main():通过类启动程序: (2)类: ScreenSaver():用于定义屏保和主画布,调用球创建.运动等函数: Random ...

  8. 用python开发一个背单词软件-python实现屏保程序(适用于背单词)

    今天要给大家分享的是一款自己写的屏保程序,大学大家最头疼的就是四六级的考试了,上次考试做阅读的时候,情不自禁的发呆,想着如果我能在电脑上写一个屏保程序,那么就可以天天记单词了! 开始 首先:我们使用的 ...

  9. 《精通Android 5 多媒体开发》——第22章,第22.3节开发一个屏保程序

    本节书摘来自异步社区<精通Android 5 多媒体开发>一书中的第22章,第22.3节开发一个屏保程序,作者 王石磊,更多章节内容可以访问云栖社区"异步社区"公众号查 ...

  10. 安装黑客帝国字母雨屏保教程(让你的屏保更加炫酷)

    1.上传文件 这里是直接上传了文件的,所有就不演示了,有想要的可以私信作者 2.给文件赋予执行权限 chmod 700 cmatrix 3.执行文件 ./cmatrix 4.给屏保文件设置软链接(作用 ...

最新文章

  1. ruby语言开源Web应用框架 Ruby on Rails 简介
  2. python人工智能——机器学习——转换器与估计器
  3. python 表格处理软件_基于Python的Excel处理工具
  4. Python内置数据类型之Tuple
  5. twrp3.3.0刷n9002_插画师必备笔刷,送你5套iPad5.0新出300款大神笔刷
  6. Linux系统编程——进程替换:exec 函数族
  7. secoclient隧道保活超时或协商超时_绕不开的TCP之超时重传
  8. 计算机win10+上锁,手把手教你在windows 10右键菜单中添加Bitlocker上锁/解锁教程-网络教程与技术 -亦是美网络...
  9. python 对 PDF 的拆分 和合并
  10. 神经内分泌肿瘤如何分级,神经系统分级调节概念
  11. 1. stm32h7 移植RTX5(AC6) 基于CUBEMX
  12. 莫纳什大学FIT1043 assignment2课业解析
  13. AI高考的信息检索策略
  14. 部件MSCOMCTL.OCX或其附件之一不能正确注册:一个文件丢失或无效
  15. msysgit - Windows Git安装配置
  16. 【阿尼亚不会CTF】第六届”蓝帽杯“全国大学生网络安全技能大赛—线上初赛部分writeup
  17. BIOS知识枝桠——常用functions查阅
  18. 磁盘和硬盘和光盘和内存的区别
  19. 四号楼5楼机房学生及教师机设置说明
  20. 公司再小,也不能在网上“裸奔”

热门文章

  1. html如何设置hr 标签的线条粗细,html中hr怎么设置粗细
  2. prinect pdf toolbox 2021中文版
  3. python读取fiddler_Python3.x+Fiddler抓取APP数据
  4. MATLAB处理txt文档数据——以处理pscad输出数据为例
  5. 1.Oracle 安装教程及使用
  6. Oracle安装图文详解!
  7. 关于路由器认证校园网的可行方案.md
  8. 五边形组合包络图matlab,华中赛基于遗传算法的钢构件排料问题.docx
  9. Java输出菱形图案
  10. pic单片机tmr1计数c语言,PIC单片机C语言编程实例五.doc