I've got a bit of a problem with transparency. I've talked to Misu and he suggested writing my own shader for dealing with these stuff but I would like to ask if there's a direct way of doing it, maybe I'm not using some gpu_set functions properly?
View attachment 18848
There's currently 2 issues: overlapping triangles in the torus not stacking properly and the terrain behind the cube not rendering at all but just showing the transparent water behind it.
I use the following 3 setters:
gpu_set_zwriteenable(true);
gpu_set_ztestenable(true);
gpu_set_cullmode(cull_counterclockwise);
while the models have the alpha attribute set to 0.5 in the vertex buffer.
I'm gonna preface this rather bluntly - transparency in 3D is a pain in the butt. There's not really one fits-all solution, but I can suggest something to help out.
Using your own shader *might* help, but it's really not necessary and is likely rather complicated (I imagine you'd have to essentially implement
Order-Independent-Transparency, which is unlikely to be a simple process - especially in GameMaker).
Basically, this all comes down to render order and z-write.
In general, you want to render opaque objects before anything else - and preferably things closer to the camera before things further away (so hidden pixels are skipped - this is not super important when your starting out or if you just target desktop platforms, but if you have a particularly complex fragment shader, it can be a good optimisation)
Then, you want to render transparent stuff after. This will stop the transparent stuff from hiding the terrain.
In this situation, for example:
Code:
Draw The Terrain
Draw The Water
Draw The Cube
Draw the knot
For best results, you want to render transparent stuff far from the camera first and stuff closer last. It's probably also a good idea to keep the number of transparent things as low as possible, with fairly simple geometry and keep them fairly spread out to reduce weird artifacts.
The next problem is the knot drawing over itself
The best thing to do to fix this is to disable z-write when drawing transparent objects, while leaving z-test enabled. This will keep things that are behind other things hidden, while still giving a nice colour blend.
Do this with gpu_set_zwriteenable(false);
For this situation:
Code:
Enable z-write
Draw The Terrain
Disable z-write
Draw The Water
Draw The Cube
Draw the knot
From the looks of it, this is kinda how Unity sorts stuff as well - it has 3 main passes, it seems: Solid Geometry then AlphaTest and finally Transparent, based on a little shader test I did recently.
This can have weird results when using meshes that mix opaque and transparent colours, so it's probably a good idea to break stuff up as necessary.
If you draw opaque stuff after the transparent stuff in the same place, it can look weird, given the transparent stuff doesn't write it's depth, so it compares with the depth of the opaque stuff drawn before, when z-write was active.
Proof of Implementation:
For a demo (All in GameMaker, so I can be sure it works, but the process is fundementally the same for any engine), here's a Mario with 30% transparency and no culling drawn before his environment:
As you can see, he blocks out the terrain (much like you see in your scene), blending only with the background colour. This is rather undesirable
Here he is drawn after the environment:
That's better as he's actually blending with the world he's in, but he's still "erasing" bits of himself and looking weird. Let's disable z-write with gpu_set_zwriteenable(false):
Ah, much better. Now he looks like a real ghost!
Whether you want backface culling or not is purely situational - it depends on the look you are trying to achieve. Here's that same Mario but with backface culling on (Generally less visible, and you can't see the dungarees straps on his back among other things):
Hopefully this helps out a bit. I think I've covered the important stuff, but if I've been unclear about anything or you need me to elaborate on something, do let me know and I'll try to do a better job!
PS: As another little tip, I've been using gpu_push/pop_state() a lot recently - it's incredibly useful for switching back to a standard state after some specific drawing.