基于OpenGL的雨雪粒子系统实现
基于OpenGL的雨雪粒子系统实现
一、系统架构设计
graph TD
A[主循环] --> B[输入处理]
A --> C[粒子更新]
A --> D[物理模拟]
A --> E[渲染]
C --> F[键盘事件]
D --> G[重力计算]
D --> H[风力计算]
E --> I[视口管理]
E --> J[粒子渲染]
二、核心代码实现
1. 粒子结构体定义
struct Particle {
glm::vec3 position; // 位置
glm::vec3 velocity; // 速度
float size; // 大小(0.1-3.0)
float life; // 生命周期(0.0-1.0)
glm::vec4 color; // 颜色(RGBA)
bool isSnow; // 类型标识
float rotation; // 旋转角度
float spinSpeed; // 旋转速度
};
2. 粒子系统类
class ParticleSystem {
public:
ParticleSystem(int maxParticles);
void Update(float deltaTime);
void Render();
// 控制接口
void SetWindSpeed(float speed);
void SetRainSize(float minSize, float maxSize);
void ToggleWeatherType(); // 切换雨雪模式
private:
Particle particles[10000];
int activeCount;
float windSpeed;
float rainMinSize, rainMaxSize;
bool isSnowing;
void EmitParticle();
void ApplyPhysics(Particle& p, float deltaTime);
};
3. 物理模拟实现
void ParticleSystem::ApplyPhysics(Particle& p, float deltaTime) {
// 重力影响
p.velocity.y -= 9.8f * deltaTime * (isSnowing ? 0.5f : 1.5f);
// 风力影响
p.velocity.x += windSpeed * deltaTime * (isSnowing ? 0.8f : 0.3f);
// 空气阻力
p.velocity *= 0.995f;
// 位置更新
p.position += p.velocity * deltaTime;
// 生命周期衰减
p.life -= deltaTime;
p.size = glm::mix(0.1f, 3.0f, 1.0f - p.life); // 大小随生命周期变化
}
4. 渲染实现(使用VBO)
void ParticleSystem::Render() {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// 动态更新VBO数据
glBufferData(GL_ARRAY_BUFFER, activeCount * sizeof(Particle),
particles, GL_STREAM_DRAW);
// 着色器参数设置
shader.use();
shader.setFloat("time", glfwGetTime());
shader.setVec3("cameraPos", cameraPos);
// 分类型渲染
if(isSnowing) {
shader.setInt("weatherType", 1);
glDrawArrays(GL_POINTS, 0, activeCount);
} else {
shader.setInt("weatherType", 0);
glDrawArrays(GL_POINTS, 0, activeCount);
}
glBindVertexArray(0);
glDisable(GL_BLEND);
}
三、关键功能实现
1. 雨雪大小控制
void ParticleSystem::SetRainSize(float minSize, float maxSize) {
rainMinSize = minSize;
rainMaxSize = maxSize;
for(auto& p : particles) {
if(p.isSnow) continue;
p.size = glm::mix(rainMinSize, rainMaxSize,
rand()/float(RAND_MAX));
}
}
2. 风速变化系统
void ParticleSystem::SetWindSpeed(float speed) {
windSpeed = speed;
// 添加风场扰动
static float noiseScale = 0.1f;
for(auto& p : particles) {
glm::vec3 windOffset = glm::vec3(
perlinNoise(p.position.x * noiseScale,
p.position.z * noiseScale),
0.0f,
perlinNoise(p.position.y * noiseScale,
p.position.z * noiseScale)
);
p.velocity += windOffset * 0.5f;
}
}
3. 粒子生成逻辑
void ParticleSystem::EmitParticle() {
if(activeCount >= 10000) return;
Particle p;
p.position = cameraPos + glm::vec3(
(rand()%100 - 50)/50.0f, // 水平随机偏移
5.0f, // 初始高度
(rand()%100 - 50)/50.0f
);
p.velocity = glm::vec3(
(isSnowing ? 0.1f : 0.3f) * (rand()%100 - 50)/50.0f,
isSnowing ? -1.0f : -3.0f,
(isSnowing ? 0.1f : 0.3f) * (rand()%100 - 50)/50.0f
);
p.size = isSnowing ? 0.5f + (rand()%100)/50.0f : 0.2f;
p.life = 1.0f;
p.color = isSnowing ? glm::vec4(1.0f, 1.0f, 1.0f, 0.8f)
: glm::vec4(0.0f, 0.0f, 1.0f, 0.5f);
particles[activeCount++] = p;
}
四、着色器实现
1. 顶点着色器
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec4 aColor;
layout(location = 2) in float aSize;
layout(location = 3) in float aRotation;
uniform mat4 projection;
uniform mat4 view;
uniform float time;
out vec4 particleColor;
out float particleSize;
void main() {
float scale = aSize * (1.0 - (time - aLife) / 2.0);
vec3 rotatedPos = vec3(
cos(aRotation) * aPos.x - sin(aRotation) * aPos.z,
aPos.y,
sin(aRotation) * aPos.x + cos(aRotation) * aPos.z
);
gl_Position = projection * view * vec4(rotatedPos, 1.0);
gl_PointSize = scale * 1000.0 / gl_Position.w;
particleColor = aColor;
particleSize = scale;
}
2. 片元着色器
#version 330 core
in vec4 particleColor;
in float particleSize;
out vec4 FragColor;
uniform sampler2D snowTexture;
uniform int weatherType;
void main() {
vec2 texCoords = gl_PointCoord * 2.0 - 1.0;
texCoords.y = 1.0 - texCoords.y;
float alpha = smoothstep(0.95, 1.0, length(texCoords));
if(weatherType == 1) {
FragColor = texture(snowTexture, texCoords) * particleColor * alpha;
} else {
FragColor = vec4(particleColor.rgb, particleColor.a * alpha);
}
}
五、交互功能实现
1. 控制台指令
void ProcessInput(GLFWwindow* window) {
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
particleSystem.SetWindSpeed(5.0f);
if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
particleSystem.SetWindSpeed(0.0f);
if(glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS)
particleSystem.ToggleWeatherType();
if(glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS)
particleSystem.SetRainSize(0.2f, 1.5f);
if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS)
particleSystem.SetRainSize(0.5f, 0.8f);
}
2. 可视化调试信息
void RenderDebugInfo() {
ImGui::Begin("Weather Controls");
ImGui::SliderFloat("Wind Speed", &windSpeed, -5.0f, 5.0f);
ImGui::SliderFloat("Rain Size", &rainMinSize, 0.1f, 3.0f);
ImGui::Checkbox("Snow Mode", &isSnowing);
ImGui::End();
}
参考代码 使用opengl模拟雨雪粒子天气系统,有雨雪大小风速变化等功能 youwenfan.com/contentcna/73047.html
六、性能优化方案
-
LOD技术:
// 根据距离调整粒子细节 float distance = glm::length(cameraPos - p.position); if(distance > 20.0f) { p.size *= 0.5f; p.color.a *= 0.7f; } -
GPU Instancing:
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(InstanceData) * instanceCount, &instances[0], GL_DYNAMIC_DRAW); -
剔除算法:
if(particle.position.y < cameraPos.y - 10.0f) { MarkForDeletion(particle); }
七、扩展功能建议
-
粒子碰撞:
// 简单地面碰撞检测 if(p.position.y < 0.0f) { p.position.y = 0.0f; p.velocity.y *= -0.7f; // 反弹 } -
动态纹理:
// 实现雨滴涟漪效果 glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, rippleTexture); glUniform1i(glGetUniformLocation(shader.program, "rippleTex"), 1); -
粒子系统分层:
// 天空层与地面层分离 if(p.isSkyParticle) { shader.setBool("isSkyLayer", true); } else { shader.setBool("isSkyLayer", false); }
八、调试与测试
-
帧率监控:
double fps = 1.0 / deltaTime; std::cout << "FPS: " << fps << std::endl; -
粒子可视化调试:
// 绘制粒子运动轨迹 glBegin(GL_LINES); for(auto& p : particles) { glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(p.position, 0.0f); glVertex3f(p.position + p.velocity, 0.0f); } glEnd();
九、完整项目结构
WeatherSystem/
├── src/
│ ├── main.cpp
│ ├── shader/
│ │ ├── particle.vert
│ │ └── particle.frag
│ ├── textures/
│ │ ├── snow.png
│ │ └── rain.png
│ └── utils/
│ └── perlin_noise.h
├── CMakeLists.txt
└── data/
└── config.ini
该方案结合了粒子系统基础原理与现代图形技术,通过可调节参数实现了逼真的天气模拟效果。开发者可根据实际需求扩展地形交互、光照反射等高级特性。

浙公网安备 33010602011771号