Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Texture a mesh based on multiple cameras/projections.

Discussion in 'Shaders' started by krakauer, Apr 7, 2019.

  1. krakauer

    krakauer

    Joined:
    Oct 18, 2015
    Posts:
    2
    Hello!

    I'm trying to get an effect working in my game but I'm a bit at a loss of how to proceed next. The idea is that there are multiple cameras (really just points with a projection matrix) that are placed around the scene. Any fragment on a mesh that is visible from any camera is colored white, and any fragment not visible is colored black. The idea would be to use the resulting black and white texture as a mask to do some other cool shader effects.

    The projections don't move while the scene is rendering so it's fine if it's expensive if I only have to do it once. This would be ideal really, since I'd like to support as many projections as possible.

    I have it working with a single camera (image attached), but I'm not sure how to extend it to more than one. I've looked into possibly passing in an array of matrices, but it doesn't really seem to fit (although I may be doing it terribly wrong).

    I've pasted my current shader below. The array only has a single camera in it at the moment.

    Any ideas are appreciated! Thank you!

    Code (CSharp):
    1. Shader "Custom/Projective Mapping" {
    2.    
    3.     Properties {
    4.         _MainTex ("Texture", 2D) = "black" {}
    5.         _WhiteTex ("Texture", 2D) = "white" {}
    6.         _MatricesLength ("Matrices Length", Int) = 0
    7.     }
    8.  
    9.     SubShader {
    10.         Pass {
    11.             CGPROGRAM
    12.  
    13.             #pragma target 3.0
    14.  
    15.             #pragma vertex MyVertexProgram
    16.             #pragma fragment MyFragmentProgram
    17.  
    18.             #include "UnityCG.cginc"
    19.  
    20.             sampler2D _MainTex;
    21.             sampler2D _WhiteTex;
    22.  
    23.             uniform float4x4 _ProjectionMatrices[100];
    24.             uniform float4 _CameraPositions[100];
    25.  
    26.             uniform int _MatricesLength = 0;
    27.  
    28.             struct VertexData {
    29.                 float4 position : POSITION;
    30.                 float3 normal : NORMAL;
    31.                 float2 uv : TEXCOORD0;
    32.             };
    33.  
    34.             struct Interpolators {
    35.                 float4 position : SV_POSITION;
    36.                 float3 normal : TEXCOORD0;
    37.                 float2 texCoord : TEXCOORD1;
    38.                 float4 projCoords : TEXCOORD2;
    39.                 float4 worldPos : TEXCOORD3;
    40.             };
    41.  
    42.             bool inRange(float2 uv) {
    43.                 return uv.x >= -1 && uv.y >= -1 && uv.x <= 1 && uv.y <= 1;
    44.             }
    45.  
    46.             Interpolators MyVertexProgram(VertexData v) {
    47.                 Interpolators i;
    48.                 i.normal = UnityObjectToWorldNormal(v.normal);
    49.                 i.texCoord = v.uv;
    50.  
    51.                 i.worldPos = mul(unity_ObjectToWorld, v.position);
    52.                 i.projCoords = mul(_ProjectionMatrices[0], v.position);
    53.                 i.position = UnityObjectToClipPos(v.position.xyz);
    54.                 return i;
    55.             }
    56.  
    57.             float4 MyFragmentProgram(Interpolators i) : SV_TARGET {
    58.                 float2 finalCoords    = i.projCoords.xy / i.projCoords.w;
    59.                 float4 vTexColor      = tex2D(_MainTex, i.texCoord);
    60.                 float4 vProjTexColor  = tex2D(_WhiteTex, finalCoords);
    61.                
    62.                 i.normal = normalize(i.normal);
    63.                 float3 viewDir = normalize(_CameraPositions[0] - i.worldPos);
    64.                 if (dot(viewDir, i.normal) < 0) {
    65.                     return float4(0,0,0,0);
    66.                 }
    67.  
    68.                 if (i.projCoords.z > 0.0)
    69.                 {
    70.                     if(inRange(finalCoords)) {
    71.                         return saturate(vTexColor + vProjTexColor);
    72.                     }
    73.                     return float4(0,0,0,0);
    74.                 }
    75.                 return float4(0,0,0,0);
    76.             }
    77.  
    78.             ENDCG
    79.         }
    80.     }
    81. }
    82.  
     

    Attached Files:

  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    I mean, an array is kind of the way to go about this. I'm not sure how your calculating the projection matrices, but you should be able to pass the world position from the vertex to the fragment, then apply each view projection matrix to that world position. Exit out of the loop on the first "success".

    If you want to have this be something you only do once per change of geomerty and/or "cameras", you'd have to bake the mask into a texture like lightmaps, which requires unique per triangle UVs and texture space rendering. Or you can bake the projections into a 3D texture if some of the geometry is dynamic.

    You might also look at how deferred shading works, or deferred decals. You project onto a depth texture rather than the actual geometry.
     
  3. krakauer

    krakauer

    Joined:
    Oct 18, 2015
    Posts:
    2
    Thank you! That totally worked, I think I just needed someone to tell me I was on the right track. :)

    Also, thank you for the pointers of what to look into for baking. I'm definitely going to do that in the future to make this more performant.