OpenGL: как сделать идеальный прямоугольный градиент? - PullRequest
5 голосов
/ 19 марта 2011

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

Но как сделать идеальный прямоугольный градиент? Я пробовал с одним квадом, но в середине получится некрасивый шов. Я также попробовал с текстурой размером 2x2, это было так, как должно быть: правильное смешивание из каждого угла, но точность выборки текстуры становится неточной, когда растягивается слишком сильно (я начал видеть пиксели размером больше 1x1).

Возможно, есть какой-нибудь способ вычислить это в шейдере?

-

Редактировать: Вот проблема:

http://img143.imageshack.us/img143/7066/gradients.png

http://img143.imageshack.us/img143/7066/gradients.png http://img143.imageshack.us/img143/7066/gradients.png

Ответы [ 4 ]

7 голосов
/ 19 марта 2011

Действительно, требуемый тип градиента зависит от 4 цветов в каждом пикселе, где OpenGL обычно интерполирует входные данные только по треугольникам (таким образом, 3 входных сигнала).Получение идеального градиента невозможно только с помощью стандартных интерполантов.

Теперь, как вы упомянули, текстура 2x2 может это сделать.Если вы видели проблемы с точностью, я предлагаю переключить формат текстуры на что-то, что обычно требует большей точности (например, текстура с плавающей точкой).

Последнее, и, как вы упомянули также в своем вопросе, вы можете решить эту проблему.с шейдером.Скажем, вы передаете дополнительный атрибут для каждой вершины, который соответствует (u, v) = (0,0) (0,1) (1,0) (1,0) на всем пути к пиксельному шейдеру (с вершинным шейдеромпросто выполняем передачу).

Вы можете сделать следующее в пиксельном шейдере (обратите внимание, идея здесь звучит, но я не тестировал код):

Фрагмент вершинного шейдера:

varying vec2 uv;
attribute vec2 uvIn;

uv = uvIn;

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

uniform vec3 color0;
uniform vec3 color1;
varying vec2 uv;

// from wikipedia on bilinear interpolation on unit square:
// f(x,y) = f(0,0)(1-x)(1-y) + f(1,0)x(1-y) + f(0,1)(1-x)y + f(1,1) xy. 
// applied here:
// gl_FragColor = color0 * ((1-x)*(1-y) + x*y) + color1*(x*(1-y) + (1-x)*y)
// gl_FragColor = color0 * (1 - x - y + 2 * x * y) + color1 * (x + y - 2 * x * y)
// after simplification:
// float temp = (x + y - 2 * x * y);
// gl_FragColor = color0 * (1-temp) + color1 * temp;
gl_FragColor = mix(color0, color1, uv.u + uv.v - 2 * uv.u * uv.v);
7 голосов
/ 19 марта 2011

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

Если я определю четверные вершины как:

  • A : нижняя левая вершина
  • B : нижняя правая вершина
  • C : верхняя правая вершина
  • D : верхняя левая вершина

Я бы сказал, что квад состоит из следующих треугольников:

  • A B D
  • D B C

Цвета, назначенные каждой вершине:

  • A : желтый
  • B : красный
  • C : желтый
  • D : красный

Принимая во внимание геометрию (два треугольника), пиксели между D и B являются результатом интерполяции между красным и красным: действительно, красный!

Решением будет геометрия с двумя треугольниками, но ориентированная по-другому:

  • A B C
  • A C D

Но, вероятно, вы не получите точного градиента, поскольку в середине четырехугольника вы получите полный желтый вместо желтого, смешанного с красным. Итак, я полагаю, что вы можете достичь точного результата, используя 4 треугольника (или веер треугольника), в которых центрированная вершина является интерполяцией между желтым и красным.


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

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

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

Вершинный шейдер принимает входные параметры (униформы и входные данные; униформы постоянны для всех вершин, передаваемых между glBegin / glEnd; входные данные характерны для каждого экземпляра вершинного шейдера (4 вершины, 4 экземпляра вершинного шейдера).

Фрагментный шейдер принимает в качестве входных данных выходные данные вершинного шейдера, который создал фрагмент (из-за растеризации треугольников, линий и точек). В ответе Bahbar единственным выводом является переменная uv (общая для обоих источников шейдеров).

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

Если у вас есть эти координаты, вам нужно только два цвета: красный и желтый в вашем случае (в Bahbar ответ соответствует color0 и color1 форма). Затем смешайте эти цвета в зависимости от UV-координат конкретного фрагмента. (*)

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

Хорошая практика!

0 голосов
/ 19 марта 2011

Ну, я не собираюсь давать вам конкретный ответ, но вы должны быть в состоянии продвинуться в этом.

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

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

Теперь, если вы хотите преобразовать это в квад, вы сначала должны подумать, как вы хотите, чтобы он тоже выглядел. Куда я собираюсь сдвинуть углы? Что должно быть в середине четверки? и т.д ... После того, как вы выяснили, что вы должны начать кодирование. Я подозреваю, что вы сразу начали кодировать, не задумываясь о том, что именно вы хотите.

0 голосов
/ 19 марта 2011

Все ли ваши вершины имеют одинаковое значение глубины (Z), и все ли ваши треугольники полностью отображаются на экране? Если это так, то у вас не должно возникнуть проблем с получением «идеального» цветового градиента над квадратом из двух треугольников с помощью glColor. Если нет, то, возможно, ваша реализация OpenGL плохо обрабатывает цвета.

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

Без дополнительной информации я рекомендую вам попытаться написать шейдер и не указывать OpenGL, что вы хотите "цвет". Если возможно, скажите, что вы хотите «texcoord», но все равно относитесь к нему как к цвету. Этот прием сработал в некоторых случаях, когда точность цвета слишком низкая.

...