当前位置: 首页 > news >正文

使用 C++/OpenCV 创建动态流星雨特效 (实时动画)

使用 C++/OpenCV 创建动态流星雨特效 (实时动画)

本文将指导你如何使用 C++ 和 OpenCV 创建一个实时的流星雨动画。与之前为静态图片添加特效不同,这次我们将利用循环和 imshow 来生成一个持续不断的动态效果,流星会在背景上划过并消失。
在这里插入图片描述

实现思路

我们将创建一个主循环,每一轮循环都代表动画的一帧。

  1. 管理流星对象: 我们需要一个容器(如 std::vector)来存储屏幕上所有活跃的流星。
  2. 定义流星类: 创建一个 Meteor 类或结构体,它不仅包含外观属性(颜色、粗细),还包含动画属性,如当前位置、速度和生命周期。
  3. 主循环:
    • 生成流星: 在每一帧,有一定几率在屏幕外随机生成新的流星。
    • 更新状态: 更新每个流星的位置(位置 += 速度)。
    • 绘制画面: 在每一帧开始时,先加载原始背景图,然后将所有更新后的流星绘制到这张图上。
    • 移除消失的流星: 当流星飞出画面或其生命周期结束时,将其从容器中移除。
    • 显示: 使用 cv::imshow() 显示绘制好的一帧画面。
    • 循环与退出: 重复以上步骤,直到用户按下退出键(如 ‘q’ 或 ESC)。

完整示例代码 👨‍💻

这个程序会加载一张背景图,并在其上实时渲染动态的流星雨。

dynamic_meteor_shower.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <random>// 使用一个类来更好地管理流星的状态
class Meteor {
public:cv::Point2f pos;       // 当前位置 (使用浮点数以获得平滑移动)cv::Vec2f velocity;    // 速度 (方向和速率)cv::Scalar color;      // 颜色int length;            // 尾巴长度int thickness;         // 线条粗细Meteor(int screenWidth, int screenHeight) {// 1. 初始化位置和速度// 随机决定从顶部还是左/右侧出现if (rand() % 2 == 0) {// 从顶部随机位置出现pos = cv::Point2f(rand() % screenWidth, -50.0f);velocity = cv::Vec2f(2.0f + (rand() % 4), 10.0f + (rand() % 10)); // 向右下} else {// 从左侧或右侧出现if (rand() % 2 == 0) {pos = cv::Point2f(-50.0f, rand() % (screenHeight / 2));velocity = cv::Vec2f(5.0f + (rand() % 10), 5.0f + (rand() % 5)); // 向右下} else {pos = cv::Point2f(screenWidth + 50.0f, rand() % (screenHeight / 2));velocity = cv::Vec2f(-(5.0f + (rand() % 10)), 5.0f + (rand() % 5)); // 向左下}}// 2. 初始化外观int brightness = 180 + rand() % 76; // 较亮的颜色color = cv::Scalar(brightness, brightness, 255); // 淡蓝色或白色length = 20 + rand() % 80;thickness = 1 + rand() % 2;}// 更新流星位置void update() {pos += cv::Point2f(velocity[0], velocity[1]);}// 绘制流星void draw(cv::Mat& canvas) const {// 计算尾巴的起点cv::Point tail_start = pos - cv::Point2f(velocity[0], velocity[1]) * (float)(length / cv::norm(velocity));// 绘制流星的主体(一条亮线)cv::line(canvas, pos, tail_start, color, thickness, cv::LINE_AA);// 绘制一个更亮的头部cv::circle(canvas, pos, thickness, cv::Scalar(255, 255, 255), -1, cv::LINE_AA);}// 检查流星是否已经飞出屏幕bool isOffscreen(int screenWidth, int screenHeight) const {return (pos.x < -100 || pos.x > screenWidth + 100 || pos.y > screenHeight + 100);}
};int main() {// 1. 加载背景图片cv::Mat background = cv::imread("background.jpg");if (background.empty()) {std::cerr << "错误: 无法加载背景图片 'background.jpg'!" << std::endl;return -1;}int width = background.cols;int height = background.rows;std::vector<Meteor> meteors;const int MAX_METEORS = 30; // 屏幕上最多同时存在的流星数量cv::namedWindow("Dynamic Meteor Shower", cv::WINDOW_AUTOSIZE);while (true) {// 2. 每一帧都从原始背景图开始绘制,避免留下残影cv::Mat frame = background.clone();// 3. 有一定几率生成新的流星if (meteors.size() < MAX_METEORS && rand() % 5 == 0) {meteors.push_back(Meteor(width, height));}// 4. 更新和绘制所有流星for (auto& meteor : meteors) {meteor.update();meteor.draw(frame);}// 5. 移除飞出屏幕的流星// 使用 C++11 的 remove_if 和 lambda 表达式,非常高效meteors.erase(std::remove_if(meteors.begin(), meteors.end(), [width, height](const Meteor& m) {return m.isOffscreen(width, height);}),meteors.end());// 6. 显示最终合成的帧cv::imshow("Dynamic Meteor Shower", frame);// 7. 检测用户输入,如果按下 'q' 键或 ESC 键则退出循环char key = (char)cv::waitKey(25); // 等待25毫秒if (key == 'q' || key == 27) {break;}}cv::destroyAllWindows();return 0;
}

