Shader "Custom/TilemapShader" {
Properties {
_MainTex ("Map", 2D) = "white" {}
_Tileset("Tileset", 2D) = "white" {}
_Size("Map & Tileset Size", Vector) = (16, 16, 20, 13)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _Tileset;
struct Input {
float2 uv_MainTex;
};
float4 _Size;
// _Size.xy is the size of the map in tiles.
// _Size.zw is the size of the tileset in tiles.
void surf (Input IN, inout SurfaceOutputStandard o) {
// Scale up the uvs into tile space.
float2 mapLocation = IN.uv_MainTex * _Size.xy;
// Get this pixel's position within the tile we're drawing.
float2 inTileUV = frac(mapLocation);
// Clamp the texel we read from.
// ("Point" filtering does this too, but its math is slightly
// different, which can show up as a shimmer between tiles)
mapLocation = (mapLocation - inTileUV) / _Size.xy;
// Slightly inset tiles so they don't bleed at the edges.
inTileUV = inTileUV * 126.0f / 128.0f + 1.0f / 128.0f;
// Calculate texture sampling gradients using original UV.
// Otherwise jumps at tile borders make the hardware apply filtering
// that lets adjactent tiles bleed in at the edges.
float4 grad = float4(ddx(IN.uv_MainTex), ddy(IN.uv_MainTex));
// Read our map texture to determine what tiles go here.
float4 tileIndex = tex2D(_MainTex, mapLocation);
// Generate UV offsets into our tileset texture.
tileIndex = (tileIndex * 255.0f + inTileUV.xyxy )/_Size.zwzw ;
// Sample two tiles from our tileset: one base tile and one overlay.
// (Hey, we have room for two indices, might as well use it!)
fixed4 base = tex2Dgrad(_Tileset, tileIndex.xy, grad.xy, grad.zw);
fixed4 over = tex2Dgrad(_Tileset, tileIndex.zw, grad.xy, grad.zw);
// Combine overlaid tile with base using standard alpha blending.
fixed4 c = lerp(base, over, over.a);
// Put all this into terms the Unity lighting model understands.
o.Albedo = c.rgb;
o.Metallic = 0.0f;
o.Smoothness = 0.0f;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
The shader takes a group of tiles and position them into a texture
That won’t work. An “unlit” Surface Shader is still a lit shader as far as Unity is concerned, and it will not work with the SRPs. You cannot use Surface Shaders with any of the SRPs and get something that works.
You’ll have to write an entirely new shader for this to work. Create an unlit shader in the project view and try modifying that. Read a few shader tutorials, like those on Catlike coding that @Gordon_G linked to, or this one:
I undertand about the SRP issue you mention, however I took his shader and modified it as noted, and it worked as far as mapping his textures in an unlit fashion. So what gives?
Add a point light to your scene and you’ll see …
Surface Shaders are always “lit shaders” in terms of the pass types that Unity uses, specifically it generates two passes with the Pass tags "LightMode"="ForwardBase" and "LightMode"="ForwardAdd". When you use an “unlit” Surface Shader, it still has both passes, and lighting information is still being calculated for the object and passed to the shader. And if you have point or spot lights the “unlit” texture will get brighter with each additional light as it’s rendering the add pass with an additive blend scissored to the bounds of the light (meaning it’ll render the mesh only within a screen space rectangle where the point or spot light is).
You can make it better by adding a few extra things to the end of the #pragma surface line in addition to the noambient you already had.
That’ll avoid most of the extra lighting calculations, and avoid the issue with additive lights. However the generated shader will probably still be slightly more expensive than a hand written vertex fragment shader, and it still won’t work in the SRPs because it’s generating a single pass with "LightMode"="ForwardBase", which the SRPs explicitly will not render.
A vertex fragment shader using no "LightMode" Pass tag, or "LightMode"="Always", what the default Unlit shaders in Unity use, will get rendered by the SRPs. And the only way to get that "LightMode" is by not using a Surface Shader.
That is indeed the path Unity is suggesting people use when converting shaders. However Unity doesn’t provide a node equivalent to tex2Dgrad which complicates things a bit. It’s possible to work around it using a Custom Function node and calling SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy), but sampler states in Custom Function nodes are a bit of a mess.