Высокая точность вывода из шейдера GLES2
Я делаю кое-что из GPGPU на платформе GLES2, которая поддерживает максимальное количество целей рендеринга RGBA8 (iOS). Мне нужно вывести vec2 в диапазоне +/- 2.0 с максимально возможной точностью, поэтому я пытаюсь упаковать каждый компонент в два компонента 8-битного вывода.
Важным требованием является то, что декодирование + кодирование в обоих направлениях сохраняет закодированное значение. Мое текущее решение не обладает этим свойством, и в результате мои значения смещаются повсюду.
Это то, что у меня сейчас (это немного многословно, потому что я все еще обдумываю это):
const float fixed_scale = 4.0;
lowp vec4 encode_fixed(highp vec2 v) {
vec2 scaled = 0.5 + v/fixed_scale; // map to range 0..1
vec2 low = fract(scaled * 255.0); // extract low order part
vec2 high = scaled - low/255.0; // subtract low from high order part
return vec4(low.x,high.x,low.y,high.y); // pack into rgba8
}
vec2 decode_fixed(highp vec4 v) {
vec2 scaled = v.yw + v.xz/255.0; // recombine low and high parts
return (scaled - 0.5) * fixed_scale; // map back to original range
}
РЕДАКТИРОВАТЬ: более простой код, но все еще дрейфует
2 ответа
Я думаю, что это поможет вам:
vec4 PackFloat8bitRGBA(float val) {
vec4 pack = vec4(1.0, 255.0, 65025.0, 16581375.0) * val;
pack = fract(pack);
pack -= vec4(pack.yzw / 255.0, 0.0);
return pack;
}
float UnpackFloat8bitRGBA(vec4 pack) {
return dot(pack, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0));
}
vec3 PackFloat8bitRGB(float val) {
vec3 pack = vec3(1.0, 255.0, 65025.0) * val;
pack = fract(pack);
pack -= vec3(pack.yz / 255.0, 0.0);
return pack;
}
float UnpackFloat8bitRGB(vec3 pack) {
return dot(pack, vec3(1.0, 1.0 / 255.0, 1.0 / 65025.0));
}
vec2 PackFloat8bitRG(float val) {
vec2 pack = vec2(1.0, 255.0) * val;
pack = fract(pack);
pack -= vec2(pack.y / 255.0, 0.0);
return pack;
}
float UnpackFloat8bitRG(vec2 pack) {
return dot(pack, vec2(1.0, 1.0 / 255.0));
}
Обратите внимание на устранение аппаратного смещения: pack -= vec4(pack.yzw / 255.0, 0.0)
- большое спасибо Арасу Пранккявичюсу за это
Хорошо, я отвечу на свой вопрос. Это, кажется, работает - это не дрейфует, но визуальные результаты выглядят немного неточными для меня. Обратите внимание на округление в декодере, что необходимо.
const float fixed_scale = 4.0;
lowp vec4 encode_fixed(highp vec2 v) {
vec2 scaled = 0.5 + v/fixed_scale;
vec2 big = scaled * 65535.0/256.0;
vec2 high = floor(big) / 255.0;
vec2 low = fract(big);
return vec4(low.x,high.x,low.y,high.y);
}
vec2 decode_fixed(highp vec4 v) {
v = floor(v * 255.0 + 0.5);
vec2 scaled = vec2(v.yw * 256.0 + v.xz) / 65535.0;
return (scaled - 0.5) * fixed_scale;
}