2015年9月25日金曜日

Maya色管理6 LightColor

ライトの色はレンダリング空間のRGB値を入力すること。
特に彩度が高いライト色はレンダ空間に合わせた数値にしないと見え方が変わってしまう。
これはMaya色管理3 数値入力のところで説明した通り

ライトの色変換のテスト

例えばsRGB(D65)[1, 0.5, 0.3]の色は
XYZ(D65)では[0.645,0.592,0.334]
ACEScg(D60)では[0.797  0.532  0.364]
とすれば同じ色を表せる

カラーチェッカーのテクスチャは入力カラースペースを指定している状態で、ライトは正面からの並行光源を当てた状態でテスト。

ライトのRGB値 [1, 0.5, 0.3] で統一した場合



ライトの色が同じになるようにした場合 (sRGBの[1, 0.5, 0.3]に合わせて変換)



ライトの色が一致したことがわかる。
ライティングされたパッチの色がレンダリング空間ごとに違うのは仕方のないことで、詳細はレンダリング時の色空間
レンダ空間による色ずれの影響が少ない4段目の無彩色のパッチを見ると差がほとんどないので正しく変換された数値でライティングされていることがわかる

実はRGB(1,1,1)のライトの場合は各レンダ空間へ変換しても(1,1,1)になる場合が多い

下記がライトの色変換に使用したマトリクス

sRGB to レンダ空間マトリクス(白色変更はcat02)
sRGB(D65) to XYZ(D65)
 0.4123907993  0.3575843394  0.1804807884
 0.2126390059  0.7151686788  0.0721923154
 0.0193308187  0.1191947798  0.9505321523
sRGB(D65) to ACEScg(D60)
 0.6130828922  0.3411670543  0.0457500538
 0.0700035498  0.9180628305  0.0119336198
 0.0204907778  0.1067639876  0.8727452346


ライトの現在の色を目的の色空間へ変換するPyMel

今回のテストで使用したPyMel
すべてのライトタイプのcolorアトリビュートが変更される
48行目で現在の色空間と白色点、目的の色空間と白色点を指定する

import pymel.core as pm
from numpy import *

#白色点のXnYnZnの配列 (3*1mat)
arrMatLightXYZ = [
matrix('1.0;1.0;1.0'),                       #E   from(x,y = 1/3,1/3)
matrix('1.09849061235,1,0.355798257455'),    #A   from(x,y = 0.44758,0.40745)
matrix('0.98070597166;1;1.18224949393'),     #C   from(x,y = 0.31006,0.31616)
matrix('0.96429567643;1;0.82510460251'),     #D50 from(x,y = 0.3457,0.3585)
matrix('0.95264607457;1.0;1.00882518435'),   #D60 from(x,y = 0.32168,0.33767)
matrix('0.950455927052;1.0;1.08905775076'),  #D65 from(x,y = 0.3127,0.3290)
matrix('0.894586894587;1.0;0.954415954416')] #dci from(x,y = 0.3140,0.3510)
arrKeyLightXYZ = ["E","A","C","D50","D60","D65","dci"]
arrMatLightXYZ = dict(zip(arrKeyLightXYZ,arrMatLightXYZ))

#XYZtoRGBの配列  (3*3mat)
arrMatXYZ2RGB = [
matrix('1 0 0; 0 1 0; 0 0 1'), #ZYZ(E)
matrix('3.2409699419 -1.5373831776 -0.4986107603; -0.9692436363 1.8759675015 0.0415550574;0.0556300797 -0.2039769589 1.0569715142'), #sRGB(D65)
matrix('2.7253940305 -1.0180030062 -0.4401631952; -0.7951680258 1.6897320548 0.0226471906;0.0412418914 -0.0876390192 1.1009293786'), #DCI-P3
matrix('1.716651188 -0.3556707838 -0.2533662814; -0.6666843518 1.6164812366 0.0157685458;0.0176398574 -0.0427706133 0.9421031212'), #Rec.2020
matrix('1.6410233797 -0.3248032942 -0.236424695; -0.6636628587 1.6153315917 0.0167563477; 0.0117218943 -0.0082844420 0.9883948585'), #ACESap1(D60)
matrix('1.0498110175 0 -0.0000974845; -0.4959030231 1.3733130458 0.0982400361; 0 0 0.9912520182'),                                   #ACESap0(D60)
matrix('1.2694188828 -0.0988302413 -0.1705886415; -0.8363858117 1.8007170555 0.0356687562; 0.0297300599 -0.0314712627 1.0017412028')] #SharpRGB(E)
arrKeyXYZ2RGB = ["XYZ","sRGB","P3","2020","ACESap1","ACESap0","SharpRGB"]
arrMatXYZ2RGB = dict(zip(arrKeyXYZ2RGB,arrMatXYZ2RGB))

#白色点変更メソッドはcat02
mAdaptation = matrix('0.7328 0.4296 -0.1624; -0.7036 1.6975 0.0061; 0.003 0.0136 0.9834')

