Пересечение Sphere-Ray на GLSL. Различное поведение в зависимости от устройства

Я пытался разработать код затенения для рендеринга атмосферных эффектов на устройствах iOS. Этот код GLSL в значительной степени опирается на исчисление пересечения сфер-лучей, которое я реализовал таким образом на основе http://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering.-simple-shapes/ray-sphere-intersection

highp vec2 intersectionsWithSphere(highp vec3 o,
                               highp vec3 d,
                               highp float r){
highp float a = dot(d,d);
highp float b = 2.0 * dot(o,d);
highp float c = dot(o,o) - pow(r, 2.0);

highp float q = pow(b,2.0) - 4.0 * a * c;
if (q < 0.0){
  return vec2(-1.0, -1.0);
}

highp float sq = sqrt(q);
highp float t1 = (-b - sq) / (2.0*a);
highp float t2 = (-b + sq) / (2.0*a);

if (t1 > t2){
  highp float a = t2;
  t2 = t1;
  t1 = a;
}
return vec2(t1,t2);
}

Код отлично работал на моем iPad Pro 9,7 и на iPhone 6 под управлением iOS 10.0.2.

Однако попытка на iPad 2 (iOS 9.3) и iPad 4 (iOS 10) не дает таких же (ожидаемых) результатов. На пути к решению я также столкнулся с очень странным поведением команды glDrawArray() OpenGL, которая, казалось, иногда давала сбой с, казалось бы, совершенно корректным и компилируемым кодом затенения.

Я проверил, что обе платформы имеют одинаковую точность для высоких чисел с плавающей запятой (27 бит), а также проверил с помощью сфер малого масштаба. Например:

intersectionsWithSphere(vec3(0.0,0.0,2.0),vec3(0.0,1.0,-1.0), 1.0);

не должен возвращать никаких пересечений, но это так.

Я немного новичок в программировании шейдеров, и любая помощь будет оценена по достоинству.


person José Miguel Santana Núñez    schedule 20.10.2016    source источник


Ответы (1)


Проверьте свои функции pow. pow(x, y) не определено, если x отрицательно.

Я хотел бы думать, что компилятор достаточно умен, чтобы преобразовать pow(x, 2.0) в x * x, но я бы не хотел на это рассчитывать.

Если показатель степени является постоянным целым числом, как в вашем случае, вы могли бы просто сделать умножение самостоятельно. Там, где это переменная, вы должны сделать что-то вроде pow(max(0.0, x), y).

Хотя не могу быть уверен, что это проблема.

Сбой в glDrawArray, скорее всего, будет отдельной проблемой, совершенно не связанной с кодом шейдера, гораздо более вероятно, что он пытается использовать вершины из какого-то недопустимого буфера или области памяти.

person Columbo    schedule 21.10.2016
comment
Спасибо за быстрый ответ. Определенно вы чем-то там занимались, pow(x, 2.0) определенно не то же самое, что x * x, и это, по крайней мере, часть моей проблемы. - person José Miguel Santana Núñez; 25.10.2016
comment
Однако похоже, что разрешение vec3 на highp float на старых машинах не позволяет с достаточной точностью вычислять пересечения со сферами. - person José Miguel Santana Núñez; 25.10.2016
comment
Тогда он работает на фрагментном шейдере? К сожалению, устройства GLES2 не должны поддерживать highp для фрагментных шейдеров и могут просто использовать mediump (с точки зрения процессора, в значительной степени половинная точность вместо одинарной точности) - person Columbo; 25.10.2016
comment
Спасибо @Коломбо. Я, наконец, получил правильное исправление. :) Прежде всего, у меня была эта проблема с pow(), как вы указали. Это дало мне некоторые результаты в виде очень низкокачественного пересечения лучей и сфер. Что касается проблемы с точностью, я попытался масштабировать всю сцену без каких-либо улучшений. Во-вторых, я перепроверил, что точность на обеих платформах одинакова. В отчаянии я передал вычисления направления луча в вершинный шейдер, и это волшебным образом сработало! - person José Miguel Santana Núñez; 27.10.2016
comment
Итак, на данный момент я пришел к двум возможным вариантам: 1) либо есть проблема с glGetShaderPrecisionFormat(), так как явно есть разница между выполнением некоторых операций над вершинным шейдером и над фрагментным шейдером. 2) интерполяция вариаций недостаточно хороша на некоторых платформах. - person José Miguel Santana Núñez; 27.10.2016