Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3D geometry cull failure and light flickering at particular camera far / near values #55070

Open
roalyr opened this issue Nov 17, 2021 · 36 comments · May be fixed by #99986
Open

3D geometry cull failure and light flickering at particular camera far / near values #55070

roalyr opened this issue Nov 17, 2021 · 36 comments · May be fixed by #99986

Comments

@roalyr
Copy link

roalyr commented Nov 17, 2021

Godot version

3.4 stable

System information

Xubuntu 20.10 AMD 3020e, Vega3, GLES3

Issue description

In 3D at extremely low camera near and extremely high camera far values, geometry fails to be culled partially or completely when moving beyond camera far distance from the object. It also makes lights misbehave and flicker depending on the view angle.

Not critical but may be worth squashing. Can be avoided by using reasonable camera clipping planes distances.

Video demonstration of both effects

Steps to reproduce

Set camera_near >0.05, set camera_far 1000000 (maximum).
Place large visible object at camera_far distance from camera, move camera away. If bug reproduces - the object won't disappear or will flicker.

Place point light source at object location, make light distance >= camera_far, move camera away, while also looking at the source or away from it, observe the flickering.

Minimal reproduction project

My project release version that illustrates the bug

Suspected culprit for this bug may be a condition check at which fragments should be discarded (shader) or a piece of code the governs camera culling. Also it may be an issue related to floating point precision, or the code check that doesn't take into account camera_near that small.

The workaround for light-related part of the issue seems to be in using larger light source range range and/or size with steeper curves on small lights.

@Calinou
Copy link
Member

Calinou commented Nov 17, 2021

Related to godotengine/godot-proposals#3539.

@lawnjelly
Copy link
Member

Needs a minimum reproduction project in practice. As you say, there are a lot of things that can break down at extreme values, z buffers, shadow maps, frustum, bvh etc etc. So we need to reproduce it to identify the bug and fix.

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Here is a modified version of my project that illustrates the bug: https://github.com/roalyr/GDTLancer/releases/tag/v0.3-pre-alpha-bug-test

@lawnjelly
Copy link
Member

lawnjelly commented Nov 19, 2021

I can't seem to reproduce any problems here, Linux Mint 20, Intel HD Graphics 630. Maybe someone else can reproduce.

Ah correction, it does in your build, when running under wine, but not when running the zipped project from Godot.

Doesn't seem to occur when I make a release export the project myself for linux. I'll try a windows export.

No problems here with a windows 64 export running under wine from 3.4 RC3 (I just have those export templates to hand). So unless it's a 3.4 stable only bug, I'd double check it's using the right export templates, something like that could be possible?

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

The issue is there for me even when not exporting.

When flying past camera far distance it doesn't cull objects and when turning camera around light flickers. In fact original post video was made from editor, not an exported project.

@lawnjelly
Copy link
Member

lawnjelly commented Nov 19, 2021

I wonder if anything could be corrupt in your project? Maybe try deleting the .import directory and let it reimport, see if that helps.

I get no problems running it from the editor in 3.4 stable BTW, the RC3 was just for the export.

@Calinou
Copy link
Member

Calinou commented Nov 19, 2021

I made a minimal reproduction project for 3.x: test_far_distance.zip

Some observations:

  • High Camera far distance seems to be doing its job fine. I don't see anything particularly unexpected here, given the limits of 32-bit floating-point coordinates.
  • When the Camera far distance is 1,000,000, the near distance must not be set below 0.3126, or nothing will be drawn (I only see the background color instead). This may be worth investigating and fixing, since we can detect and clamp the near value according to the far value.

The video below demonstrates behavior with camera far distance 1,000,000 and near distance 0.3126. The "ship" moves at a speed that increases in an exponential-like fashion.

Epilepsy warning: Video contains rapid light flashes near the end.

far-distance-flicker.mp4

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Ignore source code that comes with that release. It has proper far plane setting and logarithmic depth. I'll provide a new one.

(Github took the most recent properly set up snapshot).

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Here, uploaded the version of source that corresponds to binary https://github.com/roalyr/GDTLancer/releases/tag/v0.3-pre-alpha-bug-test

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Another thing about why camera far plane might fail in culling may be related to origin rebase (see https://github.com/roalyr/GDTLancer/blob/master/Scripts/Ship.gd under func _physics_process(_delta)):

func _physics_process(_delta):
	
	# Testing origin rebase. Start this only at some point.
	# 10k seems like the optimal.
	
	var limit = 5000
	if self.translation.x > limit:
		self.translation.x = 0
		p.local_space.translation.x = p.local_space.translation.x-limit
	elif self.translation.x < -limit:
		self.translation.x = 0
		p.local_space.translation.x = p.local_space.translation.x+limit
		
	if self.translation.y > limit:
		self.translation.y = 0
		p.local_space.translation.y = p.local_space.translation.y-limit
	elif self.translation.y < -limit:
		self.translation.y = 0
		p.local_space.translation.y = p.local_space.translation.y+limit

	if self.translation.z > limit:
		self.translation.z = 0
		p.local_space.translation.z = p.local_space.translation.z-limit
	elif self.translation.z < -limit:
		self.translation.z = 0
		p.local_space.translation.z = p.local_space.translation.z+limit

But I honestly do not have any clue if that can even affect it.

Removing it didn't change a thing.

@lawnjelly
Copy link
Member

It is reproducing now with the new source (Note to others downloading, the source code is marked as PROPER_SOURCE_CODE not the source code, I redownloaded the first source code first time... 🙂 ).

Just out of interest what is different between these two builds?

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

The snapshot (most recent code) has things properly set at camera far being 500k, camera near 0.03, and all materials have logarithmic depths. And light distance is 10 M units.

@lawnjelly
Copy link
Member

lawnjelly commented Nov 19, 2021

Your ship in the new build is jumping around soon after losing the station, much like @Calinou 's sample, and kind of like the end scene from that movie 2001. So it does seem like you are prematurely just reaching the limits of float precision.

You may just need to shrink the scale of everything (a quick guess is, it's probably about 1,000,000 times too large scale for 32 bit float).
T9pnT5A

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Don't mind that, this is for the sake of fast velocities, they won't be reaching that much in the end and causing travel by shifting coordinate system only (which is kinda interesting effect on its own). I just need a dev ship to see things fast around the place.

It all boils down to FPS, origin rebase step distance and velocity.

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

Also when the far-near planes are withing working limits no matter what the light distance is it doesn't flicker.
When far-near planes are in extremes all light flicker no matter where you are.

@roalyr
Copy link
Author

roalyr commented Nov 19, 2021

@Calinou may want to implement that origin rebase code to see lights effects in their example without jitter.

@roalyr
Copy link
Author

roalyr commented Nov 22, 2021

By the way, for what reason is camera far capped at 1M?

@Zireael07
Copy link
Contributor

@roalyr: Floating point accuracy, I imagine.

@roalyr
Copy link
Author

roalyr commented Nov 22, 2021

FP error is relative, so I suspect it should be related to the far/near ratio more than absolute far. Isn't it so?

@Calinou
Copy link
Member

Calinou commented Nov 22, 2021

By the way, for what reason is camera far capped at 1M?

I guess nobody needed a far value above 1 million until now 🙂

A far value above 1 million is not really usable without a logarithmic depth shader anyway.

@roalyr
Copy link
Author

roalyr commented Nov 22, 2021

I... I feel like I need it.
I mean, I can override and recompile it myself, but... What if... And with logarithmic depth.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

Maybe the issue is with indirect lighting seeing how it appears only at certain angles (when you look towards the light source)?
Tried different settings for light baking and indirect energy, but it doesn't seem to be a case.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

So I have attached a script like this to every light source there is:

extends OmniLight

onready var light_hard_falloff = self.omni_range * 0.95

func _process(_delta):
	var light_loc = self.global_transform.origin
	var camera_rig_loc = get_node("/root/Container/Paths")\
		.camera_rig.global_transform.origin
	var distance = light_loc.distance_to(camera_rig_loc)
	#print(distance)
	if distance < light_hard_falloff and !self.visible:
		self.visible = true
		print("lights on")
	if distance >= light_hard_falloff and self.visible:
		self.visible = false
		print("lights off")

It does remove flickering of the light when some lights have to be culled due to range normally, but other lights still misbehave, they are culled when it is not necessary.

In example of my project, when I fly away from the star past the culling distance, the light from star is disabled, light on engine keeps working, but when I turn camera towards the star, the engine lights go off.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

So it is not enough to just disable the faraway light source, because close-by lightsources keep misbehaving still, when looking at faraway light source.
https://cdn.discordapp.com/attachments/212250894228652034/912691279941484614/simplescreenrecorder-2021-11-23_15.08.06.mp4

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

In fact, since it is the nearby light source that flickers, there is no need to bother with culling the faraway source of light, so the above script may not actually do anything consequential.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

Here is a visualization of how this flickering depends on movement speed (fraction of units - couple of units):
https://cdn.discordapp.com/attachments/212250894228652034/912696885339369472/simplescreenrecorder-2021-11-23_15.30.13.mp4

Angle of camera orientation doesn't affect frequency, only if the light flickers or not.
EDIT: light can flicker in any case no matter the camera orientation

This is 30 M units away from star light source (to local engine light source).

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

https://cdn.discordapp.com/attachments/212250894228652034/912698644514344990/simplescreenrecorder-2021-11-23_15.36.31.mp4

Triple the distance from star light source to engine light source makes flickering frequency lesser.

If I stop when engine light source works properly - it stays on.

Does it indicate floating point precision error somewhere in light mixer?

Note, this is an example with floating origin, so effectively, there is no jitter related to the physical position of the model from the initial origin, but the distance between scene light sources is ever increasing (93 M units in this example).

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

https://cdn.discordapp.com/attachments/212250894228652034/912706205007360060/simplescreenrecorder-2021-11-23_16.04.41.mp4

If I disable all of the light sources, and all of the models in the scene, this is what I get with extreme Far-Near ranges.
Specifically for this case i have used re-compiled Godot 3.4 with Far limit set to 100000000 (100 M units).
This now looks like some poor behavior of camera.

Also, for some reason, when I enable background stars which have DEPTH = 1.0; the model stops disappearing from the render, and only light misbehaves.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

https://cdn.discordapp.com/attachments/212250894228652034/912708971402493992/simplescreenrecorder-2021-11-23_16.18.17.mp4

I have also checked what happens when you instead of zooming out zoom in closer to the lit surface, and in fact flicker goes away.

So I can conclude this as such:
In case of extreme difference between Far and Near planes, small light sources seem to lose precision. They require camera to be close to the lit surface to work properly, otherwise they are not rendered.

Why was the flickering observed before? Maybe it is indeed due to floating point error which affected camera distance from the lit surface, and in essence it doesn't really matter at all in case of this bug.

Why does camera angle affect flickering? Most likely that is due to rotation off-setting the distance between camera and light source (in my case camera orbits around center, and light source is a bit further to the rear).

I suspect that ONLY Far-Near distances and light source size and distance from camera affect this bug.

@Calinou you can edit your test case in this way: make a static body, attach camera to it and make it zoom in-out locally from the lit object. Let the local lights be small. Keep camera Far and Near in their extremes and observe what happens when you zoom in and out your camera.

EDIT: also take into account light source intensity and if there is or no bigger light source that overlaps it, or if there is ambient light on. I suspect that mixing gets affected and somewhere something multiplies by 0.

@roalyr
Copy link
Author

roalyr commented Nov 23, 2021

https://cdn.discordapp.com/attachments/212250894228652034/912712421343367199/simplescreenrecorder-2021-11-23_16.31.09.mp4

This last clip illustrates how presence of overwhelming overlapping light source makes this bug go away.
With this all I have pretty much exhausted my clues and means to pinpoint the bug, which may in fact be not a bug but just the limitation of light processor, however it even works....

The workaround for light-related part of the issue seems to be in using larger light source range and/or size with steeper curves on small lights.

https://cdn.discordapp.com/attachments/218361207990648832/912720414797619200/simplescreenrecorder-2021-11-23_17.01.05.mp4

An example of workaround for light applied:
3 engine light sources
Range: 50
Attenuation: 200 (yes, a lot, I know)
Intensity: varies with engine thrust.

@roalyr
Copy link
Author

roalyr commented Aug 4, 2023

This should solve it altogether, but z-far is not adjustable in my commit because I don't know how to link editor settings (or camera settings):
roalyr@ddf5b01

P.S.: the same should be applicable to z-near clipping plane since it is parallel to camera, so there is no need to recalculate plane.normal component every time from the camera matrix (as far as i understood), so it should be precision-error safe?

This also should eliminate the workaround related to light, since I have not noticed any flickering or light culling so far.

@Zireael07
Copy link
Contributor

@Calinou or @YuriSizov can probably help with editor settings part

@YuriSizov
Copy link
Contributor

Since this is core, it cannot depend on editor settings. The best way would be to provide an argument to the method and pass some settings value from the caller.

@roalyr
Copy link
Author

roalyr commented Aug 4, 2023

I don't know the best way to implement it within Godot architecture, neither am I proficient in C++. It will take way too much time which i can't afford to spend on this issue anymore, unfortunately.

@KnIfER
Copy link

KnIfER commented Nov 25, 2023

the maximum renderer distance is 1000,000 meters ? when I set camera_far to 150,000 , the world is empty.

@lawnjelly lawnjelly modified the milestones: 3.5, 3.x Mar 1, 2024
@roalyr
Copy link
Author

roalyr commented Mar 2, 2024

Might be this issue (and solution): roalyr#15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants