Использование языка шейдеров WebGL (GLSL) для произвольной векторной математики в JavaScript
Язык шейдеров WebGL (GLSL) - очень мощный инструмент для многомерной векторной математики.
Есть ли возможность использовать эту мощь из JavaScript (работает в веб-браузере) для частных не-3D-вычислений? Получение данных возможно, но есть ли способ получить данные из JavaScript после выполнения шейдерных вычислений?
Никакой фактический чертеж не нужен, только вычисление векторов.
(Я играю с идеей аппаратного ускоренного симулятора силы тяжести, написанного на JavaScript.)
Спасибо!
В новостях: Khronos, похоже, разрабатывает WebCL, который будет доступной для JavaScript версией OpenCL. Это именно то, что я ищу, но это займет некоторое время...
Ответы
Ответ 1
Насколько я могу видеть из spec, WebGL поддерживает объекты фреймбуфера и операции чтения. Этого достаточно для преобразования данных и их возврата в клиентское пространство. Ниже приведена последовательность операций:
- Создайте FBO с буферами визуализации приложений, которые вам нужны для хранения результата; связать его
- Загрузите все входные данные в текстуры (одного размера).
- Создайте шейдер обработки GLSL, который будет выполнять исчисление внутри части фрагмента, считывая входные данные из текстур и записывая вывод в целевые рендерингы; связать его
- Нарисуйте квад; прочитайте буферы визуализации через
glReadPixels
.
Ответ 2
Получение всплывающих окон из шейдера в браузере на самом деле довольно просто, ограничение - 1 float на пиксель.
Преобразуем 4 ints в 1 float (r: int, g: int, b: int, a: int) → (rgba: float).
Спасибо IEEE
float random(vec2 seed) {
return fract(cos(mod(123456780., 1024. * dot(seed / time, vec2(23.1406926327792690, 2.6651441426902251)))));
}
float shift_right(float v, float amt) {
v = floor(v) + 0.5; return floor(v / exp2(amt));
}
float shift_left(float v, float amt) {
return floor(v * exp2(amt) + 0.5);
}
float mask_last(float v, float bits) {
return mod(v, shift_left(1.0, bits));
}
float extract_bits(float num, float from, float to) {
from = floor(from + 0.5); to = floor(to + 0.5);
return mask_last(shift_right(num, from), to - from);
}
vec4 encode_float(float val) {
if (val == 0.0) return vec4(0, 0, 0, 0);
float sign = val > 0.0 ? 0.0 : 1.0;
val = abs(val);
float exponent = floor(log2(val));
float biased_exponent = exponent + 127.0;
float fraction = ((val / exp2(exponent)) - 1.0) * 8388608.0;
float t = biased_exponent / 2.0;
float last_bit_of_biased_exponent = fract(t) * 2.0;
float remaining_bits_of_biased_exponent = floor(t);
float byte4 = extract_bits(fraction, 0.0, 8.0) / 255.0;
float byte3 = extract_bits(fraction, 8.0, 16.0) / 255.0;
float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0)) / 255.0;
float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent) / 255.0;
return vec4(byte4, byte3, byte2, byte1);
}
Использование:
Shader:
outputcolor = encode_float(420.420f);
JavaScript:
// convert output to floats
output = new Float32Array(output.buffer);
Ответ 3
Да, это выполнимо - есть старая демонстрация (может потребоваться некоторые настройки, чтобы заставить ее работать над спецификацией 1.0 WebGL) Aaron Babcock здесь.