Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by d3cr1pt0r · Dec 20, 2020 at 10:01 AM · renderingrendertexturealphablendingpainting

RenderTexture Painting - color blending issues

I am trying to implement brush painting, very similar to what photoshop has. I have been "mostly" successful, except that I am having some issues with blending and I'm not really sure how this needs to be addressed.


Painting technique (C#)

 RenderTexture.active = paintRenderTexture;
 
 GL.PushMatrix();
 GL.LoadPixelMatrix(0, paintRenderTexture.width, 0, paintRenderTexture.height);
 Graphics.DrawTexture(rect, brushTexture, paintMaterial);
 GL.PopMatrix();
 
 RenderTexture.active = null;


Paint Shader - Shader used to render the brush in the render texture

 Shader "Unlit/TexturePainter/Blit"
 {
     Properties
     {
         _MainTex("MainTex", 2D) = "white" {}
         _Color("Color", Color) = (1,1,1,1)
     }
         SubShader
     {
         Tags { "RenderType" = "Opaque" }
 
         Pass
         {
             Blend One OneMinusSrcAlpha
             Lighting Off
             Cull Off
             ZTest Always
             ZWrite Off
 
             ...
 
             fixed4 frag(v2f i) : SV_Target
             {
                 fixed4 brush = tex2D(_MainTex, i.texcoord0) * _Color;
                 return fixed4(_Color.rgb * brush.a, brush.a);
             }
             ENDCG
         }
     }
 }


Preview shader - This shader is used to render the final image on the quad.

 Shader "Unlit/TexturePainter/Preview"
 {
     Properties
     {
         _MainTex( "Texture", 2D ) = "white" {}
     }
     SubShader
     {
         Tags { "RenderType" = "Transparent" }
 
         Pass
         {
             Blend One OneMinusSrcAlpha
             ZWrite Off
 
             ...            
 
             fixed4 frag(v2f i) : SV_Target
             {
                 fixed4 col = tex2D(_MainTex, i.uv);
                 return fixed4(col.rgb, col.a);
             }
             ENDCG
         }
     }
 }


Blending issue GIF - If you focus on the area where I am overpainting on one spot, you can see the artifacts start forming around the edges of the brush texture (areas where alpha is not 100%) alt text


Conclusion

've also tried using separate blending modes for RGB and for A channels, but I was mostly unsuccessful. While I got rid of the issue I mentioned above, I had a different issue where alpha values weren't blended correctly. Is it even possible to achieve what I want using this technique, or is there another way this needs to be aproached?


Any help on this issue will be greatly appreciated :)


