Mobile shader issue (mesh occasionally renders black)

Sorry again if this is the wrong section, or if this question has been answered elsewhere (I doubt it, though: I haven’t had luck finding any help online).

I’m having a shader problem on mobile devices (both Android and iOS). In our racing game, we have a reflection shader applied to the cars. This shader takes a cube map and rotates it horizontally and vertically about the car. The shader works fine in the editor and most of the time on mobile, but occasionally - seemingly at random - the cars will appear black instead. Solid black, too: the texture isn’t visible, and lighting doesn’t seem to affect it. It can’t be a hardware support issue as the shader works most of the time, and the issue seems to occur on several different devices (iPhone 4, iPad Mini, Galaxy Tab 10.1, and HTC One, to name a few).

I’ve tried dozens and dozens of tests, but nothing conclusive. All test cases consist of reloading the scene containing one or more meshes with the shader in question. If the issue occurs, it’s noticeable immediately upon scene load. The repro rate is erratic: sometimes it happens within the first 10 tests, and other times it doesn’t happen for 30+, so it’s difficult to tell if I’m getting close to a solution.

One forum thread suggested using Handheld.ClearShaderCache(), but that didn’t have any noticeable effect.
I don’t think it’s hitting the fallback shader, as Mobile/Diffuse has no color property and should render the texture white.
I’ve tried this shader and it works on its own. I tried adding the rotation and color behavior from my original shader to this one piece by piece, but there doesn’t seem to be a single piece of logic to blame.

I’ve attached screenshots of a test scene: one of intended behavior and another of the issue. I’ve also pasted the shader code below. If anyone has any insight into this issue, I would be tremendously appreciative.

Shader "CustomShaders/CubemapReflect" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_ColorTint ("Diffuse Tint Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_ReflectColor ("Reflection Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_MainTex ("Texture", 2D) = "white" {}
		_Cube ("Cubemap", CUBE) = "" {}
		_Angle ("Cubemap Rotation", float) = 0
		_Shininess ("Shininess", Float) = 0.5
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		
		CGPROGRAM
		#pragma surface surf Lambert finalcolor:mycolor
		struct Input 
		{
			float2 uv_MainTex;
			float3 worldRefl;
			float2 uv_SideTex;
			float3 worldNormal;
		};
		
		fixed4 _ReflectColor;
		fixed4 _Color;
		fixed4 _ColorTint;
		sampler2D _MainTex;
		samplerCUBE _Cube;
		uniform float _Angle;
		uniform float _Shininess;
		
      	void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
      	{
          color *= _ColorTint;
      	}

		void surf (Input IN, inout SurfaceOutput o) 
		{				
				// Apply the main texture
				o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * _Color;
				
				// Calculate the rotated top world reflection vector
				float3 topVec = IN.worldRefl;
				half cosa = cos(_Angle);
				half sina = sin(_Angle);
				half y = IN.worldRefl.y;
				half z = IN.worldRefl.z;
				topVec.x = 1;
				topVec.y = y * cosa - z * sina;
				topVec.z = y * sina + z * cosa;
				
				// Calculate the rotated side world reflection vector
				float3 sideVec = IN.worldRefl;
				half x = IN.worldRefl.x;
				sideVec.x = x * cosa - z * sina;
				sideVec.y = 1;
				sideVec.z = x * sina + z * cosa;
				
				// Calculate and clamp the ratio
				half ratio = (0.8 - IN.worldNormal.y) / 0.4;
				ratio = ratio > 1 ? 1 : ratio;
				ratio = ratio < 0 ? 0 : ratio;
				
				// Apply the top and side cubemaps
				o.Emission = texCUBE(_Cube, topVec).rgb *  _ReflectColor.rgb * (1 - ratio) * _Shininess;
				o.Emission += texCUBE (_Cube, sideVec).rgb *  _ReflectColor.rgb * ratio * _Shininess;
				
		}
		ENDCG
	} 
	FallBack "Mobile/Diffuse"
}


