Model alpha problem

I’ve banged my head towards this for too long and need to ask you guys about help.
In our game we are mixing all kinds of visual components and tries to sort them all in z-order. Now I havent been able to render the models with transparent alpha (so everything behind it is in some degree visible).
I’m stuck and cannot figure out what to do to get it working. As far as I understand it needs to be done with set_blend_func but also how I setup the depth_mask.
alpha_problem

Ive setup a testproject if anybody is interested in looking into it deeper.

Here is our render script update function:

function update(self)
	render.set_depth_mask(true)
	render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
	render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})

	render.set_projection( vmath.matrix4_orthographic(
		-p.render_w * p.cam.scale + p.cam.pos.x, 
		p.render_w * p.cam.scale + p.cam.pos.x, 
		-p.render_h * p.cam.scale + p.cam.pos.y, 
		p.render_h * p.cam.scale + p.cam.pos.y, 
		-1, 30000)
	)

	render.enable_state(render.STATE_BLEND)
	render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
	render.disable_state(render.STATE_STENCIL_TEST)

	-- 3D
	render.enable_state(render.STATE_CULL_FACE)
	render.enable_state(render.STATE_DEPTH_TEST)
	render.set_depth_mask(true)
	render.draw(self.model_pred)

	-- 2D (sprites, tiles, spine, particles)
	render.set_depth_mask(false)
	render.enable_state(render.STATE_DEPTH_TEST)
	render.disable_state(render.STATE_CULL_FACE)
	render.draw(self.tile_pred)
	render.draw(self.particle_pred)

	render.set_depth_mask(false)
	render.disable_state(render.STATE_DEPTH_TEST)
	render.draw_debug3d()

	-- GUI
	render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))
	render.enable_state(render.STATE_STENCIL_TEST)
	render.draw(self.gui_pred)
	render.draw(self.text_pred)
	render.disable_state(render.STATE_STENCIL_TEST)
	
	render.draw_debug2d()
end

Here is mine render script with working transparency.

render.enable_state(render.STATE_DEPTH_TEST)
render.set_depth_mask(true)
render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())

-- Render 2D space.
render.set_view(self.identity_matrix)
render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))
render.draw(self.background_pred)

render.set_view(view_mtx)
render.set_projection(projection_mtx)

render.enable_state(render.STATE_BLEND)
render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)

-- Render 3D space.
render.enable_state(render.STATE_CULL_FACE)
render.draw(self.model_pred, self.constants)
render.disable_state(render.STATE_CULL_FACE)
render.disable_state(render.STATE_DEPTH_TEST)
render.set_depth_mask(false)

-- Render GUI and overlays.
render.set_view(vmath.matrix4())
render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))

render.draw(self.gui_pred)
render.disable_state(render.STATE_BLEND)
4 Likes

I can’t figure out why blending wouldn’t work from what you showed to us. It should. Can you show us the fragment shader?

Also, when drawing transparent objects, you should first render all solid objects with depth writing and depth testing enabled, then disable depth writing (I think set_depth_mask does that) and keep depth testing enabled to draw transparent objects. Otherwise, if you just render transparent objects with depth writing enabled, then it might start masking out some of the back faces, depending on the draw order.

Problem is that sprites are losing it’s alpha then ( as I believe set_depth_mask is not considering alpha ).39

1 Like

My fragment shader for model (rest is builtins)

varying mediump vec4 var_position;
varying mediump vec3 var_normal;
varying mediump vec2 var_texcoord0;

uniform lowp sampler2D tex0;
uniform lowp vec4 tint;
uniform lowp vec4 full_tint;

void main()
{
    // Pre-multiply alpha since all runtime textures already are
    vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
    vec4 full_tint_pm = vec4(full_tint.xyz * full_tint.w, full_tint.w);
    vec4 color = texture2D(tex0, var_texcoord0.xy);
    color = color * full_tint_pm;
    if (color.r == color.g) { 
        color = color * tint_pm;
    }
    gl_FragColor = vec4(color.rgba);
}

Guess my problem is really that both sprites and models needs to be sorted with transparency and I cannot find a good way doing that. Either I get models to work but then sprites are not working or vice versa.

I tried adding your model material to one of my projects and it seems to break the z order/zdepth of the model. It might be a consequence of this project’s render script somehow too.

https://www.pkeod.com/defold/transparent-3d/

local camera = require("example.camera_helper")


