Вычислительные шейдеры в Dagon
С переходом на OpenGL 4.3 в движке появляется возможность использовать вычислительные шейдеры. Они довольно удобно легли поверх уже существующией шейдерной системы — добавились всего два новых класса, ComputeProgram (аналог ShaderProgram) и ComputeShader, наследующий от Shader. Механизм привязки параметров целиком остается тот же самый, никаких изменений, за исключением способа привязки текстур — для этого есть метод ComputeShader.bindImageTexture. Привязываются обычные текстуры Dagon, как-то специально их готовить не нужно. Чтобы создать свой шейдер, нужно наследовать от ComputeShader, создать программу и параметры, а в bindParameters, как обычно, задать им значения:
class TestComputeShader: ComputeShader
{
protected:
String cs;
ShaderParameter!Color4f _fillColor;
public:
Texture outputTexture;
Color4f fillColor = Color4f(1.0f, 1.0f, 1.0f, 1.0f);
this(Owner owner)
{
cs = Shader.load("data/test.comp.glsl");
ComputeProgram p = New!ComputeProgram(cs, this);
super(p, owner);
_fillColor = createParameter!Color4f("fillColor");
}
~this()
{
cs.free();
}
override void bindParameters(GraphicsState* state)
{
_fillColor = fillColor;
if (outputTexture)
bindImageTexture(0, outputTexture, TextureAccessMode.Write);
super.bindParameters(state);
}
void run()
{
if (outputTexture)
super.run(outputTexture.width, outputTexture.height);
}
}
TestComputeShader cs = New!TestComputeShader(assetManager);
cs.outputTexture = myTexture;
cs.fillColor = Color4f(0.0f, 0.5f, 1.0f, 1.0f);
cs.run();
Пример шейдера, который заполняет текстуру «синусной плазмой» заданного цвета:
#version 430
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
layout(rgba8, binding = 0) writeonly uniform image2D outputTexture;
uniform vec4 fillColor;
void main()
{
ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
const float scale = 0.25;
float value = 0.5f +
0.25f * cos(coord.x * scale) +
0.25f * cos(coord.y * scale);
vec3 color = fillColor.rgb * value;
imageStore(outputTexture, coord, vec4(color, 1.0));
}
ComputeShader.run уже ставит барьер памяти, в приложении это делать не нужно. Есть методы dispatch и barrier для запуска вручную.