#android #opengl-es #opengl-es-3.0 #mali
#Android #opengl-es #opengl-es-3.0 #mali
Вопрос:
У меня есть сообщения от моих пользователей о проблемах с отображением данных в режиме полуплавания на некоторых устройствах с графическими процессорами Mali (Huawei Honor 9 и Samsung Galaxy S10 с Mali G71 и G76 соответственно).
Это приводит к искаженному рендерингу на этих устройствах, хотя корректно работает на графических процессорах Adreno и PowerVR.
Я дважды проверил код, и он кажется правильным:
...
if (model.hasHalfFloats()) {
GLES20.glVertexAttribPointer(shaderOutline.getRm_Vertex(), 3, GLES20.GL_FLOAT, false, 18, 0);
GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, getGL_HALF_FLOAT(), false, 18, 12);
} else {
GLES20.glVertexAttribPointer(shaderOutline.getRm_Vertex(), 3, GLES20.GL_FLOAT, false, 24, 0);
GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, GLES20.GL_FLOAT, false, 24, 12);
}
...
/**
* Returns either OES extension for GL 16-bit floats (if used in ES 2.0) or ES 3.0 constant.
*/
protected int getGL_HALF_FLOAT() {
if(isES3()) {
return GLES30.GL_HALF_FLOAT;
} else {
return GL_HALF_FLOAT_OES;
}
}
Кажется, код правильно определяет OpenGL ES 3 и использует GLES30.GL_HALF_FLOAT
значение в getGL_HALF_FLOAT()
.
Пример кода шейдера:
vertexShaderCode = "attribute vec4 rm_Vertex;rn"
"attribute mediump vec3 rm_Normal;rn"
"uniform mat4 view_proj_matrix;rn"
"uniform float uThickness1;rn"
"void main( void )rn"
"{rn"
" vec4 pos = vec4(rm_Vertex.xyz, 1.0);rn"
" float dist = (view_proj_matrix * pos).w;rn"
" vec4 normal = vec4(rm_Normal, 0.0);rn"
" pos = normal * uThickness1 * dist;rn"
" gl_Position = view_proj_matrix * pos;rn"
"}";
fragmentShaderCode = "precision mediump float;rn"
"uniform vec4 uColor;rn"
"void main( void )rn"
"{rn"
" gl_FragColor = uColor;rn"
"}";
Ответ №1:
Я думаю, у вас проблема с выравниванием. Из этого фрагмента (и вашего вершинного шейдера):
GLES20.glVertexAttribPointer(shaderOutline.getRm_Normal(), 3, getGL_HALF_FLOAT(), false, 18, 12);
Я могу сделать вывод, что вы пытаетесь создать структуру вершин следующим образом:
float fPos[3];
half fNormal[3];
Вы получили высоту шага в 18 вершин, которая, предположительно, была получена путем суммирования отдельных размеров элементов (3*sizeof(float)) (3*sizeof(half)) = 12 6 = 18
.
Однако шаг должен быть равен 20, потому что в противном случае ваши вершины будут смещены. 4-байтовые числа с плавающей точкой должны начинаться с 4-байтовой границы, но это не так.
Из спецификации GLES3:
Клиенты должны согласовывать элементы данных в соответствии с требованиями клиентской платформы, с дополнительным требованием базового уровня, чтобы смещение в буфере относительно базы данных, содержащей N базовых машинных блоков, было кратным N
Комментарии:
1. Я отредактировал свой фрагмент кода, о котором идет речь, чтобы продемонстрировать размеры шага. Несоответствие выравнивания является довольно серьезной ошибкой и приведет к повреждению вывода на любом графическом процессоре. Однако это работает на графических процессорах Adreno и PowerVR.
2. Я хочу сказать, что 18 никогда не является допустимым шагом для вершины, которая включает в себя число с плавающей точкой, потому что 18 не кратно 4.
3. Как я могу получить это значение выравнивания, зависящее от оборудования?
4. Я процитировал соответствующую часть спецификации — число с плавающей точкой состоит из 4 машинных единиц (байт), поэтому его необходимо выровнять по границам в 4 байта — это не зависит от аппаратного обеспечения. То, что код работает на некоторых платформах, не означает, что он правильный. Люди часто думают, что самое худшее в неопределенном поведении — это то, что оно может привести к сбою или вызвать серьезные ошибки, но часто самое худшее в этом то, что оно может работать абсолютно нормально — пока этого не произойдет.
5. Общая рекомендация для этого заключается в том, чтобы всегда упаковывать векторы с половинными плавающими значениями в четное количество элементов, по возможности упаковывая скаляр и
vec3
вместе, чтобы создатьvec4
атрибут, чтобы избежать заполнения байтов.