PDA

View Full Version : Modify all Shaders at once?


AdamEisfeld
10-25-2010, 01:33 PM
Ive been programming with various languages for around 6 years now, but Im relatively new to MEL. I was wondering if there is a way to get a list of all of the current shaders in the scene (doesn't matter if they're in use or not, at least not yet) using MEL, that I could then loop through and add in a new shader connection to a Gamma Correction node between every shader's texture node and it's final colour output.

The reason being that the Physical Sun and Sky feature of Maya 2011 is nice and all, but it washes out the textures due to the incorrect gamma calculations. Id like to be able to quickly apply my script and have all of the textures in the scene gamma corrected, as its a little tedious to go through the entire scene and do it by hand.

Anyone have any example script on how I might go about getting a list of all of the shaders in hyphershade and looping through it, and possibly an example script of how to make a new connection for a shader (ie to the Gamma Correction node)?

Thanks,
- Adam Eisfeld

NaughtyNathan
10-25-2010, 02:18 PM
this is absolutely the purpose of MEL, and if you're familiar with other programming and scripting languages MEL should be relatively familiar.

The Maya docs have very good MEL help so I'd suggest you have a look at that first

Maya Help F1 > User Guide > General > MEL and Expressions

most of the listing of nodes starts with the ls command (again, the MEL command docs are generally decent, explaining command flags and showing examples of how to use the command.)

Maya Help F1 > Technical Documentation > Commands

so, in your example you would pipe a list of shaders to an array variable, loop through it, grabbing it's color texture (or whatever), creating a gamma node and connecting their relevent inputs.

I have no idea what you are doing here, or how one uses gamma correction, but here is some example code to give you an idea of generic approach:

// list/get all lambert-based shaders in the scene
string $shaders[] = `ls -type "lambert"`;
// iterate through each shader in the array:
for ($item in $shaders)
{
// find out what is connected to the shaders "color" attribute
string $colorTex[] = `listConnections ( $item + ".color" )`;
// if nothing conncted, ignore:
if (!`size $colorTex`) continue;
// create a node of type "gammaCorrect"
string $gammaNode = `createNode gammaCorrect`;
// set the relevent gamma node attributes:
setAttr ( $gammaNode + ".value") 1 1 1; // set value to white
// connect this gamma node between texture and shader:
connectAttr -f ( $colorTex[0] + ".outColor" ) ( $gammaNode + ".gamma" );
connectAttr -f ( $gammaNode + ".outValue" ) ( $item + ".color" );
}

And don't forget, that the Script Editor history reports most of what you do manually in Maya, so if you manually perform your operation once, then look in the Script Editor you will see most of the commands you need to replicate in your script. The only things you usually need to add or change are the for..loop and the swapping of real names with variables.

:nathaN

AdamEisfeld
10-25-2010, 02:34 PM
Thanks nathan, that is exactly what I was looking for. Ive been working my way through the documentation and I know it seems a bit lazy but I figured Id throw a post up here to see if I could get a jump start anyways. I find working off of code snippets like the one you just provided gets me up to speed with a language a lot faster, especially if I know exactly what the snippet is trying to achieve.

I understand all of the code you provided and its going to help me a lot. Thanks again, Ill be sure to put the final script up on the forums for others to use when its completed.

- Adam Eisfeld

shcmack
10-25-2010, 10:04 PM
You may not have any use for this, but I just thought I'd share it. This script creates a gammacorrector on each selected object's texture, or each selected shader's texture.

