Page 1 of 1

Improving the mhx2 importer

PostPosted: Thu Jul 13, 2017 10:39 pm
by blindsaypatten
I've been experimenting with changes to the mhx2 import code that will build "physics correct" material nodes.
It will use an ior value if it is there in the json file
It mixes diffuse and transparent and then the resulting mix with gloss so that transparency doesn't remove gloss
It uses specular_color instead of diffuse_color as the color input to the gloss node
It uses opacity as the default mix factor for transparency

Code: Select all
def buildMaterialCycles(mat, mhMat, scn, cfg):
    print("Creating CYCLES material", mat.name)
    mat.use_nodes= True
    mat.node_tree.nodes.clear()
    tree = NodeTree(mat.node_tree)
    links = mat.node_tree.links
    texco = tree.addNode(1, 'ShaderNodeTexCoord')

    fresnel = tree.addNode(4, 'ShaderNodeFresnel')
    ior = mhMat.get('ior', 1.45)
    fresnel.inputs["IOR"].default_value = ior

    mixTrans = tree.addNode(4, 'ShaderNodeMixShader')

    glossy = tree.addNode(4, 'ShaderNodeBsdfGlossy')
    glossy.inputs["Color"].default_value[0:3] = mhMat["specular_color"]
    glossy.inputs["Roughness"].default_value = 1.0 - mhMat["shininess"]
    glossyTex = tree.addTexImageNode(mhMat, texco, "specular_map_texture", cfg)
    if glossyTex:
        links.new(glossyTex.outputs['Color'], glossy.inputs['Color'])
    mixGloss = tree.addNode(5, 'ShaderNodeMixShader')
    links.new(fresnel.outputs['Fac'], mixGloss.inputs['Fac'])
    links.new(glossy.outputs['BSDF'], mixGloss.inputs[2])

    transparent = tree.addNode(3, 'ShaderNodeBsdfTransparent')
    diffuse = tree.addNode(3, 'ShaderNodeBsdfDiffuse')

    diffuse.inputs["Color"].default_value[0:3] =  mhMat["diffuse_color"]
    diffuse.inputs["Roughness"].default_value = 1.0 - mhMat["shininess"]
    diffuseTex = tree.addTexImageNode(mhMat, texco, "diffuse_texture", cfg)

    if diffuseTex:
        links.new(diffuseTex.outputs['Color'], diffuse.inputs['Color'])
        links.new(diffuseTex.outputs['Alpha'], mixTrans.inputs['Fac'])
    links.new(transparent.outputs['BSDF'], mixTrans.inputs[1])
    links.new(diffuse.outputs['BSDF'], mixTrans.inputs[2])
    links.new(mixTrans.outputs['Shader'], mixGloss.inputs[1])

    opacity = mhMat["opacity"]
    if opacity < 1:
        mixTrans.inputs["Fac"].default_value = opacity           

    normalTex = tree.addTexImageNode(mhMat, texco, "normal_map_texture", cfg)
    if normalTex:
        normalTex.color_space = 'NONE'
        normalMap = tree.addNode(2, 'ShaderNodeNormalMap')
        normalMap.space = 'TANGENT'
        normalMap.uv_map = "UVMap"
        links.new(normalTex.outputs['Color'], normalMap.inputs['Color'])
        links.new(normalMap.outputs['Normal'], fresnel.inputs['Normal'])
        links.new(normalMap.outputs['Normal'], diffuse.inputs['Normal'])
        links.new(normalMap.outputs['Normal'], glossy.inputs['Normal'])
    else:
        normalMap = None

    output = tree.addNode(6, 'ShaderNodeOutputMaterial')
    links.new(mixGloss.outputs['Shader'], output.inputs['Surface'])


The biggest issue with it is that invisible materials will have gloss unless the ior on the Fresnel node is set to 1, which makes sense, because invisible materials shouldn't refract light, pretty much by definition. Unfortunately the only way to set the ior is in Blender or by editing of the json/mhx2 file. I was hoping that I could just put it in the mhmat material file but doing so doesn't get it set in the exported file. I looked at the export code and it looks like it just writes out the internal data structures, so if the ior isn't read in by MakeHuman then it won't get exported.

