MarkelZ / pygame-render

Python package for rendering textures with shaders using pygame.
MIT License
10 stars 0 forks source link

Integrated light engine? #3

Open OlaKenji opened 10 months ago

OlaKenji commented 10 months ago

@MarkelZ Hi! I was just wondering if you happened to have an example code where you have implemented light rendering (like your light engine) into your shader engine?

MarkelZ commented 10 months ago

@OlaKenji Hi! Yes I have implemented some examples, take a look at this one: lights.zip

The example shows the core idea that my lighting engine uses for rendering lights. However, this example just renders one light and one hull, and only has hard shadows. Reimplementing the whole lighting engine would take quite some effort, and I am currently very short on time because of work :/

Are you planning on adding lights to happy ville?

OlaKenji commented 10 months ago

@MarkelZ Thanks, I will have a look! I thought it would be cool to also have lights in happy ville, so I will try :) let's see how it goes.

OlaKenji commented 10 months ago

@MarkelZ I modified your code (basically added 2 for loops) so that it can take multiple rectangles and light sources. lights.zip Not sure if it is the most optimised code but the performance seems to be good (I hope/think). I wanted to ask how you did to get "smooth" shadow in your light engine. Some bluring?

MarkelZ commented 10 months ago

Hey @OlaKenji

That's so cool! I just ran it and I get around 30 FPS on my laptop. I can give you some tips on how to optimize it:

  1. If the distance from the pixel to the light is greater than the light's radius, you can skip it. I do this in my engine, take a look at line 59 of fragment_light.glsl. I quickly implemented it in your script and I now get around 80 FPS.
  2. To get smooth shadows, you can create a small layer where you render the lights, and then render the layer to the screen by upscaling it. This should give you a huge FPS boost (depends on how small the layer is). Also make sure that you set the filter to linear:

    layer.texture.filter = (LINEAR, LINEAR)

    You can combine this with a blur shader to make the shadows a bit nicer. Otherwise you may notice some artifacts due to the scaling. My engine uses this shader: fragment_blur.glsl. Be careful with the blur radius, I remember setting it to a very large value and it crashed my computer hahaha

  3. My engine was designed to work with arbitrary polygon-shaped hulls, so the way it calculates shadows is a bit expensive. If all the hulls in your game are axis-aligned rectangles, I recommend replacing isOcluded with something like this: https://stackoverflow.com/questions/99353/how-to-test-if-a-line-segment-intersects-an-axis-aligned-rectange-in-2d
  4. Not every light needs to cast shadows, especially since occlusion checking is the most expensive part. Small objects that cast light such as particle effects may skip this operation altogether.

Please keep me posted about your lighting system, I'm very interested :)

OlaKenji commented 10 months ago

Great thank you, I will try them out!

For point 2, how should the intensities of the lights be added? For the moment, I add them up like 'finalColor += vec3(lightIntensity * fade);' inside the light source for loop in the shader. But if you would render the light separately, this wouldn't work. I guess the "adding" will be from overlapping textures with different alphas?

MarkelZ commented 10 months ago

@OlaKenji You can just render all the lights with the for loop as you already do, but instead of rendering to the screen you can render to a small layer.

In my engine, I render the lights separately and combine them additively as you mention. I don't use the alpha channel for combining them, but instead I do it like a mask. The alpha channel has a different purpose. I could explain this in more detail if you want, but honestly, if I had to reimplement my engine I would keep it simple and have a for loop as you currently do. It would run faster and it's easier to implement, so it's a win-win.

Once you render the lights to one layer and the game-scene to another, you can combine both multiplicatively. I do exactly this in fragment_mask.glsl.

OlaKenji commented 9 months ago

Thank you for all the suggestions and explanations! I think I managed to do something similar to your light engine. For reference, I have included an example here. _light.zip

MarkelZ commented 9 months ago

@OlaKenji I just ran the script and it looks nice already! One weird thing I noticed is that there are some pixels in the outline of the sprite that look bright even though they are in the shadow. Not sure why this is happening. Maybe different alpha-blending settings may fix it, the stuff I did in this commit.