Skip to main content

点精灵材质

Point Sprites

Point Sprites,是指在渲染屏幕上通过特殊方式绘制的一个点,它可以呈现出任意定义的某种图案,而且始终面向相机方向。点精灵不是实际存在的几何元素,而是用来表达特殊视觉效果的纹理,也许单个点精灵没有意义,但当您把成千上万个这些点精灵放在一起的时候,就会创造出令人惊叹的效果。

点精灵的图案,或者说是颜色分布函数,可以直接定义在fragment shade中,也可以是应用程序中定义的一个纹理图案,下面分别给出两个例子介绍如何定义点精灵。

着色器中定义颜色分布函数

首先我们应该定义几何,点精灵需要的几何就是点,在这个例子中,我们在3D空间中创建了一些随机分布的点。

auto size = 125;
auto positionData = _f32array(size * 2);
auto colorData = _f32array(size * 3);
auto sizeData = _f32array(size);

for (int i = 0; i < size; i++)
{
(*positionData)[i * 2] = rand() / (float)RAND_MAX * 2 - 1;
(*positionData)[i * 2 + 1] = rand() / (float)RAND_MAX * 2 - 1;

(*colorData)[i * 3] = rand() / (float)RAND_MAX * 0.4 + 0.3;
(*colorData)[i * 3 + 1] = rand() / (float)RAND_MAX * 0.4 + 0.3;
(*colorData)[i * 3 + 2] = rand() / (float)RAND_MAX * 0.4 + 0.3;

(*sizeData)[i] = rand() / (float)RAND_MAX * 30 + 30;
}

auto geometry = _geometry(
{
{"positions", _vertices(positionData)},
{"colors", _vertices(colorData)},
{"sizes", _vertices(sizeData)},
},
_points(size)
);

在没有添加精灵图案时,这些点的渲染结果如下:

sprites1

接下来就是重头戏,我们需要在片段着色器中定义像素图案。下面是顶点着色器和片段着色器程序:

std::string vertexSource = R"(
#version 330 core

in vec2 positions;
in vec3 colors;
in float sizes;

out vec3 v_Color;
out vec2 v_Position;

void main()
{
gl_PointSize = sizes;
gl_Position = vec4(positions, 0.0, 1.0);

v_Color = colors;
v_Position = positions;
}
)";

std::string fragmentSource = R"(
#version 330 core

in vec3 v_Color;
in vec2 v_Position;
uniform sampler2D texture0;

out vec4 fragColor;

float random(vec2 st)
{
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

void main(void) {
// 生成从0到6随机数,赋值给type
int type = int(random(v_Position) * 7.0);
vec2 coord = gl_PointCoord * 2.0 - vec2(1.0);
// 接下来定义了7种不同的像素图案,随机附着在所有的点上,成为点精灵
switch(type)
{
// 圆盘图案
case 0:
{
if (dot(coord, coord) > 1.0)
discard;
}
break;
// 螺旋纹图案
case 1:
{
float radius = sqrt(dot(coord, coord));
float theta = atan(coord.y, coord.x);
if (dot(coord, coord) > cos(theta - 20.0 * radius))
discard;
}
break;
// 花瓣图案
case 2:
{
float theta = atan(coord.y, coord.x);
if (dot(coord, coord) > cos(theta * 5.0))
discard;
}
break;
// 球形图案
case 3:
{
if (dot(coord, coord) > 1.0)
discard;

float dist = distance(gl_PointCoord, vec2(0.5));
float fNDotV = sqrt(1.0 - dist * dist / 0.25);

fragColor = vec4(v_Color * fNDotV, 1.0);

// fragColor = texture(texture0, gl_PointCoord);

return;
}
break;
// 星星图案
case 4:
{
float radius = sqrt(dot(coord, coord));
float theta = atan(coord.y, coord.x);
if (dot(coord, coord) > 0.5 * (exp(cos(theta * 5.0) * 0.75)))
discard;
}
break;
// 圆环图案
case 5:
{
float distance = dot(coord, coord);
if (distance > 1.0 || distance < 0.6)
discard;
}
break;
// 方格图案
case 6:
{
bool light = false;
if (coord.x * coord.y > 0.0)
light = true;

fragColor = vec4(light ? v_Color : v_Color * 1.5, 1.0);
return;
}
break;
}

fragColor = vec4(v_Color, 1.0);
}
)";
sprites

定义纹理图案

与纹理贴图一样,在定义材质material时,包含一个纹理贴图texture0(image),而且在该材质的面元着色器中编写了函数,该函数将贴图texture0与点结合起来,使得该贴图被铺在了这个点上。最后您可以得到下面图片中看到的效果。

auto material = _material(
_program(
vertexSource, fragmentSource
),
nullptr,
_states(
{
_point(1, true),
// 材质中包含了贴图texture0
_texture0(image) },
_blend(true)
)
);
std::string fragmentSource = R"(
#version 330 core
in vec3 v_Color;
in vec2 v_Position;
// 将材质的贴图作为uniform变量传入着色器中
uniform sampler2D texture0;

out vec4 fragColor;
void main()
{
// 贴图的图案被铺在了点所在的位置上
fragColor = texture(texture0, gl_PointCoord);
}
)";
sprites_icon