Back to top

Game Maker Billboards z-Depth Issues May 20th

Gordon Oak June 6, 2019 0 comments

JP

Okay. I feel like this has probably been asked 100 times. I have two texture quads that are overlapping in my game world. The transparent area of the front is knocking the back one out completely. I imagine it’s because of the different blend modes.

I’m wondering if it could be draw order related.
Check this out:

So the green one occludes the purple one, but not the pink one?¯\_(ツ)_/¯But if I go and look through the pink towards the blue, I can’t see the green.

SP

@JP is alpha testing turned on?

JP

It is And I’m not using interpolated colors Maybe this is something that could be solved by a shader?I’m drawing all billboards with a shader already, so if I just need to modify that, that would be awesome, actually.

LS

@JP you need to sort the surfaces to draw them from back to front and you need to disable Z-testing.
I mean, Z-writing.

JP

Why disable z-writing, though?

LS

Z-testing should be there πŸ˜›
Let me see how I can explain that the best way,
Z-writing is used to cull objects that are rendered behind already drawn objects but alpha blending is rendered, at last, so you won’t need to cull objects behind it.

JP

Ah, okay.That makes sense.

LS

and if you sort alpha objects from back to front you don’t have to check if one transparent fragment is behind the other because that would never happen. Z-testing should however still be enabled so that alpha fragments are culled behind opaque fragments

JP

Thank you. I think I understand now.

LS

np πŸ™‚
I feel like I didn’t explain it well, but I can’t find proper words to explain it better :P(edited)but the problem you currently have, is that the green smoke is drawn first and it writes to the Z-buffer. After this, the purple smoke is drawn. Some purple fragments are overlapped by green fragments, and because the purple fragments are further away from the camera they are culled away by Z-testing

This problem doesn’t occur however with the pink smoke, because that’s drawn before the green smoke. This means that there isn’t any data written to the Z-buffer by the green smoke that can cull the pink smoke away. disabling Z-writing does partially fix this problem because the purple smoke wouldn’t be culled anymore by the green smoke, but you would get strange visual results.

Think about this scenario. If you have a transparent surface that’s almost completely opaque, the transparent surfaces behind that surface should barely be visible. If you don’t draw the almost opaque surface later than the surfaces behind it, it wouldn’t be able to dim the colors of the surfaces behind it. Instead, you would see the transparent surfaces behind it quite vividly as if it’s in front of the almost opaque surface. that’s why transparent surfaces always have to be drawn in a specific order, usually back-to-front, and in some cases when necessary from front-to-back (don’t worry about this scenario).

JP

Wow. Thanks for the thorough explanation. I still don’t have much experience with 3D so this stuff helps.
Okay. So what about opaque textures that have transparent pixels? Is that considered an opaque surface or a transparent one?

TS

There is no distinction on a per-texture basis. The transparent pixels will write to the z-buffer the same way the opaque pixels do. Anything behind the texture that is drawn after will check the z-buffer, and realize that something has already been drawn to this pixel before, and thus not draw there

Once you turn z writing off, you’ll have to depth sort manually.
So how do you sort them? Do you have a ds_priority that uses distance or something?

JP

//Sort Billboards
ds_priority_clear(billboards_opaque)
ds_priority_clear(billboards_transparent);

with(par_billboard)
{
    var _dist = point_distance_3d(position[0],position[1],position[2],
                                                                other.camera_tri[0],other.camera_tri[1],other.z[V.Current]);
    if(is_opaque)
        ds_priority_add(other.billboards_opaque,id,_dist);
    else
        ds_priority_add(other.billboards_transparent,id,_dist);
}

TS

You’re not touching z testing?

JP

Then to draw

while(!ds_priority_empty(billboards_opaque))
{
    with(ds_priority_delete_max(billboards_opaque))
        event_user(0);
}

gpu_set_zwriteenable(false);

while(!ds_priority_empty(billboards_transparent))
{
    with(ds_priority_delete_max(billboards_transparent))
        event_user(0);
}

TS

One thing though – If you draw billboards after everything else, and they’re manually sorted, you don’t have to turn off z writing

Just draw them back to front like you’re doing, and they won’t cut each other off.

Also, instead of getting the distance from billboard to camera (which uses square root and is slow), you can sort them by the dot product between the vector from the camera to the billboard, and the camera’s looking direction.

JP

Then the sphere is positioned correctly but cut off by the fire in front.

TS

Weird! The problem must be the depth sorting somehowCould the object’s position be different from where the sphere is drawn?

JP

I don’t think so.
No.
I’ll double check
Nope.
Just using the x,y and a z value
I’m gonna grab some lunch and think about it.

TS

Do all the objects have the same depth value?
Is it gm 1.4 or 2 btw

JP

2
Would depth matter? I’m not even using a normal draw event

TS

At least in 1.4, depth also altered the position of things you draw

JP

Let me set them all to 0
Nope, same issue

KP

Isn’t it caused by the 2 different priority lists? I assume the sphere is in the opaque one, so you render that first and then the transparent ones.

TS

If so, the sphere has written its depth to the z-buffer. And the fires will check the depth buffer even if they don’t write to it

KP

Yeah that’s true, the fire behind would get cut off by the sphere

TS