We’re seeing the same issue with some of our own Shaders (including the erratic repro rate).

The main similarity I can see is that the Shaders presenting this issue (while not overly complex in terms of their operations) do tend to have multiple texture or cubemap unwraps… I don’t see why that should be a problem, but maybe it’s useful to you (I’m not allowed to post the Shaders due to company policy, but they’re reasonably straightforward matcap or cubemap reflection shaders, and our cubemap reflection shaders are simpler than the one you posted above in terms of calculations).

I haven’t been able to fix it, but I’ll post progress here when I make any.

I see the random black using Unity built in Bumped Spec shader. Usually it occurs on slower loading devices during a new scene load. I assumed the normal map wasn’t applied properly, but that’s just a guess. Happens randomly, the worst kind of problem to troubleshoot. Seemed to happen less when I placed some yields during instancing of scene objects, but still does occur sometimes.

not alone, all materials using the same shader will go bad occasionally.

http://forum.unity3d.com/threads/200012-4-2-issue-Textures-sometimes-degraded-on-device-showing-solid-color

Same issue, vert frag shader with cubemap reflection and 1 texture randomly appears black when loading the scene. Works fine sometimes, wrong other times.
That shader had been using light probes so I wrote another shader that used 2 cubemaps instead for reflection and lighting, same issue occurs, its not noticeably any more frequent.

So I managed to work around the problem;

I have an empty scene that loads first with a gameobject with DontDestroyOnLoad, (I always have this in my projects anyway), and I added a script which just held a public array of shaders, all shaders in my project were custom so this was pretty easy to drag and drop all the shaders that weren’t working. The result of this was that the shaders were always referenced in the scene and never unloaded, could be a small hit on memory I suppose but didn’t affect this project.

Anyway, with this setup I haven’t seen the issue at all.
Hope that helps someone

popMark, I just noticed a GraphicsSettings manager with an array of “Always Included Shaders”. I wonder if this is similar to your work around. I can’t find any documentation yet, but I will give it a go. I’m using Unity 4.2.1f4 right now.

Jmonroe: Nice find, there. I’ve dropped our custom shaders into that manager’s array/list, will update here if that fixes the issue (and hopefully there’ll be a proper fix on the engine side before long, too)

@LoTekK: The “Problem” is that unity is trying to optimize the best they can, so every shader that is not guaranteed to be used in the Build is stripped out of the game (That includes the default shader).

The “Always Included Shaders” Menu / Option it their way to give you chance to ensure that the shader that you will load later will be included.
Every shader that is referenced by any script / object that is referenced / included into a scene will be included automatically into the build.

Ordinarily I’d be “ok” with that, since it can make sense. Problem is, in this (and other cases I’ve seen), the shaders should indeed be guaranteed to be in the build, seeing as they’re referenced, either by being present in the scene, or assigned to public properties in an object in the scene.

There’s also the fact that this isn’t a case of the shaders not being present, just not initialising properly (the shaders can break within the same session on the device upon scene reload, and then be ok on the next scene reload, for example).

Ah yeah I’d heard of the “Always Included Shaders” but didn’t think to use it, I would guess though as LoTekK says, the issue isn’t that the shaders aren’t being included in the build, they’re just randomly not being loaded in the scene.

That sound like solide bug, did you report it?
That should give you the best chances to get an answer from the unity support team.

Sorry for my extended absence. I’ve been keeping tabs on this thread, but I hadn’t had time to attempt any of the suggestions until recently.

The Always Include Shaders editor setting seems to be the ticket. I made a test scene with the offending shader on a mesh and a GUI button that reloads the scene. The issue didn’t appear after 700 scene reloads, so I’m saying it’s fixed! :slight_smile:

My heartfelt thanks for all the assistance and experimentation, and I once more apologize for not replying sooner.