I am currently trying to recreate the Deferred shading tutorial form Learnopengl in defold but it seems like only the first colour buffer is read from the MRT g_buffer by the lighting pass shader. Is this a defold limitation or am I getting something wrong?
Thanks in advance!
Render script:
local NR_LIGHTS = 32
local light_positions = {}
local light_colours = {}
local LQR = {} -- used to bypass the inability to pass structs to shaders
function init(self)
local float_params = { -- position and normal
format = graphics.TEXTURE_FORMAT_RGBA16F,
width = render.get_window_width(),
height = render.get_window_height(),
min_filter = graphics.TEXTURE_FILTER_NEAREST,
mag_filter = graphics.TEXTURE_FILTER_NEAREST,
u_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE,
v_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE
}
local albedo_spec_params = {
format = graphics.TEXTURE_FORMAT_RGBA,
width = render.get_window_width(),
height = render.get_window_height(),
min_filter = graphics.TEXTURE_FILTER_NEAREST,
mag_filter = graphics.TEXTURE_FILTER_NEAREST,
u_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE,
v_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE
}
local blit_params = {
format = graphics.TEXTURE_FORMAT_RGBA32F, -- needs to be precise
width = render.get_window_width(),
height = render.get_window_height(),
min_filter = graphics.TEXTURE_FILTER_NEAREST,
mag_filter = graphics.TEXTURE_FILTER_NEAREST,
u_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE,
v_wrap = graphics.TEXTURE_WRAP_CLAMP_TO_EDGE
}
local depth_params = {
format = graphics.TEXTURE_FORMAT_DEPTH,
width = render.get_window_width(),
height = render.get_window_height()
}
self.g_buffer = render.render_target({
[graphics.BUFFER_TYPE_COLOR0_BIT] = float_params,
[graphics.BUFFER_TYPE_COLOR1_BIT] = float_params,
[graphics.BUFFER_TYPE_COLOR2_BIT] = albedo_spec_params,
[graphics.BUFFER_TYPE_COLOR3_BIT] = blit_params,
[graphics.BUFFER_TYPE_DEPTH_BIT] = depth_params
})
self.g_buffer_pred = render.predicate({"g_buffer"})
self.deferred_shading_pred = render.predicate({"deferred_shading"})
self.lightbox_pred = render.predicate({"lightbox"})
self.ds_constants = render.constant_buffer()
self.lb_constants = render.constant_buffer()
-- transfer to controller script???
math.randomseed(os.time())
for i = 1, NR_LIGHTS do
-- calculate slightly random offsets
local x_pos = ((math.random(100) / 100) * 6 - 3)
local y_pos = ((math.random(100) / 100) * 6 - 4)
local z_pos = ((math.random(100) / 100) * 6 - 3)
table.insert(light_positions, vmath.vector4(x_pos, y_pos, z_pos, 0))
-- calculate random colour
local r = ((math.random(200) / 100) + 0.5) -- between 0.5 and 1
local g = ((math.random(200) / 100) + 0.5) -- between 0.5 and 1
local b = ((math.random(200) / 100) + 0.5) -- between 0.5 and 1
table.insert(light_colours, vmath.vector4(r, g, b, 0))
end
end
local function clear_buffer()
render.clear({[graphics.BUFFER_TYPE_COLOR0_BIT] = vmath.vector4(0, 0, 0, 1), [graphics.BUFFER_TYPE_DEPTH_BIT] = 1})
end
function update(self, dt)
local w, h = render.get_window_width(), render.get_window_height()
render.enable_state(graphics.STATE_DEPTH_TEST)
---
--- 1. geometry pass: render scene's geometry/color data into gbuffer
---
render.set_render_target(self.g_buffer)
clear_buffer()
render.set_viewport(0, 0, w, h)
render.set_view(self.view)
render.set_projection(self.projection) -- might need to use vmath.matrix4_perspective
render.draw(self.g_buffer_pred)
render.set_render_target(render.RENDER_TARGET_DEFAULT)
---
--- 2. lighting pass: calculate lighting by iterating over a screen filled quad pixel-by-pixel using the gbuffer's content.
---
render.disable_state(graphics.STATE_DEPTH_TEST)
clear_buffer()
render.set_viewport(0, 0, w, h)
render.set_view(vmath.matrix4())
render.set_projection(vmath.matrix4())
render.enable_texture(0, self.g_buffer, graphics.BUFFER_TYPE_COLOR0_BIT)
render.enable_texture(1, self.g_buffer, graphics.BUFFER_TYPE_COLOR1_BIT)
render.enable_texture(2, self.g_buffer, graphics.BUFFER_TYPE_COLOR2_BIT)
-- set uniforms
self.ds_constants.light_positions = light_positions
self.ds_constants.light_colours = light_colours
for i = 1, NR_LIGHTS do
-- update attenuation parameters and calculate radius
local constant = 1 -- note that we don't send this to the shader, we assume it is always 1.0 (in our case)
local linear = 0.7
local quadratic = 1.8
-- then calculate radius of light volume/sphere
local max_brightness = math.max(math.max(light_colours[i].x, light_colours[i].y), light_colours[i].z)
local radius = (-linear + math.sqrt(linear * linear - 4 * quadratic * (constant - (256/5) * max_brightness))) / (2 * quadratic)
LQR[i] = vmath.vector4(linear, quadratic, radius, 0)
end
self.ds_constants.LQR = LQR
self.ds_constants.view_pos = vmath.vector4(0, 0, 0, 5)
render.draw(self.deferred_shading_pred, {constants = self.ds_constants})
render.disable_texture(0)
render.disable_texture(1)
render.disable_texture(2)
---
--- 2.5. copy content of geometry's depth buffer to default framebuffer's depth buffer. Blitting.
--- An extension can be written to access opengl for blitting but I will just create another colour
--- buffer in the geometry pass to write the depth values to. The lightbox shader then reads from this buffer.
---
---
--- 3. render lights on top of scene
---
render.set_view(self.view)
render.set_projection(self.projection)
render.enable_state(graphics.STATE_DEPTH_TEST)
-- how do I set the uniforms since it iterates over a table.
-- Work around for blitting.
render.enable_texture(0, self.g_buffer, graphics.BUFFER_TYPE_COLOR3_BIT)
render.draw(self.lightbox_pred, {constants = self.lb_constants})
render.disable_texture(0)
end
function on_message(self, message_id, message, sender)
if message_id == hash("set_view_projection") then
self.view = message.view
self.projection = message.projection
elseif message_id == hash("get_light_data") then
msg.post(sender, "light_data", {positions = light_positions, colours = light_colours})
end
end
g_buffer.fp
#version 330
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
layout (location = 3) out float blitDepth;
in vec2 TexCoords;
in vec3 FragPos;
in vec3 Normal;
uniform sampler2D texture_diffuse1;
uniform sampler2D texture_specular1;
/*void main()
{
// store the fragment position vector in the first gbuffer texture
gPosition = FragPos;
// also store the per-fragment normals into the gbuffer
gNormal = normalize(Normal);
// and the diffuse per-fragment color
gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb;
// store specular intensity in gAlbedoSpec's alpha component
gAlbedoSpec.a = texture(texture_specular1, TexCoords).r;
// store the depth value in blitDepth
blitDepth = gl_FragCoord.z;
}*/
// debugger
void main()
{
gPosition = vec3(1.0, 0.0, 0.0); // should be red
gNormal = vec3(0.0, 1.0, 0.0); // should be green
gAlbedoSpec = vec4(0.0, 0.0, 1.0, 1.0); // should be blue
blitDepth = gl_FragCoord.z;
}
lightpass.fp
#version 330
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D g_position;
uniform sampler2D g_normal;
uniform sampler2D g_albedo_spec;
struct Light {
vec3 Position;
vec3 Color;
float Linear;
float Quadratic;
float Radius;
};
const int NR_LIGHTS = 32;
// Can't pass structs directly so I did a workaround.
//uniform Light lights[NR_LIGHTS];
Light lights[NR_LIGHTS];
uniform frags{
vec4 light_positions[NR_LIGHTS];
vec4 light_colours[NR_LIGHTS];
vec4 LQR[NR_LIGHTS];
vec4 view_pos;
};
void set_light_data()
{
for(int i = 0; i < NR_LIGHTS; ++i)
{
Light light;
light.Position = light_positions[i].xyz;
light.Color = light_colours[i].xyz;
light.Linear = LQR[i].x;
light.Quadratic = LQR[i].y;
light.Radius = LQR[i].z;
lights[i] = light;
}
}
/*void main()
{
// retrieve data from gbuffer
vec3 FragPos = texture(g_position, TexCoords).rgb;
vec3 Normal = texture(g_normal, TexCoords).rgb;
vec3 Diffuse = texture(g_albedo_spec, TexCoords).rgb;
float Specular = texture(g_albedo_spec, TexCoords).a;
// then calculate lighting as usual
vec3 lighting = Diffuse * 0.1; // hard-coded ambient component
vec3 viewDir = normalize(view_pos.xyz - FragPos);
set_light_data();
for(int i = 0; i < NR_LIGHTS; ++i)
{
// calculate distance between light source and current fragment
float distance = length(lights[i].Position - FragPos);
if(distance < lights[i].Radius)
{
// diffuse
vec3 lightDir = normalize(lights[i].Position - FragPos);
vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lights[i].Color;
// specular
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0);
vec3 specular = lights[i].Color * spec * Specular;
// attenuation
float attenuation = 1.0 / (1.0 + lights[i].Linear * distance + lights[i].Quadratic * distance * distance);
diffuse *= attenuation;
specular *= attenuation;
lighting += diffuse + specular;
}
}
FragColor = vec4(lighting, 1.0);
}*/
//debugger
void main()
{
// switching between g_position, g_normal and g_albedo_spec produces the same red colour. See below.
FragColor = vec4(texture(g_albedo_spec, TexCoords).rgb, 1.0);
}
Result:

