Label/Tag & Coverage пассы – LabelExtractor


Это старая статья от конца 2006го года. Отчасти она устарела, но все еще содержит интересные сведения. Кроме того, на нее много линков с других ресурсов, поэтому, так как я закрываю сайт где она до сих пор лежала, решил добавить задним числом сюда.


Часть I. Базовые методы получения цветных масок
Часть II. Базовые методы использования цветных масок
Часть III. Label/tag и coverage пассы в mentalray
Часть IV. Использование Label/tag и coverage пассов
Часть V. Макро LabelExtractor

– Скачать макро LabelExtractor –


Часть I. Базовые методы получения цветных масок

Желание подкорректировать отрендеренное 3D пакетом изображение без перерендеривания – в программе композитинга – возникает постоянно. Слегка изменить цвет, размыть тени, усилить блик и прочее и прочее. Но если это нужно сделать только на определенном объекте – возникает необходимость аккуратного выделения этого объекта или рисования маски. А если нужно подкорректировать все металлические элементы сцены или костюм персонажа? А если это анимация – автомобиль проезжает по хитрой траектории, костюм персонажа деформируется и покачивается? Рисовать маски становится делом крайне напряжным.

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

Делается это очень просто – достаточно положить на объект специальный шейдер, на который не влияет освещение, тени и прочие элементы сцены. В Maya самый очевидный выбор – Surface Shader:

Для получения того же результата можно воспользоваться и рядом других вариантов:

Обычный черный ламберт с цветом в Incandescence (да и вообще любой blinn, lambert, phong, anisotropic и другие):

То же самое в случае с шейдерами mentalray – стандартные шейдера:

…или подобие surface shader для mentalray – constant shader (в данном случае p_constant из комплекта p_shaders):

К примеру, решили назначить всем кубам красный цвет, всем сферам синий, а торам – зеленый:

Можно рендерить. Но выходит, что после рендера самой сцены нам нужно отрендерить ее еще раз для масок. Хоть такие шейдера и считаются крайне быстро, но возникает естественное желание – настроить сцену, нажать рендер и получить все что нужно. Для этого можно воспользоваться специальными шейдерами для mentalray, которые за один рендер способны писать framebuffer’ы в отдельные файлы – p_megaTk из комплекта p_shaders или ctrl_buffers:

Как бы то ни было, в итоге после рендера мы получаем примерно один и тот же результат – цветные области с anti-aliasing.


Часть II. Базовые методы использования цветных масок

Когда маски получены, осталось только понять как их применять.

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

Но три маски часто бывает мало – нужно либо делать еще один пасс rgb, либо добавлять вторичные цвета – CMY. Отделение их несколько сложнее и anti-aliasing немного портится, но отдельных масок теперь можно иметь до 6ти штук (нижний ряд нод – все ColorX с назначенными на альфа-каналы формулами, указанными ниже):

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


Часть III. Label/tag и coverage пассы в mentalray

Специально для этого mentalray умеет выдавать два пасса – label/tag и coverage. Первый из них в зависимости от значения специального атрибута miLabel на объекте назначает определенный цвет всему объекту. Второй – отдельно записывает anti-aliasing, но только в тех местах, где объект с одним значением miLabel перекрывает объект с другим значением или пустоту (т.е. если объекты имеют одно и то же значение miLabel и перекрывают один другой – на их стыке coverage никакой информации не запишет).

Т.е. технология в данном случае такая:

1) создаем integer атрибут miLabel на трансформ-ноде или на шейп-ноде объекта:

2) устанавливаем нужные значения от 1 до бесконечности (ну или почти до бесконечности :)) как нам нравится – объекты с одинаковыми значениями представляют собой группу:

3) если при рендере нужно читать атрибут с шейп-ноды объекта, ставим в mentalray globals галочку на Pass Custom Label. Если с трансформ ноды – галочку надо снять:

4) и создаем пассы для label/tag и coverage пассов в атрибутах камеры, через которую будем рендерить сцену:

По науке предполагается рендерить label/tag пасс в формат ‘mentalray Tag (tt)’, а coverage – в ‘mentalray Alpha (st)’. Но по неизвестным причинам именно эти два формата не поддерживаются Shake’ом. Но если указать какой-то иной – mentalray постарается адаптировать данные к каналам формата. Для шейка самое лучшее что я нашел – iff (для label/tag) и zt (для coverage) – при рендере выскочит warning, но все будет как нужно. Дальше в шейке остается только пропустить zt через reorder (с кодом zzzz0, к примеру) и использовать.