fgi6yzhuxa.gif (328.1 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by Eno-Khaon · Dec 20, 2020 at 08:23 PM

Back before Unity 5 (and, therefore, before the "free" version supported many features like RenderTextures), I self-taught myself color blending by making a texture-painting script.
Why the preface?
It meant I was blending all the colors without using shaders.


The problem you're running into is that you're pre-multiplying the alpha into the color you're drawing onto your surface, so the edges (as you mentioned) of the brush wind up the wrong color. Pre-multiplying color is only important in the context of having no alpha channel support. Rather, pre-multiplying is how you get the resulting color to display on screen in certain context, but it's not universally necessary and definitely isn't necessary in Unity, where textures are (generally speaking) 32-bit, containing alpha data.

In regard to my aforementioned "old project", the main function I used for complex color blending was:
 // Transcribed from Unityscript, so apologies ahead of time for any typos
 public static Color Blend(Color current, Color new)
 {
     Color output;
     // Blended alpha values with a maximum of 1
     // Example: 0.5 background + 0.5 brush = 0.75 output alpha
     output.a = current.a + (new.a * (1.0f - current.a));
     if(output.a == 0.0f)
     {
         return new Color(current.r, current.g, current.b, 0f);
     }
     else
     {
         // Example: White to Black, 0.5 alpha on background (current) and brush (new)
         // ((0.0 * 0.5) / 0.75) + ((1.0 * 0.5 * (1-0.75)) / 0.75)
         // ((0) + ((0.5 * 0.25) / 0.75)
         // ~0.1666 final pixel channel color
         output.r = ((new.r * new.a) / output.a) + ((current.r * current.a * (1 - new.a)) / output.a);
         output.g = ((new.g * new.a) / output.a) + ((current.g * current.a * (1 - new.a)) / output.a);
         output.b = ((new.b * new.a) / output.a) + ((current.b * current.a * (1 - new.a)) / output.a);
     }
     return output;
 }


This is made on the basis of blending a non-zero opacity new pixel color with any existing pixel, where the opacity of the existing pixel influences how much priority the color of the new pixel will have. This is also weighted with an additional step over a shader's Blend SrcAlpha OneMinusSrcAlpha approach to transparency blending by dividing by the output color to determine the new resulting color with high accuracy, but again, this is just part of the process of doing all the blending manually.

Now, having said all that, when you're drawing a texture on top of another one through a shader, you would generally be able to just rely on that blending. At a glance, and by my own personal impression, it looks like you have your role backwards for the brush, in that it's your opaque shader. Furthermore, you're also double-multiplying its values, by combining both tex2D(_MainTex, i.texcoord0) * _Color and fixed4(_Color.rgb * brush.a (factoring in the _Color value in both scenarios).

If you make your first shader Unlit/TexturePainter/Blit transparent ("RenderType" and "Queue", most importantly), blend using Blend SrcAlpha OneMinusSrcAlpha and remove the rgb pre-multiplication (i.e. _Color), I imagine that would solve most of the problems in this situation.

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image d3cr1pt0r · Dec 21, 2020 at 05:36 PM 0
Share

Hey, first of all, thank you for the answer, but I'm afraid simply changing blending to standard alpha blending doesn't solve the issue. In fact this is what I started off with in the first place. Sorry I didn't mention this, I actually tried a lot of stuff before making this post. The technique I explained in the post was just the one that gave the best results.


I would also like to note that having RenderType set to Opaque doesn't have any actual effect in my case, since this is just used for replacement shaders. Same goes with Queue not being set to Transparent.


I would also like to note that what I am doing with premultiplied blending is correct (and by correct I mean how a premultiplied alpha shader should look like). In my case, I'm actually using the texture only for the alpha value, therefore the final formula looks the way it does: fixed4 out = fixed4(_Color.rgb * brush.a, brush.a);


Here is how it looks if I change it to SrcAlpha One$$anonymous$$inusSrcAlpha blending.alt text

avatar image Eno-Khaon d3cr1pt0r · Dec 22, 2020 at 12:16 AM 0
Share

Unfortunately, your image doesn't seem like it uploaded properly in your reply (on that note, I'm not really sure why they're so finicky, but it's nothing new).


I threw together a few simple testing scripts to try and recreate the problem/effect you're experiencing on the "brush edges" with no luck yet. Perhaps it means there's more to this than meets the eye.

That said, here is the approach I used:
 // Attached to a camera
 using UnityEngine;

 public class BrushTest : $$anonymous$$onoBehaviour
 {
     public Texture2D brushImage;
     public $$anonymous$$aterial brush$$anonymous$$at;
     Vector2 drawOffset;
     Texture2D screenTex;
     RenderTexture rt;
 
     bool drawing = false;
     
     void Start()
     {
         drawOffset = new Vector2(brushImage.width, brushImage.height) * 0.5f;
         screenTex = new Texture2D(Screen.width, Screen.height);
         rt = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
     }
 
     private void Update()
     {
         if(Input.Get$$anonymous$$ouseButtonDown(0))
         {
             drawing = true;
         }
         else if(Input.Get$$anonymous$$ouseButtonUp(0))
         {
             drawing = false;
         }
     }
 
     private void OnRenderImage(RenderTexture source, RenderTexture destination)
     {
         RenderTexture activeRT = RenderTexture.active;
         RenderTexture.active = rt;
         if(drawing)
         {
             GL.Push$$anonymous$$atrix();
             GL.LoadPixel$$anonymous$$atrix(0, Screen.width, 0, Screen.height);
             Graphics.DrawTexture(new Rect(Input.mousePosition.x - drawOffset.x, Input.mousePosition.y - drawOffset.y, brushImage.width, brushImage.height), brushImage, brush$$anonymous$$at);
             GL.Pop$$anonymous$$atrix();
         }
         Graphics.Blit(rt, destination);
         RenderTexture.active = activeRT;
     }
 }


 Shader "Unlit/BrushBlend"
 {
     Properties
     {
         _$$anonymous$$ainTex ("Texture", 2D) = "white" {}
         _Color("Color", Color) = (1,1,1,1)
     }
     SubShader
     {
         Tags { "RenderType"="Opaque" }
 
         Pass
         {
             Blend SrcAlpha One$$anonymous$$inusSrcAlpha
             Cull Off
             ZWrite Off
 
             CGPROGRA$$anonymous$$
             #pragma vertex vert
             #pragma fragment frag
 
             #include "UnityCG.cginc"
 
             struct appdata
             {
                 float4 vertex : POSITION;
                 float2 uv : TEXCOORD0;
             };
 
             struct v2f
             {
                 float2 uv : TEXCOORD0;
                 float4 vertex : SV_POSITION;
             };
 
             sampler2D _$$anonymous$$ainTex;
             float4 _$$anonymous$$ainTex_ST;
             float4 _Color;
 
             v2f vert (appdata v)
             {
                 v2f o;
                 o.vertex = UnityObjectToClipPos(v.vertex);
                 o.uv = TRANSFOR$$anonymous$$_TEX(v.uv, _$$anonymous$$ainTex);
                 return o;
             }
 
             fixed4 frag (v2f i) : SV_Target
             {
                 fixed4 col = tex2D(_$$anonymous$$ainTex, i.uv);
                 return fixed4(_Color.rgb, col.a);
             }
             ENDCG
         }
     }
 }
avatar image Eno-Khaon d3cr1pt0r · Dec 22, 2020 at 12:19 AM 0
Share

(Ran out of characters in the previous comment)

For my test, I attached the BrushTest script to my main camera, created a material using the shader and set that on the script, then used "Default-Particle" as my "brushImage" Texture2D value.

Of note, using Blend One One$$anonymous$$inusSrcAlpha with pre-multiplied alpha on the colors did have a similar-or-same visual output as Blend SrcAlpha One$$anonymous$$inusSrcAlpha without factoring in alpha, since the point of pre-multiplying *IS* related to influencing the final resulting value (I didn't exactly mean to sound quite so vehemently against the idea). I mainly draw attention to it because it could easily become misleading in how it relies on such a specific blending mode as well.

In the shader, I also set the color to have ~50% opacity.

That said, I wasn't able to reproduce the problem you're running into. Are there any other details you can provide that might make it stand out?

Edit: typo

avatar image d3cr1pt0r Eno-Khaon · Dec 22, 2020 at 06:38 PM 0
Share

I put the code you posted in my Unity project and the result is actually the same as in the previously failed uploaded video of the result.


Here is the result using SrcAlpha One$$anonymous$$inusSrcAlpha alt text

rivh9eescl.gif (259.0 kB)

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

128 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

The alpha value in the RenderTexture are not the same in Editor mode and Build mode. 2 Answers

RenderTexture.Create failed: format unsupported 0 Answers

Rendering an HD PNG off-screen? 0 Answers

Binding EGLImage to RenderTexture (or setting NativeTexturePtr) 0 Answers

How do I have multiple cameras that have a black and white view in one while the other shows a color at a certain time on a certain object? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges