Подтвердить что ты не робот

Перспектива правильного текстурирования трапеции в OpenGL ES 2.0

Я нарисовал текстурированную трапецию, однако результат не появляется так, как я предполагал.

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

Эта иллюстрация демонстрирует проблему:
Illustration demonstrating the source texture (a checkered pattern), the expected result, and the actual (incorrect) result
(Примечание: последнее изображение не должно быть 100% -ным верным представлением, но оно должно получить точку.)

Трапеция рисуется с помощью GL_TRIANGLE_STRIP в OpenGL ES 2.0 (на iPhone). Он полностью обращается к экрану и не наклонен (т.е. Это не 3D-эскиз, который вы видите!)

Я понял, что мне нужно выполнить "коррекцию перспективы", по-видимому, в моих вершинных и/или фрагментарных шейдерах, но я не понимаю, как это сделать.

Мой код включает в себя некоторую простую математическую модель Matrix Model/View/Projection, но ни одна из них в настоящее время не влияет на мои значения координат текстуры. Обновление: Предыдущее утверждение неверно, согласно комментарию пользователя infact.

Кроме того, я нашел этот лакомый кусочек в спецификации ES 2.0, но не понимаю, что это значит:

СОВЕРШЕНСТВОВАНИЕ СОВМЕСТНОЙ КОРРЕКЦИИ не поддерживается, поскольку OpenGL ES 2.0 требует, чтобы все атрибуты были объектно интерполированы.

Как правильно рисовать текстуру?


Изменить: Добавлен код ниже:

// Vertex shader
attribute vec4 position;
attribute vec2 textureCoordinate;

varying vec2 texCoord;

uniform mat4 modelViewProjectionMatrix;

void main()
{
    gl_Position = modelViewProjectionMatrix * position;
    texCoord = textureCoordinate;
}

// Fragment shader
uniform sampler2D texture;
varying mediump vec2 texCoord;

void main()
{
    gl_FragColor = texture2D(texture, texCoord);
}

// Update and Drawing code (uses GLKit helpers from iOS)

- (void)update
{
    float fov = GLKMathDegreesToRadians(65.0f);
    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
    projectionMatrix = GLKMatrix4MakePerspective(fov, aspect, 0.1f, 50.0f);
    viewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f); // zoom out
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(shaders[SHADER_DEFAULT]);

    GLKMatrix4 modelMatrix = GLKMatrix4MakeScale(0.795, 0.795, 0.795); // arbitrary scale

    GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(viewMatrix, modelMatrix);

    GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, GL_FALSE, modelViewProjectionMatrix.m);

    glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_WALLS]);
    glUniform1i(uniforms[UNIFORM_TEXTURE], 0);

    glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, wall.vertexArray);
    glVertexAttribPointer(ATTRIB_TEXTURE_COORDINATE, 2, GL_FLOAT, GL_FALSE, 0, wall.texCoords);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, wall.vertexCount);

}
4b9b3361

Ответ 1

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

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

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

Ниже показано, как предоставить текстурные координаты трапеции с использованием функций старой школы GL (которые немного легче читать для демонстрационных целей). Записанные строки являются координатами 2d-текстур, которые я заменил проективными координатами, чтобы получить правильную интерполяцию.

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,640,0,480,1,1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

const float trap_wide = 600;
const float trap_narrow = 300;
const float mid = 320;

glBegin(GL_TRIANGLE_STRIP);
glColor3f(1,1,1);

//  glTexCoord4f(0,0,0,1);
glTexCoord4f(0,0,0,trap_wide);
glVertex3f(mid - trap_wide/2,10,-10);

//  glTexCoord4f(1,0,0,1);
glTexCoord4f(trap_narrow,0,0,trap_narrow);
glVertex3f(mid - trap_narrow/2,470,-10);

//  glTexCoord4f(0,1,0,1);
glTexCoord4f(0,trap_wide,0,trap_wide);
glVertex3f(mid + trap_wide/2,10,-10);

//  glTexCoord4f(1,1,0,1);
glTexCoord4f(trap_narrow,trap_narrow,0,trap_narrow);
glVertex3f(mid + trap_narrow/2,470,-10);

glEnd();

Третья координата здесь не используется, поскольку мы просто используем 2D-текстуру. Четвертая координата будет разделять две другие после интерполяции, обеспечивая проекцию. Очевидно, если вы разделите его на вершины, вы увидите, что вы получили исходные координаты текстуры.

Вот как выглядят два визуализации:

enter image description here

Если ваша трапеция на самом деле является результатом преобразования квадроцикла, может быть проще/лучше просто нарисовать этот квад, используя GL, вместо того, чтобы преобразовывать его в программное обеспечение и подавать 2D-формы в GL...

Ответ 2

То, что вы здесь пытаетесь, это перекошенная текстура. Шаблон фрагмента фрагмента выглядит следующим образом:

precision mediump float;
varying vec4 vtexCoords;
uniform sampler2D sampler;

void main()
{
   gl_FragColor = texture2DProj(sampler,vtexCoords);
}

2 вещи, которые должны выглядеть иначе:

1) Мы используем varying vec4 vtexCoords;. Координаты текстур 4-мерные. 2) texture2DProj() используется вместо texture2D()

Основываясь на длине небольшой и большой стороны трапеции, вы назначаете координаты текстуры. Следующий URL-адрес может помочь: http://www.xyzw.us/~cass/qcoord/

Ответ 3

Принятый ответ дает правильное решение и объяснение, но для тех, кто ищет дополнительную помощь в конвейере OpenGL (ES) 2.0...

const GLfloat L = 2.0;
const GLfloat Z = -2.0;
const GLfloat W0 = 0.01;
const GLfloat W1 = 0.10;

/** Trapezoid shape as two triangles. */
static const GLKVector3 VERTEX_DATA[] = {
    {{-W0,    0,  Z}},
    {{+W0,    0,  Z}},
    {{-W1,    L,  Z}},

    {{+W0,    0,  Z}},
    {{+W1,    L,  Z}},
    {{-W1,    L,  Z}},
};

/** Add a 3rd coord to your texture data.  This is the perspective divisor needed in frag shader */
static const GLKVector3 TEXTURE_DATA[] = {
    {{0, 0, 0}},
    {{W0, 0, W0}},
    {{0, W1, W1}},

    {{W0, 0, W0}},
    {{W1, W1, W1}},
    {{0, W1, W1}},
};

////////////////////////////////////////////////////////////////////////////////////

// frag.glsl

varying vec3 v_texPos;
uniform sampler2D u_texture;

void main(void) 
{
    // Divide the 2D texture coords by the third projection divisor
    gl_FragColor = texture2D(u_texture, v_texPos.st / v_texPos.p);
}

В качестве альтернативы, в шейдере, в соответствии с ответом @maverick9888, вы можете использовать texture2Dproj, хотя для iOS/OpenGLES2 он по-прежнему поддерживает только вход vec3...

void main(void) 
{
    gl_FragColor = texture2DProj(u_texture, v_texPos);
}

Я действительно не тестировал его правильно, но в моем очень простом случае (1d текстура действительно) версия деления кажется немного более экономной.