Illumination — Ready-to-use forward shading lighting for 3D games

Unfortunately, the problem is that rendering modularity does not solve the problem of effect conflict.
For example, you have some extension with deferred rendering pipeline.
And you need to insert your custom pass there instead of the “final build” pass, which implements cluster lighting.
And other user also want to add Bloom - so he need a couple more passes after all the existing ones.
And now other user want to add SSLR - where does he stick them?
It all makes sense if you make one big extension with all the effects, and then you can just turn them off if you want to.

But making individual effects modular is quite inconvenient. And the problem is not manually changing the render script, but that it’s rather problematic to make a universal effect that will easily connect and combine with all variations of user projects.

8 Likes

I agree with you @morgerion . You make a good point with the challenges of efficiently combining different modules into something useful.

I think we will see complete and configurable rendering pipelines as single assets going forward.

2 Likes

Well, very strong points! I don’t have enough experience to tell how to make it better, other than in terms of “the easier it is to plug and run, the better”.

If the best way is to make one big extension with switchable options, some kind of consolidation is needed for such a solution. At first look it’s too unrealistic to make this usable and efficient without consolidated development.

4 Likes

This is kinda what I’m doing with my render pipeline project / asset:

dfp.configure({
		[dfp.config.LIGHTING_HDR]            = true,
		[dfp.config.POST_PROCESSING]         = true,
		[dfp.config.POST_PROCESSING_BLOOM]   = true,
		[dfp.config.SHADOWS]                 = true,
		[dfp.config.SHADOWS_SHADOW_MAP_SIZE] = 2048
	})

	self.pass_shadows_debug = dfp.add_custom_pass({
		predicate = {"postprocess_shadow_debug"},
		material  = "postprocess_shadow_debug",
		before    = dfp.node_keys.FRAMEBUFFER,
		textures  = {
			dfp.node_keys.SHADOWS
		}
	})

My idea is that you probably want to insert custom passes into the graph, but only for certain scenarios (mostly debugging) but not a completely modular approach because that is probably a lot bigger and more complex undertaking… On the whole the purpose of at least my project is that I’d rather develop it as a fixed set of features into an asset that can be dropped into any project to get a good looking game without too much hassle.

8 Likes

I happen to have seen this example before and found it very promising. :slight_smile: )
I was thinking along the same lines - create a custom pipeline where only post-processing effects or shadow renders can be plugged in. And nothing else.
With that limitation, it’s easier to make an good architecture.

7 Likes

I do have a completely modular approach for GMAI, that I hope to open source once I get the time. It’s more rather optimised with post-processing filters in mind, but it looks somewhat like this:

local pipeline = require "main.render.pipeline"
local dispatcher = require "crit.dispatcher"
local save_file = require "lib.save_file"

local h_filter_set_enabled = hash("filter_set_enabled")
local h_config_change = hash("config_change")

go.property("enabled", true)
go.property("hook", hash("gui"))
go.property("alpha", true)

local function update_enabled(self)
  pipeline.set_module_enabled(self.module, self.enabled and not save_file.config.reduce_vfx)
end

function init(self)
  self.module = pipeline.register_module(self.hook, {
    sort_order = 10,
    output = 1,
    render_passes_before = {{
      output = pipeline.INPUT,
      clear = true,
      clear_color = vmath.vector4(0),
    }},
    render_passes = {{
      inputs = {pipeline.INPUT},
      output = 1,
      settings = self.alpha and "filter_alpha" or "filter",
      predicate = "chromatic_abberation",
      -- Optimise away clears done to pipeline.OUTPUT
      -- since all the pixels will be overwritten anyway
      is_clear_invariant = not self.alpha,
    }},
  })

  self.sub_id = dispatcher.subscribe({ h_config_change })
  update_enabled(self)
end

function on_message(self, message_id, message)
  if message_id == h_filter_set_enabled then
    self.enabled = message.enabled
    update_enabled(self)

  elseif message_id == h_config_change then
    if message.key == "reduce_vfx" then
      update_enabled(self)
    end

  end
end

function final(self)
  pipeline.unregister_module(self.module)
  dispatcher.unsubscribe(self.sub_id)
end
6 Likes

Interesting! I also started on a basic post processing pipeline. Focused only on full screen post processing effects. Very basic.

Good to see that there are alternatives!

6 Likes

Hi!

Anyone experimented already with adding shadows to Illumination? It would be so better! :star_struck:
I am trying to investigate @jhonny.goransson Shadow Mapping example, but can’t figure out yet how to merge it into Illumination example, so any advises would be gold! :heart:

Shadow mapping example has one light source, while in illumination there could be many, so those are probably not compatible out of the box :frowning:

Anyone experimented already with adding shadows to Illumination?

@Pawel I’m very surprised and glad at the same time, but @d954mas makes something like that: https://twitter.com/d954mas/status/1654841624063619074

4 Likes

Aaa, so probably it is adapted from @Dragosha’s lights and shadows :thinking:

1 Like

Yes it is @Dragosha shadows.
Idea is simple. You have point light from illumination(no shadows).
And you have sun light with shadows from dragosha.

Then you try to mix two shaders in one shader)

2 Likes

Huh, after a good look it turned out that the normal maps in the example are not created correctly and do not correspond to DirectX or OpenGL :smiley:

Accordingly, the algorithm in the shader was also wrong.

Fixed the algorithm and added the ability to specify in the shader parameters which normal maps are provided - in OpenGL (default) or DirectX format :+1:.

Also fixed the sun blinking on surfaces when the sun rotates.

10 Likes

Hello!

I just tried the example project in Defold 1.6.4 and none of the lighting appears to be working?



I simply downloaded git repo, opened it in Defold 1.6.4, and made a build. :open_mouth:

Any ideas what could be wrong?

1 Like

Oh, really. It works fine on 1.6.1, but not on 1.6.4.

I’m not sure exactly why, there are no errors in the log, so it’s taking me a while to figure it out.

2 Likes

Okay, figured out with it! :smiley:
Starting from Defold 1.6.3 mesh component respects sampler names.

Their order in the editor is now sorted by alphabet instead of the sequential order from the source file as it was before. But it also affects calls like go.get('textureN'), where N now depends on the alphabet.

Screenshot 2024-04-02 at 00.20.50

So I changed texture1 to texture0 to fix the bug (DATA_TEXTURE was 2th before 1.6.3), but the dependence of the N on the alphabet looks a little flimsy…

Is it okay? Or may be there is a new way to get texture path by sampler name? My use case:

local TEXTURE_DATA = hash 'texture0'
local data_url = msg.url('#data') -- #data is mesh component
...
data_texture_path = go.get(data_url, TEXTURE_DATA)
resource.set_texture(data_texture_path, data_texture_header, data_buffer)
1 Like

I think that for meshes you should be able to get a texture by the sampler name if that’s how we save the texture mappings these days… we really don’t want resources to depend on indexing like this :frowning:

Only go.get(data_url, hash 'textureN') works at the moment :thinking:. Or should I try something else?

go.get(data_url, hash 'DATA_TEXTURE')
>>>
ERROR:SCRIPT: illumination/illumination.lua:309: '/illumination#data' does not have any property called 'DATA_TEXTURE'

No it’s the only one that works, I’m just saying as a note for myself that we should probably allow setting textures by sample name at some point :slight_smile: but for now that’s the only way to do it

1 Like

A question: is there a possibility to DETECT light? Like, a sensor of sorts, that triggers once light hits it?

No. Perhaps you can use collision objects to detect them.

(Also, please don’t ask the same thing in different threads)

1 Like