#変換先の色空間へ変換するマトリクスを求める関数(変換元の色空間名,変換先の色空間名,変換元の白色点,変換先の白色点 を指定する)
def getMatrixRGBtoRGB(sourceXYZtoRGBname,destXYZtoRGBname,sourceWname,destWname):
    print '変換元:',sourceXYZtoRGBname,'  変換先:',destXYZtoRGBname,'  変換元白色点:',sourceWname,'  変換先白色点:',destWname,'\n'
    #---LMSスケールを求める---
    lmsS = mAdaptation * arrMatLightXYZ[sourceWname]
    lmsD = mAdaptation * arrMatLightXYZ[destWname]
    mScale = matrix(zeros((3, 3)))     #対角行列を入れる準備
    fill_diagonal(mScale,(lmsD/lmsS))  #対角行列を作成
    #---白色点変換マトリクス---
    mChangeW = mAdaptation.I* mScale * mAdaptation
    #---レンダ空間変換マトリクス(from XYZ)---
    mRGBtoRGB = arrMatXYZ2RGB[destXYZtoRGBname] * mChangeW * arrMatXYZ2RGB[sourceXYZtoRGBname].I
    print "sourceRGB to destRGB matrix: \n",mRGBtoRGB,'\n'
    return mRGBtoRGB

#**** 変換前色空間と変換先色空間、さらに変換前白色と変換先白色の4個の名前を指定する   ****
#**** 色空間 XYZ sRGB P3 2020 ACESap1 ACESap0 SharpRGB| 白色点 E A C D50 D60 D65 dci (白色点変更しない場合は双方同じものを入れておけば何でもよい)****
mRGBtoRGB = getMatrixRGBtoRGB('sRGB','XYZ',"D65","D65")

#ここからmaya部分。ライトカラーを取得して目的の色空間の値へ変更
oLightList = pm.ls(type = 'light') #シーン中のライトを取得
for olight in oLightList:
    sourceColor = olight.color.get() #現在の色を取得
    #sourceColor = (1,0.5,0.3) # 指定値から変換したい場合
    mSourceColor = matrix(sourceColor).T #色をマトリクス3*1マトリクスに格納
    destColor = mRGBtoRGB * mSourceColor #色変換
    destColor = (destColor[0,0],destColor[1,0],destColor[2,0]) #色をセットするために3*1マトリクスからタプルに変更
    olight.color.set(destColor) #色をセット
    print '変換したライト','{: ^30}'.format(olight),'変換前の値',around(sourceColor,5),'   変換後の値',around(destColor,5)

数値入力でもカラーマネジメントをしたい場合の例15/09/30追記

少し強引だけど出来た。もっとシンプルな方法もありそうだけど...

ライトの色やマテリアルの色を入力する時に下図のように組まれたものを使う。カラー入力と同時に入力スペースの情報も入れておく。
こうすれば元の値を保持することができ、レンダリングスペースが変わったとしても半自動で対応できる。
画像はsRGB(D65) to XYZ(D65)。 入力値はリニアRGBなのでデガンマはしていない


ハイパーシェードのノードはコンパウンドできないのかな?。あとコメントやグループのノードもほしい
この場合例えば下記スクリプトで変換マトリクスが更新される。入力スペースを参照して現在のレンダリングスペースへの変換マトリクスが作られる。

import pymel.core as pm
from numpy import *

#白色点のXnYnZnの配列 (3*1mat)
arrMatLightXYZ = [
matrix('1.0;1.0;1.0'),                       #E   from(x,y = 1/3,1/3)
matrix('1.09849061235;1;0.355798257455'),    #A   from(x,y = 0.44758,0.40745)
matrix('0.98070597166;1;1.18224949393'),     #C   from(x,y = 0.31006,0.31616)
matrix('0.96429567643;1;0.82510460251'),     #D50 from(x,y = 0.3457,0.3585)
matrix('0.95264607457;1.0;1.00882518435'),   #D60 from(x,y = 0.32168,0.33767)
matrix('0.950455927052;1.0;1.08905775076'),  #D65 from(x,y = 0.3127,0.3290)
matrix('0.894586894587;1.0;0.954415954416')] #dci from(x,y = 0.3140,0.3510)
arrKeyLightXYZ = ["e","a","c","d50","d60","d65","dci"]
arrMatLightXYZ = dict(zip(arrKeyLightXYZ,arrMatLightXYZ))

#XYZtoRGBの配列  (3*3mat)
arrMatXYZ2RGB = [
matrix('1 0 0; 0 1 0; 0 0 1'), #ZYZ(E)
matrix('3.2409699419 -1.5373831776 -0.4986107603; -0.9692436363 1.8759675015 0.0415550574;0.0556300797 -0.2039769589 1.0569715142'), #sRGB(D65)
matrix('2.7253940305 -1.0180030062 -0.4401631952; -0.7951680258 1.6897320548 0.0226471906;0.0412418914 -0.0876390192 1.1009293786'), #DCI-P3
matrix('1.716651188 -0.3556707838 -0.2533662814; -0.6666843518 1.6164812366 0.0157685458;0.0176398574 -0.0427706133 0.9421031212'), #Rec.2020
matrix('1.6410233797 -0.3248032942 -0.236424695; -0.6636628587 1.6153315917 0.0167563477; 0.0117218943 -0.0082844420 0.9883948585'), #ACESap1(D60)
matrix('1.0498110175 0 -0.0000974845; -0.4959030231 1.3733130458 0.0982400361; 0 0 0.9912520182'),                                   #ACESap0(D60)
matrix('1.2694188828 -0.0988302413 -0.1705886415; -0.8363858117 1.8007170555 0.0356687562; 0.0297300599 -0.0314712627 1.0017412028')] #SharpRGB(E)
arrKeyXYZ2RGB = ["xyz","srgb","p3","2020","ap1","ap0","sharp"]
arrMatXYZ2RGB = dict(zip(arrKeyXYZ2RGB,arrMatXYZ2RGB))

#白色点変更メソッドはcat02
mAdaptation = matrix('0.7328 0.4296 -0.1624; -0.7036 1.6975 0.0061; 0.003 0.0136 0.9834')

#変換先の色空間へ変換するマトリクスを求める関数(変換元の色空間名,変換先の色空間名,変換元の白色点,変換先の白色点 を指定する)
def getMatrixRGBtoRGB(sourceXYZtoRGBname,destXYZtoRGBname,sourceWname,destWname):
    print '変換元:',sourceXYZtoRGBname,'  変換先:',destXYZtoRGBname,'  変換元白色点:',sourceWname,'  変換先白色点:',destWname,'\n'

    #---LMSスケールを求める---
    lmsS = mAdaptation * arrMatLightXYZ[sourceWname]
    lmsD = mAdaptation * arrMatLightXYZ[destWname]
    mScale = matrix(zeros((3, 3)))     #対角行列を入れる準備
    fill_diagonal(mScale,(lmsD/lmsS))  #対角行列を作成
    #---白色点変換マトリクス---
    mChangeW = mAdaptation.I* mScale * mAdaptation
    
    #---レンダ空間変換マトリクス(from XYZ)---
    mRGBtoRGB = arrMatXYZ2RGB[destXYZtoRGBname] * mChangeW * arrMatXYZ2RGB[sourceXYZtoRGBname].I
    
    set_printoptions(suppress=True,precision=10) #少数10桁表示
    print "sourceRGB to destRGB matrix: \n",mRGBtoRGB,'\n'
    return mRGBtoRGB

#ここから下がMaya部分
rsnList = ["ACES2065-1","ACEScg","scene-linear CIE XYZ","scene-linear DCI-P3","scene-linear Rec 2020","scene-linear Rec 709/sRGB","Sharp RGB"]
rsnSortNameList = ["ap0","ap1","xyz","p3","2020","srgb","sharp"]
wihteList = ["d60","d60","d65","dci","d65","d65","e"]

#現在のレンダリング色空間の取得し変数に設定
rsn = pm.colorManagementPrefs(q=True, renderingSpaceName=True)
destRgbSpace = rsnSortNameList[rsnList.index(rsn)] #レンダリング空間の簡易名
destWhite = wihteList[rsnList.index(rsn)] #レンダリング空間に対応する白色点の名前を指定する

#専用のアトリビュートを持たせたハイパーシェードノードを起点として目的のノードに対して求めたマトリクスを入力する。(接続先のノードの入力1に接続されているノードに対して)
ComposeMlist = pm.ls(type = 'composeMatrix') #composeMatrixタイプのノードを取得
for ComposeMnode in ComposeMlist:
    if ComposeMnode.hasAttr("myColorConvert"): #myColorConvertというアトリビュートがあれば以下を実行。
        myAttrVal = ComposeMnode.myColorConvert.get() #アトリビュートの値を取得。入力スペースと白色点の取得
        if myAttrVal != None:
            sourceRgbSpace,sourceWhite = myAttrVal.split(",")
            m = getMatrixRGBtoRGB(sourceRgbSpace,destRgbSpace,sourceWhite,destWhite) #入力色空間からレンダリング色空間へ変換するマトリクスの取得
            m2 = pm.datatypes.Matrix([[m[0,0],m[0,1],m[0,2],0],[m[1,0],m[1,1],m[1,2],0],[m[2,0],m[2,1],m[2,2],0],[0,0,0,0]]) #NumPyのマトリクスからPyMelのマトリクスに変換
            if len(ComposeMnode.outputMatrix.connections()) > 0 :
                MultMatrixNode = ComposeMnode.outputMatrix.connections()[0] 
                if len(MultMatrixNode.matrixIn[0].connections()) > 0 :
                    ComposeMnode2 = MultMatrixNode.matrixIn[0].connections()[0]
                    ComposeMnode2.outputMatrix.set(m2) #値のセット

0 件のコメント:

コメントを投稿