[译]06.打造灵活高效的粒子系统——渲染器

06_1

就像我写的本系类文章的前言一样,这里我只做了个基础的渲染系统。渲染只用到位置、颜色和一张贴图。本文中将讲解渲染系统在实现时可能碰到的问题。

转载请注明[本文链接][原文链接],谢谢!

引言

Gist地址在这: fenbf / ParticleRenderer

 

渲染系统的角色是把我们提供的数据变成像素。我尝试将粒子的更新逻辑和渲染分开,所以做了IParticleRenderer接口,它从ParticleSystem获得数据,并应用到GPU。目前只实现了opengGL的render,GLParticleRenderer。

06_2

一个render并不需要粒子的全部属性,目前这个实现只需要位置和颜色就行了。

将渲染和逻辑分开提供了很大的灵活性。比如测试逻辑效率的时候,做个EmptyRenderer就好了,其他模块一行代码都不用改,当然屏幕上也啥都不显示,但是可以收集运行的时间数据,用于查找热点什么的。这个想法也可以用在单元测试上。

Render接口

class IParticleRenderer

{

public:

IParticleRenderer() { }

virtual ~IParticleRenderer() { }

 

virtual void generate(ParticleSystem *sys, bool useQuads) = 0;

virtual void destroy() = 0;

virtual void update() = 0;

virtual void render() = 0;

};

useQuads目前是没有用到的,当它的值为true的时候表示生成四边形而不是生成Point Sprite。使用四边形的时候会增大想GPU传输的数据量。

如何使用OpenGL渲染粒子

Shaders

#version 330

 

uniform mat4x4 matModelview;

uniform mat4x4 matProjection;

 

layout(location = 0) in vec4 vVertex;

layout(location = 1) in vec4 vColor;

 

out vec4 outColor;

 

void main()

{

vec4 eyePos = matModelview * gl_Vertex;

gl_Position = matProjection * eyePos;

 

outColor = vColor;

 

float dist = length(eyePos.xyz);

float att = inversesqrt(0.1f*dist);

gl_PointSize = 2.0f * att;

}

上面vertex shader仅使用颜色和位置用于计算gl_Position和gl_PointSize。

目前用的Fragment shader太简单了,有点琐碎就不贴了。

OpenGL Particle Renderer的实现

Update()

void GLParticleRenderer::update()

{

const size_t count = m_system->numAliveParticles();

if (count > 0)

{

glBindBuffer(GL_ARRAY_BUFFER, m_bufPos);

float *ptr = (float *)(m_system->finalData()->m_pos.get());

glBufferSubData(GL_ARRAY_BUFFER, 0, count*sizeof(float)* 4, ptr);

 

glBindBuffer(GL_ARRAY_BUFFER, m_bufCol);

ptr = (float*)(m_system->finalData()->m_col.get());

glBufferSubData(GL_ARRAY_BUFFER, 0, count*sizeof(float)* 4, ptr);

 

glBindBuffer(GL_ARRAY_BUFFER, 0);

}

}

如代码所示,updater()函数只使用了渲染需要的数据去更新渲染缓存。

Render()

void GLParticleRenderer::render()

{

const size_t count = m_system->numAliveParticles();

if (count > 0)

{

glBindVertexArray(m_vao);

glDrawArrays(GL_POINTS, 0, count);

glBindVertexArray(0);

}

}

plus the whole context:

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, gParticleTexture);

 

glEnable(GL_PROGRAM_POINT_SIZE);

 

mProgram.use();

mProgram.uniformMatrix4f(“matProjection”, camera.projectionMatrix);

mProgram.uniformMatrix4f(“matModelview”, camera.modelviewMatrix);

 

glEnable(GL_BLEND);

glBlendFunc(GL_SRC_ALPHA, GL_ONE);

 

gpuRender.begin();

gCurrentEffect->render(); // << our render() method

gpuRender.end();

 

glDisable(GL_BLEND);

 

mProgram.disable();

问题

这个OpenGL renderer是能跑起来的最简单的版本,但是是不能用于商业产品的代码,以下是几条需要改进的地方:

  • buffer updates: just a simplest method right now. It could be improved by using mapping and double buffering.
  • Buffer更新:目前只是用了最简单的方法,应该使用映射(mapping)和双缓冲(double buffering)。
  • texture ID in the renderer – as a member, not outside! Additionally we could think about using texture atlas and a new parameter for a particle – texID. That way each particle could use different texture.
  • Renderer中使用的texture ID是作为属性使用的,没有提取出来,我们可以使用贴图集(texture atlas)然后将贴图ID作为粒子的一个属性。这样每个粒子就可以使用不同的贴图了。
  • 只使用Point Sprite渲染,有个变量useQuads还没有用起来,也许用geometry shader去产生四边形比Point Sprite更好。
    • 四边形让我们更容易旋转粒子。
  • 更过关于粒子渲染的优化方法可以参考这篇文章《Point Sprites for particle system》。

CPUGPU

实际上,目前主要的问题在CPU和数据传输(CPU到GPU)上。效率慢不仅是因为要传输数据,还需要同步。GPU有时(或者说经常)需要等待上一步完成,它才能更新缓存。

这也是我最初的假设也是设计的选择,我很清楚即使把CPU这面优化到最快,也不可能达到“纯GPU”的粒子系统。基于CPU的粒子系统有很大灵活性,但是也丢弃了一些性能。

下一步

本文结束了“实现”粒子系统的系列文章。我们已经有了粒子的更新和渲染,所以一个阶段完成了。现在我们开始考虑优化了。下一片文章我将讲述如何将当前系统的运行速度提高50%(基于当前速度)。期待吧~

问题:

你怎么看这个设计?

渲染模块有什么方法可以改进的?有没有什么高大上的OpengGL资料?

原文地址:http://www.bfilipek.com/2014/07/flexible-particle-system-opengl-renderer.html

No Responses

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

You must enable javascript to see captcha here!