//*************************************************************************************************************
// Title: jh_gammaCorrector.mel
// Author: Jørn-Harald Paulsen
// Created: April 8, 2010
// Last Update: September 19, 2010
// Description: Create a gammacorrector on the selected shader
//*************************************************************************************************************
// MAIN WINDOW
//*************************************************************************************************************
global proc jh_gammaCorrector ()
{
//Delete window if it already exists
if (`window -q -ex jh_gammaCorrector`) deleteUI jh_gammaCorrector;

//Main Window
window
-topEdge 30
-title "Gamma correction"
-maximizeButton false
-sizeable true
-resizeToFitChildren false
-menuBar false
-menuBarVisible false
-w 280
-h 440
jh_gammaCorrector;

//Window content
columnLayout -adjustableColumn true;
text -label "\nUtility to correct the gamma on textures" -fn boldLabelFont;
separator -w 240 -h 15;
text -label "\nSelect shader(s) or object(s), then click Load texture:";
button -label "Load texture" -c jh_loadTxt;
textScrollList -ams 1 shaderList;
separator -w 240 -h 10;

//Sliders (RGB & All)
text -label "Adjust the sliders to modify the gamma correction\n";
floatSliderGrp -label "R:"
-cw3 60 60 120 -cal 1 left
-cal 2 left -cal 3 left
-field true
-precision 3
-min 0 -max 1 -value 1.000
sliderX;
floatSliderGrp -label "G:"
-cw3 60 60 120 -cal 1 left
-cal 2 left -cal 3 left
-field true
-precision 3
-min 0 -max 1 -value 1.000
sliderY;
floatSliderGrp -label "B:"
-cw3 60 60 120 -cal 1 left
-cal 2 left -cal 3 left
-field true
-precision 3
-min 0 -max 1 -value 1.000
sliderZ;
text -label "\n";
floatSliderGrp -label "RGB:"
-cw3 60 60 120 -cal 1 left
-cal 2 left -cal 3 left
-field true
-precision 3
-min 0 -max 1 -value 1.000
-cc sliderAll
sliderA;
separator -w 240 -h 15;
text -label "Click here to update the texture";
button -label "Update texture" -c jh_updateTxt;
separator -w 240 -h 15;
text -label "Click here to remove the Gamma Correction node";
button -label "Remove node" -c jh_deleteNode;
window -e -w 280 -h 440 jh_gammaCorrector;
showWindow jh_gammaCorrector;
}

global proc jh_loadTxt()
{
//Create a global string for the shaders
global string $txtName[];
//Create an empty array to put the textures in
string $tmpTxt[];
//Get the selected object
string $tmpSel[] = `ls -sl`;
//Remove all items in the textScrollList
textScrollList -e -ra shaderList;
//For each selected object
int $a = 0;
for($a=0;$a<`size $tmpSel`;$a++)
{
//If a transform-node is selected
if(`objectType $tmpSel[$a]` == "transform")
{
//Get the shape of the object
string $shapes[] = `ls -o -dag -s $tmpSel[$a]`;
//Get the connected shadingEngines
string $shadingEngines[] = listConnections("-type","shadingEngine", $shapes);
//If $shadingEngines is not empty
if(`size $shadingEngines` > 0)
{
//List the connected materials (shaders)
string $material[] = ls("-mat", listConnections($shadingEngines));
//Get the file node of the texture
string $file[] = `listConnections -type "file" $material[0]`;
//If the texture has a filenode, add it to the array
if(`size $file` > 0) $tmpTxt[$a] = $material[0];
}
}
//Else if it's not a transform-node
else if(`objectType $tmpSel[$a]` != "transform")
{
//Get the file node of the texture
string $file[] = `listConnections -type "file" $tmpSel[$a]`;
//If the texture has a filenode, add it to the array
if(`size $file` > 0) $tmpTxt[$a] = $tmpSel[$a];
}
}
//Remove duplicates from the selection
string $shaders[] = stringArrayRemoveDuplicates($tmpTxt);
//For each member of the $tmpTxt
int $a = 0;
for($a=0;$a<`size $shaders`;$a++)
{
//Update the textScrollList with the selected shaders
textScrollList -e -append ("Shader " + ($a + 1) + ": " + $shaders[$a]) shaderList;
}
//Put the content of $tmpSel in the global string $txtName
$txtName = $shaders;
//If the $txtName is not empty, open hyperShade
if(`size $txtName` != 0)
{
//Open hypershade, and list input/output connections on the selected shader(s)
HypershadeWindow;
select $txtName;
evalDeferred("hyperShadePanelGraphCommand hyperShadePanel1 showUpAndDownstream");
}
}

global proc sliderAll()
{
//Initialize the slider variables
float $sliderXvalue;
float $sliderYvalue;
float $sliderZvalue;
float $sliderAvalue;
//When the RGB slider changes, change R,G,B to the same value
$sliderXvalue = `floatSliderGrp -q -value sliderX`;
$sliderYvalue = `floatSliderGrp -q -value sliderY`;
$sliderZvalue = `floatSliderGrp -q -value sliderZ`;
$sliderAvalue = `floatSliderGrp -q -value sliderA`;
floatSliderGrp -e -value $sliderAvalue sliderX;
floatSliderGrp -e -value $sliderAvalue sliderY;
floatSliderGrp -e -value $sliderAvalue sliderZ;
}

