Необъяснимое поведение в шейдере WebGL на iOS - PullRequest
0 голосов
/ 30 апреля 2018

Я вижу странное поведение, которое не могу объяснить в шейдере WebGL, который я написал. Для меня это не область знаний, поэтому вполне возможно, что я неправильно понимаю что-то довольно простое, но я не уверен, что именно.

У меня есть кодовая ручка, иллюстрирующая странное поведение здесь https://codepen.io/bjvanminnen/pen/XqMpvL.

void main () {
    vec3 color = vec3(0.);

    vec2 loc1 = vec2(0.5, 0.) / u_res;
    vec2 loc2 = vec2(1.5, 0.) / u_res;
    float val1 = texture2D(u_tex, loc1).r * 255.;
    float val2 = texture2D(u_tex, loc1).r * 255.;
    color.r = val1 == val2 ? 1. : 0.;
    // commenting/uncommenting this line somehow affects the b value
    // on iOS, when uncommented we end up with (53, 0, 255, 255)
    // when commented we end up with (255, 0, 0, 255)
    // I can think of no reason why the below line should affect color.b
    color.r = floor(val1) / 255.;

    color.b = val1 == 53. ? 1. : 0.;      
    gl_FragColor = vec4(color, 1.);
}

Краткое изложение того, что происходит

  • В JS я создаю текстуру, где каждый пиксель имеет вид # 350000

  • В моем фрагментном шейдере прочитайте первые два пикселя

  • Первоначально установить значение r на выходе равным 1, если два пикселя идентичны (они должны быть)

  • При отсутствии комментариев измените значение color.r на основании минимального значения val1 (шаг, который я не ожидал бы повлиять на color.b)

  • Установите значение b на выходе равным 1, если значение пикселя равно 53 (то есть 0x35).

  • Затем я проверяю полученные значения пикселей в JS.

Мои ожидания: Когда вторая строка color.r закомментирована, я ожидаю результат (53, 0, 255, 255). Это то, что я вижу на своем рабочем столе, но на моем устройстве iOS я вижу (53, 0, 0, 255). Похоже, что в iOS значение val1 немного больше 53. Я предполагаю, что это просто странность с плавающей точкой.

Что очень странно, и я не могу понять, что когда я закомментирую вторую строку color.r, я получаю (255, 0, 255, 255) на своем рабочем столе - что имеет смысл - но (255, 0, 0, 255) на моем устройстве iOS.

Другими словами, наличие / отсутствие строки color.r = floor(val1) / 255.; как-то меняет результат color.b.

Сталкивался ли я с какой-то странной ошибкой iOS, или я что-то здесь неправильно понимаю?

1 Ответ

0 голосов
/ 01 мая 2018

В спецификации GLSL ES 1.0 есть несколько разделов об инвариантности, начиная с раздела 4.6

Важной частью вашего дела, вероятно, является 4.6.2

4.6.2 Инвариантность внутри шейдеров

Когда значение сохраняется в переменной, обычно предполагается, что оно останется постоянным, если явно изменилось. Однако в процессе оптимизации возможно, что компилятор выберет пересчитать значение, а не сохранять его в регистре. Поскольку точность операций не полностью заданный (например, операция низкой точности может быть выполнена со средней или высокой точностью), это было бы возможно для пересчитанного значения, чтобы отличаться от исходного значения.

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

В шейдере нет инвариантности для значений, генерируемых различными неконстантными выражениями, даже если эти выражения идентичны.

Пример 1:

precision mediump;
vec4 col;
vec2 a = ...
...
col = texture2D(tex, a); // a has a value a1
...
col = texture2D(tex, a); // a has a value a2 where possibly a1 ≠ a2

Для обеспечения неизменности в этом примере используйте:

#pragma STDGL invariant(all)

Пример 2:

vec2 m = ...;
vec2 n = ...;
vec2 a = m + n;
vec2 b = m + n; // a and b are not guaranteed to be exactly equal

Не существует механизма для обеспечения инвариантности между a и b.

Вы сравниваете число с плавающей точкой с точным значением, которое кажется опасным практически на любом языке. Если я изменю эту строку

color.b = val1 == 53. ? 1. : 0.; 

до

color.b = val1 > 52.9 && val1 < 53.1 ? 1. : 0.; 

Тогда это работает, как и ожидалось для меня. Кроме того, если я пишу val1 с

color.g = val1 / 153.;

Я не вижу, чтобы это изменилось. Это 88 независимо от того, закомментирована ли эта строка, даже если ваш тест не пройден. Это подсказывает мне, что это не 53.0 в одном случае. Это 53,000000000001 или 52,99999999999999 и т. Д.

...