Можно ли сделать десатурацию в OpenGL ES 1.0 и как? - PullRequest
1 голос
/ 03 августа 2011

Я хотел бы сделать цветную текстуру серым.Делать это в ES 2.0 с помощью шейдера - это очень просто, но возможно ли это сделать в ES 1.x?

ОБНОВЛЕНИЕ

Благодаря @datenwolf, я делаю этовот так:

GLfloat weights_vector[4] = {0.2126, 0.7152, 0.0722, 1.0};
GLfloat additions_vector[4] = {0.5, 0.5, 0.5, 0.0};

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* First part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weights_vector);

/* Second part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, additions_vector);

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

ВТОРОЕ ОБНОВЛЕНИЕ

Если я пытаюсь использовать GL_TEXTURE0 вместо GL_PREVIOUS, я действительно получаю тот же результат, что и GL_TEXTURE.Однако, если бы я использовал GL_TEXTURE1, я получил бы даже не серые пиксели, а черный.Я заблудился здесь ...

ТРЕТЬЕ ОБНОВЛЕНИЕ

Вторая часть работает сейчас.Надо было просто использовать имя предыдущей текстуры, как подсказал @datenwolf!

Однако вывод был неверным, поскольку он был инвертирован.Я исправил это, добавив:

glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);

Теперь слишком темно.И я не могу понять это правильно.Пробовал и не работал:

  • умножение весов на два
  • добавление 0,5
  • модулирование всего изображения на 2
  • умножение весовна 2 и затем вычитая на 0,5

ЧЕТВЕРТНОЕ ОБНОВЛЕНИЕ (извините, что их так много)

Я начинаю подозревать, что это невозможно из-за смещения и умножения в DOT3_RGB.Правильный способ сделать это в сумматоре:

  1. Добавить 0.5 к входной текстуре
  2. Рассчитать до весовых коэффициентов: weight_new = (weight_real + 2.0) / (4.0);
  3. Сделайте GL_DOT3_RGB

Например, вместо использования:

GLfloat weights_vector[4] = {0.30, 0.59, 0.11, 0.0};

Использование:

GLfloat weights_vector[4] = {0.575, 0.6475, 0.5275, 0.0};

Это действительно становится почти правильный результат, но некоторая часть контраста теряется из-за первого шага и того факта, что числа зажаты в диапазоне [-1.0, 1.0]

Зачем нужны расчеты?Ну, согласно API:

Dot calculation

Так что я не вижу другого пути, отличного от того, который я показал, и из-за ограничения точности.Конечно, я мог бы сначала разделить входные данные на 2,0, затем добавить 0,5, сделать точку 3, а затем снова умножить на 2, таким образом, фактически имея все значения в диапазоне [-1,0, 0,0].Но я боюсь, что из-за деления она все равно потеряет точность.

Я подозреваю, что DOT не был предназначен для этой цели, скорее всего, только для разметки или чего-то еще.Очень плохо.Надеюсь, я ошибаюсь, но не понимаю, как.

Ответы [ 3 ]

5 голосов
/ 03 августа 2011

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

dot(v1, v2) = v1[0]*v2[0] + v1[1]*v2[1] + ... + v1[n]*v2[n]

Десатурация достигается путем суммирования каналов с весовым коэффициентом каждый

L{r,g,b} = w_r * R + w_g * G + w_b * B

Но это не что иное, как точечное произведение цвета с весовым вектором. OpenGL-1.5 имеет текстурную среду под названием combiner , и эта среда объединителя имеет режим dot product :

GLfloat weighting_vector[4];
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weighting_vector);

РЕДАКТИРОВАТЬ в соответствии с комментарием

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

glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);

Что я забыл, так это то, что режим точечного произведения вводит смещение 0,5. Но это не проблема, так как вы получаете в аренду по меньшей мере 3 этапа объединения, поэтому вы завершаете этап GL_SUBSTRACT, вычитая 0,5 каждого канала и умножая свой вес на 2,0, чтобы компенсировать это.

Взгляните на glTexEnv manpage http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml и оригинальную спецификацию расширения http://www.opengl.org/registry/specs/ARB/texture_env_combine.txt (того времени, когда это было расширение). Я признаю, что объединители текстур немного запутывают, если вы новичок в них. Исторически они являются предшественниками фрагментных шейдеров; NVidia начала все это с того, что они тогда называли «объединителями регистров» , которые позже стали объединителями текстур.

РЕДАКТИРОВАТЬ2 из-за добавления к вопросу

Вы должны выполнить вторую часть на отдельной стадии объединения (= текстурный блок). Вы переключаете текстурные единицы с помощью glActiveTexture. Измените свой код следующим образом:

GLfloat weights_vector[4] = {0.2126, 0.7152, 0.0722, 1.0};
GLfloat additions_vector[4] = {0.5, 0.5, 0.5, 0.0};

glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* First part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weights_vector);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID); // we need some dummy texture active
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

/* Second part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, additions_vector);

Также я думаю, что вы намерены вычесть, чтобы компенсировать смещение 0,5 вместо добавления 0,5; FTFY.

5 голосов
/ 13 марта 2012

Это возможно при использовании 2 или 3 текстурных объединителей. Скорее всего, вы ищете поддержку более старого устройства (например, iPhone 3G), которое поддерживает только 2 объединителя текстур. Вы можете определить, сколько текстурных объединителей поддерживает ваше устройство, используя следующий код:

int maxTextureUnits;
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxTextureUnits);

Основные шаги для отрисовки текстуры в оттенках серого без шейдеров:

  1. Разделите все ваши значения RGB пикселей на 2
  2. Вычтите .5 из всех ваших значений RGB пикселей
  3. Рассчитать значение GL_DOT3_RGB для каждого значения RGB, используя пользовательские взвешенные значения

Теперь каждый из этих шагов может быть выполнен с помощью Combiner Texture. Или, в качестве альтернативы, вы можете использовать только два объединителя текстур, заменив шаг 1 пользовательским кодом, который корректирует значения ваших пикселей при чтении в текстуре. Если вы решите использовать только два объединителя текстур, вы все равно сможете визуализировать скорректированную текстуру в цвете, вам просто понадобится один объединитель текстур, который удваивает значения RGB перед рендерингом.

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

Причина, по которой мы делим все значения пикселей на 2, заключается в том, что наши значения не ограничиваются при переходе от шага 2 к шагу 3. Если у нас было значение RGB (.5, .6, .7), и мы добавили .5 к каждому из значений RGB, наше результирующее значение RGB, переходящее к шагу 3, будет (1.0, 1.0, 1.0). После того, как уравнение DOT3 вычитает .5 из каждого значения, оно будет рассчитывать яркость на основе (.5, .5, .5).

Вот пример кода, который будет отображать текстуру в оттенках серого с использованием 3 текстурных блоков:

//Enable texture unit 0 to divide RGB values in our texture by 2
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glClientActiveTexture(GL_TEXTURE0);

//GL_MODULATE is Arg0 * Arg1    
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);

//Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

//Configure Arg1
float multipliers[4] = {.5, .5, .5, 0.0};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&multipliers);

//Remember to set your texture coordinates if you need them
//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer...

//Enable texture unit 1 to increase RGB values by .5
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glClientActiveTexture(GL_TEXTURE1);

//GL_ADD is Arg0 + Arg1
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);

//Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

//Configure Arg1
GLfloat additions[4] = {.5, .5, .5, 0.0};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&additions);

//Set your texture coordinates if you need them
//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer...

//Enable texture combiner 2 to get a DOT3_RGB product of your RGB values
glActiveTexture(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textureId);
glClientActiveTexture(GL_TEXTURE2);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

//GL_DOT3_RGB is 4*((Arg0r - 0.5) * (Arg1r - 0.5) + (Arg0g - 0.5) * (Arg1g - 0.5) + (Arg0b - 0.5) * (Arg1b - 0.5))
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);   

//Configure Arg0
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);

//Configure Arg1
//We want this to adjust our DOT3 by R*0.3 + G*0.59 + B*0.11
//So, our actual adjustment will need to take into consideration
//the fact that OpenGL will subtract .5 from our Arg1
//and we need to also take into consideration that we have divided 
//our RGB values by 2 and we are multiplying the entire
//DOT3 product by 4
//So, for Red adjustment you will get :
//   .65 = (4*(0.3))/2 + 0.5  = (0.3/2) + 0.5
GLfloat weights[4] = {.65, .795, .555, 1.};
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, (GLfloat*)&weights);

//Set your texture coordinates if you need them
//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer...

//Render your objects or sprite

//Clean up by disabling your texture combiners or texture units. 
glActiveTexture(GL_TEXTURE2);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisable(GL_TEXTURE_2D);

glActiveTexture(GL_TEXTURE0);
glClientActiveTexture(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1 голос
/ 03 августа 2011

Перед загрузкой вашей текстуры в OpenGL обесцветьте ее, используя ColorMatrix

Paint paint = new Paint();
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
paint.setColorFilter(new ColorMatrixColorFilter(matrix));

Bitmap bmp = Bitmap.createBitmap(resource.getWidth(), resource.getHeight(), resource.getConfig());
Canvas canvas = new Canvas(bmp);
canvas.drawBitmap(resource, null, paint);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...