【译】基于几何着色器的粒子特效公告板技术(GLSL)

发表于2015-12-24
评论4 2.9k浏览
  效果图:


  我们要学习什么是粒子特效的公告板技术和如何在顶点着色器中实现这个技术。我们现在可以看看特别适合于粒子渲染的另一种公告版技术的实现方法。
    通常,粒子是一个点(每个粒子的三维位置)并且渲染一个带有纹理的粒子需要一个点经过四次变换
    对于粒子渲染来说,点精灵是一种非常棒的解决方案。但是,如果你想要一个更易于针对四次变换的操控(四个角的坐标、纹理坐标、剪切和视椎剔除技术 等等)的话,我们需要一种方法用来制作成在GPU中复制点精灵的内置功能。
  几何着色器是一个非常强大的复制点精灵功能的方法。动态几何着色器允许创建新的几何,这正是我们所想要的:从一个顶点(粒子)中创建四个顶点(一个四边形)。在Radeon GPU有特殊硬件支持的条件下,这个放大任务(1:4)非常的重要。例如这些情况:
  …同样的,因为几何着色器将通常用于替换点精灵(通过将一个基本点扩展到两个三角形中的一条三角带)也会有特殊硬件的(1:4)的支持。
  来源:  Radeon HD 2000 Programming Guide (page 9)
  所以,我们主要的工作是让我们的几何着色器去转换一个即将进入的顶点(这个顶点来自顶点着色器中)到四个顶点中(a,b,c,d)这样就会形成粒子的四边形。


    再次提醒:(看这篇关于公告版的文章)。我们将会使用模型视图矩阵,但是这一次,我们要从广告版的矩阵中提取值而不是修改这个矩阵。
    那么哪些才是关键的值呢?这些值都有两个向量来定义在摄像机平面中。这两个向量是:Right和Up。是从模型视图中的逆矩阵提取出来的。通过一个常规的模型-视图矩阵(这是一个正交矩阵),矩阵的逆等于转置:列和行反应了矩阵的主对角线。
  下图是 模型视图 矩阵:


  还有它的逆矩阵:


  Right 和Up向量可以像这样构建:
right.x = ModelView[0][0] // 0
right.y = ModelView[1][0] // 4
right.z = ModelView[2][0] // 8
up.x = ModelView[0][1] // 1
up.y = ModelView[1][1] // 5
up.z = ModelView[2][1] // 9
    现在我们已经有了这两个向量来在摄像机平面中定义了,我们可以创建一个四边形框来始终面对着摄像机,通过将一个点(使用一个属于任意一个平行于摄像机平面的面板上的位置P)转换成一个四边形方框(一个Size变量的大小)像下面这样:
vec3 a = P - (right + up) * size;
vec3 b = P - (right - up) * size;
vec3 d = P + (right - up) * size;
vec3 c = P + (right + up) * size;
  果不其然,我们已经有了我们的广告版的边框了
  很好,最难的那部分工作已经完成了,下面是全部的关于公告板技术GLSL的程序代码,用于GLSL Hacker的一个附带文本的示例
  Vertex shader(顶点着色器)
#version 150
in vec4 gxl3d_Position;
in vec4 gxl3d_Color;
// GLSL Hacker automatic uniforms:
uniform mat4 gxl3d_ModelMatrix;
out Vertex
{
vec4 color;
} vertex;
void main()
{
gl_Position = gxl3d_ModelMatrix * gxl3d_Position;
vertex.color = gxl3d_Color;
}
  Geometry shader (几何着色器)
