开发者

High precision output from a GLES2 shader

开发者 https://www.devze.com 2023-03-19 23:41 出处:网络
I\'m doing some GPGPU stuff on a GLES2 platform that supports maximum RGBA8 render targets (iOS). I need to o开发者_开发知识库utput a vec2 in the range +/- 2.0 with as much precision as I can get, so

I'm doing some GPGPU stuff on a GLES2 platform that supports maximum RGBA8 render targets (iOS). I need to o开发者_开发知识库utput a vec2 in the range +/- 2.0 with as much precision as I can get, so I'm trying to pack each component into two components of the 8-bit output.

An important requirement is that a decode+encode round trip preserves the encoded value. My current solution does not have this property and as a result my values are drifting all over the place.

This is what I have now (it's a bit verbose because I'm still thinking my way through it):

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
}

EDIT: simpler code, but still drifts


i think that will help you:

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));
}

note the elemination of hardware biasing: pack -= vec4(pack.yzw / 255.0, 0.0) - big thanks to Aras Pranckevičius for that


Ok, I'll answer my own question. This seems to work -- it doesn't drift, but the visual results look a bit inaccurate to me. Notice the rounding in the decoder, which is necessary.

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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号