Search Unity

Question How to draw sprite mask to render texture

Discussion in 'General Graphics' started by Elringus, Jan 16, 2021.

  1. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    I've used to draw sprites with CommandBuffer to a render texture and everything worked fine, but now I need to support SpriteMask and can't figure how to do that correctly.

    Below is an approximation on how I'm drawing the sprites (I've omitted the details on creation of ortho matrix and other stuff for clarity; it's working fine, so I guess the details are irrelevant to the case):

    Code (CSharp):
    1. commandBuffer.Clear();
    2. commandBuffer.SetRenderTarget(renderTexture);
    3. commandBuffer.ClearRenderTarget(true, true, Color.clear);
    4. commandBuffer.SetProjectionMatrix(orthoMatrix);
    5. spriteRenderer.GetPropertyBlock(propertyBlock);
    6. commandBuffer.DrawMesh(spriteMesh, meshTRSMatrix, spriteMaterial, 0, -1, propertyBlock);
    7. Graphics.ExecuteCommandBuffer(commandBuffer);

    Now, to support the masks, I'm retrieving an additional render texture:

    depthTexture = RenderTexture.GetTemporary(width, height, 24, RenderTextureFormat.Depth)


    Setting it as depth render target:

    commandBuffer.SetRenderTarget(renderTexture, depthTexture)


    And drawing the mask before sprites:

    commandBuffer.DrawMesh(maskMesh, meshTRSMatrix, maskMaterial, 0, -1, propertyBlock)


    But the sprites just ignore the mask. I'm sure the mask and renderers are configured correctly, as the mask is rendering properly when the prefab is placed on scene.

    I've been experimenting for several days now, googling and trying to find something related in the Unity source code on GitHub, but it seems the rendering is on C++ side and not exposed to the open source repo.

    Would be really grateful for any hints or advice.
     
    Last edited: Jan 16, 2021
  2. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    I've created a simple repro project for the issue and reported a bug (Case 1319547).

    Below is the repro script for rendering masked sprite to render texture:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Rendering;
    4.  
    5. public class RenderToTexture : MonoBehaviour
    6. {
    7.     public RenderTexture TargetTexture;
    8.     public SpriteRenderer Sprite;
    9.     public SpriteMask Mask;
    10.  
    11.     private const int pixelsPerUnit = 100;
    12.  
    13.     private MaterialPropertyBlock propertyBlock;
    14.     private CommandBuffer commandBuffer;
    15.     private Mesh spriteMesh, maskMesh;
    16.  
    17.     private void Awake ()
    18.     {
    19.         propertyBlock = new MaterialPropertyBlock();
    20.         commandBuffer = new CommandBuffer();
    21.         spriteMesh = BuildSpriteMesh(Sprite.sprite);
    22.         maskMesh = BuildSpriteMesh(Mask.sprite);
    23.     }
    24.  
    25.     private static Mesh BuildSpriteMesh (Sprite sprite)
    26.     {
    27.         var mesh = new Mesh();
    28.         mesh.hideFlags = HideFlags.HideAndDontSave;
    29.         mesh.name = $"{sprite.name} Sprite Mesh";
    30.         mesh.vertices = Array.ConvertAll(sprite.vertices, i => new Vector3(i.x, i.y));
    31.         mesh.uv = sprite.uv;
    32.         mesh.triangles = Array.ConvertAll(sprite.triangles, i => (int)i);
    33.         return mesh;
    34.     }
    35.  
    36.     private void Update ()
    37.     {
    38.         PrepareCommandBuffer();
    39.         DrawMesh(maskMesh, Mask);
    40.         DrawMesh(spriteMesh, Sprite);
    41.         Graphics.ExecuteCommandBuffer(commandBuffer);
    42.     }
    43.  
    44.     private void PrepareCommandBuffer ()
    45.     {
    46.         commandBuffer.Clear();
    47.         commandBuffer.SetRenderTarget(TargetTexture);
    48.         commandBuffer.ClearRenderTarget(true, true, Color.clear);
    49.         commandBuffer.SetProjectionMatrix(BuildProjectionMatrix());
    50.     }
    51.  
    52.     private Matrix4x4 BuildProjectionMatrix ()
    53.     {
    54.         var drawDimensions = new Vector3(TargetTexture.width, TargetTexture.height);
    55.         var drawPosition = transform.position * pixelsPerUnit;
    56.         var orthoMin = -drawDimensions / 2f + drawPosition;
    57.         var orthoMax = drawDimensions / 2f + drawPosition;
    58.         return Matrix4x4.Ortho(orthoMin.x, orthoMax.x, orthoMin.y, orthoMax.y, float.MinValue, float.MaxValue);
    59.     }
    60.  
    61.     private void DrawMesh (Mesh mesh, Renderer renderer)
    62.     {
    63.         var drawPosition = renderer.transform.position * pixelsPerUnit;
    64.         var drawRotation = renderer.transform.rotation;
    65.         var drawScale = renderer.transform.lossyScale * pixelsPerUnit;
    66.         var drawTransform = Matrix4x4.TRS(drawPosition, drawRotation, drawScale);
    67.         renderer.GetPropertyBlock(propertyBlock);
    68.         commandBuffer.DrawMesh(mesh, drawTransform, renderer.material, 0, -1, propertyBlock);
    69.     }
    70. }
    71.  
    Which yields the following result:



    Still not sure whether I'm doing something wrong or it's a bug.
     
  3. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483