function init(self)

    self.gui_pred = render.predicate({"gui"})
    self.text_pred = render.predicate({"text"})


    self.threede_pred = render.predicate({"3d"})
    self.threede_transparent_pred = render.predicate({"3d_transparent"})
    self.sprite_pred = render.predicate({"sprite"})
    
    self.clear_color = vmath.vector4(0, 0, 0, 0)
    self.clear_color.x = sys.get_config("render.clear_color_red", 0)
    self.clear_color.y = sys.get_config("render.clear_color_green", 0)
    self.clear_color.z = sys.get_config("render.clear_color_blue", 0)
    self.clear_color.w = sys.get_config("render.clear_color_alpha", 0)    
    
    self.timer = 0
    
    
    
end


function update(self)
	
	self.timer = self.timer + 0.01

	camera.window_width = render.get_window_width()
	camera.window_height = render.get_window_height()
    
    ----------------------------------------------------------------------------------------
    -- setup viewport and clear the screen
    render.set_viewport(0, 0, render.get_window_width(), render.get_window_height())
    render.clear({[render.BUFFER_COLOR_BIT] = self.clear_color, [render.BUFFER_DEPTH_BIT] = 1, [render.BUFFER_STENCIL_BIT] = 0})
    
    -- setup state (we need to enable depth testing for 3D stuff)
    render.enable_state(render.STATE_DEPTH_TEST)
    render.set_depth_mask(true)
    
    
    --[[
    -- create view and projection matrices
    local cam_lookat = vmath.vector3(0.0,0.0,0.0)
    local cam_pos = vmath.vector3(-10.0,10.0 * math.abs(math.sin(self.timer * 0.2) * 3),1.0 + math.sin(self.timer) * 3 * 4)
    --local cam_pos = vmath.vector3(0.0,0.0,20.0) -- this moves the camera further away
    local cam_up = vmath.vector3(0,1,0)
    local view_mtx = vmath.matrix4_look_at(cam_pos, cam_lookat, cam_up)
    local fov = (math.pi / 180.0) * 60.0 -- 60 degrees
    local proj_mtx = vmath.matrix4_perspective(fov, render.get_window_width() / render.get_window_height(), 0.1, 100.0)
    
    --]]
    
    -- set matrices
    render.set_view(camera.view_matrix)
    render.set_projection(camera.proj_matrix)
    
    -- draw cube predicate
	render.draw(self.threede_pred)
	
	--
	render.draw_debug3d()
    render.disable_state(render.STATE_DEPTH_TEST)
    render.disable_state(render.STATE_STENCIL_TEST)
    render.enable_state(render.STATE_BLEND)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    render.enable_state(render.STATE_DEPTH_TEST)
    render.set_depth_mask(false)
    render.draw(self.threede_transparent_pred)
    render.draw(self.sprite_pred)
    
    render.set_depth_mask(true)
    render.disable_state(render.STATE_DEPTH_TEST)
    render.disable_state(render.STATE_CULL_FACE)

    render.set_view(vmath.matrix4())
    render.set_projection(vmath.matrix4_orthographic(0, render.get_window_width(), 0, render.get_window_height(), -1, 1))

    render.enable_state(render.STATE_STENCIL_TEST)
    render.draw(self.gui_pred)
    render.draw(self.text_pred)
    render.disable_state(render.STATE_STENCIL_TEST)

    render.draw_debug2d()

end


function on_message(self, message_id, message)
    if message_id == hash("clear_color") then
        self.clear_color = message.color
    elseif message_id == hash("set_view_projection") then
        self.view = message.view
    end
end

Edit: I remember the issue now. If you don’t disable the depth mask then transparent objects below are not drawn. If you do disable the depth mask then objects are drawn based on the order of their predicate so you have to choose if you want transparent models or transparent sprites above each other, I think.

I think we currently have no control over the features with other 3d engines use to get the sorting right.

2 Likes

Yes I solved my problem now but it was weird and it feels clunky.

Simplified it looks like this:

Draw all 2D (tilemaps/sprites), no depth-writing, no depth-testing
Draw all models without transparancy, depth-writing, depth-reading
Draw all models with transparancy, depth_writing, depth_reading
Draw 2D AGAIN (except tilemaps which always in furthest back), no depth_writing, depth_reading.

Without first 2D drawing I cannot blend it with transparent models.
Don’t know the performance impact on this one though :frowning:

I’ll try to make a minimal example with these features working as best as possible. I think even with your list there would still be interaction issues, but may be okay with your game.

1 Like