gpt4 book ai didi

c++ - 粒子系统的点 Sprite

转载 作者:IT老高 更新时间:2023-10-28 12:49:14 25 4
gpt4 key购买 nike

点精灵是构建粒子系统的最佳选择吗?

新版OpenGL和最新图形卡的驱动程序中是否存在点精灵?还是应该使用vbo和glsl来做?

最佳答案

点精灵确实非常适合粒子系统。但是它们与VBO和GLSL没有任何关系,这意味着它们是完全正交的功能。无论是否使用点精灵,都必须始终使用VBO上载几何图形,无论它们只是点,预制精灵还是其他任何东西,并且始终必须通过一组着色器将此几何图形放置(在现代OpenGL中)当然)。

话虽如此,点子精灵在现代OpenGL中得到了很好的支持,只是不像旧的固定功能方法那样自动获得。不支持点衰减功能,这些功能可让您根据点到相机的距离来缩放点的大小,而您必须在顶点着色器中手动执行此操作。以同样的方式,您必须使用特殊的输入变量gl_PointCoord在适当的片段着色器中手动对点进行纹理处理(即当前片段在整个点的[0,1]平方中的位置)。例如,基本的点精灵管道可能看起来像这样:

...
glPointSize(whatever); //specify size of points in pixels
glDrawArrays(GL_POINTS, 0, count); //draw the points

顶点着色器:
uniform mat4 mvp;

layout(location = 0) in vec4 position;

void main()
{
gl_Position = mvp * position;
}

片段着色器:
uniform sampler2D tex;

layout(location = 0) out vec4 color;

void main()
{
color = texture(tex, gl_PointCoord);
}

就这样。当然,这些着色器只是对纹理精灵进行最基本的绘制,但是它们是进一步功能的起点。例如,要根据精灵到相机的距离来计算精灵的大小(也许是为了为其提供固定的世界空间大小),您必须 glEnable(GL_PROGRAM_POINT_SIZE)并写入顶点着色器中的特殊输出变量 gl_PointSize:
uniform mat4 modelview;
uniform mat4 projection;
uniform vec2 screenSize;
uniform float spriteSize;

layout(location = 0) in vec4 position;

void main()
{
vec4 eyePos = modelview * position;
vec4 projVoxel = projection * vec4(spriteSize,spriteSize,eyePos.z,eyePos.w);
vec2 projSize = screenSize * projVoxel.xy / projVoxel.w;
gl_PointSize = 0.25 * (projSize.x+projSize.y);
gl_Position = projection * eyePos;
}

这将使所有点精灵具有相同的世界空间大小(并因此具有不同的屏幕空间大小(以像素为单位))。

但是,在现代OpenGL中仍得到完美支持的点精灵也有其缺点。最大的缺点之一是它们的剪切行为。点在其中心坐标处被剪裁(因为剪裁是在栅格化之前完成的,因此在点被“放大”之前)。因此,如果该点的中心在屏幕外部,则可能会一直到达查看区域的其余部分不会显示,因此,最糟糕的是,一旦该点离开屏幕的一半,它就会突然消失。但是,只有当点精灵太大时,才可以注意到(或调音)。如果它们是非常小的粒子,无论如何都不能覆盖多于几个像素,那么这将不是什么大问题,我仍将粒子系统视为点精灵的典型用例,只是不要将它们用于大型广告牌。

但是,如果这是一个问题,那么除了将所有子图预先构建为CPU上的单个四分之一的天真方法之外,现代OpenGL还提供了许多其他方法来实现点子图。您仍然可以将它们渲染为充满点的缓冲区(从而可以将它们从基于GPU的粒子引擎中提取出来)。然后,要实际生成四边形几何图形,可以使用几何图形着色器,它使您可以从单个点生成四边形。首先,您仅在顶点着色器中执行modelview转换:
uniform mat4 modelview;

layout(location = 0) in vec4 position;

void main()
{
gl_Position = modelview * position;
}

然后,几何着色器完成其余工作。它将点的位置与通用[0,1] -quad的4个角相结合,并完成了到剪贴空间的转换:
const vec2 corners[4] = { 
vec2(0.0, 1.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(1.0, 0.0) };

layout(points) in;
layout(triangle_strip, max_vertices = 4) out;

uniform mat4 projection;
uniform float spriteSize;

out vec2 texCoord;

void main()
{
for(int i=0; i<4; ++i)
{
vec4 eyePos = gl_in[0].gl_Position; //start with point position
eyePos.xy += spriteSize * (corners[i] - vec2(0.5)); //add corner position
gl_Position = projection * eyePos; //complete transformation
texCoord = corners[i]; //use corner as texCoord
EmitVertex();
}
}

在片段着色器中,您当然会使用自定义的 texCoord变量而不是 gl_PointCoord进行纹理化,因为我们不再绘制实际点。

另一个可能的方法(可能是更快的方法,因为我记得几何着色器以其速度较慢而著称)将使用实例渲染。这样,您就有了一个额外的VBO,它仅包含单个通用2D方形的顶点(即[0,1]正方形),而您的旧VBO仅包含点位置。然后,您要做的是多次绘制该单个四边形(有实例),同时从VBO点获取单个实例的位置:
glVertexAttribPointer(0, ...points...);
glVertexAttribPointer(1, ...quad...);
glVertexAttribDivisor(0, 1); //advance only once per instance
...
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count); //draw #count quads

然后在顶点着色器中,将每个点的位置与实际的角/四角位置(这也是该顶点的纹理坐标)组合起来:
uniform mat4 modelview;
uniform mat4 projection;
uniform float spriteSize;

layout(location = 0) in vec4 position;
layout(location = 1) in vec2 corner;

out vec2 texCoord;

void main()
{
vec4 eyePos = modelview * position; //transform to eye-space
eyePos.xy += spriteSize * (corner - vec2(0.5)); //add corner position
gl_Position = projection * eyePos; //complete transformation
texCoord = corner;
}

这实现了与基于几何着色器的方法相同的效果,即正确裁剪的点精灵具有一致的世界空间大小。如果您实际上想模仿实际点精灵的屏幕空间像素大小,则需要付出更多的计算努力。但这是一项练习,并且与从点精灵着色器到世界到屏幕的转换完全相反。

关于c++ - 粒子系统的点 Sprite ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17397724/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com