global proc jh_updateTxt()
{
//Initialize the slider variables
float $sliderXvalue;
float $sliderYvalue;
float $sliderZvalue;
$sliderXvalue = `floatSliderGrp -q -value sliderX`;
$sliderYvalue = `floatSliderGrp -q -value sliderY`;
$sliderZvalue = `floatSliderGrp -q -value sliderZ`;
//Initialize the global string for the shaders
global string $txtName[];
//For each shader
int $a=0;
for($a=0;$a<`size $txtName`;$a++)
{
//Get the selected texture node and file node
string $file[] = `listConnections -type "file" $txtName[$a]`;
string $color = ($txtName[$a] + ".color");

//If a file node exists
if (objExists ($file[0]))
{
//If the file node exist, check if the gammacorrector is present
if (objExists($txtName[$a] + "_gcr"))
{
//Change the values of the gammacorrector
setAttr ($txtName[$a] + "_gcr.gammaX") $sliderXvalue;
setAttr ($txtName[$a] + "_gcr.gammaY") $sliderYvalue;
setAttr ($txtName[$a] + "_gcr.gammaZ") $sliderZvalue;
}
//If the file node exist, and the gammacorrector doesn't exists
else
{
//Create a new gammacorrector with the specified values
shadingNode -n ($txtName[$a] + "_gcr") -asUtility gammaCorrect;
disconnectAttr ($file[0] + ".outColor") $color;
connectAttr -force ($file[0] + ".outColor") ($txtName[$a] + "_gcr.value");
setAttr ($txtName[$a] + "_gcr.gammaX") $sliderXvalue;
setAttr ($txtName[$a] + "_gcr.gammaY") $sliderYvalue;
setAttr ($txtName[$a] + "_gcr.gammaZ") $sliderZvalue;
connectAttr -force ($txtName[$a] + "_gcr.outValue") $color;
}
}
//If the file node doesn't exist
else
{
//Check if the gammacorrector is present
if (objExists($txtName[$a] + "_gcr"))
{
//Set the values of the gammacorrector
setAttr ($txtName[$a] + "_gcr.gammaX") $sliderXvalue;
setAttr ($txtName[$a] + "_gcr.gammaY") $sliderYvalue;
setAttr ($txtName[$a] + "_gcr.gammaZ") $sliderZvalue;
}
//If neither of the file node or the gammacorrector exists
else
{
//Print error message
print ("Could not find a file node attached to " + $txtName[$a] + ".\n");
}
}
}
//If an object was selected
if(`size $txtName` != 0)
{
//Update the hypershade input/output connections
HypershadeWindow;
select $txtName;
evalDeferred("hyperShadePanelGraphCommand hyperShadePanel1 showUpAndDownstream");
}
}

global proc jh_deleteNode()
{
//Initialize the global string for the shaders
global string $txtName[];
//For each shader
int $a=0;
for($a=0;$a<`size $txtName`;$a++)
{
string $color = ($txtName[$a] + ".color");
//If the gammacorrector exists
if (objExists($txtName[$a] + "_gcr"))
{
//Get the file name, then delete the gammacorrector and connect the file node to color
string $file[] = `listConnections -type "file" ($txtName[$a] + "_gcr")`;
delete ($txtName[$a] + "_gcr");
connectAttr -force ($file[0] + ".outColor") $color;
}
//If the gammacorrector does not exist
else
{
//Print error message
print "Could not find a gammacorrector node.\n";
}
//If an object was selected
if(`size $txtName` != 0)
{
//Update the hypershade input/output connections
HypershadeWindow;
select $txtName;
evalDeferred("hyperShadePanelGraphCommand hyperShadePanel1 showUpAndDownstream");
}
}
}

jh_gammaCorrector;