CMakeLists.txt

编译文件保持不变。

cmake_minimum_required(VERSION 3.10)
project(DynamicMeteorShower)find_package(OpenCV REQUIRED)include_directories(${OpenCV_INCLUDE_DIRS})add_executable(dynamic_meteor_shower dynamic_meteor_shower.cpp)
target_link_libraries(dynamic_meteor_shower ${OpenCV_LIBS})

如何编译和运行

  1. dynamic_meteor_shower.cppCMakeLists.txt 保存到同一个目录下。
  2. 准备一张背景图片,命名为 background.jpg 并放在同一目录。
  3. 编译和运行:
    mkdir -p build && cd build
    cmake ..
    make
    ./dynamic_meteor_shower
    

运行后,你会看到一个窗口,其中有流星不断地从屏幕外划入,飞过背景图,然后消失。按 q 键或 Esc 键可以关闭程序。


核心代码解析

  • Meteor:

    • 构造函数 Meteor(width, height) 负责在屏幕外随机生成流星的初始位置和速度。
    • update() 方法简单地将速度加到当前位置上,实现移动。
    • draw(canvas) 方法在给定的图像(canvas)上绘制流星。它通过计算一个尾巴起点来画出一条线,并在线的头部画一个更亮的圆点,模拟流星头。
    • isOffscreen() 用于判断流星是否已经飞出可视区域,以便我们将其移除。
  • while 循环:

    • cv::Mat frame = background.clone();: 这是实现动画的关键。每一帧我们都重新复制背景图,确保上一帧的流星被清除,只绘制当前帧的流星位置。
    • 生成: if (meteors.size() < MAX_METEORS && rand() % 5 == 0) 控制流星的生成速率,既不会太多也不会太少。
    • 更新与绘制: 遍历 meteors 向量,调用每个对象的 update()draw() 方法。
    • 移除: meteors.erase(std::remove_if(...)) 是从 std::vector 中安全高效地移除满足特定条件(在这里是 isOffscreen)的元素的标准方法。
    • cv::waitKey(25): 这个函数不仅等待用户按键,也为动画提供了大约 40FPS (1000ms / 25ms = 40) 的帧率。你可以调整这个值来改变动画的速度。
http://www.lqws.cn/news/202447.html

相关文章:

  • ngx_stream_geo_module在传输层实现高性能 IP Region 路由
  • 学习STC51单片机30(芯片为STC89C52RCRC)
  • 【论文阅读笔记】《A survey on deep learning approaches for text-to-SQL》
  • 以SMMUv2为例,使用Trace32可视化操作SMMU的常用命令详解
  • 深入理解 Agent 与 LLM 的区别:从智能体到语言模型
  • 反向传播的核心是什么:计算损失函数对可训练参数的梯度=== 损失函数能通过计算图连接到可训练参数
  • 快速运行Dify前端,无需搭建后端环境
  • CADisplayLink、NSTimer、GCD定时器
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(一)
  • opencv_stereoRectify源码解析
  • 客户端和服务器已成功建立 TCP 连接【输出解析】
  • Clahs——问题解决:软件所有节点均超时
  • 能上Nature封面的idea!强化学习+卡尔曼滤波
  • C++之STL--list
  • 智能客服路由实战之RunnableBranch条件分支
  • 复旦联合百度发布Hallo4:让AI肖像“活”起来!新型扩散框架实现高保真音频驱动动画生成!
  • Python 函数全攻略:函数进阶(生成器、闭包、内置函数、装饰器、推导式)
  • AI大模型:(二)3.2 Llama-Factory微调训练deepseek-r1实践
  • 微前端架构下的B端页面设计:模块化与跨团队协作的终极方案
  • 【图像处理基石】如何构建一个简单好用的美颜算法?
  • 向 AI Search 迈进,腾讯云 ES 自研 v-pack 向量增强插件揭秘
  • JAVA理论第五章-JVM
  • JVM 垃圾回收器 详解
  • LVGL手势识别事件无上报问题处理记录
  • C++图书管理
  • 《前缀和》题集
  • [yolov11改进系列]基于yolov11融合改进检测头特征融合模块AFPN的python源码+训练源码
  • CCPC chongqing 2025 H
  • 振动力学:多自由度系统
  • AI书签管理工具开发全记录(十五):TUI基本逻辑实现与数据展示