приложение для рисования iPhone (на основе glPaint). Смешивание с белым фоном - PullRequest
5 голосов
/ 06 июля 2010

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

Я изменил erase метод сделать белый фон.Как я застрял с кистями и смешивания.В примере Apple использует текстуру «белый на черном» для кисти (сначала на рисунке ниже).Но у меня это не сработало (я играл с разными режимами наложения).Поэтому я решил использовать разные кисти, но я не нашел правильного пути.Я нашел несколько вопросов о стековом потоке, но все они остались без ответа.Вот картинка (из другого вопроса, спасибо Kevin Beimers ). Результаты http://www.straandlooper.com/GLPaint.png

Таким образом, вопрос заключается в том, как реализовать обводку, подобную «желаемой» на рисунке.И как смешать 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];

1 Ответ

18 голосов
/ 07 июля 2010

Давайте начнем с определения типа смешивания, который вы ищете.Звучит так, как будто вы хотите, чтобы ваш буфер начинал с белого, а смешивание цветов соответствовало вычитающей цветовой модели .Самый простой способ сделать это - определить результат смешивания C brush over C dst как:

C = C кисть × C dst

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

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

C = ( C кисть × C dst) × A кисть + C dst × (1 - A кисть )

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

C src = C щетка × A щетка

A src = A кисть

S = C dst

D = (1 - A кисть )

Теперь давайте посмотримна то, что нужно, чтобы установить это в 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. Настройка C SRC , A SRC , S и D .
    После переключения на альфа-текстуру, предполагая, что цвет кисти все еще задается с помощью glColor4f, вы обнаружите, что стандартная текстурная среда OpenGL ES даст вам следующее:

    C Src = C Кисть

    A Src = A кисть

    Чтобы получить дополнительное умножение на A кисть для C src , вам нужно настроить пользовательскую функцию объединения в текстурной среде следующим образом (вы можете сделать это в функции инициализации для 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 дает вам C кисть × 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. Убедитесь, что любые изменения в A кисть за пределами текстуры кисти отражаются по другим каналам.
    Вышеуказанные модификации тВ текстурной среде учитывается только часть непрозрачности кисти, которая исходит от текстуры кисти.Если вы изменяете непрозрачность кисти в альфа-канале в другом месте (то есть, масштабируя ее, как в AppController), вы должны убедиться, что вносите те же самые изменения в другие три канала:

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

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

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...