A few months ago I posted to the answers site, but never got a concrete answer. hopefully someone here can help me.
Consider the following example image:
Here we have an irregular shaped mesh (although guaranteed to be flat). I want to write a shader that will render the mesh not where it actually is in the scene, but projected onto the surface below (as shown by the green arrows).
I thought about using a rendertexture to capture the mesh render, then using that in a projector; but I feel that as the mesh grows in size, the quality would reduce since the rendertexture is of a finite size. I could be wrong though.
A real world use case is to visualize a selection area that is not guaranteed to be a perfect rectangle, and needs to relay to the user the contour of the terrain (and other terrain decorations) in the selection area. Another use case woudl be to add terrain decals that are based off of dynamic 3D objects as opposed to flat images.
How would you go about this? There are games that I see this effect in (Cities Skylines has this effect when placing roads), but I haven’t a clue regarding the best way to accomplish this. Looking for any and all solutions/ideas/clues.
This is very cool, but I am almost certain it is not a mesh. The reason being that the resolution is far far far too high to be a mesh; it even conforms to the leaves on trees (in the cities skylines example).
I had weird results with projectors last time I tried to use them for something like this, but I gave it another go today and finally figured out a way to do what I need (at least the core concept). Here is the result:
The trick is to do the following:
Create a gameobject with both a projector AND a camera. have it face the direction you want (down)
Create a material for the projector, and give it the correct shader. any projection shader will do, but I made a new one that doesn’t treat it as light or shadow
Create a RenderTexture. Give it the settings you want.
Create a layer for the object you want to project. I called mine ProjectionSample. set the object you want to project’s layer to this.
set the layer mask for both the projector and the camera to be this layer.
set the texture for the camera to be the rendertexture (so that it renders there instead of screen)
set the texture of the projector material to the same rendertexture.
ensure the camera and the projector are both set to orthographic, and the orthographic size must match in both.
make sure the camera’s clear flag is set to solid color, and that the color is black.
This is basically all you need. As an additional step, I wrote a small script to dynamically change the size of the projector and camera combo, and position to match that of the object being projected (to be placed on the camera/projector combo gameobject):
using UnityEngine;
using System.Collections;
public class DecalTracker : MonoBehaviour {
public GameObject targetObject;
private Renderer targetRenderer;
private Projector projector;
private Camera camera;
void Start() {
targetRenderer = targetObject.GetComponent<Renderer>();
projector = GetComponent<Projector>();
camera = GetComponent<Camera>();
}
void Update () {
Bounds targetBounds = targetRenderer.bounds;
Vector3 newPosition = targetBounds.center;
newPosition.y += targetBounds.extents.y + 100;
transform.position = newPosition;
// find largest side
float size = (targetBounds.extents.x > targetBounds.extents.z) ? targetBounds.extents.x : targetBounds.extents.z;
// add 1 to prevent possibility of render reaching edge of texture and giving stretched results
projector.orthographicSize = size + 1;
camera.orthographicSize = size + 1;
camera.farClipPlane = targetBounds.size.y + 100;
}
}
This is far from perfect, but it is a good start in case anyone else needs something similar.
EDIT: If you want the texture to be splatted on the surface exactly; just change the Blend mode to SrcAlpha OneMinusSrcAlpha. also ensure the background color of the camera has full transparency in addition to being black.