Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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 Triumanity · Mar 04, 2017 at 09:02 AM · shadergamecircledrawtransparent shader

Need to improve 2D circle shader performance

Hi, I'm working on a mobile game. I need resizeable rings (not stroke size but radius). I've written a shader works great but shows poor performance on mobile phones.

I set shader's float array(RadiusArray) from outside once in a while to add more circles. However whenever I add new circle, it gets slower and slower until I remove all circles from the array. Array has two information about a circle: Radius and Opacity.

Is there any way that I don't have to use for-loop like matrix multiplication etc.? Or any tips for optimization?

Thank you.

Here is how it looks; circles

 Shader "Custom/CircleShader" {
     Properties{
         _Color("Color", Color) = (1,1,0,0)
         _Thickness("Thickness", Range(0.0,0.5)) = 0.05
         _Radius("Radius", Range(0.0, 0.5)) = 0.4
     }
     SubShader{
     //Tags{ "Queue" = "Transparent"  "RenderType" = "Transparent" "IgnoreProjector" = "True" }
     Pass{
     Blend SrcAlpha OneMinusSrcAlpha // Alpha blending
  
  
     //Cull Off
     //Lighting Off
     //ZWrite Off //Make On for background
     //Fog{ Mode Off }
  
     CGPROGRAM
  
 #pragma vertex vert
 #pragma fragment frag
 #include "UnityCG.cginc"
  
  
     fixed4 _Color; // low precision type is usually enough for colors
     fixed _Thickness;
     fixed _Radius;
     fixed RadiusArray[80]; //Coming from outside
  
     struct appdata_t
     {
         float4 vertex   : POSITION;
         float2 texcoord : TEXCOORD0;
     };
     struct fragmentInput {
         fixed4 pos : POSITION;
         fixed2 uv : TEXCOORD0;
     };
     fragmentInput vert(appdata_t v)
     {
         fragmentInput o;
         o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
         o.uv = v.texcoord.xy - fixed2(0.5,0.5);
         return o;
     }
  
     // r = radius
     // d = distance
     // t = thickness
    
     fixed antialias(fixed d, fixed t) {
         fixed innerRadius = 0;
         fixed outerRadius = 0;
         fixed totalPixelOpacity = 0;
         fixed circleOpacity = 0.0;
         fixed currentcircleOpacity = 0.0;
  
         fixed radius;
         int circleCount = (int)RadiusArray[0];
  
         for (int ri = circleCount * 2; ri > 0; ri -= 2)
         {
             radius = RadiusArray[ri - 1];
             if (radius < 0 || totalPixelOpacity >= 1) //Last item
                 break;
  
             innerRadius = radius - 0.5*t;
             outerRadius = radius + 0.5*t;
             circleOpacity = RadiusArray[ri];
  
             currentcircleOpacity =
                 1 - (1 - (smoothstep(innerRadius - 0.003,
                     innerRadius,
                     d))
  
                     + (smoothstep(outerRadius,
                         outerRadius + 0.003,
                         d)));
  
             currentcircleOpacity *= circleOpacity;
             totalPixelOpacity += currentcircleOpacity;
         }
  
         return totalPixelOpacity > 1 ? 1 : totalPixelOpacity;
  
     }
  
  
     fixed4 frag(fragmentInput i) : SV_Target{
  
  
     fixed distance = 0;
         distance = length(fixed2(i.uv.x, i.uv.y));
         return fixed4(_Color.r, _Color.g, _Color.b, _Color.a*antialias(distance, _Thickness));
     }
  
  
         ENDCG
     }
     }
 }
  


capture.jpg (22.7 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
2
Best Answer

Answer by Glurth · Mar 04, 2017 at 05:49 PM

Rather than specify the circles, and compute the colors , inside the shader, I would recommend you pass a texture, 1 pixel wide, to the shader instead.

You can compute and generate (and assign to the shader) this texture ONCE, only when you CHANGE the number of circles, their radius', or thicknesses, using a regular c# class. In this class you would loop though all the possible "distance" values, and assign the resultant color to the 1 pixel wide texture. The color would be stored at the uv.y coordinate equal to the distance.

Then, your fragment shader will only need to lookup the pixel color (via UV) of this texture based upon your fragment-shader-computed "distance" value.

So, if your texture was say 1 pixel wide (in uv.X direction), you could use this to get the pixel color in your fragment shader:

 distance = length(fixed2(i.uv.x, i.uv.y));
 fixed4 color = tex2D(_MainTex, float2(0,distance));

Alternatively, you can use this method (compute texture outside of shader, only on changes) to generate a FULL texture (not one pixel), and just assign it to a standard shader.

Comment
Add comment · Show 6 · 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 Triumanity · Mar 07, 2017 at 10:14 AM 0
Share

Such an interesting solution. I'm having hard times to understand it since I'm a beginner on Unity and shaders. Let me tell you what I understood so far; you see circles as one dimension objects which is true from symmetry perspective. And you put all possible colors(alphas in my case) in one dimension array starting from the center of the circle. And in the fragment shader you pull the color only in the given distance value.

So, if I have a circle with 4 pixel radius, the one dimension array(just showing alpha values) would look like this; {0,0,0,1,0,0, ...}

Please let me know if I got it wrong.

After I implement this and see it working, I'll let you know and accept the answer. Thank you very much.

avatar image Triumanity · Mar 07, 2017 at 11:23 AM 1
Share

Such a performance improvement. Brilliant idea. Thank you very much.

So the basic code looks like;

 public void SetTexture(float[] radiusList)
     {
         Texture2D texture = new Texture2D(1, 500);
         // Reset all pixels color to transparent
         Color32 resetColor = new Color32(0, 0, 0, 0);
         Color32[] resetColorArray = texture.GetPixels32();
 
         for (int i = 0; i < resetColorArray.Length; i++)
         {
             resetColorArray[i] = resetColor;
         }
         texture.SetPixels32(resetColorArray);
 
 
         int circleCount = (int)radiusList[0];
         for (int ri = circleCount * 2; ri > 0; ri -= 2)
         {
             float radius = radiusList[ri - 1] / 0.5f * 250;
             texture.SetPixel(0, (int)$$anonymous$$ath.Floor(radius), new Color32(0, 0, 0, 255));
         }
         texture.Apply();
 
         Renderer rend = gameObject.GetComponent<Renderer>();
         rend.material.SetTexture("_$$anonymous$$ainTex", texture);
     }
avatar image Glurth Triumanity · Mar 07, 2017 at 02:08 PM 0
Share

Great glad it helped!

avatar image Bunny83 Triumanity · Mar 07, 2017 at 03:03 PM 1
Share

Don't use "SetPixel". It's slow and inefficient. Since you already have an array of colors, change the color in that array and use SetPixels or SetPixels32. Also you might want to cache your texture and the color array and reuse it. Currrently you recreate a new texture and array everytime. Also note that Textures have to be destroyed when you no longer use them. Otherwise they stay in memory until you call Resources.UnloadUnusedAssets.

I'm just mentioning those points as your main question was how to improve performance ^^.

So just create the texture and the Color32 array in Start. Something like that:

 Texture circleTex;
 Color32[] circleColors;
 static Color32 blank = new Color32(0,0,0,0);
 static Color32 black = new Color32(0,0,0,255);
 
 void Start()
 {
     circleTex = new Texture2D(1, 500);
     circleColors = new Color32[500];
     Renderer rend = gameObject.GetComponent<Renderer>();
     rend.material.SetTexture("_$$anonymous$$ainTex", circleTex);
 }
 
 public void SetTexture(float[] radiusList)
 {
     for (int i = 0; i < circleColors.Length; i++)
     {
         circleColors[i] = blank;
     }
     int circleCount = (int)radiusList[0];
     for (int ri = circleCount * 2; ri > 0; ri -= 2)
     {
         float radius = radiusList[ri - 1] / 0.5f * 250;
         circleColors[ (int)$$anonymous$$ath.Floor(radius) ] = black;
     }
     texture.SetPixels32(circleColors);
     texture.Apply();
 }

Btw: Your use of the "radiusList" is certainly "creative" but not a particular good approach. You may simply use a List ins$$anonymous$$d. It internally uses an array. You can simply add and remove elements. If the capacity of the List is large enough it won't allocate any new memory when you add / remove items. A List has a "Count" property so no need for storing the count as float ^^.

avatar image Triumanity Bunny83 · Mar 07, 2017 at 03:54 PM 0
Share

The array thing was designed for SetFloatArray and apparently it's time to change it. I had made all changes except Destroy (I recreate texture only when resolution changes). Very good tips indeed. Thank you :)

avatar image Triumanity · Mar 08, 2017 at 10:12 AM 0
Share

When I put 1000 px height texture, I can only use its half (500 px). Is there any way to say shader that its width is 1000 pixel and set its texture only 500 px?

Hope this makes it more clear;

circle

circle.jpg (40.7 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

6 People are following this question.

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

Related Questions

weird shader problem (correct in editor, weird ingame) 2 Answers

LineRenderer shows up behind transparent shaders 0 Answers

How to prevent shader overlap in LWRP Shadergraph. 0 Answers

Skybox color pls help! 0 Answers

LightingLambert': cannot implicitly convert from 'half3' to 'struct UnityGI' 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