3D When using a 3D camera all transparency is filled in with the room background color

B

Bayesian

Guest
All my sprites and primitives' alpha are being filled in with what ever the background color is. Deleting or making the background transparent isn't an option because then transparent areas will get smeared with pixels that don't clear.

How can I fix this?
 

FredFredrickson

Artist, designer, & developer
GMC Elder
Hehe, nice! I bet it's come a long way since then.

Someday I'd love to revisit some of those old 3D games I'd made - either to remake them in GMS2 with my current knowledge, or to take them into another game engine entirely - but there are only so many hours in the day, unfortunately... and I [try to] run a business during the day. :D
 
B

Bayesian

Guest
can you give a screenshot so we can be sure of what you mean?
Here's a screen shot. I made the background layer in the room red so you can very clearly see the problem. The problem still clearly exists no matter the color though. The trails and rain are primates, only te rain has a texture. The blue player is a sprite and the grey enemies are all draw calls which is what I was going to switch to before I realized the actual problem.



You have to draw the transparent stuff after the stuff you want to show through it. The draw order is hugely important in 3D.
I could be wrong but this is my understanding of what my draw order should be in my project based on creation order, what should I change?
  1. Room
    1. Tile layer(for checkered background)
    2. Instances layer
    3. Mangers layer
      1. Create oGame
    4. Background layer(red)
  2. oGame
    1. Create
      1. Set tile data
      2. Create oRain on instances layer
      3. Create Platforms on instances layer
      4. Create oPlayer on instances layer
        1. Create trail object
      5. Create 3D camera
    2. Step
      1. Create oEnemy on instances layer
        1. Create trail object
 
what is the checkerboard pattern in your screenshot?

EDIT: don't you have an object that controls the order in which things are drawn? Also, are you just pointing the camera downard (toward +z)?
 
G

guyinboots

Guest
If your using gms2 do this in create event to fix that problem

gpu_set_alphatestenable(true);
gpu_set_alphatestref(0);

This will disable anything with a an alpha value of 0 or less from drawing, so weird borders around objects that are just sprites will go away
 

Bart

WiseBart
As said above, make sure to draw transparent stuff last so it gets drawn above everything.
Also, before doing that, set z-writing to false using gpu_set_zwriteenable and z-testing to true using gpu_set_ztestenable.
Lastly, you might want to turn off culling using gpu_set_cullmode so both sides of transparent faces are visible.
Code:
// Start of translucent drawing
gpu_set_zwriteenable(false);
gpu_set_ztestenable(true);            // set to true for non-translucent models as well
gpu_set_cullmode(cull_noculling);
 
Last edited:
B

Bayesian

Guest
Sorry for the ridiculously late reply guys,
what is the checkerboard pattern in your screenshot?
Its a tile set that I set programmaticly, sorry for not mentioning that.
EDIT: don't you have an object that controls the order in which things are drawn?
I wasn't aware I could do that. How would I do that? Is it different from depth sorting?
Also, are you just pointing the camera downard (toward +z)?
My understanding is that its pointing toward -z, increasing the depth moves it closer to the camera and my camera's look at xup yup zup is 0,-1,0
gpu_set_alphatestenable(true);
gpu_set_alphatestref(0);
I already experimented with gpu_set_alphatestenable, set to true it changes the red corners into a jagged red outline
gpu_set_alphatestref(0) doesn't do anything. even when I use both
As said above, make sure to draw transparent stuff last so it gets drawn above everything.
Also, before doing that, set z-writing to false using gpu_set_zwriteenable and z-testing to true using gpu_set_ztestenable.
Lastly, you might want to turn off culling using gpu_set_cullmode so both sides of transparent faces are visible.
gpu_set_zwriteenable(false) seems to reverse my draw order, I had to remove the checkered tile background to see anything and the trails draw on top of the player or enemies sprite even though I draw them before calling draw_self also my 3D platforms are doing this, I changed the room background layer color so your eyes don't bleed.
 
