All the batched objects have the same rotation. To simulate the effect of directional light I need to determine the object sides actual (world) directions. Normally I would use unity_ObjectToWorld
& unity_ObjectToWorld
for that, which is not possible when the objects are batched.
Is it possible to pass the object rotation to the shader in one action (since it's the same for all of them) without breaking the batching?
This is a simplified shader example (here I use colors instead of light/shadow effect and only 2 sides instead of 4):
Shader "Test/UnlitTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TopColor ("TopColor", Color) = (1, 0, 0, 1)
_BottomColor ("_BottomColor", Color) = (0, 0, 1, 1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
ZTest Off
ZWrite Off
Lighting Off
Cull Off
Fog { Mode Off }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
fixed4 _TopColor, _BottomColor;
struct vertData
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct fragData
{
float2 uv : TEXCOORD0;
float2 uvW : TEXCOORD1;
float4 vertex : SV_POSITION;
};
fragData vert (vertData v)
{
fragData f;
f.vertex = UnityObjectToClipPos(v.vertex);
f.uv = TRANSFORM_TEX(v.uv, _MainTex);
f.uvW = mul(unity_ObjectToWorld, f.uv);
return f;
}
fixed isTransparentPixel(fixed2 uv)
{
return step(tex2D(_MainTex, uv).a, 0.0);
}
fixed isEdgePixel(fixed2 uv)
{
uv = mul(unity_WorldToObject, uv); // back to object space
return isTransparentPixel(uv);
}
fixed4 frag (fragData f) : SV_Target
{
fixed isTransparent = isTransparentPixel(f.uv);
clip(-isTransparent);
fixed4 c = tex2D(_MainTex, f.uv);
float2 borderSize = float2(3, 3); // border size in tex pixels
float dy = borderSize.y * _MainTex_TexelSize.y;
fixed3 borderColor = fixed3(0, 0, 0);
float borderColorsCount = 0;
float2 uvW = f.uvW; // UV in the world space
// Top edge:
fixed isEdge = isEdgePixel(uvW + float2(0, dy));
borderColor += isEdge * _TopColor.rgb;
borderColorsCount += isEdge;
// Bottom edge:
isEdge = isEdgePixel(uvW + float2(0, -dy));
borderColor += isEdge * _BottomColor.rgb;
borderColorsCount += isEdge;
fixed noBorderColor = step(length(borderColor), 0);
fixed hasBorderColor = 1 - noBorderColor;
c.rgb = c.rgb * noBorderColor + hasBorderColor * borderColor / max(borderColorsCount, 1);
return c;
}
ENDCG
}
}
}
It gets a semi-transparent image as a texture. The image is completely transparent at the edges and contains an opaque shape in the center.
The shader colors the top edges of the non-transparent shape in red and the bottom ones in yellow. It uses unity_WorldToObject
& unity_ObjectToWorld
to check the edges facing directions in the world space.