Преломление в OpenGL ES 2.0 / 3.0. Текстура больших пикселей - PullRequest
1 голос
/ 08 мая 2020

Попытка реализовать рефракцию в 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]
}

Но текстура получается с большими пикселями типа:

enter image description here

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

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

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

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

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

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

vec3 eyeDirectModel = normalize(- eyePositionModel.xyz);

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

enter image description here

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

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

#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
}

1 Ответ

1 голос
/ 16 мая 2020

Во-первых, это ошибка в коде шейдера. a_position.xyz - eyePositionModel.xyz не имеет никакого смысла, поскольку a_position - координата вершины в пространстве модели, а eyePositionModel - координата вершины в пространстве просмотра.
Вы должны вычислить вектор инцидентов для refract в виду 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);

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

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

...