MyTetra Share
Делитесь знаниями!
Как запоминать данные в GLSL шейдерах в текстурах
Время создания: 11.08.2021 01:16
Текстовые метки: glsl, шейдер, данные, массив, переменная, текстура, запомнить, передача, хранение, данные
Раздел: Компьютер - Программирование - Шейдеры - Шейдеры GLSL
Запись: xintrea/mytetra_syncro/master/base/1628633780zwhuhnonzj/text.html на raw.github.com

Здесь собирается информация о том, как в шейдерах передавать данные между кадрами через текстуру. Автор этой заметки знаком с шейдерами две недели, урывками по ночам, и многого не понимает.


* * *


Иногда в шейдерах нужно иметь возможность запоминать данные между кадрами. Сделать это можно с помощью текстур. Идея в том, что в текстуре можо организовывать хранение данных в определенном программистом формате.


Далее идет разбор кода в web-среде ShaderToy.


Чтение и запись в текстуру может выглядеть так:



#define txBuf iChannel0

#define txSize iChannelResolution[0].xy


const float txRow = 32.;


vec4 Loadv4 (int idVar)

{

float fi = float (idVar);

return texture (txBuf, (vec2 (mod (fi, txRow), floor (fi / txRow)) + 0.5) / txSize);

}


void Savev4 (int idVar, vec4 val, inout vec4 fCol, vec2 fCoord)

{

float fi = float (idVar);

vec2 d = abs (fCoord - vec2 (mod (fi, txRow), floor (fi / txRow)) - 0.5);

if (max (d.x, d.y) < 0.5) fCol = val;

}



Здесь предполагается, что в текстуру записывается массив значений типа vec4. У функции Savev4() следующие параметры:




Параметр


Описание


int idVar

-

числовой идентификатор частицы (по сути, индекс в массиве)

vec4 val

-

запоминаемое значение, например направление движение частицы

inout vec4 fCol

-

цвет текущего экранного текселя

vec2 fCoord

-

координата текущего экранного текселя



Запоминание и чтение происходит через указание номера элемента массива.


Пример записи значения:



vec4 p;

...

Savev4 (mId, p, fragColor, fragCoord);



А чтение происходит просто путем вызова функции Loadv4() с указанием индекса значения.


Как видно из кода, сохранение значения происходит в цвет текущего пикселя fragColor. И это не совсем то, что надо. Совершенно непонятно, как в этом случае формируется изображение, если fragColor используется и для задания цвета точки изображения, и для сохранения каких-то пользовательских данных.


Пример работы с текстурой как с хранилищем данных можно найти по ссылке: https://www.shadertoy.com/view/4dG3RW. Похоже, что такой подход работает только в ShaderToy, так как он позволяет задать сразу несколько шейдеров, и они друг другу по кругу передают данные. При этом непонятно, как формируется цвет пикселя, который видит пользователь. А как сделать то же самое используя один шейдер, как в Bonzomatic, пока что непонятно.


Нужно подумать, возможно можно сделать более простой механизм запоминания, без использования fragColor и fragCoord.


* * *


Возможно, что для Bonzomatic подойдет возможность, которая появилась в OpenGL 4.2. Это пара функций для работы не с текстурами, а с изображениями: imageStore() и imageLoad(). Понять, что такое изображение в отличие от текстуры, достаточно сложно, и этот вопрос пока остается открытым.


В любом случае, в Bonzomatic без ошибок компилируется такой код:



#version 420 core


layout(r8ui) uniform uimage2D emptyTexture;


void main(void)

{

imageStore(emptyTexture,

ivec2( floor(gl_FragCoord.x*1024),

floor(gl_FragCoord.y*1024)),

uvec4(128));


float value = imageLoad(emptyTexture,

ivec2( floor(gl_FragCoord.x*1024),

floor(gl_FragCoord.y*1024))).r;


...

}



Здесь текстура emptyTexture - это заранее подготовленная текстура в виде *.png-файла, залитого черным цветом или полностью прозрачного, размером 1024x1024 pix. Данная текстура по задумке, должна быть просто промежуточным хранилищем данных. Текстура подключается к Bonzomatic в файле config.json стандартным для Bonzomatic способом:



{

"skipSetupDialog":


"window":{

"fullscreen":true,

},


"textures": {

"textureLabel": "textures/label.png",

"emptyTexture": "textures/emptyTexture.png"

},

}



Пока что сделать работающий пример не удалось. При первом вызове функции main() нужно сделать инициализацию этого "хранилища" какими-то данными, и в конце работы кода шейдера надо сделать imageStore(). При последующих вызовах main() код каким-то образом должен узнать, что он вызывается не в первый раз, и вместо инициализации сделать считываение предыдущих данных с помощью imageLoad().


Говорят, что данный метод не очень эффективен с точки зрения производительности, потому что сохранение и считывание данных происходит с помощью центрального процессора, а не в GPU.


В общем, с этим методом нужно разбираться отдельно.


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования