- Home /
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):
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);
Answer by frarees · Aug 24, 2013 at 11:49 AM
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.
Texgen ObjectLinear can be omitted because you are using a vertex/fragment shader combo.
Anyway the shader looks great, and you did a good job. But you should avoid if/else statements and your clamp is only partial (you forgot to clamp negative values), you should clamp in the uv range (0,1). You don't need to have an else because you are blending so the texture coordinates you will not touch will remain unchanged. So you can rewrite the whole thing with a one liner:
return tex2D (_ShadowTex, fmod (float2(saturate(i.uv.x),saturate(i.uv.y)), 1 / _Size) * _Size) * _Color;
P.S. I was trying to make the same projector texture tiling stuff, tried your code, it works but only on orthographic mode, could you help me making it work in perspective mode, thanks.
@mayorc1978 sure. Contact me via twitter P$$anonymous$$ (@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.
Answer by Aras · Aug 22, 2013 at 04:24 AM
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?
$$anonymous$$y 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.
Your answer
Follow this Question
Related Questions
How to draw a tiled texture on a post process shader 0 Answers
Why not tiling on Shader (Sprite/Diffuse)? Unity 5.4.1 f1 1 Answer
cg shader blend textures in between two planes/tiles 0 Answers
How to force the compilation of a shader in Unity? 5 Answers
How to clean on RenderTexture to transparent with brush 0 Answers