Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

GPU Paint on DX11

Discussion in 'General Graphics' started by Transdermal_Celebration, Jul 3, 2015.

  1. Transdermal_Celebration

    Transdermal_Celebration

    Joined:
    Nov 30, 2014
    Posts:
    2
    I am developing a GPU-based paint program that draws quads in OpenGL with a paint shader into a rendertexture canvas. The paint shader makes use of the rendertexture for blending.
    This strategy works fine in DirectX9 as seen in this screenshot:
    PaintTestDX9.jpg
    However, when I switch over to DirectX11, it looks like the paint shader treats the rendertexture as a black texture, as seen in the next screenshot.
    PaintTestDX11.jpg

    I have stripped down the code into a single script:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PaintTool : MonoBehaviour {
    5.  
    6.     public static Vector4 screenDimensions;
    7.  
    8.     private RenderTexture colorTexture;
    9.  
    10.     public Color lineColor = Color.green;
    11.     public float brushRadius = 100;
    12.  
    13.     private Color paintColor;
    14.     public Material paintMaterial;
    15.  
    16.     private Vector2 pos;
    17.  
    18.     public Material clearColorMaterial;
    19.  
    20.     protected void Awake()
    21.     {
    22.         float swf = Screen.width;
    23.         float shf = Screen.height;
    24.         screenDimensions = new Vector4(swf, shf, 1.0f / swf, 1.0f / shf);
    25.         Shader.SetGlobalVector("_ScreenDimensions", screenDimensions);
    26.  
    27.         colorTexture = new RenderTexture((int)screenDimensions.x, (int)screenDimensions.y, 0, RenderTextureFormat.ARGB32);
    28.  
    29.         GetComponent<Renderer>().material.SetTexture("_MainTex", colorTexture);
    30.  
    31.         paintMaterial.SetTexture("_MainTex", colorTexture);
    32.  
    33.         colorTexture.filterMode = FilterMode.Point;
    34.  
    35.         Graphics.Blit(null, colorTexture, clearColorMaterial);
    36.     }
    37.  
    38.     public void Update()
    39.     {
    40.         if (Input.GetMouseButton (0)) {
    41.  
    42.             pos = Input.mousePosition;
    43.             paintMaterial.SetFloat("_Radius", brushRadius);
    44.             paintMaterial.SetVector("_FromPosition", pos);
    45.             paintMaterial.SetVector("_ToPosition", pos);
    46.  
    47.             RenderOntoTexture(paintMaterial, BoundingRect(), colorTexture);
    48.         }
    49.     }
    50.  
    51.     //Renders a rectangle in screen space using a certain paintMaterial ontop of a full screen background material
    52.     protected virtual void RenderOntoTexture(Material paintMaterial, Rect paintBounds, RenderTexture renderOnto)
    53.     {
    54.         GL.PushMatrix();
    55.         GL.LoadPixelMatrix(0, screenDimensions.x, 0, screenDimensions.y);
    56.    
    57.         RenderTexture.active = renderOnto;
    58.  
    59.         //Render the altered offsets in a bounded rectangle (should produce good performance)
    60.         RenderMaterialRect(paintMaterial, paintBounds);
    61.    
    62.         RenderTexture.active = null;
    63.    
    64.         GL.PopMatrix();
    65.     }
    66.  
    67.     //Subtract one due to screen's apparent "off by one" issue
    68.     public static Rect ScreenRectToUV(Rect screenBounds)
    69.     {
    70.         Vector2 screenInv = new Vector2(screenDimensions.z, screenDimensions.w);
    71.         Vector2 min = Vector2.Scale(screenBounds.min, screenInv);
    72.         Vector2 max = Vector2.Scale(screenBounds.max, screenInv);
    73.         return Rect.MinMaxRect(min.x, min.y, max.x, max.y);
    74.     }
    75.  
    76.     protected virtual void RenderMaterialRect(Material m, Rect boundsRect)
    77.     {
    78.         //Determine UV Coordinates based on Screen Rectangle
    79.         Rect uvRect = ScreenRectToUV(boundsRect);
    80.    
    81.         // activate the first pass (in this case we know it is the only pass)
    82.         m.SetPass (0);
    83.    
    84.         // draw a quad
    85.         GL.Begin (GL.QUADS);
    86.         GL.TexCoord2 (uvRect.xMin, uvRect.yMin); GL.Vertex3 (boundsRect.xMin, boundsRect.yMin, 0.1f);
    87.         GL.TexCoord2 (uvRect.xMax, uvRect.yMin); GL.Vertex3 (boundsRect.xMax, boundsRect.yMin, 0.1f);
    88.         GL.TexCoord2 (uvRect.xMax, uvRect.yMax); GL.Vertex3 (boundsRect.xMax, boundsRect.yMax, 0.1f);
    89.         GL.TexCoord2 (uvRect.xMin, uvRect.yMax); GL.Vertex3 (boundsRect.xMin, boundsRect.yMax, 0.1f);
    90.         GL.End ();
    91.     }
    92.  
    93.     public Rect BoundingRect()
    94.     {
    95.         float minX = Mathf.Max (0, pos.x - brushRadius);
    96.         float maxX = Mathf.Min (screenDimensions.x, pos.x + brushRadius);
    97.         float minY = Mathf.Max (0, pos.y - brushRadius);
    98.         float maxY = Mathf.Min (screenDimensions.y, pos.y + brushRadius);
    99.    
    100.         return Rect.MinMaxRect(minX, minY, maxX, maxY);
    101.     }
    102. }
    103.  
    Here is the shader code that does the painting:

    Code (CSharp):
    1. Shader "Custom/Paint" {
    2.     Properties {
    3.           _Radius ("Radius (Screen Space)", Float) = 128.0
    4.           _FromPosition ("From Position (Screen Space)", Vector) = (0,0,0,0)
    5.           _ToPosition("To Position (Screen Space)", Vector) = (0,0,0,0)
    6.           _RadialPaint("Radial Ease Buffer", 2D) = "" {}
    7.           _MainTex("Current Color Texture", 2D) = "" {}
    8.           _Color("Main Color", Color) = (0,0,0,0)
    9.     }
    10.     SubShader {
    11.  
    12.         Lighting Off
    13.         Blend One Zero, One Zero
    14.         Cull Off
    15.         ZWrite Off
    16.         Fog { Mode Off }
    17.         ZTest Always
    18.  
    19.         Pass {
    20.             Tags { "RenderType" = "Opaque" }
    21.             CGPROGRAM
    22.             #pragma vertex vert_img
    23.             #pragma fragment frag
    24.        
    25.             uniform float4 _Color;
    26.             uniform sampler2D _MainTex;
    27.             uniform sampler2D _RadialPaint;
    28.             uniform float _Radius;
    29.             //From Position in Screen coordinates
    30.             uniform float4 _FromPosition;
    31.             //To Position in Screen coordinates
    32.             uniform float4 _ToPosition;
    33.             //ScreenDimensions (zw are inverted)
    34.             uniform float4 _ScreenDimensions;
    35.        
    36.             #include "UnityCG.cginc"
    37.        
    38.             inline float DistanceToSegment(float2 v, float2 p0, float2 p1)
    39.             {
    40.                 float2 p = p1 - p0;
    41.                 float2 w = v - p0;
    42.                 float c1 = dot(w, p);
    43.                 if(c1 <= 0)
    44.                 {
    45.                     return distance(v, p0);
    46.                 }
    47.                 else
    48.                 {
    49.                     float c2 = dot(p, p);
    50.                     if(c2 <= c1)
    51.                     {
    52.                         return distance(v, p1);
    53.                     }
    54.                     else
    55.                     {
    56.                         float b = c1 / c2;
    57.                         float2 pb = p0 + b * p;
    58.                         return distance(v, pb);
    59.                     }
    60.                 }
    61.             }
    62.        
    63.             float4 frag(v2f_img i) : COLOR {
    64.            
    65.                 float2 screenPos = i.uv * _ScreenDimensions.xy;
    66.                 float2 fromPos = _FromPosition.xy;
    67.                 float2 toPos = _ToPosition.xy;
    68.                
    69.                 float d = DistanceToSegment(screenPos, fromPos, toPos);
    70.                 float easeValue = 1.0f - d / _Radius;
    71.                 float4 currentColor = tex2D(_MainTex, i.uv);
    72.                 float radiusClamp = ceil(saturate(_Radius - d));
    73.                 //Paint amount is scaled by distance from radius (Currently linear), and by distance from starting point
    74.                 return (1.0 - easeValue * radiusClamp) * currentColor + radiusClamp * easeValue * _Color;
    75.             }
    76.             ENDCG
    77.         }
    78.     }
    79. }
    80.  
    Is there some aspect of DX11 that is preventing this strategy from working?

    I am attaching a unitypackage containing this test. Switch to/from DX9/11 in BuildSettings to see the effect.
    Any help or advice is appreciated.
     

    Attached Files:

    Last edited: Jul 5, 2015
  2. varfare

    varfare

    Joined:
    Feb 12, 2013
    Posts:
    227
    Code (CSharp):
    1.      Lighting Off
    2.         Blend One Zero, One Zero
    3.         Cull Off
    4.         ZWrite Off
    5.         Fog { Mode Off }
    6.         ZTest Always
    7.         Pass {
    8.             Tags { "RenderType" = "Opaque" }
    Looks like blending issue for me. Try setting "RenderQueue"="Transparent" in Tags section and change "RenderType"="Transparent" .
     
  3. Transdermal_Celebration

    Transdermal_Celebration

    Joined:
    Nov 30, 2014
    Posts:
    2
    I substituted these as my subshader tags: Tags { "RenderType" = "Transparent" "IgnoreProjector"="True" "Queue" = "Transparent"}

    Unfortunately no luck. If I put another texture into my SetTexture call, it works like I expect, with the texture being drawn into the rendertexture canvas. I have also tried generating another rendertexture and using it in the SetTexture call, which also results in no black background problems. It seems like the issue only crops up when I feed the canvas rendertexture back into the material that is used to render into it.