Как я уже сказал, значения miLabel могут быть условно любыми, но лучше по порядку от 1 – потом удобнее с ними работать. Если объектов много и не хочется для каждого вручную создавать атрибуты, можно воспользоваться скриптом (засоурсить и просто запустить нужную из двух процедуру – он проставит всем объектам в сцене персональный miLabel):

//-----------------------------------------------------------------------------
//miLABEL ALL SHAPES

global proc sag_tagShapes() {

select -cl;
select -adn;
int $i=1;
string $objShapes[] = `ls -sl -type geometryShape`;

for ($curObjShape in $objShapes) {
	string $objType = `objectType $curObjShape`;

	if ($objType == "mesh" || $objType == "nurbsSurface" ||
							$objType == "subdiv") {
		addAttr -ln miLabel -at long -k 1 $curObjShape;
		setAttr ($curObjShape+".miLabel") $i;
		$i++;
	}
}
}

//-----------------------------------------------------------------------------
//miLABEL ALL TRANSFORMS

global proc sag_tagTransforms() {

select -cl;
select -adn;
int $i=1;
string $objShapes[] = `ls -sl -type geometryShape`;

for ($curObjShape in $objShapes) {
	string $objType = `objectType $curObjShape`;

	if ($objType == "mesh" || $objType == "nurbsSurface" ||
							$objType == "subdiv") {
		string $curObjParent = `firstParentOf($curObjShape)`;
		addAttr -ln miLabel -at long -k 1 $curObjParent;
		setAttr ($curObjParent+".miLabel") $i;
		$i++;
	}
}
}
//-----------------------------------------------------------------------------

При наличии mentalray standalone с помощью m2mr скрипта можно рендерить label/tag и coverage (а так же прочие native mentalray пассы) намного проще.


Часть IV. Использование Label/tag и coverage пассов

Вот для примера фрейм из моего недавнего проекта. Это beauty пасс напрямую из рендера:

Соответствующий ему label/tag пасс, в котором я группировал под одним лейблом схожие объекты (в итоге получилось примерно 30 разных цветов):

и coverage пасс с данными об anti-aliasing:

Теперь при желании можно изменить какие-то вещи для определенных предметов (само собой, маски использовать можно не только для beauty, но и для любого другого – менять спекулярность бутылок, втыкая маску в 32-битный specular pass, к примеру). Но нужно каким-то образом отдирать только определенные цветовые регионы. Можно это делать разными кеерами, можно написать в ColorX небольшой экспрешен, отправляющий в альфу только пикселы заданного цвета. К примеру:

floor(r*256)==64&&floor(g*256)==0&&floor(b*256)==128?1:0

выделит все синие миски на столе (по дефолту значения каналов отображаются не совсем четкие, поэтому я делаю конверсию в привычные 8-бит и округление каждого канала – floor(r*256) и т.п.) Остается только обрезать ее по coverage (к примеру, Inside или IMult) и можно использовать.

Это финальный компоуз (правда не того кадра, что выше, но суть та же самая :)) – вышеописанными методами я делал размытие теней, легкий тинт яблокам, подобие DOF для дальней стены, выделение цветка для RBlur и ряд других вещей (которых я попросту уже не помню).


Часть V. Макро LabelExtractor

Ну вот, изложив все это, можно объяснить что же делает моя макро-нода LabelExtractor 🙂 Она упрощает процесс отделения упомянутых цветовых регионов и дает возможность выбирать регионы по номеру, введенному в miLabel в Майе.

Есть два режима выбора лейбла – по номеру (соответствующему miLabel в Майе) или тыкнув в нужный регион прямо во вьюэре.

Переключаться между первым и вторым режимом – с помощью useColor – если он активирован, используется цвет pickColor, если деактивирован (по дефолту) – номер лейбла labelNum.

Хотите тыкать во вьюэере – включаете useColor, жмете на свотч pickColor (вокруг него должна появиться желтая рамка) и тыкаете в нужный регион – labelExtractor теперь имеет альфу с выбранными объектами.

