Преломление в OpenGL ES 2.0 / 3.0. Текстура больших пикселей

Пробуем реализовать преломление в OpenGL ES 2.0 / 3.0. Использованы следующие шейдеры:

Вершинный шейдер:

#version 300 es
precision lowp float; 
uniform mat4 u_mvMatrix;

in vec4 a_position;  
in vec3 a_normal;
...
out mediump vec2 v_refractCoord;

const mediump float eta = 0.95;

void main() {
    vec4 eyePositionModel = u_mvMatrix * a_position;

    // eye direction in model space
    mediump vec3 eyeDirectModel = normalize(a_position.xyz - eyePositionModel.xyz);

    // calculate refraction direction in model space
    mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);

    // project refraction
    refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;

    // map refraction direction to 2d coordinates
    v_refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;
    ...
}

Фрагментный шейдер:

...
in mediump vec2 v_refractCoord;
uniform samplerCube s_texture; // skybox

void main() {
    outColor = texture(s_texture, vec3(v_refractCoord, 1.0));
}

Метод загрузки текстуры:

@JvmStatic
fun createTextureCubemap(context: Context, rowID: Int) {
    val input = context.resources.openRawResource(rowID)
    val bitmap = BitmapFactory.decodeStream(input)

    val textureId = IntArray(1)
    glGenTextures(1, textureId, 0)
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureId[0])

    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, bitmap, 0)
    GLUtils.texImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, bitmap, 0)

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    return textureId[0]
}

Но текстура получается с крупными пикселями вроде:

введите описание изображения здесь

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

Примечание: чем меньше полигонов - тем хуже становится качество.

Заранее благодарим за любой комментарий / ответ!

изображение с сайта goodfon.ru

Решение: по совету @ Rabbid76 поменял нормальные данные. Оказалось, что в Blender'е нужно установить Shading для объекта как smooth (no flat) - это увеличивает количество нормалей при экспорте в формат * .obj: Почему экспорт OBJ записывает нормали граней вместо нормалей вершин

Также по совету @ Rabbid76 я изменил строку:

vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);

В результате пикселизация исчезла:

введите описание изображения здесь

Кроме того, пиксельные артефакты также могут появляться при вычислении рефракции в вершинном шейдере, поэтому я перенес вычисления во фрагментный шейдер. Вот модифицированный код шейдера:

Вершинный шейдер:

#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;

in vec4 a_position; 
in vec3 a_normal;

out vec3 v_normal;
out lowp float SpecularIntensity;

out vec3 v_eyeDirectModel;

float getSpecularIntensity(vec4 position, vec3 a_normal, vec3 eyeDirectModel) {
    float shininess = 30.0;
    vec3 lightPosition = vec3(-20.0, 0.0, 0.0);
    mediump vec3 LightDirModel = normalize(lightPosition - position.xyz);
    mediump vec3 halfVector = normalize(LightDirModel + eyeDirectModel);
    lowp float NdotH = max(dot(a_normal, halfVector), 0.0);
    return pow(NdotH, shininess);
}

void main() {
    v_normal = a_normal;
    vec4 eyePositionModel = u_mvMatrix * a_position;
    // Eye direction in model space
    vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);
    // specular lighting
    SpecularIntensity = getSpecularIntensity(a_position, a_normal, eyeDirectModel);
    v_eyeDirectModel = eyeDirectModel;
    gl_Position = u_mvpMatrix * a_position;
}

Фрагментный шейдер:

#version 300 es
precision lowp float;
uniform mat4 u_mvpMatrix;

in vec3 v_normal;
in lowp float SpecularIntensity;
in vec3 v_eyeDirectModel;

out vec4 outColor; 
uniform samplerCube s_texture; // skybox

const float eta = 0.65;
void main() {
    // Calculate refraction direction in model space
    vec3 refractDirect = refract(v_eyeDirectModel, normalize(v_normal), eta);
    // Project refraction
    refractDirect = (u_mvpMatrix * vec4(refractDirect, 0.0)).xyw;
    // Map refraction direction to 2d coordinates
    vec2 refractCoord = 0.5 * (refractDirect.xy / refractDirect.z) + 0.5;

    vec4 glassColor = texture(s_texture, vec3(refractCoord, 1.0));
    outColor = glassColor + SpecularIntensity;
    outColor.a = 0.8; // transparent
}

person alexrnov    schedule 08.05.2020    source источник
comment
Установите параметр функции уменьшения текстуры на G_LLINEAR. См. glTexParameter: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR).   -  person Rabbid76    schedule 08.05.2020
comment
@ Rabbid76 Спасибо за внимание к проблеме! Сделал, как вы сказали, и добавил код в ответ. Но это пока не помогло. В общем вроде размер пикселя соответствует размеру многоугольника, может в шейдере ошибка?   -  person alexrnov    schedule 09.05.2020
comment
Я видел подобные артефакты на некотором оборудовании, когда целая часть UV-координаты становилась большой (например, 100,3 вместо 0,3). Я не работал с математикой в ​​вершинном шейдере, чтобы выяснить, может ли это быть проблемой, но я бы начал с попытки визуализировать UV, чтобы проверить, разумны ли они.   -  person Columbo    schedule 09.05.2020
comment
@Columbo Сделал UV-развёртку в Blender - хорошо смотрится без эффекта преломления. Тоже перенес вычисления во фрагментный шейдер, но проблема осталась. Но спасибо за совет! Буду смотреть в этом направлении.   -  person alexrnov    schedule 10.05.2020
comment
@alexrnov Вы вычисляете векторы нормалей для каждой вершины или используете векторы нормалей граней?   -  person Rabbid76    schedule 16.05.2020
comment
@ Rabbid76 Простите, но не знаю))). Данные нормалей взяты из файла * .obj (из Blender) в формате: vn 0.5416 -0.3089 0.7818. Код шейдера частично взят из PVRShamanGUI. Там картинка хорошая.   -  person alexrnov    schedule 16.05.2020


Ответы (1)


Во-первых, это ошибка в коде шейдера. a_position.xyz - eyePositionModel.xyz не имеет никакого смысла, поскольку a_position - координата вершины в пространстве модели, а eyePositionModel - координата вершины в пространстве просмотра.
Вы должны вычислить вектор инцидентов для _ 4_ в поле зрения sapce. Это вектор от положения глаза к вершине. Поскольку положение глаз в пространстве обзора равно (0, 0, 0), это:

vec4 eyePositionView = u_mvMatrix * a_position;

// eye direction in model space
mediump vec3 eyeDirectView = normalize(- eyePositionView.xyz);

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

mediump vec3 refractDirect = refract(eyeDirectModel, a_normal, eta);

Поскольку векторы нормали различаются между смежными поверхностями, вы можете увидеть заметную грань между гранями сетки.

Если векторы нормалей вычисляются для каждой вершины, то смежные грани имеют общие координаты вершин и соответствующие векторы нормалей. Это вызовет плавный переход от лица к лицу.

person Rabbid76    schedule 16.05.2020
comment
Подскажите, пожалуйста, нужно ли менять данные нормалей или вносить изменения в код шейдера? - person alexrnov; 16.05.2020
comment
@alexrnov Шейдер правильный. Как упоминалось в ответе, векторы нормалей являются векторами нормалей лицевых сторон. Это вопрос атрибутов вектора нормали, а не шейдера. - person Rabbid76; 16.05.2020
comment
Большое спасибо!!! Поменяю данные на нормальный атрибут. - person alexrnov; 16.05.2020
comment
Потрясающе! Использование даже одного: normalize (- eyePositionView.xyz) сделало изображение намного лучше! - person alexrnov; 16.05.2020