Mobile shader for texture and specular only?

Found this shader:

// Simplified Bumped Specular shader. Differences from regular Bumped Specular one:
// - no Main Color nor Specular Color
// - specular lighting directions are approximated per vertex
// - writes zero to alpha channel
// - Normalmap uses Tiling/Offset of the Base texture
// - no Deferred Lighting support
// - no Lightmap support
// - fully supports only 1 directional light. Other lights can affect it, but it will be per-vertex/SH.

Shader "Mobile/Bumped Specular" {
Properties {
	_Shininess ("Shininess", Range (0.03, 1)) = 0.078125
	_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
	_BumpMap ("Normalmap", 2D) = "bump" {}
}
SubShader { 
	Tags { "RenderType"="Opaque" }
	LOD 300
	
CGPROGRAM
#pragma surface surf MobileBlinnPhong exclude_path:prepass nolightmap noforwardadd halfasview

inline fixed4 LightingMobileBlinnPhong (SurfaceOutput s, fixed3 lightDir, fixed3 halfDir, fixed atten)
{
	fixed diff = max (0, dot (s.Normal, lightDir));
	fixed nh = max (0, dot (s.Normal, halfDir));
	fixed spec = pow (nh, s.Specular*128) * s.Gloss;
	
	fixed4 c;
	c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten*2);
	c.a = 0.0;
	return c;
}

sampler2D _MainTex;
sampler2D _BumpMap;
half _Shininess;

struct Input {
	float2 uv_MainTex;
};

void surf (Input IN, inout SurfaceOutput o) {
	fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
	o.Albedo = tex.rgb;
	o.Gloss = tex.a;
	o.Alpha = tex.a;
	o.Specular = _Shininess;
	o.Normal = UnpackNormal (tex2D(_BumpMap, IN.uv_MainTex));
}
ENDCG
}

FallBack "Mobile/VertexLit"
}

What do I need to delete in order to remove all traces of the normal map and speed it up so its just specular based on alpha + texture? thanks!

The o.Normal line and the related definitions of the bumpmap naturally.

will be interesting to see how much performance you gain

Do you really need a surface shader? What you probably want to do is use a 1D lookup texture, instead of using pow in the fragment shader. (1D doesn’t work on iOS but you could have one of the dimensions of a 2D texture be 1.)

http://altdevblogaday.com/2011/01/18/ios-shader-tricks-or-its-2001-all-over-again/

I’m also using this shader as basic for some modifications. Is it actually really slower when using only one color channel of the MainTex RGB like

o.Albedo = tex.r;

I also tried to set some to fixed but I fear this could be actually slower, is there anything I can set to lower precision in this shader?

What exactly is a 1D lookup texture? I tried one of the shaders, maybe I made something wrong, all I get is a black mesh. Is this the correct way to write such a shader?:

Shader “New Shader” {
Properties {
u_texcolor (“Base (RGB) Gloss (A)”, 2D) = “white” {}
u_texLUT (“LUT”, 2D) = “white” {}
u_texnormal (“Normalmap”, 2D) = “bump” {}

}

SubShader { // Unity chooses the subshader that fits the hardware best

Tags { “RenderType”=“Opaque” }
LOD 300

Pass { // some shaders require multiple passes
GLSLPROGRAM // here begins the part in Unity’s GLSL

#ifdef VERTEX
attribute vec4 a_position;
attribute vec2 a_uv;
attribute vec3 a_normal;
attribute vec4 a_tangent;

uniform mat4 u_mvp;
uniform mat4 u_world2object;
uniform vec4 u_worldlightdir;
uniform vec4 u_worldcampos;

varying vec2 v_uv;
varying vec3 v_lightdir;
varying vec3 v_halfdir;

void main()
{
gl_Position = u_mvp * a_position;
v_uv = a_uv;

vec3 bitan = cross (a_normal.xyz, a_tangent.xyz) * a_tangent.w;
mat3 tsprotation = mat3 (
a_tangent.x, bitan.x, a_normal.x,
a_tangent.y, bitan.y, a_normal.y,
a_tangent.z, bitan.z, a_normal.z);

vec3 objLightDir = (u_world2object * u_worldlightdir).xyz;
vec3 objCamPos = (u_world2object * u_worldcampos).xyz;
vec3 objViewDir = objCamPos - a_position.xyz;

v_lightdir = tsprotation * objLightDir;
vec3 viewdir = normalize(tsprotation * objViewDir);
v_halfdir = normalize (v_lightdir + viewdir);
}
#endif

#ifdef FRAGMENT
uniform lowp vec4 u_lightcolor;
uniform lowp vec4 u_matcolor;
uniform mediump float u_spec;

varying mediump vec2 v_uv;
varying lowp vec3 v_lightdir;
varying lowp vec3 v_halfdir;

uniform sampler2D u_texcolor;
uniform sampler2D u_texnormal;
uniform sampler2D u_texLUT;

void main()
{
lowp vec3 albedo = texture2D (u_texcolor, v_uv).rgb;
lowp vec3 normal = texture2D (u_texnormal, v_uv).rgb * 2.0 - 1.0;

lowp float diff = dot (normal, v_lightdir);
lowp float nh = dot (normal, v_halfdir);
lowp vec2 luv = vec2(diff,nh);
lowp vec4 l = texture2D (u_texLUT, luv);

lowp vec4 c;
c.rgb = albedo * l.rgb + l.a;
c.a = 0.0;
gl_FragColor = c;
}
#endif

ENDGLSL // here ends the part in GLSL
}
}

FallBack “Diffuse”
}