Code: Select all
def addMaterial(mhMaterials, mat, mname, texhandler):
    mhMat = OrderedDict()
    mhMaterials.append(mhMat)
    mhMat["name"] = mname

    mhMat["diffuse_color"] = mat.diffuseColor.asTuple()
    mhMat["diffuse_map_intensity"] = 1.0
    mhMat["specular_color"] = mat.specularColor.asTuple()
    mhMat["specular_map_intensity"] = mat.specularMapIntensity
    mhMat["shininess"] = mat.shininess
    mhMat["opacity"] = mat.opacity
    mhMat["translucency"] = mat.translucency
    mhMat["emissive_color"] = mat.emissiveColor.asTuple()
    mhMat["ambient_color"] = mat.ambientColor.asTuple()
    mhMat["transparency_map_intensity"] = mat.transparencyMapIntensity

    mhMat["shadeless"] = mat.shadeless
    mhMat["wireframe"] = mat.wireframe
    mhMat["transparent"] = mat.transparent
    mhMat["alphaToCoverage"] = mat.alphaToCoverage
    mhMat["backfaceCull"] = mat.backfaceCull
    mhMat["depthless"] = mat.depthless
    mhMat["castShadows"] = mat.castShadows
    mhMat["receiveShadows"] = mat.receiveShadows

    mhMat["sssEnabled"] = mat.sssEnabled
    mhMat["sssRScale"] = mat.sssRScale
    mhMat["sssGScale"] = mat.sssBScale
    mhMat["sssBScale"] = mat.sssBScale

    texhandler.addTexture(mhMat, "diffuse_texture", mat.diffuseTexture)
    texhandler.addTexture(mhMat, "specular_map_texture", mat.specularMapTexture)
    texhandler.addTexture(mhMat, "normal_map_texture", mat.normalMapTexture)
    texhandler.addTexture(mhMat, "transparency_map_texture", mat.transparencyMapTexture)
    texhandler.addTexture(mhMat, "bump_map_texture", mat.bumpMapTexture)
    texhandler.addTexture(mhMat, "displacement_map_texture", mat.displacementMapTexture)
    texhandler.addTexture(mhMat, "ao_map_texture", mat.aoMapTexture)

Re: Improving the mhx2 importer

PostPosted: Sat Jul 15, 2017 11:45 pm
by jujube
In renderman's ubershader, it's easy to do this by turning transparency on and leaving refraction (and everything else) off. Cycles's principled shader doesn't seem to be able to do this unforunately (but it would still be cool to see the principled shader in mhx2 anyway, when it's officially released in the next blender version.)

Re: Improving the mhx2 importer

PostPosted: Mon Jul 24, 2017 1:27 am
by blindsaypatten
I have attached an experimental version of my MHX2 materials importer. It replaces the materials.py in import_runtime_mhx2 in your Blender addons folder. In my case it is in:
C:\Users\Lindsay\AppData\Roaming\Blender Foundation\Blender\2.78\scripts\addons\import_runtime_mhx2

The most recent change is that it now detects whether the Principled shader is available and builds a node network that uses it when it is.
BlueEyeNodes.png

Unfortunately, while the node setup deals with solid objects okay, it has challenges with invisibility and partial transparency.

Here's a hair mesh set up to be completely invisible, after 37 samples it looks like this:
VisibleAfter30.png
After 500 samples it's much better but still clearly visible: (click and look at the full resolution image)
VisibleAfter500.png
After 1000 samples it is not obvious, but you can still see it if you look closely:
InvisibleAfter1000.png

Perhaps this is why they are about to introduce a denoising filter? Also note the artifacts where the invisible mesh intersects slightly with the underlying body mesh, very ugly.

Furthermore, while the mixing with white on alpha works for full transparency, it doesn't handle partial transparency:
SemiTransparentIssue.png


So, the Principled shader support is only good for experimentation at this point. If you want to get the other benefits, comment out the Principled shader support test.

Another small observation, the Transparent node has a BSDF output, so it can't be used before the Principled shader which takes a color input. That means that if you use it you will be back to having transparency remove glossiness.

Another minor observation, the glossy node took a color as input, the specularity input on the Principled shader is a value. They also seem to have different ranges. Even values like 25 for rgb produced a strong reflection with the glossy node, I went back to using a value of 255 for the input to the Specular input. Hmm, after several hundred samples the reflection using the smaller value does fill in, it just takes crazy long.

Re: Improving the mhx2 importer

PostPosted: Tue Jul 25, 2017 2:21 am
by jujube
blindsaypatten wrote:The most recent change is that it now detects whether the Principled shader is available and builds a node network that uses it when it is.


Niiiiiiiiiiice. 8-)

...That's all I have to say. Keep up the good work!