Tuesday, April 27, 2010

de linear a srgb & viceversa

Contrario a otros espacios de color basados en RGB, el sRGB no puede expresarse con un valor numerico absoluto (gamma=1.0 en secciones cercanas al negro y otro valor en otras zonas).

Basicamente es entender esto: http://renderwonk.com/blog/index.php/archive/adventures-with-gamma-correct-rendering/ que es lo mismo que e
sto otro: http://www.harrybardak.co.uk/sRGB.htm e implementar una solucion en 3delight. mental ray for maya incluye lens shaders y un nodo en las opciones de render para convertir el espacio de color de los mapas que se usan.

Pero antes de hablar de mapas, hablemos de la iluminacion. Comunmente, las luces que utilizamos no tienen decay porque nunca funciona correctamente. El problema no esta en las luces, sino en la interpretacion que tenemos del resultado. Lo que obtenemos por defa
ult es una salida en espacio de color lineal (linear colorspace). Esto no es incorrecto, pero aun falta commpensar la salida, es decir, convertir el resultado al espacio de color que tenemos en el display (monitor, con pc un gamma de 2.2).

En el primer link lo manejan como 1+1=3.
Pero si a ese resultado lo desplegamos como sRGB, veremos los resultados "correctamente". En 3delight, eso se hace a traves del i-display.

Una vez que hacemos esto, lo que veremos sera una imagen lineal
pero en el espacio de color correcto.


Tenemos esta imagen. A la izquierda es la salida sin modificar nada en el display. A la derecha, ya tenemos lo que necesitamos ver.

Ahora, veamos el caso en un ejemplo mas claro:


En este punto solo hemos modificado el display a gamma 2.2.

Pero que pasa con un mapa de color? generalmente vamos a tener mapas de color de 8 bits. Estos se pintaron en photoshop o en bodypaint o zbrush. Se encuentran en espacio sRGB, asi que si los inclumos en nuestros renders con el i-display gamma=2.2, vamos a encontrar la textura "deslavada" (mezcla de espacios de color).

Una solucion es modificar el gamma de las imagenes a 0.4545, pero 8 bits no son suficientes para ajustar los valores oscuros y esto crearia banding en los renders. Si usamos tdlmake para optimizar las imagenes, podemos especificar el espacio de color (tdlmake -colorspace srgb in.tif out.tdl), pero podemos tambien solucionarlo por shading.

Para esto, necesitamos crear un par de funciones:
//decode from sRGB luma to linear light
float sRGB_f(float f)
{
float lin;
if(f <= 0.04045)
lin = f / 12.92;
else
lin = pow ((f + 0.055) / 1.055, 2.4);

return lin;
}

color sRGB(color c)
{
color d;

d[0] = sRGB_f (c[0]);
d[1] = sRGB_f (c[1]);
d[2] = sRGB_f (c[2]);
return d;
}

Con esto tenemos "linearizada" la entrada de color, pero como lo integramos al shading?. Si por ejemplo tenemos este shader (txtplastic.sl en 3delight/shaders/src):
/* Copyrighted Pixar 1989 */
/* From the RenderMan Companion p.374 */
/* Listing 16.29 Plastic surface shader using a texture map*/

/*
* txtplastic(): version of plastic() shader using an optional texture map
*/

surface txtplastic(
float Ks = .5,
Kd = .5,
Ka = 1,
roughness = .1;
color specularcolor = 1;
string mapname = "")
{
point Nf = faceforward(normalize(N), I );

if( mapname != "" )
Ci = color texture( mapname );/* Use s and t */
else
Ci = Cs;

Oi = Os;
Ci = Os * ( Ci *
(Ka*ambient() + Kd*diffuse(Nf))
+ specularcolor * Ks * specular(Nf, normalize( -I ), roughness) );
}

Solo necesitamos incluir las funciones y llamar sRGB (color c) justo antes de asignar Oi:

/* Copyrighted Pixar 1989 */
/* From the RenderMan Companion p.374 */
/* Listing 16.29 Plastic surface shader using a texture map*/

/*
* txtplastic(): version of plastic() shader using an optional texture map
*/
//decode from sRGB luma to linear light
float sRGB_f(float f)
{
float lin;
if(f <= 0.04045)
lin = f / 12.92;
else
lin = pow ((f + 0.055) / 1.055, 2.4);
return lin;
}

color sRGB(color c)
{
color d;
d[0] = sRGB_f (c[0]);
d[1] = sRGB_f (c[1]);
d[2] = sRGB_f (c[2]);
return d;
}

surface txtplastic(
float Ks = .5,
Kd = .5,
Ka = 1,
roughness = .1;
color specularcolor = 1;
string mapname = "")
{
point Nf = faceforward(normalize(N), I );

if( mapname != "" )
Ci = sRGB (color texture( mapname ));/* Use s and t */
else
Ci = Cs;

Oi = Os;
Ci = Os * ( Ci *
(Ka*ambient() + Kd*diffuse(Nf))
+ specularcolor * Ks * specular(Nf, normalize( -I ), roughness) );
}

Asi, con el ejemplo de las calaveras, podemos ver la diferencia entre visualizar linear color space y su version en sRGB.


Finalmente, la ventaja de usar una funcion a nivel de shading es q ue s i usamos AOVs, la salida de color no se altera, por lo que si se esta trabajando c o mpuesto en, digamos Nuke, al momento de cargar las imagenes podemos ver if icar el espacio de color. Por default, el viewer es sRGB, las imagenes de 8 bi ts s on sRGB y de 32 bits son linear. L a s de 16 bits son sRGB, pero lo podemos modificar en el nodo del Read.

Como lo simplifica SeB en el foro de 3delight:
"If I understand it well he did a gamma correct of 2.2 and use a gamma of .455 for textures color to fix the wash out".



Monday, April 26, 2010

Thai Thing

I was about to make Yakimeshi for lunch, but then I found a can of coconut milk (leite de coco) that was lost in the back of the paper bags compartment. So I decided to switch what was in the wok (broccoli, chauchas, etc) and added the coconut milk instead of rice and soy.

A pinch of curry powder, a pinch of salt, a pinch of garlic and more curry powder. Then some garam masala (home made ;) ) and black pepper.

Well, in teh end everything was just right. The steamed rice was a bit cold because It wasn't supposed to be served like that but didn't feel too cold either. The sriracha sauce was in charge of that anyway :)

Want to join for the next taithing zupu?

more cg related stuff soon.