Create Image from rendertexture at runtime with prefab

Hello,

I have a problem I can’t figure it out how to solve it.

I have a crafting system where the player can chose the mesh he want.
I have a classe playerdata with a list of unlock string wich are the unlock mesh name.

I want to create at runtime Icon of the mesh by instanciate the mesh and “take a picture”.
I have a panel wich instanciate Item for every unlock mesh.
Thoses Item prefab call a icon manager to get a sprite :

public void GetIconSimpleForm(string formName, Material Mat,bool isWeapon, string type , Action<Sprite> callBackSprite)
    {
        callbackSprite = callBackSprite;
        GameObject IconMaker = Instantiate(IconMakerPrefab, this.transform,false) as GameObject;

        IconMaker.transform.localPosition = CurrentPOS;
        CurrentPOS.y += 100;
       
        iconScript = IconMaker.GetComponent<IconMakerScript>();
        iconScript.InstanciateMesh(formName, isWeapon, type, callBackSprite);
        iconScript.SetMaterial(Mat);
    }

those item call the manager with a callback methode, this methode is call after the camera render.

for each “icon demande” the manager instanciate another prefab ( “IconMakerPrefab”) , wich is a mesh + a camera + a light source.

this prefab instanciate the mesh, move the camera corresponding to the mesh type, an render the camera view on a new texture

public void InstanciateMesh(string meshName, bool isWeapon, string type,  Action<Sprite> callbacksprite)
    {
        meshname = meshName;
        CallbackIcon = callbacksprite;
        CalbbackLoad = true;
        string path = "";
        if (isWeapon)
        {
            path = "PrefabsIcons/Weapons/" + meshName + "_Static";
        }
        else
        {
            path = "PrefabsIcons/Gears/" + meshName + "_Static";
        }
        ObjectDisplay = Instantiate(Resources.Load(path, typeof(GameObject)), this.transform, false) as GameObject;

        ObjectDisplay.transform.localEulerAngles = ROT;
        switch (type.ToLower())
        {
            case "head":
                ObjectDisplay.transform.localPosition = HeadPOS;
                camera.orthographicSize = HeadSIZE/2;
                break;
            case "torso":
                ObjectDisplay.transform.localPosition = TorsoPOS;
                camera.orthographicSize = TorsoSIZE / 2;
                break;
            case "upperright":
                ObjectDisplay.transform.localPosition = UpperRightPOS;
                camera.orthographicSize = UpperRightSIZE / 2;
                break;
            case "upperleft":
                ObjectDisplay.transform.localPosition = UpperLeftPOS;
                camera.orthographicSize = UpperLeftSIZE / 2;
                break;
            case "lowerright":
                ObjectDisplay.transform.localPosition = LowerRightPOS;
                camera.orthographicSize = LowerRightSIZE / 2;
                break;
            case "lowerleft":
                ObjectDisplay.transform.localPosition = LowerLeftPOS;
                camera.orthographicSize = LowerLeftSIZE / 2;
                break;
            case "handright":
                ObjectDisplay.transform.localPosition = HandRightPOS;
                camera.orthographicSize = HandRightSIZE / 2;
                break;
            case "handleft":
                ObjectDisplay.transform.localPosition = HandLeftPOS;
                camera.orthographicSize = HandLeftSIZE / 2;
                break;
            case "legsright":
                ObjectDisplay.transform.localPosition = LegsRightPOS;
                camera.orthographicSize = LegsRightSIZE / 2;
                break;
            case "legsleft":
                ObjectDisplay.transform.localPosition = LegsLeftPOS;
                camera.orthographicSize = LegsLeftSIZE / 2;
                break;
            case "hips":
                ObjectDisplay.transform.localPosition = HipsPOS;
                camera.orthographicSize = HipsSIZE / 2;
                break;
            case "shoulderleft":
                ObjectDisplay.transform.localPosition = ShoulderLeftPOS;
                camera.orthographicSize = ShoulderLeftSIZE / 2;
                break;
            case "shoulderright":
                ObjectDisplay.transform.localPosition = ShoulderRightPOS;
                camera.orthographicSize = ShoulderRightSIZE / 2;
                break;
        }
        associatedTexture = new RenderTexture(OriginaleTexture);
        camera.targetTexture = associatedTexture;
        associatedTexture.name = meshname;


    }

this script also wait the camera to render to execute the call back methode :

void OnEnable()
    {
        RenderPipelineManager.endCameraRendering += RenderPipelineManager_endCameraRendering;
    }
    void OnDisable()
    {
        RenderPipelineManager.endCameraRendering -= RenderPipelineManager_endCameraRendering;
    }
    private void RenderPipelineManager_endCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        if (CalbbackLoad && !CallBackDone)
        {
            CallBackMethode();
        }
    }

the call back methode create a sprite from the camera targetTexture :

public void CallBackMethode ()
    {
        //camera.Render();
        Texture2D texture = new Texture2D(associatedTexture.width, associatedTexture.height);
        Rect rect = new Rect(0, 0, associatedTexture.width, associatedTexture.height);
        var oldRt = RenderTexture.active;
        RenderTexture.active = associatedTexture;


        texture.ReadPixels(rect, 0, 0);
        texture.Apply();

        RenderTexture.active = oldRt;
        Sprite sprite = Sprite.Create(texture, rect, Vector2.zero);
        sprite.name = meshname;
        CallBackDone = true;
        CallbackIcon(sprite);

    }

And call back the item with his new sripte.

at the next frame the 'iconmaker" prefab is destroy:

 private void Update()
    {
        if (CallBackDone)
        {
            Destroy(this.gameObject);
        }
    }

But at the end the sprite is a blank one.
The textures are good but the sprite are not.

When I uncomment Camera.render(); it’s crash but before that my sprites are good.
I must have miss something with the URP rendering.

Any Iea?
Thanks for your help.

Hi I tried something else,

I tried to return a RenderTexture and not a sprite and convert the Image of the item to a RawImage.
It work but now I can’t destroy me camera or the texture reset.

Is there a way to “fix” the texture?
Then I can destroy the rest?

Thanks for your help.

Hi,

I foud a way.

First correction, as i’m working with a lot of different Camera, I had a controle to the end cameraRendering to check if it’s the good camera I want :

void OnEnable()
    {
        RenderPipelineManager.endCameraRendering += RenderPipelineManager_endCameraRendering;
       
    }
    void OnDisable()
    {
        RenderPipelineManager.endCameraRendering -= RenderPipelineManager_endCameraRendering;
    }
    private void RenderPipelineManager_endCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        if (CalbbackLoad && !CallBackDone && camera.Equals(myCamera))
        {
            CallBackMethode(context, camera);
        }
    }

Then, as I am using urp, I can"'t use the camera.Render().
Instead I have to use UniversalRenderPipeline.RenderSingleCamera(context, camera);

My call back methode become :

public void CallBackMethode (ScriptableRenderContext context, Camera camera)
    {

        RenderTexture oldRt = RenderTexture.active;
        RenderTexture.active = associatedTexture;

        UniversalRenderPipeline.RenderSingleCamera(context, camera);
        // Create a new Texture2D       
        Texture2D texture2D = new Texture2D(associatedTexture.width, associatedTexture.height, TextureFormat.ARGB32, false);
        // copies the pixels into the Texture2D         
        texture2D.ReadPixels(new Rect(0, 0, associatedTexture.width, associatedTexture.height), 0, 0, false);

        texture2D.Apply();

        NewSprite = Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));



        CallBackDone = true;
        CallbackIcon(NewSprite);
    }

Like than I can call back with a Sprite which became the new Icon Sprite.
Then I can destroy what I don’t need anymore.