Projector + custom fragment shader = endless texture draw

Hi there,

What I want to do is, using a Projector, draw a texture several times within a unit, creating a grid / matrix view (i.e. draw a grid of tiles below my player). My shader defines three properties: color, texture and grid size.

Here’re the results I’m getting (using grid size = 1 and grid size = 2 respectively):

14563-gridsize1.png
14564-gridsize2.png

They repeat properly within a unit (you see the capsule covering a 2x2 grid), but it keeps painting them forever in the direction you see in the screenshots.

Here’s the shader I’ve implemented.

Shader "Projector/Custom Grid" {

	Properties {
		_Color ("Color", Color) = (1,1,1,0)
		_ShadowTex ("Cookie", 2D) = "black" { TexGen ObjectLinear }
		_Size ("Grid Size", Float) = 1
	}
	
	Subshader {
		Tags { "RenderType"="Transparent" "Queue"="Transparent+100" }
		Pass {
			ZWrite Off
			Offset -1, -1
			Fog { Mode Off }
			ColorMask RGBA
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma fragmentoption ARB_fog_exp2
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
			 
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};
			 
			sampler2D _ShadowTex;
			float4 _ShadowTex_ST;
			float4 _Color;
			float4x4 _Projector;
			fixed _Size;
			 
			v2f vert (appdata_tan v) {
				v2f o;
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX (mul (_Projector, v.vertex).xy, _ShadowTex);
				return o;
			}
			 
			half4 frag (v2f i) : COLOR {
				return tex2D (_ShadowTex, fmod (i.uv, 1 / _Size) * _Size) * _Color;
			}
			
			ENDCG
		}
	}
}

The issue seems to be generated at this line:

return tex2D (_ShadowTex, fmod (i.uv, 1 / _Size) * _Size) * _Color;

And specifically when applying fmod operation. If I don’t use fmod I don’t see the texture repeating over and over.

The texture is set to clamp, I’ve been reading a while about projectors and couldn’t find a solution to this. Any ideas?
Thanks.

Here's a top down view of what's happening behind the scenes. Using this frag: return half4 (fmod (i.uv, 1 / _Size) * _Size, 0, 1); [14593-debug.png|14593]

2 Answers

2

Found a way to fix this. Basically I had not to write if I get outside UV (1,1)

Shader "Projector/Grid Test" {

	Properties {
		_Color ("Color", Color) = (1,1,1,0)
		_ShadowTex ("Cookie", 2D) = "black" { TexGen ObjectLinear }
		_Size ("Grid Size", Float) = 1
	}
	
	Subshader {
		Tags { "RenderType"="Transparent" "Queue"="Transparent+100" }
		Pass {
			ZWrite Off
			Offset -1, -1
			Fog { Mode Off }
			ColorMask RGB
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma fragmentoption ARB_fog_exp2
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
			 
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};
			 
			uniform sampler2D _ShadowTex;
			float4 _ShadowTex_ST;
			float4 _Color;
			float4x4 _Projector;
			fixed _Size;
			 
			v2f vert (appdata_tan v) {
				v2f o;
				o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX (mul (_Projector, v.vertex).xy, _ShadowTex);
				return o;
			}
			 
			fixed4 frag (v2f i) : COLOR {
				if (i.uv.x < 1 && i.uv.y < 1)
					return tex2D (_ShadowTex, fmod (i.uv, 1 / _Size) * _Size) * _Color;
				else
					return fixed4 (0,0,0,0);
			}
			
			ENDCG
		}
	}
}

Probably not the most optimized and clean way to do this (how could I optimize it?), but it’s working now.

@mayorc1978 sure. Contact me via twitter PM (@frarees).

I've created a gist to keep track of changes: https://gist.github.com/frarees/8258128

No problem yesterday i remained awake till 6am and I was able to write that shader from scratch.

Nice! Can you share it? Thanks

It has some limitations, due to the fact I've optimized for mobile performance, but it should work properly for every case adding with a simple if/else... Texture rotation is only partial at the moment but you can rotate projector to obtain different rotation angles. I added you on twitter.

Not sure I understood the problem correctly. You do want the texture to repeat forever, or not? If you do, then just remove the fmod() call and set the texture wrap mode to Repeat?

My explanation is confusing. What I want is to repeat _ShadowTex _Size times. So, for the first example, I get a tile, and for the second I get four tiles (a 2x2 grid). The area that gets drawn should always be the same (1 unit), so the tiles should get smaller and smaller when _Size grows.