Search Unity

Shader slowing game down on iPad

Discussion in 'Shaders' started by orphicsage, Dec 5, 2013.

  1. orphicsage

    orphicsage

    Joined:
    Jul 10, 2013
    Posts:
    5
    I wrote a shader that cuts a cube shape out of a mesh. It's working fine, but the problem is when I am zoomed in on an object that is using it, the game significantly slows down on my iPad. When I set my rendering settings to vertex, from forward rendering, the game ran fast, but the shader broke, I'm thinking because the shader won't work in vertex mode because its calculating per pixel.

    I also noticed that lights in the scene set to, important double the draw calls. This does not seam related to the problem, but for additional information, I read somewhere in Unty's documentation, that forward rendering creates duplicate draw calls per object in the scene.

    Does anyone know what the problem is? Here's my shader. Any help would be greatly appreciated! :D

    Shader "Custom/CutInCubeSolid"
    {
    Properties
    {
    _MainTex ("Texture", 2D) = "white" {}
    _Color ("Color", Color) = (1,1,1,1)
    }

    SubShader
    {
    Tags { "Queue"="Geometry"} //RenderType - shader replacement...
    Cull back
    Fog { Mode Off }
    LOD 10

    CGPROGRAM
    #pragma surface surf Lambert

    uniform float4 _TopPoint;
    uniform float4 _BottomPoint;
    uniform float4 _LeftPoint;
    uniform float4 _RightPoint;
    uniform float4 _FrontPoint;
    uniform float4 _BackPoint;

    uniform float4 _TopDirection;
    uniform float4 _BottomDirection;
    uniform float4 _LeftDirection;
    uniform float4 _RightDirection;
    uniform float4 _FrontDirection;
    uniform float4 _BackDirection;

    uniform fixed4 _Color;

    struct Input
    {
    float2 uv_MainTex;
    float3 worldPos;
    };

    sampler2D _MainTex;

    void surf (Input IN, inout SurfaceOutput o)
    {
    float epsilon = 0.001;

    float3 topDirection = IN.worldPos.xyz - _TopPoint.xyz;
    float3 bottomDirection = IN.worldPos.xyz - _BottomPoint.xyz;
    float3 leftDirection = IN.worldPos.xyz - _LeftPoint.xyz;
    float3 rightDirection = IN.worldPos.xyz - _RightPoint.xyz;
    float3 frontDirection = IN.worldPos.xyz - _FrontPoint.xyz;
    float3 backDirection = IN.worldPos.xyz - _BackPoint.xyz;

    bool render = dot(topDirection, _TopDirection.xyz) < epsilon
    dot(bottomDirection, _BottomDirection.xyz) < epsilon
    dot(frontDirection, _FrontDirection.xyz) < epsilon
    dot(backDirection, _BackDirection.xyz) < epsilon
    dot(rightDirection, _RightDirection.xyz) < epsilon
    dot(leftDirection, _LeftDirection.xyz) < epsilon;

    if(render)
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * _Color.rgb;
    else
    clip(-1);
    }

    ENDCG
    }
    Fallback "Diffuse"
    }
     
    Last edited: Dec 5, 2013
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I'm guessing it's all those float operations.
     
  3. orphicsage

    orphicsage

    Joined:
    Jul 10, 2013
    Posts:
    5
    If that's the case, than do you think creating this shader in several passes would do the trick? If it's even possible?
     
  4. TheShane

    TheShane

    Joined:
    May 26, 2013
    Posts:
    136
    The problem is just that you're doing a lot of work. Even for pixels that aren't drawn you are doing all those calculations before ultimately discarding them. That's at least a couple dozen GPU operations before you even start rendering the actual color of the pixel. That's a lot for an iPad.

    You are calculating the bounding box on a per pixel basis which is immensely expensive. You want to shift the culling operation to the actual geometry if you can. Then you can do the calculations once on the CPU, update your mesh data, and the GPU can use all of its cycles to shade the scene instead of doing pixel-by-pixel geometry culling. When you are doing per-pixel calculations, they should only be performing work that changes on a per-pixel basis (lighting, reflections, etc.).

    You may be able to optimize the shader a bit, but I think you ultimately need to re-evaluate how you are approaching the problem. You have more leeway on a desktop if you have GPU power to burn, but you are going to run into trouble on mobile.
     
  5. brianasu

    brianasu

    Joined:
    Mar 9, 2010
    Posts:
    369
    TheShane is right but if you can't do that try avoiding clip and alphatest on iOS (opposite on android) and maybe just use alpha.

    Can you do a stencil test if you have unity pro? Or a shader that just writes to the z buffer to cut a hole? Just overlay a cube that writes to the zbuffer using the colomask 0.
     
  6. TheShane

    TheShane

    Joined:
    May 26, 2013
    Posts:
    136
    Something like that could work.

    Maybe if you explained the reason for you shader and provide a screenshot of what you're attempting to do, someone might be able to provide a suggestion on a more efficient way of achieving the same goal.
     
  7. orphicsage

    orphicsage

    Joined:
    Jul 10, 2013
    Posts:
    5
    Wow great information, thanks guys! :) I'm curious about using the stencil buffer. That's a good idea :) Here's a screen shot. Also, if it's not too much to ask, could you give me an example of how you would use the stencil buffer? I'm assuming I can render a cube, and add it to the stencil buffer, showing only rendering within the cubes volume?

    In the following image, the shader is currently cutting a cube-shaped hole out of the cube shaped mesh.

    $CutInMesh.png
     
    Last edited: Dec 6, 2013
  8. brianasu

    brianasu

    Joined:
    Mar 9, 2010
    Posts:
    369
    Unity docs have a pretty cool example of making a crater in a mesh using stencil buffers.

    http://docs.unity3d.com/Documentation/Components/SL-Stencil.html

    But I'm not sure how you would get a hole using the stencil buffer.
    That might be a bit harder.

    It's a really cool shader though. You could optimise it more by using a standard cg shader and not a surface shader.