Is it possible to create custom light node that use own created shader fragment?

Is it possible to create custom light node that use own created shader fragment?

zhangjialin6010
Participant Participant
1,348 Views
14 Replies
Message 1 of 15

Is it possible to create custom light node that use own created shader fragment?

zhangjialin6010
Participant
Participant

Greetings maya forum!

 

Maya's native arealight can only render in rectangular shape in viewport. I want to create a custom area light that support various shape (sphere, round, cylinder, etc). I've gone through the documentation but it only says we can render custom light in viewport by specifing the classification as the same of native light.

 

I first tried registering a shading node and assign it a shader fragment XML (https://help.autodesk.com/view/MAYAUL/2023/ENU/?guid=Maya_SDK_Writing_a_Software_Shading_Node_Writin... in this page light seems to be a shading node) but failed to bind the override. 

 

Then I find fragment manager can list and dump all non-hidden internal shader fragment- I found "mayaRectangleAreaLight3" is the one that calculate the irradiance of maya's native area light and I can override it to change maya's area light behavior.

<implementation  render="OGSRenderer" language="HLSL" lang_version="11.000000" >
        <function_name val="mayaRectAreaLight3" />
        <source>
            <![CDATA[
float3 LwAtMaxSpec( float3 ltPts[4], float3 ltCenter, float3 Pw, float3 Nw, float3 Vw )
{ 
    float RL, RNl; 
    float3 L; 
    float3 R = reflect( -Vw, Nw ); 
    const int nSampPerSide = 6; 
    float3 V = ( ltPts[1]-ltPts[0] ) / (nSampPerSide-1); 
    float3 U = ( ltPts[3]-ltPts[0] ) / (nSampPerSide-1); 
    float3 P0 = ltPts[0]; 
    float3 maxL = float3(0,0,0); 
    float endSamp = nSampPerSide - 0.01f; 
    for( float u = 0; u < endSamp; u += 1.0f ) { 
        for( float v = 0; v < endSamp; v += 1.0f ) { 
            float3 LP = P0 + (u * U + v * V); 
            L = normalize( LP - Pw ); 
            RL = dot( R, L ); 
            if( RL > 0.0f )  maxL += RL * RL * L; 
        } 
    } 
    float d = length( maxL ); 
    return ( d > 0.0f ) ? maxL/d : maxL; 
} 
float3 nearestPtOnLt( float3 ltPts[4], float3 ltCenter, float3 Pw )
{ 
    float3 V = ( ltPts[1]-ltPts[0] ); 
    float lenV = length( V ); 
    V /= lenV;  lenV *= 0.5; 
    float3 U = ( ltPts[3]-ltPts[0] ); 
    float lenU = length( U ); 
    U /= lenU;  lenU *= 0.5f; 
    float3 L = Pw - ltCenter; 
    float LU = dot( L, U ); 
    float LV = dot( L, V ); 
    float3 Lw = ltCenter 
                 +  U * clamp( LU, -lenU, lenU ) 
                 +  V * clamp( LV, -lenV, lenV ); 
    return Lw;  
} 
irradiance mayaRectAreaLight3( float lightOn, bool lightActive, float intensity, float3 color, float emitDiff, float emitSpec, 
                      float3 LP0, float3 LP1, float3 LP2, float3 LP3,  float3 Pw, float3 Nw, float3 Vw,
                      float decayRate, bool useArea, int shape )
{ 
    color = float3(0.0, 0.0, color.z);
    float3  ltPts[4] = { LP3, LP2, LP1, LP0 }; 
    float3  T = ddx( Pw ); 
    float3  B = ddy( Pw ); 
    float3  fN = normalize( cross( T, B )); 
    Nw = (dot( fN, Nw ) < 0.0f) ? -Nw : Nw; 
    float sumNG = 0; 
    for( int i = 0; i < 4; ++i ) { 
       int nextI = (i == 3)? 0 : (i+1);  
       float3 Li = normalize(ltPts[i] - Pw); 
       float3 Ln = normalize(ltPts[nextI] - Pw); 
       float3 Gi = normalize( cross(Ln, Li) ); 
       float bi = acos( dot(Ln, Li) ); 
       sumNG += bi * dot( Gi, Nw ); 
    } 
    float ff = 6.0 * saturate( sumNG ); 
    float3 ltCenter = (ltPts[0]+ltPts[1]+ltPts[2]+ltPts[3])/4.0f; 
    float3 Lp = nearestPtOnLt( ltPts, ltCenter, Pw ); 
    float3 Ld = lerp( Lp, ltCenter, 0.35f ) - Pw;  
    float d = length( Ld ) + 1.0f; 
    float attn = 1.0f; 
    if( decayRate > 2.999f ) attn /= d; if( decayRate > 1.999f ) attn /= d; if( decayRate > 0.999f ) attn /= d; 
    float3 clr = lightOn *  color * intensity * ff * attn;  
    if (useArea) {
        float area = length(ltPts[1] - ltPts[0]) * length(ltPts[3] - ltPts[0]);
        clr = clr / area;
    }
    irradiance irrad; 
    irrad.diffuseI = emitDiff * clr; 
    irrad.specularI = emitSpec * clr; 
    irrad.Ls = LwAtMaxSpec( ltPts, ltCenter, Pw, Nw, Vw );  
    irrad.Ld = normalize( Ld ); 
    irrad.Lg = float4(0.0f, 0.0f, 0.0f, 1.0f); 
    irrad.lightType = 0x41524c54; 
    return irrad; 
} 
            ]]>
        </source>
    </implementation>

However, in order to implement multiple light shape I need to add more parameters to it and I cannot find a way to do that. The function is wrapped in <implementation render="OGSRenderer" ...> and I have no idea where is it being called. 

 

FYI I'm using Maya2022. Appreciate for any suggestions! 

@cheng_xi_li 

0 Likes
1,349 Views
14 Replies
Replies (14)
Message 2 of 15

cheng_xi_li
Autodesk Support
Autodesk Support

Hi,

 

I don't think you can specify a shader for a light.

 

What you get was a fragment and will be added to the whole fragment graph. Maya generates the whole shader based on these internal fragments. If you check the final output of the Maya blinn shader, you can find this fragment was added to it, and I don't think Maya will add any other shader fragments to build its own surface shader(e.g., blinn).

 

As far as I am concerned, the only way to make a custom light is rendering different to Maya's own implementation is to write your own surface shader, and calculate the irrdiance in it. 

 

Yours,

Li 

Message 3 of 15

zhangjialin6010
Participant
Participant

Hi @cheng_xi_li ,

 

Thanks so much for the reply! I found in Arnold for maya plugin there's a node called aiAreaLight and there's an xml file called aiRectangleAreaLight.xml that looks similar to maya's native arealight fragment. Based on that I think there should be some way for a plugin to insert a light fragment. However, it seems this mtoa plugin is not open source so I cannot get to know how that is implemented either. 

 

Im looking for an alternative way, which is to add the feature to maya's native area light instead of creating a new node type. I found in 2022.0 version a new function MFragmentManager::addAutomaticShaderStageInput has been added and I wonder if this could be helpful... There's no example in devkit that makes use of it.. Do you happen to know where I can get some example of it?

 

Thanks again for the help!

 

Jialin

0 Likes
Message 4 of 15

cheng_xi_li
Autodesk Support
Autodesk Support
Hi,

Thanks for looking it up! It seems I've missed this one. It was a feature added back in 2018.4 for mtoa specifically and it seems not documented.

According to the internal system, you can specify a shader for a light node with following classification in following format:
"lightShader/name_of_registered_light_fragment"
All shader parameters must match the attribute names exactly for them to be set properly on the shader.

So, for the aiAreaLight, it has an additional classification to use the fragment.

drawdb/light/areaLight:lightShader/aiRectangleAreaLight

I tried to search in the devkit samples, and I didn't find any samples using this classification. Sorry for the misleading at the beginning.

Yours,
Li
Message 5 of 15

zhangjialin6010
Participant
Participant

Hi @cheng_xi_li ,

 

Happy new year, and thanks so much for digging this deep for me!

 

I first copied maya's native area light fragment to create a new fragment with a different name and this works;  however it won't receive more parameters that I added to match my custom area light's attribute name ( a enum "shape" and a float "exposure" ).

    <properties>
        <int   name="shape" flags="varyingInputParam" />
        <float name="exposure" flags="varyingInputParam"/>
    </properties>
    <values>
        <int name="shape" value="0" />
        <float name="exposure" value="0.2" />
    </values>

is there anything more I should do to make this work? cos in that case, it would be the same problem as replacing native fragment- it doesn't allow me to add more parameters to control the manipulate the shape display.. 

 

Thanks,

Jialin

 

0 Likes
Message 6 of 15

cheng_xi_li
Autodesk Support
Autodesk Support
Hi,

I am still asking engineers about this feature, but I am not sure how the mapping working in the details at the moment. However, I think the flags for shape and exposure attribute shouldn't be varyingInputParam. varyInputParams are variables stored in vertices, e.g., Pm, Nm, Tm, Bm, U0 - U8, C0 - C1 or vtx->pix interpolants. Maybe we could try the flag arnold uses(e.g., isNotSharableParam) and check if it works?

Yours,
Li
Message 7 of 15

zhangjialin6010
Participant
Participant

Hi @cheng_xi_li ,

 

I tried isNotSharableParam but it doesn't work either.. though I found something interesting with the term "exposure"-  if and only if I name the the attribute with "exposure" , it doesn't change the value of the function input variable, but it does change the overall intensity of the light (not sure what exactly it changes, but visually, it's the same effect as I adjust intensity value). 

zhangjialin6010_1-1704380316237.png

 

zhangjialin6010_0-1704380273052.png

and if i change the attribute name (and the variable name in shader fragment) to "sr_exposure" or "exp"  then it won't do anything when the attribute value changes. I guess it's because "exposure" is used somewhere internally and I unintentionally triggered an undefined behavior?

 

Jialin

0 Likes
Message 8 of 15

zhangjialin6010
Participant
Participant

And another observation- the custom area light node and native area light node cannot be rendered in viewport at the same time. 

zhangjialin6010_0-1704394828529.png

The later created light breaks render pipeline. Does that mean it is still somehow an override of native area light?

 

Jialin

 

Message 9 of 15

cheng_xi_li
Autodesk Support
Autodesk Support

Have you changed the fragment's name? If your light fragment uses an internal fragment name, it may overwrite the original one.

 

I could find exposure attribute in some devkit samples or aiRectangleLight, according to Arnold's comment, it seems like an internal attribute. Another internal attribute seems to be normalize. 

Message 10 of 15

zhangjialin6010
Participant
Participant

hi @cheng_xi_li 

 

just double checked this-

zhangjialin6010_0-1704812320068.png

when there is no custom area light in the viewport, native area light draws with native fragment (my custom fragment is red color only for testing) so i think it's not overrided. 

 

Jialin

0 Likes
Message 11 of 15

cheng_xi_li
Autodesk Support
Autodesk Support

arealight.png

arealight2.png

Hi, I tried to use both Arnold's area light and Maya native area light in the same scene. I switched lights' positions in two screen shots, and they are clearly different. 

Could you post your fragments and codes? We can check it and try to figure it out.

 

Yours,

Li

0 Likes
Message 12 of 15

zhangjialin6010
Participant
Participant

Hi @cheng_xi_li ,

 

Thanks for the hint! This inspired me to looke at aiAreaLight fragment- I copy/paste aiAreaLight fragment code and it doesn't interfare with native light.  I spent all day playing with the code, broke them into pieces and did tests, finally found the reason seems to be double declaration of functions- in maya's native arealight fragment there are three functions: LwAtMaxSpec, nearestPtOnLt and mayaRectAreaLight3. I didn't change the name of the two children functions and that's why it interfares with native fragment. 

 

Now I'm able to let my custom arealight and native arealight play together, I'm still having trouble inserting new parameters. Even aiArealight doesn't have parameters that native arealight doesn't have. It seems not enough just simply add new attributes in fragment <properties> and <values> sections...

 

Another new observations is that for native area light fragment, there are parameters called "emitsDiffuse" and "emitsSpecular". However, for areaLight node attribute they are called "emitDiffuse" and "emitSpecular". What's even more weird is the parameter of function mayaRectAreaLight3(), they are called "emitDiff" and "emitSpec". I'm not sure how OGS render operates internally to pipeline these. 

 

Any idea? 

 

Thanks again, 

Jialin

0 Likes
Message 13 of 15

cheng_xi_li
Autodesk Support
Autodesk Support

Hi,

 

I checked the parameter binding code for area light shaders today. It seems that OGS will find parameters using the names in properties (e.g., emitsDiffuse) and set it according to the index in the fragment input definition.  

 

I didn't find any codes on custom attributes. Both name and types for input parameters are hardcoded, so my initial guess is that custom attributes won't work. I am double checking this with our engineers, I'll post the conclusion once I've got feedback from them.

 

FYI: About exposure attribute, Maya will use it internally. The final intensity input for a light shader should be intensity * exposure in general. 

 

Yours,

Li

Message 14 of 15

zhangjialin6010
Participant
Participant

Hi @cheng_xi_li ,

 

Thanks so much! Maybe the last thing I can do are some wicked tricks-e.g. to encode custom attributes to intensity or some other existed attributes.

 

Btw, I know this probably doesn't help in this task, but may I get an idea what do MFragmentManager::addAutomaticShaderStageInput() and MFragmentManager::addDomainShaderInputNameMapping() do? Because it is pretty new, there's no devkit example I can refer to.  

 

Best, 

Jialin

0 Likes
Message 15 of 15

cheng_xi_li
Autodesk Support
Autodesk Support

Hi,

 

addDomainShaderInputNameMapping() is designed for tessellation, so I guess it is irrelevant to your needs.  

 

For addAutomaticShaderStageInput(), I checked the design purpore for this feature (including domain shader input). It was designed for a feature in MayaUSD plugin. There is a requirement to pass custom color from VS -> Tessellation Control Shader -> Tessellation Evaluation Shader -> PS in Maya USD. 

Please check the usage in MayaUSD here. I looked at it briefly myself, and I think it looks like to map a struct as input to COLOR0 between shader stages. You can find the original pull request here

 

Hope it answers your question. 

 

Yours,

Li