Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix export of metallic and normal texture map. Add support for any shader that can support metallic roughness workflow. #133

Merged
merged 9 commits into from
Apr 4, 2018

Conversation

justinctlam
Copy link
Contributor

Fix export of metallic and normal texture map. Add support for any shader that can support metallic roughness workflow.

…ader that can support metallic roughness workflow.
@justinctlam
Copy link
Contributor Author

Some notes about my changes.

I decided to check the properties to determine which material to export instead of using the name is because it will let developers create their own types of shaders that can be used with this system. For example, we are using a shader in the Unity store called Standard Plus which allows for light maps.

Copy link
Contributor

@ziedbha ziedbha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let me know if I understood this correctly: for each texture, you are going in and making sure it is readable and not compressed by essentially accessing the Unity Editor, right? If that's the case, I think it will be really slow to make sure that each texture readable in order to get its pixels. The original idea was to pass it through the GPU and to get the pixels from there. I think I have a fix that can be combined with yours to make texture exporting as fast as it currently is. I'll make a PR.

@justinctlam
Copy link
Contributor Author

@ziedbha Yes, I took the idea from the SketchFab Unity to GLTF exporter. I thought it would be slow and would think using the GPU to do it would be faster. I haven't had time to figure out how to do it as I'm still learning the Unity APIs. I would love to learn how to do it. Would you be able to update my PR with that enhancement?

@ziedbha
Copy link
Contributor

ziedbha commented Mar 5, 2018

I can definitely do that. I'll sync with your PR and add some changes and it will hopefully boost the speed. I saw you checked PR #134 . To explain, it only fixes color space issues since a lot of textures were not converted in the correct color space using the Blit method. Your suggested changes handle that, but might be really slow. So combining the ability to specify shader properties + speed of getting pixels is the best way to approach this. Therefore I think we should combine both of our changes!

@justinctlam
Copy link
Contributor Author

@ziedbha I agree. Thank you! I can't wait to learn how to do it better.

@justinctlam
Copy link
Contributor Author

Hi, I haven't heard of any feedback other than from @ziedbha. Should I close this PR? Maybe it's not needed?

@blgrossMS
Copy link

blgrossMS commented Mar 10, 2018 via email

Copy link

@blgrossMS blgrossMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution. I'm a bit confused by some of the logic and left some comments. Once they are addressed everything else looks good!

exportTexture.Apply();
File.WriteAllBytes(Path.Combine(path, image.name + ".png"), exportTexture.EncodeToPNG());
}
exportImages(path);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExportImages is more the C# style and what we have been using in this code base

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

break;
case IMAGETYPE.RGB:
case IMAGETYPE.RGBA:
// inputColor = inputColor;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is removed.

switch (inputChannel)
{
case IMAGETYPE.R:
inputColor = new Color(inputColor.r, inputColor.r, inputColor.r, inputColor.r);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you creating a new Vec4 out of a single float field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has been removed.

{
outputColors[i] = new Color(1.0f, 1.0f, 1.0f);
}
addTexturePixels(ref texture, ref outputColors, IMAGETYPE.R, IMAGETYPE.B);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the point of this to swap the R and B channel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed, the channel swap is done in the shader.

File.WriteAllBytes(Path.Combine(outputPath, texture.name + ".png"), newtex.EncodeToPNG());
}

private void addTexturePixels(ref Texture2D texture, ref Color[] outputColors, IMAGETYPE inputChannel, IMAGETYPE outputChannel)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these functions could all use comments, pretty confused

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are no longer necessary.

private NormalTextureInfo ExportNormalTextureInfo(UnityEngine.Texture texture, UnityEngine.Material material)
private NormalTextureInfo ExportNormalTextureInfo(
UnityEngine.Texture texture,
TextureMapType textureMapType,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use tabs instead of space

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now.

G_INVERT
}

enum TextureMapType

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicitly declare access modifier

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

{
var textureWidth = texture.width;
var textureHeight = texture.height;
Color[] outputColors = new Color[textureWidth * textureHeight];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just make outputColors an out parameter to the addtexturepixels function and have it create the array. The preinitialization with default values shouldn't be needed if you are filling it all in the calling function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are removed, now I am using Graphics.blit.

outputColors[i] = new Color(1.0f, 1.0f, 1.0f);
}
addTexturePixels(ref texture, ref outputColors, IMAGETYPE.R, IMAGETYPE.B);
addTexturePixels(ref texture, ref outputColors, IMAGETYPE.A, IMAGETYPE.G_INVERT);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you swap all the values you want to in a single pass instead of two passes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is removed, now the swap is done in the shader.

