• 3D
  • Better way to Handle First Person Weapon Clipping?

I've been trying to get an FPS camera to work for a while in Godot, and I've never been able to find/figure out a good solution to weapon clipping.

So far, I've seen the following suggested:

1: Use a raycast to animate the weapon into an idle position when against a wall. 2: Render the weapon in a different viewport 3: Scale the weapon down small enough that it sits within the player's collision shape.

So far all of these options have their problems. I really dislike option #1 as a matter of personal taste but it is also limiting to the size and positions of weapons you can have, because they have to fit inside the player's collision shape.

#2 seems to work, but I'm unable to get environment lighting to affect the weapon, or weapon effects to light the environment while they are rendered separately. This also seems like the most "correct" solution, even though it's a bit clunky to use.

Solution #3 seems to be able to get the best of both worlds, but there's definitely an issue with lighting effects on the weapon, as shadows will overtake it immediately, rather than gradually.

Does anyone know of any other solutions for something like this?

I've done some looking around, and found pretty much only these three solutions, but I haven't found many discussions about these drawbacks.

I think there is a flag on the material setting to recalculate the normals when doing non-uniform scaling. That should fix the lighting issue, and you are correct that is probably the best solution. The multiple viewport thing doesn't work as you can only have one WorldEnvironment in a scene, so the viewports will have different lighting and effects. The animation option does also work, but you might have to scale the model anyhow, unless the animation is really fast. Though this is more realistic in terms of not clipping into the wall.

Thanks for the reply @cybereality

The scaling for solution #3 is definitely uniform in this case, so I'm not sure the normals would be relevant, unfortunately. I believe the issue with that one is just that the object has to be so small to fit in the collision area completely, that the relative size of the rest of the world (and the shadows it casts) is so massive (it's also so close to the camera, that it will start/stop getting a shadow cast on it at inappropriate locations).

Can you explain how you are doing it? I'm not sure I totally understand.

So the player is essentially structured like:

KinematicBody -->Camera -->Weapon(MeshInstance) -->CollisionShape(Capsule)

Basically, I'm scaling the Weapon MeshInstance down way further than its actual size, and then moving it really close to the camera's origin. This make is look the proper size, while allowing it to be small enough to completely fit within the KB's CollisionShape, so because that Collider won't clip through walls, neither will the weapon.

The shadow problem is because, as far as the rest of the world is concerned, my rifle is about the size of a banana, and it's being held inside the player's body. So shadows that you'd expect to be cast only on the muzzle of the weapon (because you're supposed to be holding it out in front of you) would not show up until that shadow would envelop the player, which looks strange. Combined with movement, a somewhat hard shadow will fully envelop the weapon instantly, rather than gradually, which is what the video in the OP is meant to showcase.

I don't think there's a way to do this and avoid this shadow problem, because the lighting is technically working as intended here. It is casting a shadow over an object of its size appropriately, it's just that the scale is intentionally being made wrong to fit it in the collider.

Position the weapon vertically when near walls. You can also additionally scale and push it towards the camera a bit. When far from walls display it normally.

Thanks for the reply @xyz

As far as adjusting the way the weapon is held, I'm not a fan of this approach in this case, aesthetically. I'll see if I can make the scaling dynamically apply when pointing at a close surface. While that won't eliminate the lighting issue, maybe it can hide it somewhat.

Also: I was able to find: https://github.com/godotengine/godot-proposals/issues/956 Looks like this exact conversation has happened already, but maybe with some hope that there will be a solution somewhere in 4.x

You could make your weapon shader write custom depth values that are pushed/scaled towards the camera.

@xyz said: You could make your weapon shader write custom depth values that are pushed/scaled towards the camera.

This would work. However an easy thing to do without shader programming would be to place the gun with a higher render priority (like 1) then then disable the depth check. This can be done on the material flags with a Spatial Material and no code.

Not sure if this will work though @cybereality. The gun would still need depth test for its own geometry. I think it can't be done without shader intervention.

The only problem I see with this approach is shadow reception. When gun geometry passes through wall its shadow reception should be disabled. I'd do it by gradually blending in the second shadowless pass.

Oh, yeah, you're right. The gun would clip itself. I used this trick on Decay, but it was for fake decals on the graffiti, but they were flat planes so I didn't have to worry about overlapping geometry.

I believe this is why most AAA games use a separate camera and FOV for the gun. You could theoretically do that in Godot, however, the transparent viewport has a serious bug that makes it so the WorldEnvironment doesn't affect the second viewport. Meaning it does work, except the gun has completely different ambient lighting and no post-processing, so it looks bad.

But I guess it would depend on the game. If it were a retro shooter and you didn't need advanced lighting or post-processing, then you could do it in Godot right now.

Notice how the background is blurred but not the gun.

Hey guys,

After a bit more research, I found out that the default shadow atlas size in new viewports is set to 0 (for some reason).

Setting it to any positive value re-enables DirectionalLight shadows for objects rendered in that viewport (though I was under the impression that the Docs (https://docs.godotengine.org/en/stable/tutorials/3d/lights_and_shadows.html) imply that DirectionalLights aren't affected by shadow atlas.

So I whipped up a secondary viewport/camera setup, and set its shadow atlas size to 4800. This lets me set the culling mask to how I want it, and still get shadows. I think I can live without world/environment effects for now.

Thanks for the help!

For directional light, you can set the shadow atlas to 1. Higher numbers are only for omni/spot lights.

After a bit more research, I found out that the default shadow atlas size in new viewports is set to 0 (for some reason).

The reason for having the default atlas size set to 0 is that it allows creating a shadow atlas only when it's actually needed. An unused point light shadow atlas still takes up valuable memory and GPU time.

So I whipped up a secondary viewport/camera setup, and set its shadow atlas size to 4800.

The shadow atlas size property is raised to the next power of 2, which means you actually set it to 8192 (the default is 4096 on desktop). Consider this if you're running into performance problems.