OpenGL: как отобразить идеальный прямоугольный градиент?
Я могу отображать треугольный градиент просто с одним треугольником и использовать glColor для каждого угла.
но как сделать идеальный прямоугольный градиент? Я пробовал с одним квадроциклом, но середина получит уродливый шов. Я также попытался с текстурой размера 2x2, это было так, как должно быть сделано: правильное смешивание с каждого угла, но точность выборки текстуры становится неточной, когда растягивается слишком много (я начал видеть пиксели больше размера 1x1).
есть ли какой-то способ возможно, вычислить это в шейдере?
--
Edit: вот в чем проблема:
4 ответов
действительно, вид градиента, который вы хотите, зависит от 4 цветов в каждом пикселе, где OpenGL обычно интерполирует вход только по треугольникам (так что 3 входа). Получения идеального градиент-это не возможно, просто со стандартным интерполянтами.
теперь, как вы упомянули, текстура 2x2 может это сделать. Если вы видели проблемы с точностью, я предлагаю переключить формат текстуры на то, что обычно требует большей точности (например, текстуру float).
последний, и как вы упоминается также в вашем вопросе, вы можете решить это с помощью шейдеров. Скажем, вы передаете дополнительный атрибут на вершину, соответствующий (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);
проблема в том, что вы используете квадроцикл. Квадроцикл рисуется с использованием двух треугольников, но треугольники не находятся в нужной вам ориентации.
Если я определяю вершины квадрата как:
- A: нижняя левая вершина
- B: нижняя правая вершина
- C: верхняя правая вершина
- D: верхняя левая вершина
Я бы сказал, что quad состоит из следующие треугольники:
- A B D
- D B C
цвета, назначенные каждой вершине:
- A: белый
- B: белый
- C: белый
- D: белый
имея в виду геометрию (два треугольника), пиксели между D и B результат интерполяция между красным и красным: действительно, красный!
решением будет геометрия a с двумя треугольниками, но ориентированная по-другому:
- A B C
- A C D
но, вероятно, вы не получите точный градиент, так как в середине квадрата вы получите полный желтый, а не желтый, смешанный с красным. Итак, я полагаю, вы можете достичь точного результата, используя 4 треугольника (или треугольник вентилятора), в какая центрированная вершина является интерполяцией между желтым и красным.
Wooop! Результат оказался совсем не таким, как я ожидал. Я думал, что градиент был произведен линейной интерполяцией между цветами, но, конечно, нет (мне действительно нужно настроить цветовое пространство LCD!). Действительно, наиболее масштабируемым решением является рендеринг с использованием шейдеров фрагментов.
сохранить решение, предложенное Bahbar. Я бы посоветовал начать реализацию пройдите шейдер вершин/фрагментов (указав только вершины и цвета, которые вы должны получить предыдущий результат); затем начните играть с mix функция и координата текстуры, переданная шейдеру вершин.
вам действительно нужно понять конвейер рендеринга с программируемыми шейдерами: вершинный шейдер вызывается один раз для каждой вершины, фрагмент шейдер вызывается один раз для каждого фрагмента (без мультисэмплинга, фрагмент пиксель; с мультисэмплинга, а пиксель состоит из многих фрагментов, которые интерполируются, чтобы получить цвет пикселя).
вершинный шейдер принимает входные параметры (униформы и входы; униформы постоянны для всех вершин, выпущенных между glBegin/glEnd; входы характерны для каждого экземпляра вершинного шейдера (4 вершины, 4 экземпляра вершинного шейдера).
шейдер фрагмента принимает за вход выходы вершинного шейдера, который произвел фрагмент (из-за растеризации треугольников, линий и точки.) В Bahbar ответ единственным выходом является uv переменная (общая для обоих источников шейдеров).
в вашем случае вершинный шейдер выводит координаты текстуры вершины UV (передается "как есть"). Эти УФ-координаты доступны для каждого фрагмента и вычисляются путем интерполяции значений, выводимых вершинным шейдером в зависимости от положения фрагмента.
Если у вас есть эти координаты, нужно только два цвета: красный и желтый в вашем случае (в Bahbar ответ соответствует color0 и цв1 формы). Затем смешайте эти цвета в зависимости от УФ-координат конкретного фрагмента. (*)
(*) вот сила шейдеров: вы можете указать различные методы интерполяции, просто изменив источник шейдеров. Линейная, билинейная или сплайновая интерполяция реализуется путем задания дополнительных униформ шейдеру фрагментов.
хорошее практика!
все ли ваши вершины имеют одинаковое значение глубины (Z), и все ли ваши треугольники полностью на экране? Если это так, то у вас не должно быть проблем с получением "идеального" градиента цвета над квадроциклом, сделанным из двух треугольников с glColor. Если нет, то возможно, что ваша реализация OpenGL плохо обрабатывает цвета.
Это заставляет меня подозревать, что у вас может быть очень старым или странная реализация OpenGL. Я рекомендую вам рассказать нам, какую платформу вы используете, и что версия OpenGL у вас есть...?
без дополнительной информации я рекомендую вам попробовать написать шейдер и не говорить OpenGL, что вам нужен "цвет"."Если возможно, скажите ему, что вы хотите "texcoord", но все равно относитесь к нему как к цвету. Этот трюк сработал в некоторых случаях, когда точность цвета слишком низкая.
Ну, я не собираюсь дать вам конкретный ответ, но вы должны быть в состоянии прогрессировать с этим.
сначала вы должны знать, чего именно вы хотите. Вы сказали, что можете сделать треугольный градиент. Я подозреваю, что вы имеете в виду код от NeHe: изображение.
теперь в нашей палитре есть три основных цвета: красный, зеленый и синий. Так что, как вы думаете, создание треугольной палитры кажется логичным и легким.
теперь, если вы хотите превратить это в quad вы сначала должны думать, что вы хотите, чтобы это тоже выглядело. Где я буду менять углы? Что должно быть в середине двора? так далее... После того, как вы поняли, что вы должны начать кодирование. Я подозреваю, что вы сразу же начали кодировать, не думая о том, чего именно хотите.