- Use Graphics.blit to speed up texture read/write
- Update texture image paths to use full paths to avoid name collision
- Don't do anything if user cancels the dialog
@justinctlam
Copy link
Contributor Author

I've fixed all the feedback. @ziedbha I've updated to use Graphics.blit. Thanks!

@justinctlam
Copy link
Contributor Author

Hi! Just wanted to see if there are any more issues I can address for this. At the moment exporting GLTF with pbrmaterials aren't working correctly.

Copy link

@blgrossMS blgrossMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple more comments. I'd also like @ziedbha to approve of this PR before checkin since they are driving export currently.

@@ -30,6 +33,8 @@ static void ExportScene()

var exporter = new GLTFSceneExporter(transforms);
var path = EditorUtility.OpenFolderPanel("glTF Export Path", "", "");
exporter.SaveGLTFandBin(path, scene.name);
if (path != "") {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string.IsNullOrEmpty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

}
}

private SceneId ExportScene(string name, Transform[] rootObjTransforms)
private void ExportMetallicGlossTexture (Texture2D texture, string outputPath)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments

{
var scene = new Scene();
var destRenderTexture = new RenderTexture (texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
var shader = Resources.Load ("MetalGlossChannelSwap", typeof(UnityEngine.Shader)) as UnityEngine.Shader;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache the shaders at the start of the exporter instead of doing it here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caching the material with shaders


var exportTexture = new Texture2D (texture.width, texture.height);
exportTexture.ReadPixels (new Rect (0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0);
exportTexture.Apply ();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will do a CPU and GPU copy even though we just care about the CPU version here to write out. Is there a way to do this without creating an additional texture (like copying the bytes of the destRenderTexture into a file?)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking around, it seems like this is the recommended pattern, add a comment explaining why we create another gpu copy of the data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this is the recommended pattern. Is there a better way to do this? I was following the same pattern as before.

exportTexture.Apply ();

var finalFilenamePath = ConstructImageFilenamePath (texture, outputPath);
File.WriteAllBytes (finalFilenamePath, exportTexture.EncodeToPNG ());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more a general note for how export to be done than anything that you should do on your review, but we should match the ILoader interface in the importer so that we are using streams in the exporter through a loader like interface and not assuming File I/O or being in control of it here even

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I was just following the existing code already in place.

{
var scene = new Scene();
var destRenderTexture = new RenderTexture (texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make your rendertextures with gettemporary so that it will potentially pool them: https://docs.unity3d.com/ScriptReference/RenderTexture.GetTemporary.html

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call release at the end of the function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool tip! thanks!

File.WriteAllBytes (finalFilenamePath, exportTexture.EncodeToPNG ());
}

private void ExportNormalTexture (Texture2D texture, string outputPath)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments

@justinctlam
Copy link
Contributor Author

@blgrossMS I've updated the PR with the feedback you provided. Thanks again for looking at it!
Hi @ziedbha, would appreciated if you can take a quick look at this again.

@justinctlam
Copy link
Contributor Author

Hi, just wanted to see if there are any more issues I should address? Thanks!

@blgrossMS
Copy link

blgrossMS commented Mar 26, 2018 via email

@blgrossMS
Copy link

@justinctlam was testing before merging in, no texture were generated when I did a scene or selection export on the object. I also noticed that an error is thrown if RenderTexture.active is not set to null before release.

@justinctlam
Copy link
Contributor Author

@blgrossMS Thanks for looking, I will investigate those issues.

@justinctlam
Copy link
Contributor Author

@blgrossMS Do you happen to have a test scene I can look at? The scene I'm using exports textures fine for scene and selection object and I don't see any error thrown.

@blgrossMS
Copy link

blgrossMS commented Apr 2, 2018 via email

@justinctlam
Copy link
Contributor Author

@blgrossMS I tried to export a scene and selected object on Master build and it also didn't export the textures. Is this something my PR should also fix?

@blgrossMS
Copy link

It does not have to. Can you include the scene that you are testing with in the PR (or is it one of the included ones already) just so that I can verify the functionality?

@justinctlam
Copy link
Contributor Author

@blgrossMS I am using these free pbr assets from the Unity asset store:
https://assetstore.unity.com/packages/3d/props/industrial/pbr-barrels-83508

If you download them, there is a test scene in it. You can also try out many other free pbr assets on the Unity asset store. They should all work.

@RPSpicer
Copy link
Contributor

RPSpicer commented Apr 3, 2018

This changeset adds dependencies on UnityEditor to GLTFSceneExporter -- this breaks the ability to use GLTFSceneExporter at runtime, since that assembly is not available. The UnityEditor dependency is used to resolve the relative path of the texture with AssetDatabase.GetAssetPath, and assumes that the texture file being exported is present in the Assets folder of the project in the Editor.

In my use case, I'm converting OBJs to GLTFs at runtime (and also exporting some additional metadata while doing the conversion -- think json for cesium 3dtiles, but not quite), so 1) I'd like to use this at runtime, and 2) I'd like to find a way to export textures that are not stored inside `/Assets/'. My naive approach was to substitute:

//var path = UnityEditor.AssetDatabase.GetAssetPath (texture);
var path = texture.name;

This is probably not ideal (doesn't guarantee uniqueness of the texture filename, for instance) but seems like a better option than losing runtime support.

Also (minor nitpick) it might be worth moving the shaders from the Resources folder into /UnityGLTF/Resources which makes it easier to copy just the bits that the library requires into a new Unity project.

@justinctlam
Copy link
Contributor Author

@RPSpicer I can look into removing the dependency on UnityEditor. If we are okay with naming collision or have an alternative way to resolve it, I'm happy to hear any feedback.

@RPSpicer
Copy link
Contributor

RPSpicer commented Apr 4, 2018

@justinctlam Ideally we'd want to know the relative path from the input scene/mesh file to each input texture file, but that can't be guaranteed in all cases, for instance my runtime case (where these were loaded into Unity datastructures using another piece of code and now exist in memory). Since there's so many use cases we may end up needing configuration options to meet all needs.

As a default, I am biased in favor of "name the exported texture using the Unity Texture2d's name (which should be the filename if imported in the editor), and put it next to the exported mesh in the filesystem" because it handles my case. EG: foo.obj referencing texture bar.jpg ends up with foo.gltf, foo.bin, and bar.png in one folder.

I'd advocate for refactoring Editor specific calls over into the Scripts/Editor/GLTFExportMenu.cs script so that the functionality you implemented to find relative paths can be preserved, as well as runtime use. That makes for a more complex call into GLTFSceneExporter, since you'd have to pass information about the relative paths to textures in, but isolates the Editor-specific code. What if we allowed GLTFSceneExporter.SaveGltfAndBin() to optionally accept a Dictionary of Texture2Ds to relative paths (strings)? If empty, do the simple/naive thing, if populated, use that to define the relative path for each exported texture.

@justinctlam
Copy link
Contributor Author

@RPSpicer I added a delegate to decouple the UnityEditor from the runtime exporter. Please see the latest updates. Let me know if this would work for you.

@RPSpicer
Copy link
Contributor

RPSpicer commented Apr 4, 2018

@justinctlam Thank you! This approach works for me, and enables end users to specify their own arbitrary path logic, which seems like a best case for this library.

@blgrossMS blgrossMS merged commit 9d5a2df into KhronosGroup:master Apr 4, 2018
@PabloMonfort
Copy link

PabloMonfort commented Sep 29, 2022

sorry to bump over this but, nice tool im try something i need with but there are issues with texture not exporting right. i have to export an avatar model to glb thats have a shared material where the textures inputs changes at runtime from some short of list and this dosnt work , even the shoes that has lit urp default not detect the correct textures that has the prefab in runtime, its like if its always pointing to the fbx model or original root of the mesh model and not current materials of the prefab i want to export? sounds confusing can anyone help please ? thanks

github-actions bot pushed a commit to Rhinox-Training/UnityGLTF that referenced this pull request Nov 9, 2022
…ader that can support metallic roughness workflow. (KhronosGroup#133)

* Fix export of metallic and normal texture map. Add support for any shader that can support metallic roughness workflow.

* Addressed feedback
- Use Graphics.blit to speed up texture read/write
- Update texture image paths to use full paths to avoid name collision
- Don't do anything if user cancels the dialog

* Revert irrelevant files

* Fix indentation

* Update accessor for structs and enum

* Update formatting

* Address feedback

* Remove exporter dependency on UnityEditor.
hybridherbst pushed a commit that referenced this pull request Mar 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants