приложение краски iPhone (glPaint базирующийся). Смешивание с белым фоном

Я разрабатываю приложение рисования. Я попытался сделать это с 2D и тянущим алгоритмом кривых CoreGraphics/Quartz, является довольно медленным. Таким образом, мы решили переключиться на ES OpenGL. У меня никогда не было опыта OpenGL, таким образом, я нашел glPaint пример от яблока и запустил игру с него.

Я изменился erase метод действительно делает белый фон. Как я придерживался кистей и смешивания. В примере Apple использует "белый на черной" структуре для кисти (сначала на рис. ниже). Но это не работало на меня (я играл с различными режимами наложения). Таким образом, я решил использовать различные кисти, но я не нашел надлежащий путь. Я нашел немного вопросов на stackoverflow, но все они были оставшимися без ответа. Вот изображение (от другого вопроса благодаря Kevin Beimers). Results
(источник: straandlooper.com)

Таким образом, вопрос состоит в том, как реализовать штрих, любят "желаемый" в изображении. И как смешать 2 штриха ближе к реальному опыту (синий по желтому = темно-зеленый).

Спасибо.

Существует текущий код (бит, измененный от glPaint) для кисти (от initWithFrame метод:

// Make sure the image exists
if(brushImage) {
  // Allocate  memory needed for the bitmap context
  brushData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
  // Use  the bitmatp creation function provided by the Core Graphics framework. 
  brushContext = CGBitmapContextCreate(brushData, width, width, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);
  // After you create the context, you can draw the  image to the context.
  CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushImage);
  // You don't need the context at this point, so you need to release it to avoid memory leaks.
  CGContextRelease(brushContext);
  // Use OpenGL ES to generate a name for the texture.
  glGenTextures(1, &brushTexture);
  // Bind the texture name. 
  glBindTexture(GL_TEXTURE_2D, brushTexture);
  // Set the texture parameters to use a minifying filter and a linear filer (weighted average)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  // Specify a 2D texture image, providing the a pointer to the image data in memory
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
  // Release  the image data; it's no longer needed
        free(brushData);
  // Make the current material colour track the current color
  glEnable( GL_COLOR_MATERIAL );
  // Enable use of the texture
  glEnable(GL_TEXTURE_2D);
  // Set a blending function to use
  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
  // Enable blending
  glEnable(GL_BLEND);
  // Multiply the texture colour by the material colour.
  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

}

//Set up OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
glOrthof(0, frame.size.width, 0, frame.size.height, -1, 1);
glViewport(0, 0, frame.size.width, frame.size.height);
glMatrixMode(GL_MODELVIEW);

glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
  glEnable(GL_BLEND);




// Alpha blend each "dab" of paint onto background
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );


//glBlendFunc(GL_SRC_COLOR, GL_ONE);
glEnable(GL_POINT_SPRITE_OES);
glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);

self.brushScale = 3;
self.brushStep = 3;
self.brushOpacity = (1.0 / 1.5);

glPointSize(width / brushScale);

//Make sure to start with a cleared buffer
needsErase = YES;
[self erase];

5
задан Glorfindel 1 August 2019 в 03:04
поделиться

1 ответ

Давайте начнем с определения типа смешивания, который вам нужен. Похоже, вы хотите, чтобы ваш буфер начинался с белого цвета, а смешивание цветов подчинялось субтрактивной цветовой модели. Самый простой способ сделать это - определить результат смешивания Cbrush над Cdst как:

C = Cbrush × Cdst

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

Наличие кисти, которая затухает по краям, несколько усложняет ситуацию. Допустим, у вас есть значение непрозрачности кисти Abrush- где Abrush - 1, вы хотите, чтобы цвет кисти смешивался в полную силу, а где Abrush - 0, вы хотите сохранить исходный цвет. Теперь то, что вы ищете, это:

C = (Cbrush × Cdst) × Abrush + Cdst × (1 - - Abrush)

Поскольку результаты смешивания в OpenGL ES вычисляют C = Csrc × S + Cdst × D, мы можем получить именно то, что хотим, если сделаем следующие замены:

Csrc = Cbrush × Abrush

Asrc = Abrush

S = Cdst

D = (1 - ... Abrush)

Теперь давайте посмотрим, что нужно сделать, чтобы настроить это в OpenGL ES. Здесь есть 4 шага:

  1. Измените цвет фона на белый.

  2. Измените текстуру кисти на альфа-текстуру.
    По умолчанию GLPaint создает текстуру кисти как RGBA-текстуру с формой кисти, нарисованной в каналах RGB, что несколько неинтуитивно. По причинам, которые вы увидите позже, полезно иметь форму кисти в альфа-канале. Лучший способ сделать это - нарисовать форму кисти в градациях серого с помощью CG и создать текстуру как GL_ALPHA:

    CGColorSpaceRef brushColorSpace = CGColorSpaceCreateDeviceGray();
    brushData = (GLubyte *) calloc(width * height, sizeof(GLubyte));
    brushContext = CGBitmapContextCreate(brushData, width, width, 8, width, brushColorSpace, kCGImageAlphaNone);
    CGColorSpaceRelease(brushColorSpace);
    

    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, brushData); 
    
  3. Установите Csrc, Asrc, S и D.
    После переключения на альфа-текстуру, предполагая, что цвет кисти по-прежнему задается через glColor4f, вы обнаружите, что текстурное окружение OpenGL ES по умолчанию даст вам следующее:

    Csrc = Cbrush

    Asrc = Abrush

    Чтобы получить дополнительное умножение на Abrush для Csrc, вам нужно будет настроить пользовательскую функцию комбинатора в текстурном окружении следующим образом (это можно сделать в функции инициализации для PaintingView):

    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
    

    Изменение GL_TEXTURE_ENV_MODE на GL_COMBINE дает вам Cbrush × 0 (чтобы понять, почему это так, прочитайте раздел 3.7.12 в спецификации OpenGL ES 1.1). Замена GL_OPERAND0_RGB на GL_SRC_ALPHA изменяет второй член в умножении на то, что нам нужно.

    Чтобы настроить S и D, достаточно изменить коэффициенты смешивания (это можно сделать там, где коэффициенты смешивания были установлены ранее):

    glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); 
    
  4. Убедитесь, что любые модификации Abrush вне текстуры кисти отражаются на других каналах.
    Приведенные выше изменения в окружении текстуры учитывают только ту часть непрозрачности кисти, которая исходит из текстуры кисти. Если вы изменяете непрозрачность кисти в альфа-канале в другом месте (например, путем масштабирования, как в AppController), вы должны убедиться, что вы делаете такие же изменения в трех других каналах:

    glColor4f(components[0] * kBrushOpacity, components[1] * kBrushOpacity, components[2] * kBrushOpacity, kBrushOpacity);
    

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

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Это даст вам простое рисование цвета кисти по белому, но без фактического смешивания цветов (цвета кисти в конечном итоге перезапишут фон).

18
ответ дан 18 December 2019 в 09:05
поделиться