// ------------------------------------------------------------------------------------------ // // Create a USD scene for testing // ------------------------------------------------------------------------------------------ // static private Scene CreateSceneWithShading() { var scene = Scene.Create(); var cubePath = kCubePath; var materialPath = "/Model/Materials/SimpleMat"; var shaderPath = "/Model/Materials/SimpleMat/StandardShader"; var albedoMapPath = "/Model/Materials/SimpleMat/AlbedoMap"; var normalMapPath = "/Model/Materials/SimpleMat/NormalMap"; var emissionMapPath = "/Model/Materials/SimpleMat/EmissionMap"; var metallicMapPath = "/Model/Materials/SimpleMat/MetallicMap"; var occlusionMapPath = "/Model/Materials/SimpleMat/OcclusionMap"; var parallaxMapPath = "/Model/Materials/SimpleMat/ParallaxMap"; var detailNormalMapPath = "/Model/Materials/SimpleMat/DetailNormalMap"; var detailMaskPath = "/Model/Materials/SimpleMat/DetailMask"; var textureFilePath = @"Assets/Samples/USD/1.0.1-preview.1/ImportMaterials/Textures/"; var cube = new CubeSample(); cube.size = 7; cube.transform = Matrix4x4.identity; // // Setup Material. // var material = new MaterialSample(); material.surface.SetConnectedPath(shaderPath, "outputs:out"); // Various shader keywords are required to enable the standard shader to work as intended, // while these can be encoded as part of the schema, often they require some logic (e.g. is // emission color != black?). material.requiredKeywords = new string[] { "_EMISSION" }; // // Setup Shader. // var shader = new StandardShaderSample(); shader.id = new pxr.TfToken("Unity.Standard"); // Note that USD requires all connections target attributes, hence "outputs:out" appended to // the paths below. Think of this as a shader network graph, where a texture object could // be executed per pixel with a sampler, "outputs:out" would be the sampled color. shader.smoothnessScale.defaultValue = 0.99f; shader.enableSpecularHighlights.defaultValue = true; shader.enableGlossyReflections.defaultValue = true; shader.albedo.defaultValue = Color.white; shader.albedoMap.SetConnectedPath(albedoMapPath, "outputs:out"); shader.normalMapScale.defaultValue = 0.76f; shader.normalMap.SetConnectedPath(normalMapPath, "outputs:out"); shader.emission.defaultValue = Color.white * 0.3f; shader.emissionMap.SetConnectedPath(emissionMapPath, "outputs:out"); shader.metallicMap.SetConnectedPath(metallicMapPath, "outputs:out"); shader.occlusionMapScale.defaultValue = 0.65f; shader.occlusionMap.SetConnectedPath(occlusionMapPath, "outputs:out"); shader.parallaxMapScale.defaultValue = 0.1f; shader.parallaxMap.SetConnectedPath(parallaxMapPath, "outputs:out"); shader.detailMask.SetConnectedPath(detailMaskPath); shader.detailNormalMapScale.defaultValue = .05f; shader.detailNormalMap.SetConnectedPath(detailNormalMapPath, "outputs:out"); // // Setup Textures. // var albedoTexture = new Texture2DSample(); albedoTexture.sourceFile.defaultValue = textureFilePath + "albedoMap.png"; albedoTexture.sRgb = true; var normalTexture = new Texture2DSample(); normalTexture.sourceFile.defaultValue = textureFilePath + "normalMap.png"; normalTexture.sRgb = true; var emissionTexture = new Texture2DSample(); emissionTexture.sourceFile.defaultValue = textureFilePath + "emissionMap.png"; emissionTexture.sRgb = true; var metallicTexture = new Texture2DSample(); metallicTexture.sourceFile.defaultValue = textureFilePath + "metallicMap.png"; metallicTexture.sRgb = true; var occlusionTexture = new Texture2DSample(); occlusionTexture.sourceFile.defaultValue = textureFilePath + "occlusionMap.png"; occlusionTexture.sRgb = true; var parallaxTexture = new Texture2DSample(); parallaxTexture.sourceFile.defaultValue = textureFilePath + "parallaxMap.png"; parallaxTexture.sRgb = true; var detailNormalTexture = new Texture2DSample(); detailNormalTexture.sourceFile.defaultValue = textureFilePath + "detailMap.png"; detailNormalTexture.sRgb = true; var detailMaskTexture = new Texture2DSample(); detailMaskTexture.sourceFile.defaultValue = textureFilePath + "metallicMap.png"; detailMaskTexture.sRgb = true; // // Write Everything. // scene.Write(cubePath, cube); scene.Write(materialPath, material); scene.Write(shaderPath, shader); scene.Write(albedoMapPath, albedoTexture); scene.Write(normalMapPath, normalTexture); scene.Write(emissionMapPath, emissionTexture); scene.Write(metallicMapPath, metallicTexture); scene.Write(occlusionMapPath, occlusionTexture); scene.Write(parallaxMapPath, parallaxTexture); scene.Write(detailNormalMapPath, detailNormalTexture); scene.Write(detailMaskPath, detailMaskTexture); // // Bind Material. // MaterialSample.Bind(scene, cubePath, materialPath); // // Write out the scene as a string, for debugging. // string s; scene.Stage.ExportToString(out s); Debug.Log("Loading:\n" + s); return(scene); }
// // Start generates a USD scene procedurally, containing a single cube with a material, shader // and texture bound. It then inspects the cube to discover the material. A Unity material is // constructed and the parameters are copied in a generic way. Similarly, the texture is // discovered and loaded as a Unity Texture2D and bound to the material. // // Also See: https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html // void Start() { // Create a scene for this test, but could also be read from disk. Scene usdScene = CreateSceneWithShading(); // Read the material and shader ID. var usdMaterial = new MaterialSample(); string shaderId; // ReadMaterial was designed for Unity and assumes there is one "surface" shader bound. if (!MaterialSample.ReadMaterial(usdScene, kCubePath, usdMaterial, out shaderId)) { throw new System.Exception("Failed to read material"); } // Map the shader ID to the corresponding Unity/USD shader pair. ShaderPair shader; if (shaderId == null || !m_shaderMap.TryGetValue(shaderId, out shader)) { throw new System.Exception("Material had no surface bound"); } // // Read and process the shader-specific parameters. // // UsdShade requires all connections target an attribute, but we actually want to deserialize // the entire prim, so we get just the prim path here. var shaderPath = new pxr.SdfPath(usdMaterial.surface.connectedPath).GetPrimPath(); usdScene.Read(shaderPath, shader.usdShader); // // Construct material & process the inputs, textures, and keywords. // var mat = new UnityEngine.Material(shader.unityShader); // Apply material keywords. foreach (string keyword in usdMaterial.requiredKeywords ?? new string[0]) { mat.EnableKeyword(keyword); } // Iterate over all input parameters and copy values and/or construct textures. foreach (var param in shader.usdShader.GetInputParameters()) { if (!SetMaterialParameter(mat, param.unityName, param.value)) { throw new System.Exception("Incompatible shader data type: " + param.ToString()); } } foreach (var param in shader.usdShader.GetInputTextures()) { if (string.IsNullOrEmpty(param.connectedPath)) { // Not connected to a texture. continue; } // Only 2D textures are supported in this example. var usdTexture = new Texture2DSample(); // Again, we want the prim path, not the attribute path. var texturePath = new pxr.SdfPath(param.connectedPath).GetPrimPath(); usdScene.Read(texturePath, usdTexture); // This example also only supports explicit sourceFiles, they cannot be connected. if (string.IsNullOrEmpty(usdTexture.sourceFile.defaultValue)) { continue; } // For details, see: https://docs.unity3d.com/Manual/MaterialsAccessingViaScript.html foreach (string keyword in param.requiredShaderKeywords) { mat.EnableKeyword(keyword); } var data = System.IO.File.ReadAllBytes(usdTexture.sourceFile.defaultValue); var unityTex = new Texture2D(2, 2); unityTex.LoadImage(data); mat.SetTexture(param.unityName, unityTex); Debug.Log("Set " + param.unityName + " to " + usdTexture.sourceFile.defaultValue); unityTex.Apply(updateMipmaps: true, makeNoLongerReadable: false); } // // Create and bind the geometry. // // Create a cube and set the material. // Note that geometry is handled minimally here and is incomplete. var cubeSample = new CubeSample(); usdScene.Read(kCubePath, cubeSample); var go = GameObject.CreatePrimitive(PrimitiveType.Cube); go.transform.SetParent(transform, worldPositionStays: false); go.transform.localScale = Vector3.one * (float)cubeSample.size; m_cube = transform; go.GetComponent <MeshRenderer>().material = mat; }
public static void MaterialBindTest() { // Game plan: // 1. Create a cube // 2. Create a material // 3. Create a shader // 4. Create a texture // 5. Connect the material to the shader's output // 6. Connect the shader's albedo parameter to the texture's output // 7. Connect the texture to the source file on disk // 8. Write all values // 9. Bind the cube to the material var scene = Scene.Create(); var cubePath = "/Model/Geom/Cube"; var materialPath = "/Model/Materials/SimpleMat"; var shaderPath = "/Model/Materials/SimpleMat/StandardShader"; var texturePath = "/Model/Materials/SimpleMat/AlbedoTexture"; var cube = new CubeSample(); cube.size = 1; var material = new MaterialSample(); material.surface.SetConnectedPath(shaderPath, "outputs:out"); var shader = new StandardShaderSample(); shader.albedo.defaultValue = Color.white; shader.albedo.SetConnectedPath(texturePath, "outputs:out"); AssertEqual(shader.albedo.connectedPath, texturePath + ".outputs:out"); var texture = new Texture2DSample(); texture.sourceFile.defaultValue = @"C:\A\Bogus\Texture\Path.png"; texture.sRgb = true; scene.Write("/Model", new XformSample()); scene.Write("/Model/Geom", new XformSample()); scene.Write("/Model/Materials", new XformSample()); scene.Write(cubePath, cube); scene.Write(materialPath, material); scene.Write(shaderPath, shader); scene.Write(texturePath, texture); MaterialSample.Bind(scene, cubePath, materialPath); var material2 = new MaterialSample(); var shader2 = new StandardShaderSample(); var texture2 = new Texture2DSample(); scene.Read(materialPath, material2); scene.Read(shaderPath, shader2); scene.Read(texturePath, texture2); var param = shader2.GetInputParameters().First(); AssertEqual(shader.albedo.connectedPath, param.connectedPath); AssertEqual("albedo", param.usdName); AssertEqual(shader.albedo.defaultValue, param.value); AssertEqual("_Color", param.unityName); var paramTex = shader2.GetInputTextures().First(); AssertEqual(shader.albedoMap.connectedPath, paramTex.connectedPath); AssertEqual("albedoMap", paramTex.usdName); AssertEqual(shader.albedoMap.defaultValue, paramTex.value); AssertEqual("_MainTex", paramTex.unityName); AssertEqual(material.surface.defaultValue, material2.surface.defaultValue); AssertEqual(material.surface.connectedPath, material2.surface.connectedPath); AssertEqual(shader.albedo.defaultValue, shader2.albedo.defaultValue); AssertEqual(shader.albedo.connectedPath, shader2.albedo.connectedPath); AssertEqual(shader.id, shader2.id); AssertEqual(texture.sourceFile.defaultValue, texture2.sourceFile.defaultValue); AssertEqual(texture.sRgb, texture2.sRgb); PrintScene(scene); }