Некоторые лица прозрачны, другие непрозрачны - PullRequest
3 голосов
/ 19 февраля 2009

Я создал обычный додекаэдр с OpenGL. Я хотел сделать лица прозрачными (как на картинке в Википедии), но это не всегда работает. После некоторых копаний в документации OpenGL появляется сообщение о том, что «мне нужно отсортировать прозрачные грани сзади ». Гектометр Как мне это сделать?

Я имею в виду, что я вызываю glRotatef (), чтобы вращать систему координат, но опорные координаты граней остаются прежними; Эффект поворота применяется «вне» моего оживляющего кода.

Если применить преобразование к координатам, все остальное перестанет двигаться.

Как я могу отсортировать грани в этом случае?

[ПРАВИТЬ] Я знаю почему это происходит. Я понятия не имею, как может выглядеть решение. Может кто-нибудь, пожалуйста, направьте меня на правильные вызовы OpenGL или образец кода? Я знаю, когда преобразование координат закончено, и у меня есть координаты вершин граней. Я знаю, как рассчитать координаты центра граней. Я понимаю, что мне нужно отсортировать их по значению Z. Как преобразовать Vector3f с помощью матрицы текущего представления (или как это называется, вращая мою систему координат)?

Код для поворота вида:

    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);

Ответы [ 4 ]

3 голосов
/ 19 февраля 2009

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

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

Это необходимо сделать, потому что OpenGL использует технику Z-буферизации .

(Я должен добавить, что техника «сортировки по расстоянию от центра лица» немного наивна и приводит к неверному результату в случаях, когда лица большие или близки к камере. Но это просто и вы начнете; у вас будет достаточно времени, чтобы побеспокоиться о более сложных подходах к Z-сортировке.)


Обновление: Аарон, вы уточнили пост, чтобы указать, что вы понимаете вышеизложенное, но не знаете, как рассчитать подходящее значение Z для каждого лица. Это правильно? Обычно я делаю это, измеряя расстояние от камеры до лица, о котором идет речь. Я думаю, это означает, что вы не знаете, где находится камера?

Если вы правильно сформулировали проблему, см. FAQ по OpenGL 8.010 :

Что касается OpenGL, камеры нет. Более конкретно, камера всегда расположена в координате пространства глаза (0., 0., 0.).

Обновление: Может быть, проблема в том, что вы не знаете, как преобразовать точку по матрице вида модели? Если проблема , см. FAQ по OpenGL 9.130 :

Преобразуйте точку в пространство глазных координат, умножив ее на матрицу ModelView. Затем просто вычислите его расстояние от начала координат.

Используйте glGetFloatv(GL_MODELVIEW_MATRIX, dst), чтобы получить матрицу вида модели в виде списка из 16 чисел с плавающей запятой. Я думаю, вам придется делать умножение самостоятельно: насколько мне известно, OpenGL не предоставляет API для этого.

1 голос
/ 19 февраля 2009

Для справки вот код (используется lwjgl 2.0.1). Я определяю свою модель, используя массив массивов с плавающей точкой для координат:

        float one = 1f * scale;

        // Cube of size 2*scale
        float[][] coords = new float[][] {
            {  one,  one,  one }, // 0
            { -one,  one,  one },
            {  one, -one,  one },
            { -one, -one,  one },
            {  one,  one, -one },
            { -one,  one, -one },
            {  one, -one, -one },
            { -one, -one, -one }, // 7
        };

Грани определены в массиве массивов int. Элементы во внутреннем массиве являются индексами вершин:

        int[][] faces = new int[][] {
            { 0, 2, 3, 1, },
            { 0, 4, 6, 2, },
            { 0, 1, 5, 4, },
            { 4, 5, 7, 6, },
            { 5, 1, 3, 7, },
            { 4, 5, 1, 0, },
        };

Эти строки загружают матрицу Модель / Вид:

        Matrix4f matrix = new Matrix4f ();
        FloatBuffer params = FloatBuffer.allocate (16);
        GL11.glGetFloat (GL11.GL_MODELVIEW_MATRIX, params );
        matrix.load (params);

Я храню информацию о каждом лице в классе Face:

public static class Face
{
    public int id;
    public Vector3f center;

    @Override
    public String toString ()
    {
        return String.format ("%d %.2f", id, center.z);
    }
}

Этот компаратор затем используется для сортировки граней по глубине Z:

public static final Comparator<Face> FACE_DEPTH_COMPARATOR = new Comparator<Face> ()
{
    @Override
    public int compare (Face o1, Face o2)
    {
        float d = o1.center.z - o2.center.z;
        return d < 0f ? -1 : (d == 0 ? 0 : 1);
    }

};