AdamEisfeld
10-26-2010, 03:25 AM
Thanks for the help but Ive pretty much finished my script. It allows you to enter a gamma value in a textbox and apply the effect to all blinn, phong, phongE, anisotropic and lambert shaders in the scene, as well as toggle the effect on / off with a radio button set. Seems to work good so far, and I think Ive trapped all of the errors. Just need to clean the code up a bit here and there, as I have some minor redundancies.

surreal-reality
10-26-2010, 03:49 PM
Hey, glad to see you got it figured out, this may not be of importance or you may already know it, but it plagued me for months so here it is.

Sun and sky creates 3 nodes, and one of them is the exposure simple node. the gamma of 2.2 is default, and pretty high / washed out. I like 1.8, the help docs mentioned it as a more realistic normal outdoor level, but you can bring it all the way back down to 1 if you want.

If you want individual control over textures though gamma nodes are the way to go!

Just thought I'd throw that out there : )

AdamEisfeld
10-26-2010, 04:17 PM
Hah I had no idea that was happening. My teacher brought it up and mentioned that he was unable to find any other fix other than adjusting the gamma levels with the gammaCorrect nodes one by one, so I figured Id just write him a script that would automate the process and save everyone the time and hassle. He mentioned something about how photoshop automatically adjusts gamma levels when saving any image files, and this effect is only seen when the physical sun and sky is created, and to counter this you must either save your textures with lowered gamma levels in photoshop or adjust them in maya with the nodes.

Ah well! It was a good learning experience, as Im pretty sure Im now up to speed with most of MEL's language and how everything works, so Ill be able to write some more complex scripts in the future.

In fact this little project led me to a more advanced idea of writing a script that allows you to create "node layers" in Maya that you then apply whatever nodes you wish to these layers, and select which shaders in Maya use these layers via the script as well, along with a toggle option to turn the layers on / off. I figured this would be useful for quickly adjusting render settings on a global level for groups of mesh's as opposed to having to go in and apply the nodes to each shader you desire manually. Then again this functionality could already exist somewhere but I've not really looked into it too much.

For anyone interested, here is the final script, however as I said it has a few redundancies and pieces of code that could definitely be optimized.

//Gamma Correction Utility - Programmed by Adam Eisfeld

// updateGamma procedure: called to apply the current gamma settings to all shaders in the scene with textures applied to them.
global proc updateGamma () {

//delete all existing gammaCorrect utilities in the scene.
removeGammaCorrect;

//get access to the radio buttons for toggling the gamma correction on/off
global string $rad_ga_ON;
global string $radCol_gammaAdjust;
radioCollection -edit -select $rad_ga_ON $radCol_gammaAdjust;

//get access to the text box that controls the amount of gamma correction
global string $txt_gamma;

//store the gamma correction text box's value in a float
float $gamma = `floatField -q -value $txt_gamma`;

// list/get all lambert, phong, phongE, anisotropic and blinn shaders in the scene
string $shaders[] = `ls -type "lambert" -type "phong" -type "blinn" -type "anisotropic" -type "phongE"`;
// iterate through each shader in the array:
for ($item in $shaders)
{
//skip over the default lambert1 shader, as it must not be modified.
if ($item == "lambert1"){

}else{

// find out what is connected to the shaders "color" attribute
string $colorTex[] = `listConnections ( $item + ".color" )`;

// if there are no connections, jump to the next shader
if (!`size $colorTex`) continue;

// get the type of node connected to the shader's "color" attribute
string $id = `nodeType $colorTex[0]`;

// if the type of node is a file, begin the process of applying gamma correction
if( $id == "file" ){

// create a node of type "gammaCorrect"
string $gammaNode = `createNode gammaCorrect`;
// set the relevent gamma node attributes:
setAttr ( $gammaNode + ".value") $gamma $gamma $gamma; // set gamma value
// connect this gamma node between texture and shader:
connectAttr -f ( $colorTex[0] + ".outColor" ) ( $gammaNode + ".gamma" );
connectAttr -f ( $gammaNode + ".outValue" ) ( $item + ".color" );
}
}
}

};