У меня обычно был список лейблов объектов из Майи, сохранявшийся на нескольких шотах. Я просто вводил их в labelNum и ничего не тыкал 🙂 Поэтому labelNum активен по дефолту. К сожалению, додуматься до универсальной формуле, по которой mentalray назначает объекту цвет в зависимости от значения miLabel мне не удалось – работает только для 1-63 включительно. Пока максимальное кол-во лейблов доходило только до 30-35, поэтому считаю это не большим недостатком. pickColor же работает для чего угодно.

Тестовые файлы прилагаются в архиве:

– Скачать макро LabelExtractor –

16 Responses

Subscribe to comments via RSS

  1. Written by wiss
    on 16 December 2009 at 17:33
    Permalink

    This is a great tutorial that i have referred to many times. Thank you soo much.

    I have a question that I have never been able to resolve, and perhaps its because I misunderstand your tutorial.

    I am using photoshop for compositing still 3D images, and I make use of the label/coverage technique to photo-retouch and color correct one element at a time.

    Anyway, my problem is that I have noticed at high zooms that the label always represents less pixels than the object in the master-beauty, and so I do not quite understand how to combine all the pixels from the label, along with the coverage to make a smooth edge for that element, that actually covers all the pixels that that element is represented by in the master-beauty pass. No being to do this is always leaving me with about 1 pixel width of pixels that are contrasting in color on the edge of the objevct and are noticeable to the eye. I would appreciate any insight you might have on this…

    many thanks

    wiss

  2. Written by Sagroth
    on 17 December 2009 at 20:20
    Permalink

    Yeah, I want to make a post on how to use these passes for masking in compositing for a pretty long time already… I’ll do it eventually 🙂

    Anyway, the trick is – label pass shows the object that contributes THE MOST to the particular pixel and coverage pass shows exactly how much. So, in the anti-aliased edges when object’s contribution becomes fainter than 0.5 – label and coverage switches to the object behind. That way it’s obvious that label shows as the object with just a half (>0.5) of it’s anti-aliased fringe and coverage is ranging 1.0-0.5 for this object and then raises up to 1.0 again for the one behind it.

    So, what you need to do is:
    1) get your hard mask from label pass;
    2) get an outline around it (dilate/expand with one pixel should be enough) and multiply this outline by inverted coverage – that way you get those faint anti-aliased pixels you lack;
    3) multiply hard mask from label by coverage and add/plus pixels from the step 2 – and you’ve got a pretty similar result compared to standard RGBA masks.

    It’s pretty easy to do in any node-based compositing package, though it could be tricky to use in Photoshop.

    P.S. If you make this in Nuke, you’d want to gamma up your coverage from the start.

  3. Written by wiss
    on 20 December 2009 at 12:24
    Permalink

    You are the man!

    I actually am doing the same thing as you describe above, but honestly I thought it was a “hack” and not the correct way to handle the passes.

    I was only using photoshop to simulate what I ultimately need to do in a Adobe Air application in realtime. I wanted to understand it first, so I could code it properly. Now to figure out how to dilate the coverage by one pixel ! 😉

    Again, thank you soo much for your original article and for your reply!

    wiss

  4. Written by wiss
    on 21 December 2009 at 13:46
    Permalink

    You’re still the man, but … I’m still having problems. Can I send you an example label/coverage set-up to show you?

    The issue is that when using this method, some of the edges of the hard mask (after going throug the process) are treated properly and anti-aliased succesfully, whereas other edges remain outside of the reach of the label pass (even after reasonable dilation), thereby being left untreated and “hard” by the process!

    Its driving me up the wall! Feel free to send me an email on my address if you’re willing to have a look.

    tx

  5. Written by avak
    on 5 January 2010 at 12:30
    Permalink

    How it works in combination of XSI and Shake?

  6. Written by Sagroth
    on 5 January 2010 at 16:48
    Permalink

    I don’t know XSI, sorry. In mi file miLabel attribute adds a line ‘tag #’ in object’s instance block:

    instance “pSphere1” “pSphereShape1”
    light “exclusive” []
    material [“initialShadingGroup”]
    tag 1
    caustic on

    And you need just to output both buffers in camera block:

    framebuffer “buff1”
    datatype “coverage”
    filtering on
    filetype “tif”
    filename “coverage.tif”

    framebuffer “buff2”
    datatype “tag”
    filtering on
    filetype “tif”
    filename “label.tif”

    Don’t know whether this is useful for XSI though.

  7. Written by avak
    on 7 January 2010 at 4:09
    Permalink

    thanks for the response, Actually I am not very good (and I am lazy)in technical issues and I want to find an easy way such as the one that RPF format is doing for max users , as you now this format can save all data such as UV ,depth , object and Render id , coverage and etc. and then combustion can read them in a very easy way .just you need to load the RPF image and apply a 3d filter and mask the specified object based on its id (not rgb) …any chance for Maya and XSI users to have a format like this?
    I don’t think exr can save object label information as integer id (am I wrong?)
    any plugin for shake that can load tt file not only to load its RGBA data but also to load its integer data that it saves?

    thanks for your response

  8. Written by Sagroth
    on 7 January 2010 at 17:45
    Permalink

    Yeap, I’ve found no way to output label as integers to be able to use it in comp (old shaders_p did that as well as I remember – with it’s own openexr writer). Shake supports custom channels from version 4.0, I think, but not tt. Maybe .map file would do – it’s supported by shake and, I suppose, it can store tag data. imf_copy to convert tt into map. You need shake 4.x anyway though to try it.

  9. Written by Ramon
    on 31 March 2010 at 16:02
    Permalink

    Скажи а если маски по цвету вышли с гребенкой то coverage пасс используется для anti-aliasingа? Как это потом сложить в композе?
    со Fusion ты работаешь?

  10. Written by Sagroth
    on 1 April 2010 at 22:23
    Permalink

    Если под “гребенкой” подразумевается отсутствие анти-алиасинга, то да. Как это вытягивать в композе написано тут же в комменте #2, правда на английском. В кратце – нужен каверадж в пределах лэйбла и еще остатки вокруг этого лейбла – за его пределами. Я постараюсь в эти выходные сделать статью о том как это собирать – давно уже собираюсь.

    С Fusion не работаю, но человек воссоздал в нем мой нюковский сетап по этому делу – думаю добавлю его к статье.

  11. Written by pryaneek
    on 30 June 2010 at 17:31
    Permalink

    Когда же уже будет продолжение статьи? (о том как собирать + сетап для Fusion)

  12. Written by Ahmed
    on 2 November 2010 at 11:52
    Permalink

    hey Sagroth,

    thanks for the nice tut, do u have any idea how to write the same expression in Nuke ?!

  13. Written by Sagroth
    on 4 November 2010 at 16:47
    Permalink

    Not exactly the same, but really similar – here’s the expression node, just copy/paste it into your nuke. You need to activate Tag color swatch in User tab and ctrl-click on the label color you want to pass into alpha:

    
    set cut_paste_input [stack 0]
    push $cut_paste_input
    Expression {
     expr3 "r>(tag.r-tol)&&r< (tag.r+tol)&&g>(tag.g-tol)&&g< (tag.g+tol)&&b>(tag.b-tol)&&b < (tag.b+tol)?1:0"
     name Expression1
     selected true
     xpos 138
     ypos -108
     addUserKnob {20 User}
     addUserKnob {7 tol l Tolerance}
     tol 0.001
     addUserKnob {18 tag l Tag}
     tag {0.2160000056 0 0}
    }
    
  14. Written by Ahmed
    on 6 November 2010 at 7:38
    Permalink

    thanks Sagroth ,
    it works fine u r the man 🙂

  15. Written by Korinkite
    on 16 November 2010 at 8:56
    Permalink

    You’ve mentioned Fusion, Nuke, and Shake as compositing programs that can be used to run your label extractor macro but you do have a script for a Toxik (Maya Composite) user like myself?

    Nuke just keeps getting better and better but until I make the switch I’m stuck with Toxik :/.

    Any help on the question would be greatly appreciated.

  16. Written by Sagroth
    on 16 November 2010 at 23:04
    Permalink

    LabelExtractor is shake-only node. Anyway, the same thing could be done in any compositing software (no doubts it applies to Toxic, though I’ve never used it). You just need to extract a solid color from label pass and multiply it by coverage. For a better anti-aliasing you need to add inverted coverage one pixel around selected label color (I’ve explained that in one of the comments above – #2). So, you just need to make these steps with toxic tools and that’s it.

Subscribe to comments via RSS

Leave a Reply