Right, so I restructured my shader to something which seems to be correct.
Basically I have 2 material modes, metallic and non metallic (as UE4 does), and if I want anything in between, I interpolate. The shader code as it stands now looks like this:
half3 reflection = texCUBEbias (_ReflectionTex, float4(i.reflectDir, (1-roughness)*6)); half specPower = exp2(10 * roughness + Log2Of1OnLn2_Plus1); half3 light = DecodeLightmap(lightmap); half3 fresnel = schlick(NdotV, metalness); half3 specular = (LN2DIV8 * specPower + 0.25) * SphericalGaussianApprox(HdotN, specPower) * fresnel; half3 ec = saturate(1 - (specular + reflection)); half3 plastic = albedo.rgb * light * ec + specular; half3 metallic = (specular*albedo + reflection * albedo.rgb) * Luminance(light); return half4(lerp(plastic, metallic, metalness), 1);
I am also using Lagarde's spherical gaussian approximation for the specular component which saves me quite a bit with negligible quality loss. Thumbs up for that!
So basically if it's metal, I ignore the albedo and simply have specular + reflection, multiplied by the lightmap.
If it's plastic, I use the albedo times lightmap, with added specular.
The specular colour depends on the metalness. For metals, it's coloured (using the albedo), for non-metals it's constant white.
The result looks like this (all the spheres have an albedo of "gold", but metalness and roughness varies diagonally, specular on the plastic balls is there, but it's hidden by the geometry at times)
The reflection cubemap is not prefiltered properly, so that's my next step!
Total fragment shader cost is about 27 instructions, with room for more optimisation.