Sistema de partículas con geometry shader
Esta escena, al igual que en anteriores ocasiones, se realizó con OpenGl en Golang (Go) y es una continuación de la escena anterior. En esta ocasión se implementó un sistema de partículas el cual usa un pipeline propio con geometry shaders para lograr dibujar dichas partículas.
Para lograr el resultado visto fue primero necesario lograr dibujar correctamente las partículas. Para esto se modificaron los shaders presentados en el tutorial de geometry shaders de learnopengl.com. Siguiendo este tutorial es posible obtener el siguiente resultado, proporcionando solo 4 puntos (ubicaciones bidimensionales) con sus respectivos colores el geometry shader es capaz de dibujar una figura semejante a una casa en cada uno de dichos puntos.
Tomando esto como base, se realizaron los cambios necesarios para que dadas ubicaciones tridimensionales y una textura como la que se muestra a continuación, el geometry shader fuera capaz de crear una figura simple de 4 vértices con dicha textura en la ubicación especificada. Esto se logró recibiendo la ubicación y la textura desde el vertex shader y creando 4 vértices relativos a dicha ubicación, a la vez que se asignaron las coordenadas de textura correspondiente a cada vértice.
Esta escena, al igual que en anteriores ocasiones, se realizó con OpenGl en Golang (Go) y es una continuación de la escena anterior. En esta ocasión se implementó un sistema de partículas el cual usa un pipeline propio con geometry shaders para lograr dibujar dichas partículas.
Para lograr el resultado visto fue primero necesario lograr dibujar correctamente las partículas. Para esto se modificaron los shaders presentados en el tutorial de geometry shaders de learnopengl.com. Siguiendo este tutorial es posible obtener el siguiente resultado, proporcionando solo 4 puntos (ubicaciones bidimensionales) con sus respectivos colores el geometry shader es capaz de dibujar una figura semejante a una casa en cada uno de dichos puntos.
Tomando esto como base, se realizaron los cambios necesarios para que dadas ubicaciones tridimensionales y una textura como la que se muestra a continuación, el geometry shader fuera capaz de crear una figura simple de 4 vértices con dicha textura en la ubicación especificada. Esto se logró recibiendo la ubicación y la textura desde el vertex shader y creando 4 vértices relativos a dicha ubicación, a la vez que se asignaron las coordenadas de textura correspondiente a cada vértice.
En este punto ya era posible dibujar un cuadrado con una textura específica dado un punto en el espacio, sin embargo para obtener el comportamiento de una partícula, con el cual esta siempre debe posicionarse de cara a la cámara fue necesario realizar una modificación más. La forma más sencilla de conseguir este resultado fue trabajar directamente en el espacio de vista de la cámara, es decir que se modificó la posición en el vertex shader para que ahora se multiplique por la matriz de modelo y vista. Por último se agregó la perspectiva en el geometry shader, multiplicando esta con los componentes X y Y de la posición calculada anteriormente en el vertex shader.
... void main() { vs_out.color = aColor; size = aSize; gl_Position = model * view * vec4(aPos, 1.0); }
... void main (void) { vec4 P = gl_in[0].gl_Position; // a: left-bottom vec2 va = P.xy + vec2(-0.5, -0.5) * size[0]; gl_Position = projection * vec4(va, P.zw); fUV = vec2(0.0, 0.0); fColor = gs_in[0].color; EmitVertex(); ...
Además de estas modificaciones imprescindibles para lograr el comportamiento típico de una partícula, se realizaron otras modificaciones menores, como el poder enviar el tamaño o el valor de alpha desde el programa principal. Esto con el fin de poder cambiar estos valores con el tiempo y dar un efecto de desvanecimiento al final del tiempo de vida de la partícula.
Una vez se tuvo la posibilidad de dibujar partículas dado los puntos, solo restaba crear el sistema de partículas que manejara su comportamiento. Para esto se creó un sistema propio basado en la teoría presentada en How To Create Instanced Particles In OpenGL. Para esto fue necesario crear dos estructuras, para el origen de las partículas y para las partículas individuales, en estas estructuras se guardó información como la posición, el color, la velocidad, el tiempo de vida, entre otros atributos necesarios para realizar la animación. Para cada estructura se crearon funciones de inicialización y una función de actualización, para actualizar constantemente la posición, dirección, tamaño y opacidad de cada partícula.
... type Particles struct { particles []Particle points []float32 color mgl32.Vec3 position, velocity, amplitude mgl32.Vec3 minLife, maxLife, size float32 } type Particle struct { x, y, z, alpha, size *float32 life0, life float32 velocity mgl32.Vec3 } ...
Por último, se combinó esta implementación de partículas con la implementación anterior de luces, con lo cual fue posible crear una fuente de luz que cambiara de posición junto a la fuente de partículas. Esto se puede observar con mayor facilidad en la siguiente prueba de concepto. Para esto fue necesario usar distintos programas, cada uno de ellos con sus propios shaders.
Por último, se integró el sistema de partículas con la escena realizada anteriormente para obtener el siguiente resultado.
Shaders
Vertex Shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #version 410 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec4 aColor; layout (location = 2) in float aSize; out VS_OUT { vec4 color; } vs_out; out float size; uniform mat4 model; uniform mat4 view; void main() { vs_out.color = aColor; size = aSize; gl_Position = model * view * vec4(aPos, 1.0); } |
Fragment Shader
1 2 3 4 5 6 7 8 9 10 11 12 | #version 410 uniform sampler2D tex0; out vec4 FragColor; in vec2 fUV; in vec4 fColor; void main (void) { vec2 uv = fUV.xy; uv.y *= -1.0; vec4 texColor = texture(tex0, uv); FragColor = texColor * fColor; } |
Geometry Shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #version 410 core layout (points) in; layout (triangle_strip, max_vertices = 380) out; in VS_OUT { vec4 color; } gs_in[]; in float[] size; uniform mat4 projection; out vec2 fUV; out vec4 fColor; void main (void) { vec4 P = gl_in[0].gl_Position; // a: left-bottom vec2 va = P.xy + vec2(-0.5, -0.5) * size[0]; gl_Position = projection * vec4(va, P.zw); fUV = vec2(0.0, 0.0); fColor = gs_in[0].color; EmitVertex(); // b: left-top vec2 vb = P.xy + vec2(-0.5, 0.5) * size[0]; gl_Position = projection * vec4(vb, P.zw); fUV = vec2(0.0, 1.0); fColor = gs_in[0].color; EmitVertex(); // d: right-bottom vec2 vd = P.xy + vec2(0.5, -0.5) * size[0]; gl_Position = projection * vec4(vd, P.zw); fUV = vec2(1.0, 0.0); fColor = gs_in[0].color; EmitVertex(); // c: right-top vec2 vc = P.xy + vec2(0.5, 0.5) * size[0]; gl_Position = projection * vec4(vc, P.zw); fUV = vec2(1.0, 1.0); fColor = gs_in[0].color; EmitVertex(); EndPrimitive(); } |








Comentarios
Publicar un comentario