The opaque priority is superfluous though since they will be depth sorted without problem anyway. Unless they contain partly transparent pixels.

KP

Having 2 different lists seems still weird to me, just have one, sort them by depth from back to front, keep ztesting on and disable zwrite

JP

Yeah, I was following a guide and I had wondered about that. And I’ve tried drawing the sphere as both a transparent and opaque.

KP

You could also disable writing to the alpha channel since that’s not going to do anything anyways.Oh wait no, it makes sense to have two

JP

Does it?

KP

Yep and even the list names tell why so.
You use the opaque one with z writing, z testing and alpha testing enabled (threshold near to 1)(edited).
And the transparent one with z writing disabled, z testing enabled and with a lower alpha testing threshold.

TS

The opaque ones don’t need to be sorted at all. In fact it’s preferable to not sort them. Drawing back to front results in the maximum possible overdrawIdeally, everything should be drawn front to back to completely avoid overdraw, but that completely rules out transparency.

KP

But he could use it to render them in opposite direction

TS

But then you also have to weigh it against the cost of sorting them in the first place. Which I think is greater than just not sorting them.

JP

So disabling zwrite, enabling ztest, and setting alphtestref to <30 or so didn’t help. The sphere shows through the flame.

KP

Are you showing the full code to us?

JP

lol. No. There is TONS of code here.

KP

I mean the relevant parts of course. All gpu_do_something included.

JP

I think so. I could show you how I’m actually drawing the billboards.

///gmode7_billboard_draw
/// @param billboard
/// @param texture
/// @param {array} pos_xyz
/// @param {array} angle_xyz
/// @param {array} scale_xyz Though z doesn't actually do anything
/// @param color
/// @param alpha
/// @param <cull> doesn't draw if the object isn't in view of the camera
/// @param <lock_hori> rotate along the y axis to face the camera
/// @param <lock_vert> rotate along the x axis to face the camera
var _billboard = argument[0],
		_texture=argument[1],
		_pos = argument[2],
		_angle = argument[3],
		_scale = argument[4],
		_color=argument[5],
		_alpha=argument[6],
		_cull = argument_count > 7 ? argument[7] : true,
		_lock_hori = argument_count > 8 ? argument[8] : true,
		_lock_vert = argument_count > 9 ? argument[9] : false,
		_cx,_cy,_horidir,_vertdir;
		
if(_cull && !gmode7_in_view(_pos[0],_pos[1])) exit;


with(GM7)
{
	_cx  = x - lengthdir_x(camera_distance, yaw);
	_cy  = y - lengthdir_y(camera_distance, yaw);
	_horidir = _lock_hori ? point_direction(_pos[0], _pos[1], _cx, _cy)-90 : 0;
	var _dist = _pos[2]-z[V.Current];
	_vertdir = _lock_vert ? angle_difference(0,point_direction(0,0,point_distance(_pos[0],_pos[1],_cx,_cy),_dist)) : 0;
}

var _matrix = matrix_combine(matrix_get(matrix_world),
														 matrix_build_scale(_scale[0],_scale[1],1), //We don't use the Z scale for billboards
														 matrix_build_rotation(_angle[0]-_vertdir,_angle[1],_angle[2]+_horidir),
														 matrix_build_translation(_pos[0],_pos[1],_pos[2]));
matrix_set(matrix_world, _matrix);

shader_set(shd_billboard);
var _uniform = shader_get_uniform(shd_billboard, "u_vColour");
shader_set_uniform_f_array(_uniform,color_to_rgba_array(_color,_alpha));
vertex_submit(_billboard,pr_trianglestrip,_texture);
shader_reset();
matrix_set(matrix_world,matrix_build_identity());

KP

What does enabling z test mean? Do you have it enabled globally?

JP

while(!ds_priority_empty(billboards_opaque))
{
    with(ds_priority_delete_max(billboards_opaque))
        event_user(0);
}
gpu_set_zwriteenable(false);
gpu_set_ztestenable(true);
gpu_set_alphatestref(40);
while(!ds_priority_empty(billboards_transparent))
{
    with(ds_priority_delete_max(billboards_transparent))
        event_user(0);
}

(and for the record, I don’t have any opaque billboards right now)

KP

What does your perspective projection setup look like? What’s the znear?

JP

Okay. It’s a draw order issue, thanks so much everyone!

Author avatar

Gordon Oak

https://gamebin.tv
Gordon Oak has been a Software Consultant and Webmaster for over 10 years in the Gaming and Web Applications space. Currently enamored with Artifical Intelligence.

Post a comment

Your email address will not be published. Required fields are marked *

Explore GAMEBIN.tv

    Indie Video Game Studios

    Indie video game studios are usually run by a small number of people, and...

    Unread β€” 3 minute read

    Video Game Publishers

    What do Video Game Publishers do? Real publishers can offer a wide range of...

    Unread β€” 3 minute read

    Game Design & Development Courses

    Game Design & Development courses provided by GAMEBIN.tv are one of a kind, ad-free,...

    Unread β€” 1 minute read

    The Gaming Industry

    What would we say if we could say anything about the gaming industry? Look...

    Unread β€” 1 minute read

    Making Your First Game

    Making a video game for the first time can seem just as hard as...

    Unread β€” 8 minute read