I was thinking that this process (just specular and texture) would probably be faster avoiding pixel shaders altegether somehow and approximating it using fixed function opengl.

Vic, I wish I know. I’m still confused by a lot of shader techniques. Perhaps this is one of the optimised shaders coming in 3.4?

I honestly have no idea what you’re trying to do there. :face_with_spiral_eyes:

Here’s the fastest possible shader I can think of*, that fits what I think hippocoder is asking for. You design a horizontal greyscale gradient, and use that as a specular ramp, instead of using math in the fragment shader. You import that into Unity as an Alpha 8 (generate alpha from greyscale), because PVRTC doesn’t work with non-square textures. Make sure it’s set to clamp. I’m attaching a 64-pixel wide one as an example. That gets multiplied by the gloss map and light color (one directional light for performance), and added to the texture.

Warning: Unity doesn’t always render this, because in iOS mode, you can’t choose the rendering path, and if Unity decides that it wants to be in VertexLit mode, it won’t work. Unfortunately, I have no solutions for that, other then to temporarily switch platforms, restart, or switch apps, which sometimes works. I asked about this problem today but haven’t heard back yet. (You can force your cameras to use the forward path, but you can’t do that to the Scene View camera, as far as I know.)

  • “fastest possible” is theoretical. I’d definitely appreciate someone profiling my shader against the builtin one and sharing some numbers.

623044–22273–$Specular (ramp + gloss).shader (1.17 KB)
623044–22274–$specular ramp.psd (22.5 KB)

Hey I just checked back. Thanks Jessy. Will profile and report back soon as able. Looks pretty interesting code there.

I also tried it - it’s a super fast shader (fps were in the high 50s). But there is a little problem: in the editor it looks like it should but on the device the mesh is not brightened till the horizon when you use this shader on a floor mesh.

Lame! :rage: Get me some screenshots, or ideally, a video and a small Unity package, and I’ll see what I can do.

Was it an opengl 2.0 device?

It had to be. This shader doesn’t have a fixed function SubShader/Fallback.

And I believe that the issue is a difference in texture filtering between the Edtior and iOS device. Did you have anisotropic filtering turned on? I think that will show in the Editor, but not work on the device, which will screw with the gloss map at lower mip levels.

Yes OGLES2 Device (iPad 1)

Filtering on or off makes no difference. Please check PM Jessy.

Video showing the difference in the editor and on the iPad: http://www.hessburg.com/shader.mov

The way to debug is to look at individual expressiones by “visualizing” them as fragment colors, e.g. by setting this as last line of the fragment shader :

gl_FragColor = vec4(dot(normal, halfDirection), 0.0, -dot(normal, halfDirection), 1.0);

This should show whether a negative dot product is a problem (if there is blue at the horizon) or whether the normal is not orthogonal to the halfDirection at the horizon (red).

It turns out that low precision just isn’t enough to store the view direction; bumping it up to medium precision solved the problem for me. Sorry about that. I updated the other shader, and here’s a version without the gloss map calculation, which VIC20 didn’t need:

625253–22271–$Specular (ramp).shader (1.1 KB)

Does this still work on alpha channel for specular? have to test later tonight…

The first one I wrote, and said I updated, does. Not this one; that’s the only difference between them.

Thank you! Now it works.

Actually I was looking for the bumped specular shader with that “ramp”, but I also need this one. Now I need to figure out how to implement the bump texture.
I also want to include a greyscale texture (saved in the alpha channel of the main texture) which blends by vertex colors. (I’ve already got this shader perfectly working as a surface shader but I only got 15-20 fps on the iPad)

It turns out that the rendering path is project wide, so you just need to choose the rendering path for a platform that allows you to choose, and it will update for iOS. I’ll log a bug about this so we get the drop-down menu in the iOS Player Settings Inspector as well.