getCenter() возвращает центр лица:

    public static Vector3f getCenter (float[][] coords, int[] face)
    {
        Vector3f center = new Vector3f ();
        for (int vertice = 0; vertice < face.length; vertice ++)
        {
            float[] c = coords[face[vertice]];
            center.x += c[0];
            center.y += c[1];
            center.z += c[2];
        }
        float N = face.length;
        center.x /= N;
        center.y /= N;
        center.z /= N;
        return center;
    }

Теперь мне нужно настроить массив лиц:

        Face[] faceArray = new Face[faces.length]; 
        Vector4f v = new Vector4f ();
        for (int f = 0; f < faces.length; f ++)
        {
            Face face = faceArray[f] = new Face ();
            face.id = f;
            face.center = getCenter (coords, faces[f]);
            v.x = face.center.x;
            v.y = face.center.y;
            v.z = face.center.z;
            v.w = 0f;
            Matrix4f.transform (matrix, v, v);
            face.center.x = v.x;
            face.center.y = v.y;
            face.center.z = v.z;
        }

После этого цикла у меня есть преобразованные центральные векторы в faceArray, и я могу отсортировать их по значению Z:

        Arrays.sort (faceArray, FACE_DEPTH_COMPARATOR);
        //System.out.println (Arrays.toString (faceArray));

Рендеринг происходит в другом вложенном цикле:

        float[] faceColor = new float[] { .3f, .7f, .9f, .3f };
        for (Face f: faceArray)
        {
            int[] face = faces[f.id];
            glColor4fv(faceColor);

            GL11.glBegin(GL11.GL_TRIANGLE_FAN);
            for (int vertice = 0; vertice < face.length; vertice ++)
            {
                glVertex3fv (coords[face[vertice]]);
            }
            GL11.glEnd();
        }
0 голосов
/ 19 февраля 2009

Эта цитата говорит само за себя - вам нужно отсортировать лица.

При рисовании такого простого объекта вы можете просто визуализировать задние грани в первую очередь, а передние грани - вторые, используя z-буфер (путем рендеринга дважды с различными функциями сравнения z-буфера).

Но обычно вы просто хотите преобразовать объект, а затем отсортировать лица. Вы преобразуете только свое представление объекта в памяти, затем определяете порядок рисования путем сортировки, а затем рисуете в этом порядке с исходными координатами, используя преобразования по мере необходимости (должны быть согласованы с выполненной вами сортировкой). В реальном приложении вы, вероятно, сделаете преобразование неявным образом, например. сохраняя сцену в виде BSP- или Quad- или R- или любого другого дерева и просто обходя дерево со всех сторон.

Обратите внимание, что сортировка может быть сложной, потому что функция is-obsucred-by, которая является функцией, с которой вы хотите сравнить грани (потому что сначала вам нужно нарисовать затемненные грани), не является упорядочением, например , могут быть циклы (лицо А скрывает B && лицо B скрывает A). В этом случае вы, вероятно, разбили бы одну из граней, чтобы разорвать петлю.

EDIT:

Вы получаете z-координату вершины, беря координаты, которые вы передаете glVertex3f(), делая ее 4D (однородные координаты), добавляя 1, преобразуйте ее с матрицей вида модели, затем преобразуйте ее с матрицей проекции, затем сделать перспективное деление. Подробности в спецификациях OpenGL в Главе 2, раздел Преобразования координат.

Однако нет никакого API для вас, чтобы фактически выполнить преобразование. Единственное, что позволяет вам сделать OpenGL, - это нарисовать примитивы и сообщить рендереру, как их рисовать (например, как их преобразовать). Он не позволяет вам легко преобразовывать координаты или что-то еще (хотя есть IIUC, позволяющий OpenGL записывать преобразованные координаты в буфер, это не так просто). Если вы хотите, чтобы какая-то библиотека помогала вам управлять фактическими объектами, координатами и т. Д., Рассмотрите возможность использования какой-либо библиотеки сценографических изображений (OpenInventor или что-то в этом роде)

0 голосов
/ 19 февраля 2009

Вы пробовали просто нарисовать каждое лицо относительно обычных мировых координат сзади-вперед? Часто кажется, что формулировка в некоторых документах OpenGL странная. Я думаю, что если вы получите рисунок в правильном порядке, не беспокоясь о вращении, он может автоматически работать при добавлении вращения. OpenGL может позаботиться о переупорядочении граней при вращении матрицы.

В качестве альтернативы вы можете получить текущую матрицу при рисовании ( glGetMatrix () ) и изменить порядок алгоритма рисования в зависимости от того, какие грани будут повернуты назад / вперед.

...