- Home /
Why Do Android Devices Hate This Shader?
Hi,
I've just started Unity Android development and found out that one of the modified shaders that our project is using is causing the devices to crash.
The shader is a modified cutout soft-edge shader. I removed the color component out of the shader computation (at least I think I did) because I didn't need to do any color modulation.
When using the vanilla Soft-Edge shader it works fine, but only when I try to use this optimized soft-edge shader I run into problems. For now I'm just using the vanilla one. However, is there a reason why this version doesn't work? It works fine in the Editor, it just creates a Signal 11 crash when I run it on my HTC Nexus One device. Any help would be appreciated so that I can better understand what I did wrong.
Thanks!
Properties { _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {} _Cutoff ("Base Alpha cutoff", Range (0,.9)) = .5 }
SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="TransparentCutout" } Lighting off
// first pass:
// render any pixels that are more than [_Cutoff] opaque
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Cutoff;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 _Color;
half4 frag (v2f i) : COLOR
{
half4 col = tex2D(_MainTex, i.texcoord);
clip(col.a - _Cutoff);
return col;
}
ENDCG
}
// Second pass:
// render the semitransparent details.
Pass {
Tags { "RequireOption" = "SoftVegetation" }
// Dont write to the depth buffer
ZWrite off
// Set up alpha blending
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Cutoff;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 _Color;
half4 frag (v2f i) : COLOR
{
half4 col = tex2D(_MainTex, i.texcoord);
clip(-(col.a - _Cutoff));
return col;
}
ENDCG
}
}
SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="TransparentCutout" } Lighting off
// first pass:
// render any pixels that are more than [_Cutoff] opaque
Pass {
AlphaTest Greater [_Cutoff]
SetTexture [_MainTex] {
combine texture
}
}
// Second pass:
// render the semitransparent details.
Pass {
// Dont write to the depth buffer
ZWrite off
// Only render pixels less or equal to the value
AlphaTest LEqual [_Cutoff]
// Set up alpha blending
Blend SrcAlpha OneMinusSrcAlpha
SetTexture [_MainTex] {
Combine texture
}
}
}
I don't know anything about Android. But the way that Unity handles soft edges is goofy. You shouldn't Alpha Test in the second pass, but rather, use ZTest Less, and multiply the alpha of the texture by 1/_Cutoff. That way, you don't have stupid-looking hard alpha tested edges next to ghostly apparitions of the edge. To do this in fixed function, multiply by .25/_Cutoff ins$$anonymous$$d, and use Quad in the alpha calculation. Also, "RequireOption"="SoftVegetation" is deprecated.
Answer by Jessy · Apr 18, 2011 at 06:14 PM
This does not answer your question, but here's what I was talking about, in my comment (sorry, I don't know Cg or feel a need to learn it presently):
Shader "Transparent/Cutout/Soft Edge Unlit +" {
Properties { _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {} _Cutoff ("Base Alpha cutoff (Use Script)", Float) = 1 _CutoffInverseQuarter ("Cutoff inverse / 4 (Use Script)", Float) = 1 }
Category { Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
SubShader {
Pass {
GLSLPROGRAM
varying mediump vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
uniform lowp float _Cutoff;
void main() {
vec4 texture = texture2D(_MainTex, uv);
if (texture.a < _Cutoff) discard;
gl_FragColor = texture;
}
#endif
ENDGLSL
}
Pass {
ZTest Less ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
GLSLPROGRAM
varying mediump vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
uniform lowp float _Cutoff;
void main() {
vec4 texture = texture2D(_MainTex, uv);
gl_FragColor = vec4(texture.rgb, texture.a / _Cutoff);
}
#endif
ENDGLSL
}
}
SubShader {
Pass {
AlphaTest GEqual[_Cutoff]
SetTexture[_MainTex]
}
Pass {
ZTest Less ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
SetTexture[_MainTex] {
ConstantColor(0,0,0, [_CutoffInverseQuarter])
Combine texture, texture * constant Quad
}
}
}
}
}
The material inspector is inadequate for this, though; an EditorWindow is usable:
using UnityEngine; using UnityEditor;
class SoftEdgePlusCutoffWindow : EditorWindow {
static SoftEdgePlusCutoffWindow window; static Material material;
[MenuItem("Assets/Adjust Soft Edge + Cutoff", true)] static bool Validate () { if (window) return false;
try {material = (Selection.activeObject as GameObject).renderer.sharedMaterial;}
catch {material = Selection.activeObject as Material;}
return material ? material.HasProperty("_CutoffInverseQuarter") : false;
}
[MenuItem ("Assets/Adjust Soft Edge + Cutoff", false, 2000)] static void Open () { window = GetWindow<SoftEdgePlusCutoffWindow>(true, material.name); window.autoRepaintOnSceneChange = true; }
// The alpha channel of the texture needs to be scaled so that that it is white at the cutoff value. // That way, there will always be a full invisible-opaque transition, // instead of there being a visible alpha tested edge with lower cutoff values. // You can only make the alpha 4x as bright, with a fixed function shader, // hence the .25 here, and Quad in the shader. bool listeningForGuiChanges; void OnGUI () { Event currentEvent = Event.current; if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0) { Undo.SetSnapshotTarget(material, material.name + " Cutoff Adjustment"); Undo.CreateSnapshot(); Undo.ClearSnapshotTarget(); listeningForGuiChanges = true; }
material.SetFloat("_Cutoff", EditorGUILayout.Slider("Cutoff", material.GetFloat("_Cutoff"), .25F, 1));
material.SetFloat("_CutoffInverseQuarter", .25F / material.GetFloat("_Cutoff"));
if (listeningForGuiChanges && GUI.changed) {
Undo.SetSnapshotTarget(material, material.name + " Cutoff Adjustment");
Undo.RegisterSnapshot();
listeningForGuiChanges = false;
}
}
}
This works on iOS. Please let us know what happens on Android.
Thanks for the response... unfortunately with my tests it doesn't appear to work. Your shader produces the same crash on my device with only an object with your shader in the scene.
Is this under OpenGL ES 1.1 or 2.0? You should try forcing the rendering mode to one or the other, to see which SubShader is a problem.
Sorry for the late response, I tested it under 1.1 and it works fine it is just the programmable path that is causing the crash.
Answer by LoungeKatt · Oct 10, 2014 at 06:33 AM
In case anyone stumbles across this like I did, Android has issues with AlphaTest and clip(), which can be resolved by editing the shader above to read:
Shader "Transparent/Cutout/Soft Edge Unlit +" {
Properties {
_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
_Cutoff ("Base Alpha cutoff (Use Script)", Float) = 1
_CutoffInverseQuarter ("Cutoff inverse / 4 (Use Script)", Float) = 1
}
Category {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
SubShader {
Pass {
GLSLPROGRAM
varying mediump vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
uniform lowp float _Cutoff;
void main() {
vec4 texture = texture2D(_MainTex, uv);
if (texture.a < _Cutoff) discard;
gl_FragColor = texture;
}
#endif
ENDGLSL
}
Pass {
ZTest Less ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
GLSLPROGRAM
varying mediump vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
uniform lowp float _Cutoff;
void main() {
vec4 texture = texture2D(_MainTex, uv);
gl_FragColor = vec4(texture.rgb, texture.a / _Cutoff);
}
#endif
ENDGLSL
}
}
SubShader {
Pass {
ZTest Less ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
SetTexture[_MainTex] {
ConstantColor(0,0,0, [_CutoffInverseQuarter])
Combine texture, texture * constant Quad
}
}
}
}
}