OpenGL перекрывает уродливый рендеринг - PullRequest
5 голосов
/ 09 мая 2019

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

Я пробовал несколько вещей с Gl_DEPTH_TEST, включаю / отключаю сглаживание без успеха.

Вот мой результат с 2 gluSphere:

enter image description here

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

Я использую SharpGL , но я думаю, что это не важно (так как я использую его только как оболочку OpenGL).Вот мой самый простой код для рендеринга того же (вы можете скопировать его в форму для проверки):

OpenGL gl;
IntPtr hdc;
int cpt;

private void Init()
{
    cpt = 0;
    hdc = this.Handle;

    gl = new OpenGL();
    gl.Create(SharpGL.Version.OpenGLVersion.OpenGL2_1, RenderContextType.NativeWindow, 500, 500, 32, hdc);

    gl.Enable(OpenGL.GL_DEPTH_TEST);
    gl.DepthFunc(OpenGL.GL_LEQUAL);

    gl.ClearColor(1.0F, 1.0F, 1.0F, 0);
    gl.ClearDepth(1);

    gl.MatrixMode(OpenGL.GL_PROJECTION);
    gl.Perspective(30, 1, 0.1F, 1.0E+7F);

    gl.MatrixMode(OpenGL.GL_MODELVIEW);
    gl.LookAt(0, 3000, 0, 0, 0, 0, 0, 0, 1);
}

private void Render(int angle)
{
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);

    RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red);
    RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue);

    gl.Blit(hdc);
}

private void RenderSphere(OpenGL gl, int x, int y, int z, int angle, int radius, Color col)
{
    IntPtr obj = gl.NewQuadric();

    gl.PushMatrix();
    gl.Translate(x, y, z);
    gl.Rotate(angle, 0, 0);

    gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
    gl.Sphere(obj, radius, 20, 10);

    gl.Color(new float[] { 0, 0, 0, 1 });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
    gl.Sphere(obj, radius, 20, 10);

    gl.DeleteQuadric(obj);

    gl.PopMatrix();
}

Заранее благодарим вас за советы!

РЕДАКТИРОВАТЬ:

Я проверил это без успеха:

gl.Enable(OpenGL.GL_LINE_SMOOTH);
gl.Enable(OpenGL.GL_POLYGON_SMOOTH);
gl.ShadeModel(OpenGL.GL_SMOOTH);

gl.Hint(OpenGL.GL_LINE_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_POLYGON_SMOOTH_HINT, OpenGL.GL_NICEST);
gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST);

EDIT2: с большим количеством лиц, изображение с линиями и без

enter image description here

Itэто ... другое ... но не приятное.

Ответы [ 3 ]

3 голосов
/ 09 мая 2019

Решение 1 (не очень хорошее): применение gl.Scale(0.0001, 0.0001, 0.0001); к матрице ModelView

Решение 2: Ближняя плоскость должна быть как можно дальше, чтобы избежать сжатия значения z в небольшом диапазоне. В этом случае достаточно использовать 10 вместо 0,1. Лучше всего рассчитать адаптированное значение в зависимости от расстояния до объекта (в этом случае ближайший объект находится на 2700)

Я думаю, что мы можем сосредоточиться на z is stored non-linearly в ссылке @PikanshuKumar и неявных последствиях.

Результат: Только грани обрезаются линией: на экваторе есть прямая в качестве разделителя. enter image description here

Эти линии исчезают, как и ожидалось, когда мы увеличиваем количество граней. enter image description here

2 голосов
/ 09 мая 2019

У проблемы есть 2 причины.

Первая действительно проблема Z-боя , вызванная чудовищным расстоянием между ближним и дальним самолетом

gl.Perspective(30, 1, 0.1F, 1.0E+7F);

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

Это можно улучшить, поместив ближнюю плоскость как можно ближе к геометрии. Поскольку расстояние до объекта составляет 3000,0, а радиус сферы равен 300, ближняя плоскость должна быть меньше 2700,0:

, например * * 1016

gl.Perspective(30, 1, 2690.0F, 5000.0F);

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

Я предоставлю альтернативное решение, используя плоскость отсечения. Клип красный шар внизу и синий шар сверху. Именно в плоскости, где сферы пересекаются, так что колпачок отрезан от каждой сферы.
Плоскость обрезки может быть установлена ​​с помощью glClipPlane и активирована с помощью glEnable.
Параметры для плоскости отсечения интерпретируются как Уравнение плоскости . Первые 3 компонента уравнения плоскости представляют собой вектор нормали к плоскости отсечения. 4-й компонент - это расстояние до начала координат.

Таким образом, уравнение плоскости отсечения для красной сферы должно быть {0, 0, -1, 50}, а для голубой сферы {0, 0, 1, -50}.
Обратите внимание, что когда вызывается glClipPlane, уравнение преобразуется в обратную сторону матрицы вида модели. Таким образом, плоскость отсечения должна быть установлена ​​до преобразования модели, такого как вращение, перемещение и масштабирование.

* * +1036 например,

private void Render(int angle)
{
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);

    double[] plane1 = new double[] {0, 0, -1, 50};
    RenderSphere(gl, 0, 0, 0, 0, 300, Color.Red, plane1);

    double[] plane2 = new double[] {0, 0, 1, -50};
    RenderSphere(gl, 0, 0, 100, angle, 300, Color.Blue, plane2);

    gl.Blit(hdc);
}
private void RenderSphere(
    OpenGL gl, int x, int y, int z, int angle, int radius,
    Color col, double[] plane)
{
    IntPtr obj = gl.NewQuadric();

    gl.ClipPlane(OpenGL.GL_CLIP_PLANE0, plane);
    gl.Enable(OpenGL.GL_CLIP_PLANE0);

    gl.PushMatrix();
    gl.Translate(x, y, z);
    gl.Rotate(angle, 0, 0);

    gl.Color(new float[] { col.R / 255f, col.G / 255f, col.B / 255f, col.A / 255f });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_FILL);
    gl.Sphere(obj, radius, 20, 10);

    gl.Color(new float[] { 0, 0, 0, 1 });
    gl.QuadricDrawStyle(obj, OpenGL.GLU_SILHOUETTE);
    gl.Sphere(obj, radius, 20, 10);

    gl.DeleteQuadric(obj);

    gl.PopMatrix();

    gl.Disable(OpenGL.GL_CLIP_PLANE0);
}
1 голос
/ 09 мая 2019

Вы убиваете точность буфера глубины с помощью настройки матрицы проекции

gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.Perspective(30, 1, 0.1F, 1.0E+7F);

По сути, это сжимает почти всю точность буфера глубины в диапазоне от 0,1 до 0,2 или около того (я не делал математику, просто смотрю на это здесь).

В общем, вы должны выбрать расстояние для ближайшей плоскости отсечения, чтобы быть как можно дальше, сохраняя все объекты в вашей сцене. Расстояние до далекой плоскости не имеет большого значения (на самом деле, с помощью правильной матричной магии вы можете поместить ее на бесконечность), но в целом также неплохо держать ее как можно ближе.

...