- Home /
Disable Frustum Culling?
Is there a way to disable frustum culling on specific objects? Or perhaps disable it entirely?
I'm displacing the vertices in my mesh in a vertex shader. They are displaced to the extent that the original mesh (on the CPU side) is out of the view frustum, whereas the displaced vertices (on the GPU side) ARE in the view frustum. This causes the mesh to suddenly stop rendering and disappear, which is obviously not the desired behavior.
I tried solving this by overriding the OnBecameInvisible() method and forcing the renderer to be enabled, but that did nothing. (I had thought that the frustum culling disabled the renderer, but that is not the case.)
I wish the isVisible field on the renderer was able to be set. I feel that would solve this issue. Any other ideas?
Update: I thought about forcing the bounds on the renderer to be the same size as the maximum displacement, but the bounds property is read-only too.
I then tried having a second camera in the scene that is static and moved back enough to always have the displaced mesh in view, but that didn't work either. Apparently each camera does it's own frustum culling, which makes sense.
Answer by Lemon · Apr 14, 2011 at 04:32 AM
I have also had issues with the frustum culling taking place too early / incorrectly. When using the exact same code, the frustum would cull early if the Initialization of the GameObject took place in my Update() call rather than Start(). Very strange. I changed the bounds size to be extremely large and less culling occurred, but it was still bad. When I moved the bounds center to the center of the frustum, it worked every time. To move the frustum I placed the following code at the end of my Update() - after the mesh modification:
Transform camTransform = Camera.main.transform;
float distToCenter = (Camera.main.farClipPlane - Camera.main.nearClipPlane) / 2.0f;
Vector3 center = camTransform.position + camTransform.forward * distToCenter;
float extremeBound = 500.0f;
MeshFilter meshFilter = frontRenderer.GetComponent<MeshFilter>();
meshFilter.sharedMesh.bounds = new Bounds (center, new Vector3.one * extremeBound);
Obviously if you need the bounds for anything else you'll run into problems and will want to make a backup before you force the change.
Answer by H-Alex · Sep 12, 2017 at 08:57 AM
I'm pretty sure the best solution here is to use the cullingMatrix (the matrix used for culling only) which should normally be equal to projection * worldToCameraMatrix.
I'm exactly in your case changing vert position in a shader and I wrote this script :
public class DisableFrustrumCulling : MonoBehaviour
{
private Camera cam;
void Start()
{
cam = this.GetComponent<Camera>();
}
void OnPreCull()
{
cam.cullingMatrix = Matrix4x4.Ortho(-99999, 99999, -99999, 99999, 0.001f, 99999) *
Matrix4x4.Translate(Vector3.forward * -99999 / 2f) *
cam.worldToCameraMatrix;
}
void OnDisable()
{
cam.ResetCullingMatrix();
}
}
What is does is create a cullingMatrix equivalent to the culling matrix that would be built by Unity with a camera moved back from your actual point of view and a gigantic orthogonal frustrum (you could use a perspective projection, it's equivalent as soon as all objects are in the frustrum).
When you want to enable frustrum culling again, you disable the script.
The culling$$anonymous$$atrix has been introduced around Unity 5.4. He asked the question in Dec 2010 around that time we had Unity version 2.6
I do hope that if he's still working on his software he did upgrade Unity. If he's not maybe this will help someone with the same problem with recent version of Unity. This is the main post when doing a google search on the subject, so I'm sure it's worth keeping this up to date.
Well, this answer hub is for everyone and it just helped me to solve a very annoying unity behaviour.
Best solution for completely disabling culling without dropping a script on each GameObject in the whole game.
For projects using SRP, OnPreCull is deprecated but there's a slightly similar event. Here is what worked for me:
using UnityEngine;
using UnityEngine.Rendering;
public class DisableCamCull : $$anonymous$$onoBehaviour
{
private void Start()
{
RenderPipeline$$anonymous$$anager.beginCameraRendering += RenderPipeline$$anonymous$$anager_beginCameraRendering;
}
private void RenderPipeline$$anonymous$$anager_beginCameraRendering(ScriptableRenderContext context, Camera camera)
{
Camera.main.culling$$anonymous$$atrix = $$anonymous$$atrix4x4.Ortho(-99999, 99999, -99999, 99999, 0.001f, 99999) *
$$anonymous$$atrix4x4.Translate(Vector3.forward * -99999 / 2f) *
Camera.main.worldToCamera$$anonymous$$atrix;
}
}
Just got to know we can use Separate matrix for culling, which doesn't have to be equal to projection. It's awesome, Thanks a lot mate.
Thanks for posting that! Helped me quite a lot for a current problem =)
Answer by EeroH · Dec 25, 2012 at 05:16 PM
After fighting with a similar issue (displacement in the vertex shader), I found a possible solution to change the bounds: Replace your model's MeshRenderer with SkinnedMeshRenderer, which is actually ment to be used with animated meshes that have larger bounds than the mesh itself.
One way to get a SkinnedMeshRenderer component automatically generated for your mesh, is to create one (useless) bone/armature parented to your mesh in your 3d modelling software. Then in Unity, import the rig for the model as well (you don't need to import animations, at least not in Unity 4). When you have the model in the project hierarchy, you can even disable the animator component and the armature/bone gameobject (everything animation-related excess except for SkinnedMeshRenderer itself. Deleting those seems to cause problems, though). There may be an other way to create a proper SkinnedMeshRenderer, but I don't know yet if it's possible just by scripting.
Now, the trick is that SkinnedMeshRenderer has a localBounds member that can be changed. This is because an animated model may move/change inside its boundaries. Supposing that you know the maximum bounds (or maximum displacement as you mentioned) for your model, you can initialize the renderer with proper bounds once (in OnEnable(), for example):
// Get the SkinnedMeshRenderer component
skinnedMeshRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
// Create and set your new bounds
Bounds newBounds = new Bounds(myMaxBoundsCenter, myMaxBoundsSize);
skinnedMeshRenderer.localBounds = newBounds;
As a result, the rendering will use these bounds for frustum culling, and you don't have to worry about the bounds after initializing them once. A nice thing about SkinnedMeshRenderer is also that it shows the actual AABB bounding box in the editor, so it's easy to see if the runtime-set boundaries are correct.
This solution took a while to come up with, but at least so far it has worked like a charm.
Very comfortable solution! It's also possible to just add a Skinned $$anonymous$$esh Renderer component to the object in Unity (don't forget to remove the original $$anonymous$$esh Filter and $$anonymous$$esh Renderer components) and then assign the mesh to the corresponding field. Only thing to keep in $$anonymous$$d is that meshes must be named in a recognizable way to be able to reassign them in the editor. And also Skinned $$anonymous$$esh Renderers won't be batched as far as I know.
Answer by allenwp · Dec 20, 2013 at 02:25 PM
Lemon, your answer looks pretty close, but doesn't work out of the box for me. I just made a blog post answering this question:
http://allenwp.com/blog/2013/12/19/disabling-frustum-culling-on-a-game-object-in-unity/
// boundsTarget is the center of the camera's frustum, in world coordinates:
Vector3 camPosition = camera.transform.position;
Vector3 normCamForward = Vector3.Normalize(camera.transform.forward);
float boundsDistance = (camera.farClipPlane - camera.nearClipPlane) / 2 + camera.nearClipPlane;
Vector3 boundsTarget = camPosition + (normCamForward * boundsDistance);
// The game object's transform will be applied to the mesh's bounds for frustum culling checking.
// We need to "undo" this transform by making the boundsTarget relative to the game object's transform:
Vector3 realtiveBoundsTarget = this.transform.InverseTransformPoint(boundsTarget);
// Set the bounds of the mesh to be a 1x1x1 cube (actually doesn't matter what the size is)
Mesh mesh = GetComponent<MeshFilter>().mesh;
mesh.bounds = new Bounds(realtiveBoundsTarget, Vector3.one);
Thanks. Ins$$anonymous$$d, I used the solution of the comment in the link. Just enlarge the bounds of mesh. Simplest and works enough to me now.
float boundwidth = 500.0f;
model$$anonymous$$esh.bounds = new UnityEngine.Bounds(new Vector3(0,0,0), new Vector3(1,1,1) * boundwidth);
Answer by soulburner · Mar 01, 2017 at 03:47 PM
Spent 2 days trying to solve this problem.
And I found the solution!!! The key is:
camera.cullingMatrix
My final solution is:
public void OnPreCull()
{
_camera.ResetCullingMatrix();
Matrix4x4 m = _camera.cullingMatrix;
m.SetRow(0, m.GetRow(0)*0.5f);
m.SetRow(1, m.GetRow(1)*0.5f);
_camera.cullingMatrix = m;
}
Don't ask me why I'm multiplying two rows by 0.5f. I don't know how that matrix works, so I had to experiment :)
Your answer
Follow this Question
Related Questions
Modify water displacement 0 Answers
nvidia 3dvision 3 Answers
GPU at 15FPS, please help 0 Answers
GPU spikes from SubmitVBO 0 Answers
Character animation smooth on old GPU, strobing on new GPU 0 Answers