//removeGammaCorrect procedure: Runs through all of the shaders in the scene and disconnects their gammaCorrect utilities.
global proc removeGammaCorrect () {

// list/get all lambert, phong, phongE, anisotropic and blinn shaders in the scene
string $shaders[] = `ls -type "lambert" -type "phong" -type "blinn" -type "anisotropic" -type "phongE"`;
// iterate through each shader in the array:
for ($con_lamb in $shaders)
{
// list all of the nodes connected to the shader's "color" attribute
string $nodes[] = `listConnections ( $con_lamb + ".color" )`;

// iterate through each of these nodes
for ($con_gamma in $nodes){

// determine the type of node being worked with
string $id = `nodeType $con_gamma`;

// if the type of node is a gammaCorrect utility, we can proceed with disconnecting it / deleting it
if( $id == "gammaCorrect" ){

// list all of the nodes connected to the gammaCorrect node's "gamma" attribute. This will allow us to reconnect the shader's texture to it's color
string $gammaNodes[] = `listConnections ( $con_gamma + ".gamma" )`;

// iterate through each of these nodes connected to the gammaCorrect node
for ($con_file in $gammaNodes){

// if the node connected to the gammaCorrect node is a file, we've found what we're looking for
string $id2 = `nodeType $con_file`;
if( $id2 == "file" ){

// delete the gammaCorrect node
delete $con_gamma;

// reconnect the shader's file node to it's "color" attribute
connectAttr -f ( $con_file + ".outColor" ) ( $con_lamb + ".color" );
}
}
}

}

}

};

// setupGUI: Creates the graphical user interface for the gamma correction utility.
global proc gammaCorrect () {

// if the utility's window has already been created and not closed, delete it.
if (`window -exists win_GammaCorrect`){
deleteUI win_GammaCorrect;
}

// create the gamma correction's window.
window -title "Gamma Correction - Adam Eisfeld" -resizeToFitChildren 1 -sizeable 0 -widthHeight 300 200 win_GammaCorrect;

// apply a form layout to the window with 100 divisions in the x / y
string $form = `formLayout -numberOfDivisions 100`;

// create the adjustment button, and connect it's onPress action to the updateGamma procedure
string $btn_adjust = `button -label "Adjust Gamma" -command "updateGamma"`;

// create a label for the gamma floatField
string $lbl_gamma = `text -label "Gamma: "`;

// create the gamma float field. We use a float field to restrict the user to entering numbers with decimals
global string $txt_gamma;
$txt_gamma = `floatField -width 40 -precision 3`;

// create an info label helping the user understand what to do
string $lbl_info = `text -align "left" -label "Input the desired gamma correction value into the text box, \nand press the Adjust button to automatically adjust the \ngamma levels for all shaders with textures connected to \nthem in the scene. Toggle the effect with the on/off radio \nbuttons."`;

// create the radio buttons for toggling the gamma correction effect on / off
global string $radCol_gammaAdjust;
$radCol_gammaAdjust = `radioCollection`;
global string $rad_ga_ON, $rad_ga_OFF;
$rad_ga_ON = `radioButton -label "On" -onCommand updateGamma`;
$rad_ga_OFF = `radioButton -label "Off" -onCommand removeGammaCorrect`;

// attach the controls to the form and set their positions
formLayout -edit

-attachForm $rad_ga_ON "left" 3
-attachForm $rad_ga_ON "top" 50

-attachForm $rad_ga_OFF "left" 40
-attachForm $rad_ga_OFF "top" 50

-attachForm $lbl_gamma "left" 3
-attachForm $lbl_gamma "top" 3

-attachForm $txt_gamma "left" 43
-attachForm $txt_gamma "top" 3

-attachForm $btn_adjust "top" 25
-attachForm $btn_adjust "left" 3

-attachForm $lbl_info "top" 3
-attachForm $lbl_info "left" 93
$form;

// select the "off" radio button to begin with
radioCollection -edit -select $rad_ga_OFF $radCol_gammaAdjust;

// display the window
showWindow;
};

(Save the script as gammaCorrect.mel, run it and enter "gammaCorrect" in the script editor / hit the return key on the numpad to run it).

Things like storing a list of all of the shaders both in the removeGamma proc and the updateGamma proc could obviously be optimized into one list storage, along with other nuances here and there. So by no means am I saying this code is up to par with a good quality snippet! :P. Just something thrown together as learning practice, that definitely needs cleaning up. ( /end disclaimer )

Thanks for all of the help again.
- Adam Eisfeld

CGTalk Moderation
10-26-2010, 04:17 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.