#version 150
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 4) out;
// GLSL Hacker automatic uniforms:
uniform mat4 gxl3d_ViewProjectionMatrix;
uniform mat4 gxl3d_ModelViewMatrix;
uniform float size; // Particle size
in Vertex
{
vec4 color;
} vertex[];
out vec2 Vertex_UV;
out vec4 Vertex_Color;
void main (void)
{
mat4 MV = gxl3d_ModelViewMatrix;
vec3 right = vec3(MV[0][0],
MV[1][0],
MV[2][0]);
vec3 up = vec3(MV[0][1],
MV[1][1],
MV[2][1]);
vec3 P = gl_in[0].gl_Position.xyz;
mat4 VP = gxl3d_ViewProjectionMatrix;
vec3 va = P - (right + up) * size;
gl_Position = VP * vec4(va, 1.0);
 Vertex_UV = vec2(0.0, 0.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
vec3 vb = P - (right - up) * size;
gl_Position = VP * vec4(vb, 1.0);
 Vertex_UV = vec2(0.0, 1.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
vec3 vd = P + (right - up) * size;
gl_Position = VP * vec4(vd, 1.0);
 Vertex_UV = vec2(1.0, 0.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
vec3 vc = P + (right + up) * size;
gl_Position = VP * vec4(vc, 1.0);
 Vertex_UV = vec2(1.0, 1.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
 EndPrimitive();
}
  Fragment shader(片段着色器)
#version 150
uniform sampler2D tex0;
in vec2 Vertex_UV;
in vec4 Vertex_Color;
out vec4 FragColor;
void main (void)
{
vec2 uv = Vertex_UV.xy;
uv.y *= -1.0;
vec3 t = texture(tex0,uv).rgb;
 FragColor = vec4(t, 1.0) * Vertex_Color;
}
  我们可以让几何着色器在摄像机的视空间中工作的更加简单和直接一点,在几何着色器中,来自顶点着色器中的顶点的位置乘以模型-视图矩阵可以用来代替模型矩阵。在视图空间中我们可以简单的扩展这个一个点到一个四边形框中。由于我们在视图视图空间中,我们只需要简单的将这个点转换成2D向量的形式。之后,对于每一个新的顶点位置(va,vb,vc and vd)已经被乘以投影矩阵,因此不在需要计算Right和Up向量了.
  Vertex shader(顶点着色器)
#version 150
in vec4 gxl3d_Position;
in vec4 gxl3d_Color;
// GLSL Hacker automatic uniforms:
uniform mat4 gxl3d_ModelViewMatrix;
out Vertex
{
vec4 color;
} vertex;
void main()
{
gl_Position = gxl3d_ModelViewMatrix * gxl3d_Position;
vertex.color = gxl3d_Color;
}
Geometry shader
#version 150
layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 4) out;
// GLSL Hacker automatic uniforms:
uniform mat4 gxl3d_ProjectionMatrix;
uniform float particle_size;
in Vertex
{
vec4 color;
} vertex[];
out vec2 Vertex_UV;
out vec4 Vertex_Color;
void main (void)
{
vec4 P = gl_in[0].gl_Position;
 // a: left-bottom
vec2 va = P.xy + vec2(-0.5, -0.5) * particle_size;
gl_Position = gxl3d_ProjectionMatrix * vec4(va, P.zw);
 Vertex_UV = vec2(0.0, 0.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
 // b: left-top
vec2 vb = P.xy + vec2(-0.5, 0.5) * particle_size;
gl_Position = gxl3d_ProjectionMatrix * vec4(vb, P.zw);
 Vertex_UV = vec2(0.0, 1.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
 // d: right-bottom
vec2 vd = P.xy + vec2(0.5, -0.5) * particle_size;
gl_Position = gxl3d_ProjectionMatrix * vec4(vd, P.zw);
 Vertex_UV = vec2(1.0, 0.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
 // c: right-top
vec2 vc = P.xy + vec2(0.5, 0.5) * particle_size;
gl_Position = gxl3d_ProjectionMatrix * vec4(vc, P.zw);
 Vertex_UV = vec2(1.0, 1.0);
 Vertex_Color = vertex[0].color;
 EmitVertex();
 EndPrimitive();
}
  Fragment shader(片段着色器)
#version 150
uniform sampler2D tex0;
in vec2 Vertex_UV;
in vec4 Vertex_Color;
out vec4 FragColor;
void main (void)
{
vec2 uv = Vertex_UV.xy;
uv.y *= -1.0;
vec3 t = texture(tex0,uv).rgb;
 FragColor = vec4(t, 1.0) * Vertex_Color;
}
  Demos
  GLSL Hacker  的多个示例已经可以使用,你可以在host_api/GLSL_Billboarding_Geometry_Shader/文件夹下的示例代码包中找到它们。


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引