Last edited by a moderator:
I think it is a good idea in pretty much any 3d game to take over completely the drawing order from gamemaker. In other words, in one controller object's draw event, you do all of the drawing for your 3d scene. This way, there can be no confusion about the order in which things are drawn.

Draw your background stuff first. Then draw foreground stuff. If you have partially transparent stuff you are drawing in the foreground, you must do one of two things... either draw them in order from greatest depth (z distance in camera space) to lowest depth, or else enable alpha testing (which simply won't draw fragments below a certain alpha). Anthing that is partially transparent should probalby be drawn after all opaque stuff has been drawn. When drawing anything fully opaque, make sure zwriting is turned on. Also, in most cases you'll want to turn on backface culling for anthing that can have faces that could point away from the camera.
 

Bart

WiseBart
gpu_set_zwriteenable(false) seems to reverse my draw order, I had to remove the checkered tile background to see anything and the trails draw on top of the player or enemies sprite even though I draw them before calling draw_self also my 3D platforms are doing this, I changed the room background layer color so your eyes don't bleed.
It doesn't actually reverse the draw order. Although it might seem that way.
Did you re-enable z-writing after drawing the transparent stuff? It should still be enabled for all other drawing.
 
B

Bayesian

Guest
Did you re-enable z-writing after drawing the transparent stuff?
Ok I didn't understand that I was supposed to be switching it back and forth, check out my code below.
In other words, in one controller object's draw event, you do all of the drawing for your 3d scene.
Ok I have this partially set up, I can't figure out how to control the room background and tile layer's draw order with the method I'm using.

This is at the beginning of oGame

Code:
gpu_set_zwriteenable(true)
gpu_set_ztestenable(true)
gpu_set_alphatestenable(true)
gpu_set_cullmode(cull_noculling);
gpu_set_alphatestref(0);
This is my oDraw controller

Code:
gpu_set_zwriteenable(false)
with(oPlayer){
    draw_self()
}
with(oEnemy){
    draw_self()
}
with(oTrail){
    if(abs(owner.vx)>owner.vxTemp||abs(owner.vy)>-owner.vyJump){
        trailAlpha = Approach(trailAlpha,0.5,0.01)
    }else{
        trailAlpha = Approach(trailAlpha,0,0.01)
    }
   
    if(trailAlpha>0){
        DrawTrail(16,48,c_white,-1,true,trailAlpha)  
    }else{
        ArrayTrail = 0
    }
}
with(oRain){
  //I have to cut this because its a marketplace asset
}
gpu_set_zwriteenable(true)
The problem now is that I can't see anything drawn with oDraw. But at least my platforms draw correctly.


Removing the tile set like last time reveals that everything is being drawn under it, though misaligned, I didn't change my sprite's origin and I just copied their draw code into oDraw's draw event

 

Bart

WiseBart
That's almost right :).
But the order should really be: draw all stuff with no transparent textures, disable z-writing, draw all stuff with transparent textures, next re-enable z-writing so drawing happens correctly again on higher layers or in later draw events (Draw End, Draw GUI, ...)
Here's how it should look in the code:
Code:
with(oPlayer){
   draw_self()
}
with(oEnemy){
   draw_self()
}
with(oTrail){
   if(abs(owner.vx)>owner.vxTemp||abs(owner.vy)>-owner.vyJump){
       trailAlpha = Approach(trailAlpha,0.5,0.01)
   }else{
       trailAlpha = Approach(trailAlpha,0,0.01)
   }
 
   if(trailAlpha>0){
       DrawTrail(16,48,c_white,-1,true,trailAlpha)
   }else{
       ArrayTrail = 0
   }
}
gpu_set_zwriteenable(false)
// Draw everything with transparent textures between these two calls to gpu_set_zwriteenable.
with(oRain){
  //I have to cut this because its a marketplace asset
}
with(some_other_transparent_stuff) {
  // Draw me
}
gpu_set_zwriteenable(true)
 
B

Bayesian

Guest
But the order should really be: draw all stuff with no transparent textures, disable z-writing, draw all stuff with transparent textures, next re-enable z-writing so drawing happens correctly again on higher layers or in later draw events (Draw End, Draw GUI, ...)
That's exactly what I did, oPlayer and oEnemy all have transparency in their sprites and oTrail is an array of semi transparent primitives. The only things that don't have transparency are the platforms which are fine and the tile set which now covers everything for some reason which is my only current problem. I figured out the misalignment was my fault and was just a depth sorting error. Here's my current room layer setup.
Code:
Instances 0
Managers -100
Tiles -200
Background -300
 

Bart

WiseBart
That's exactly what I did, oPlayer and oEnemy all have transparency in their sprites and oTrail is an array of semi transparent primitives. The only things that don't have transparency are the platforms which are fine and the tile set which now covers everything for some reason which is my only current problem. I figured out the misalignment was my fault and was just a depth sorting error. Here's my current room layer setup.
Code:
Instances 0
Managers -100
Tiles -200
Background -300
Indeed, your code was right. My mistake there..
To continue on your current issue, in GM's Room Editor, +z points into the screen and -z points out of the screen. That's also the order in which the layers are drawn: from +z to -z (or higher depth to lower depth).
So it seems like your Tiles layer is actually closer than the Instances and Managers layers.
That would explain why it covers everything (assuming you don't have the background set).
 
B

Bayesian

Guest
To continue on your current issue, in GM's Room Editor, +z points into the screen and -z points out of the screen. That's also the order in which the layers are drawn: from +z to -z (or higher depth to lower depth).
This is extremely confusing. +z gets closer to the screen as it should yet high z gets drawn first then overwritten by things that are supposed to be behind it since they are drawn after.

What exactly am I expected to do here? Point my camera the other way and invert the z and x axis?

So it seems like your Tiles layer is actually closer than the Instances and Managers layers.
Closer to the camera? I set up my camera the way I thought it was supposed to work, pointing down -z. So the tile layer is 2nd furthest from the camera to the background layer. Besides my player, enemies and platforms are all on the instances layer, yet only the platforms show
That would explain why it covers everything (assuming you don't have the background set).
I've had the background set this entire time and never disabled it.
 

Bart

WiseBart
This is extremely confusing. +z gets closer to the screen as it should yet high z gets drawn first then overwritten by things that are supposed to be behind it since they are drawn after.
The manual has a nice image on this on the layer_depth page.
As you can see there, when looking downward from the camera, depth values (or z values) become more positive. If you now apply the same logic to your own layer setup, you notice that drawing happens in the following order:
Code:
Camera
 v v
Instances 0        // Drawn first/below everything
Managers -100
Tiles -200
Background -300    // Drawn last/on top of everything
 ^ ^
Screen
I added the camera to show that you're actually looking towards -z here.

But here's the catch. Although you have inverted the layers' depth/z values and setup the camera to correspond to that, the layers are still drawn from highest (most +) depth to lowest (most -) depth.
Most important is that each layer's contents are drawn and then that layer is finished.
It's as if you would draw that layer's contents to a surface and immediately draw that surface to the screen (or the 'back buffer', I guess). Then the next layer (more negative z/depth) is drawn and, once again, drawn to the screen. The order in which that happens is fixed and is from highest depth to lowest depth.
In fact this means that depth comparisons occur between primitives on the same layer but not between primitives on different layers (because of the above).
So it's best to indeed point the camera to +z, "into the screen", and then also change the depth (i.e. draw order) of the layers.
Another way would be to put everything onto one (instance) layer, but then you loose the ease of use for layers like the Tiles layer.

Admittedly, 3D in GMS does get a bit confusing when combined with layers :).
I've had the background set this entire time and never disabled it.
Not sure about this, though..
 
Last edited:
Top