0
\$\begingroup\$

I quite understand how projective texturing works. I implemented successfully a shader for that following nvidia doc.

The major problem I'm facing is that with that implementation the projector frustum is used only to determine the texture coordinate in projective space, but it doesn't clip anything outside the projective frustum volume.

In other words, if I have a projector pointing toward a object, the texture will be projected on it even if the object is outside its frustum. In addition when projector is almost parallell to the projected object, the texture stretching is too evident and I would like to fade it out.

Now, I'm trying to understand how Unity handles their built-in projectors. I found this example. Here's vertex and fragment relevant code:

v2f vert (float4 vertex : POSITION)
{
  v2f o;
  o.pos = mul (UNITY_MATRIX_MVP, vertex);
  o.uvShadow = mul (_Projector, vertex);
  o.uvFalloff = mul (_ProjectorClip, vertex);
  return o;
}

fixed4 frag (v2f i) : SV_Target
{
  fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
  texS.rgb *= _Color.rgb;
  texS.a = 1.0-texS.a;

  fixed4 texF = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff));
  fixed4 res = texS * texF.a;
  return res;
}

I think that _Projector matrix is a classical matrix to transform vertices coordinates into projector space. (In fact the fragment just use the transformed coordinates to sample the projected texture).

What I'm really missing is how Unity construct _ProjectionClip matrix. The transformed vertex coordinates are used to sample the falloff texture (that I think it is what I really need).

Does anyone know how the _ProjectionClip is constructed? Or how to achieve a similar effect?

Note: I did something similar for calculating spotlights falloff, but there I used a single matrix multiplication in the vertex shader to transform vertices into lightspace, and the squared distance in the fragment shader to calculate texture coords for a lookup into the attenuation texture.

\$\endgroup\$
1
\$\begingroup\$

For clipping pixels outside the frustum, you can use a texture with the border mode enabled and a black border. (Or a black 1-pixel border authored directly in the texture). Alternatively, in an additive blending pass, you can add code to the shader to discard the pixel if the UVs are outside the unit square.

For fading the projection out when the projected object is close to parallel, the ordinary lighting N·L factor will mostly take care of this. You can fade things out sooner by biasing the N·L factor a little bit.

As far as the Unity shader goes, my guess is it simply lets you have different scales for the main texture and falloff texture. For instance, the main texture could be some image and the falloff texture might be a white circle fading into a black background, which multiplies with the main texture and restricts it to only appear in that circle. The user could adjust the frusta for the two textures independently.

In other words, the _ProjectionClip matrix is simply another classical projection matrix, potentially built with different parameters. It doesn't magically clip anything; it's just that some combination of factors reduces the light color to zero outside the desired frustum.

\$\endgroup\$
  • \$\begingroup\$ "For clipping pixels outside the frustum, you can use a texture with the border mode enabled and a black border." I guess you intend this for avoiding artifact caused by clamping, but this is not my problem. Suppose I have a projector oriented toward Z+ (0,0,1) with a far plane at distance 1. If there is a plane at distance 2 point toward Z- (0,0,-1) the texture will be projected on it even if outside the frustum, am I wrong? \$\endgroup\$ – Heisenbug Jun 21 '14 at 18:11
  • \$\begingroup\$ Just for precision, I'm not using built-in unity projectors. There an object outside the frustum in simply not rendered. When it is rendered it uses an additional shading pass (the blending you suggested). I'm trying to avoid multipass and blending, optimizing it for mobile devices. \$\endgroup\$ – Heisenbug Jun 21 '14 at 18:21
  • \$\begingroup\$ @Heisenbug Ah, so you're talking about limiting the distance that the projector can project? You can do that as well; just check the z coordinate after multiplying by the projection matrix and doing the perspective divide, and discard or set the light color to zero if it's not between 0 and 1 (or -1 and 1, for GL-style matrices). As for culling objects, that would usually be done by intersecting the object's bounding box with the projector frustum. \$\endgroup\$ – Nathan Reed Jun 21 '14 at 19:40
  • \$\begingroup\$ yes, perhaps my question wasn't so clear. Btw thanks for your answer, I realized that my question didn't make much sense. As you said the 2 projection matrices probably only deserve the purpose to support different scaling for the textures. And the limit distance is implicitly handled without rendering the additional pass for objects outside the frustum. \$\endgroup\$ – Heisenbug Jun 21 '14 at 21:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.