We have a Steam curator now. You should be following it. https://store.steampowered.com/curator/44994899-RPGHQ/
gamedev megathread
gamedev megathread
This thread is for discussing implementation details of your own game development projects (rendering APIs, pathfinding, algorithms, AI...) as well as for bragging about completed milestones.
I'd like to ask @rusty_shackleford to help me with getting light attenuation to work when light strength is expressed as distance where brightness of the light is 0%. Expressing light intensity as percentile distance makes it easier to operate for level design purposes.
Let D_max be the distance in pixels where the light has no effect anymore. This is set up by the level designer. For quadratic lights, this is the distance where the light brightens the image by less than 1/255th of a pixel. For linear light falloff, this is (linearly) extrapolated point where brightness becomes 0 or below.
Let pos be the position of the current pixel that we're determining the color for.
Let center be the point where the light originates.
Something like that comes to mind:
I'm having trouble reasoning about this algebraically due to this being an inverse function, having to manage multiple attenuation types, and having to visualize it as a radial gradient.
If you find this less counterintuitive than it is for me then I'd be grateful for help.
-twig
Let D_max be the distance in pixels where the light has no effect anymore. This is set up by the level designer. For quadratic lights, this is the distance where the light brightens the image by less than 1/255th of a pixel. For linear light falloff, this is (linearly) extrapolated point where brightness becomes 0 or below.
Let pos be the position of the current pixel that we're determining the color for.
Let center be the point where the light originates.
Something like that comes to mind:
Code: Select all
vec2 tmp = pos - center;
float D = sqrt(tmp.x*tmp.x + tmp.y*tmp.y);
float alpha = 1 - min(1, D / D_max);
color = vec4(light_color.xyz, alpha);
Code: Select all
# one-liner for testing
>>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation)
0.4980392156862745
-twig
Could you give some context to this? I'm guessing it's a 2D game since you're calculating distance as a vec2. I only have any experience doing standard phong shading for a class in OpenGL/GLSL so keep that in mind, but I do find it strange that you're messing with the alpha for light falloff. I'm not sure why this couldn't be done with a normal diffuse calculation multiplied by your special attenuation for lighting.twig wrote: ↑ June 10th, 2023, 14:19I'd like to ask @rusty_shackleford to help me with getting light attenuation to work when light strength is expressed as distance where brightness of the light is 0%. Expressing light intensity as percentile distance makes it easier to operate for level design purposes.
Let D_max be the distance in pixels where the light has no effect anymore. This is set up by the level designer. For quadratic lights, this is the distance where the light brightens the image by less than 1/255th of a pixel. For linear light falloff, this is (linearly) extrapolated point where brightness becomes 0 or below.
Let pos be the position of the current pixel that we're determining the color for.
Let center be the point where the light originates.
Something like that comes to mind:I'm having trouble reasoning about this algebraically due to this being an inverse function, having to manage multiple attenuation types, and having to visualize it as a radial gradient.Code: Select all
vec2 tmp = pos - center; float D = sqrt(tmp.x*tmp.x + tmp.y*tmp.y); float alpha = 1 - min(1, D / D_max); color = vec4(light_color.xyz, alpha);
If you find this less counterintuitive than it is for me then I'd be grateful for help.Code: Select all
# one-liner for testing >>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation) 0.4980392156862745
-twig
I'm personally working on an SDL port of JA2 without all the BS of Stracciatella. It's very slow going since I don't have a firm understanding of either SDL or DirectDraw so a lot of time is spent figuring out what has been more or less automated with SDL compared to DirectDraw's bookkeeping as well as the fact that JA2 does not do a great job of decoupling all the screen drawing code from game logic so there's tons of files to go through before it will even be able to compile. My butthurt at Stracciatella being a contributor toybox where nobody actually plays the game and instead just make it worse for people trying to play the standard game keeps me going.
- rusty_shackleford
- Site Admin
- Posts: 10674
- Joined: Feb 2, '23
- Gender: Watermelon
- Contact:
Linear:twig wrote: ↑ June 10th, 2023, 14:19I'd like to ask @rusty_shackleford to help me with getting light attenuation to work when light strength is expressed as distance where brightness of the light is 0%. Expressing light intensity as percentile distance makes it easier to operate for level design purposes.
Let D_max be the distance in pixels where the light has no effect anymore. This is set up by the level designer. For quadratic lights, this is the distance where the light brightens the image by less than 1/255th of a pixel. For linear light falloff, this is (linearly) extrapolated point where brightness becomes 0 or below.
Let pos be the position of the current pixel that we're determining the color for.
Let center be the point where the light originates.
Something like that comes to mind:I'm having trouble reasoning about this algebraically due to this being an inverse function, having to manage multiple attenuation types, and having to visualize it as a radial gradient.Code: Select all
vec2 tmp = pos - center; float D = sqrt(tmp.x*tmp.x + tmp.y*tmp.y); float alpha = 1 - min(1, D / D_max); color = vec4(light_color.xyz, alpha);
If you find this less counterintuitive than it is for me then I'd be grateful for help.Code: Select all
# one-liner for testing >>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation) 0.4980392156862745
-twig
A = (d₂-d₁)÷d₂
Where d₁ is the distance the current fragment is from the lightsource, and d₂ is the distance of the light where the intensity is 0.
Quadratic:
A = ((d₂-d₁)÷d₂)²
Technically linear is just a special case where the exponent of the attenuation is 1.
Code: Select all
import math
from PIL import Image
import matplotlib.pyplot as plt
w, h = 1024, 1024
light_pos = (512, 512)
light_dist = 512 # d₂
def compute_light(img, exponent):
for x in range(img.size[0]):
for y in range(img.size[1]):
dist = math.sqrt((x - light_pos[0])**2 + (y - light_pos[1])**2) # d₁
attenuation = (light_dist - dist / light_dist) ** exponent
if attenuation < 0 or dist > light_dist:
continue
# map to 8-bit rgb
color = 255 * attenuation
pixels[x, y] = (int(color), int(color), int(color))
img = Image.new('RGB', (w, h), "green")
pixels = img.load()
compute_light(img, 1)
img_2 = Image.new('RGB', (w, h), "green")
pixels = img_2.load()
compute_light(img_2, 2)
# display
f = plt.figure()
f.add_subplot(1, 2, 1)
plt.imshow(img)
f.add_subplot(1, 2, 2)
plt.imshow(img_2)
plt.show(block=True)
Technically wouldn't look like that in a game due to gamma correction:
Does this help?
[edit]
woops, had the light inverse, fixed
Last edited by rusty_shackleford on June 11th, 2023, 00:33, edited 1 time in total.
Reason: assume dist ≤ light_dist, can simplify expression
Reason: assume dist ≤ light_dist, can simplify expression
Yes. Thank you.
vec3 uses the same amount of bytes as vec4. It's useful from the level design perspective (edit color brightness). Because light is always blended onto blackness and a constant blend mode is used, it's guaranteed to be equivalent toMaggot wrote: ↑ June 10th, 2023, 17:13but I do find it strange that you're messing with the alpha for light falloff. I'm not sure why this couldn't be done with a normal diffuse calculation multiplied by your special attenuation for lighting
Code: Select all
color = vec3(color_intensity.rgb * A);
Seems like a lot more work than making a hostile fork from a known good revision and cherry-picking the following commits.Maggot wrote: ↑ June 10th, 2023, 17:31My butthurt at Stracciatella being a contributor toybox where nobody actually plays the game and instead just make it worse for people trying to play the standard game keeps me going.
Here's another problem I'm having:
The idea is to output lighting and shadows without tanking performance by repeated calls to glNamedReadBuffer. Two color attachments are used, let's call them 'scratch' (0) and 'accum' (1).
glNamedReadBuffer is set up for the above.
The shader is conditionally outputing depending on a uniform value.
Now the whole thing has big artifacts that are flickering all the time.
If this approach is wrong and a completely different solution is needed, what can be done to prevent the need to bind buffers each time 'scratch' is to be generated and blended into 'accum'?
The idea is to output lighting and shadows without tanking performance by repeated calls to glNamedReadBuffer. Two color attachments are used, let's call them 'scratch' (0) and 'accum' (1).
glNamedReadBuffer is set up for the above.
Code: Select all
framebuffer.fb.mapForDraw({
{ 0u, GL::Framebuffer::ColorAttachment{0} },
{ 1u, GL::Framebuffer::ColorAttachment{1} },
});
// src_C, dst_C src_A, dst_A
GL::Renderer::setBlendFunction(0, BlendFunction::One, BlendFunction::Zero, BlendFunction::One, BlendFunction::Zero);
GL::Renderer::setBlendFunction(1, BlendFunction::One, BlendFunction::One, BlendFunction::One, BlendFunction::One);
Code: Select all
#if 1
setUniform(ModeUniform, DrawLightmapMode); // == 1
AbstractShaderProgram::draw(light_mesh);
#endif
#if 0
setUniform(ModeUniform, DrawShadowsMode); // == 0
[...]
AbstractShaderProgram::draw(mesh_view);
#endif
#if 1
[...]
setUniform(ModeUniform, BlendLightmapMode); // == 2
AbstractShaderProgram::draw(light_mesh);
#endif
Code: Select all
layout (location = 8) uniform uint mode;
layout (location = 0) out vec4 color0;
layout (location = 1) out vec4 color1;
[...]
void main() {
if (mode == 1)
{
[...]
color0 = vec4(light_color.rgb * A, 1);
}
[...]
else if (mode == 2) // blend
{
color1 = texture(sampler0, (gl_FragCoord.xy + vec2(0.5)) * scale);
} [...]
}
If this approach is wrong and a completely different solution is needed, what can be done to prevent the need to bind buffers each time 'scratch' is to be generated and blended into 'accum'?
- rusty_shackleford
- Site Admin
- Posts: 10674
- Joined: Feb 2, '23
- Gender: Watermelon
- Contact:
This isn't an opengl function, did you mean something else?
glNamedFramebufferReadBuffer.rusty_shackleford wrote: ↑ August 24th, 2023, 18:39This isn't an opengl function, did you mean something else?
This might be a better visualization of what's wrong with it. It's with the distance metric removed. Lights are green and red.
- rusty_shackleford
- Site Admin
- Posts: 10674
- Joined: Feb 2, '23
- Gender: Watermelon
- Contact:
I honestly don't know, I think I need to see more of the code.twig wrote: ↑ August 24th, 2023, 19:33glNamedFramebufferReadBuffer.rusty_shackleford wrote: ↑ August 24th, 2023, 18:39This isn't an opengl function, did you mean something else?
This might be a better visualization of what's wrong with it. It's with the distance metric removed. Lights are green and red.
I posted the clone URL on IRC. You can do basically:rusty_shackleford wrote: ↑ August 24th, 2023, 19:44I honestly don't know, I think I need to see more of the code.
Code: Select all
git clone --recurse-submodules [url from IRC]
cd project-name
mkdir build
cd build
# note: there's a hack for save location that won't work 'if '..' of the install directory isn't the build directory.
mkdir save
cd save
wget [save-url from IRC]
cd ..
cmake ..
make install # will install into an 'install' subdir
install/bin/project-name-editor
# press f9
# right-click on a light icon and press 'Test'
# now look at shaders/lightmap.{cpp,hpp,frag,vert}
- rusty_shackleford
- Site Admin
- Posts: 10674
- Joined: Feb 2, '23
- Gender: Watermelon
- Contact:
cmake fails at trying to find sdl2(it is installed)twig wrote: ↑ August 24th, 2023, 21:24I posted the clone URL on IRC. You can do basically:rusty_shackleford wrote: ↑ August 24th, 2023, 19:44I honestly don't know, I think I need to see more of the code.
Code: Select all
git clone --recurse-submodules [url from IRC] cd project-name mkdir build cd build # note: there's a hack for save location that won't work 'if '..' of the install directory isn't the build directory. mkdir save cd save wget [save-url from IRC] cd .. cmake .. make install # will install into an 'install' subdir install/bin/project-name-editor # press f9 # right-click on a light icon and press 'Test' # now look at shaders/lightmap.{cpp,hpp,frag,vert}
I see no issues with the code tbh
- Shillitron
- Turtle
- Posts: 1674
- Joined: Feb 6, '23
- Location: ADL Head Office
Redaxium 3 sneak peak? Looks ready to ship IMO.
The watermelon dimension.twig wrote: ↑ August 24th, 2023, 19:33glNamedFramebufferReadBuffer.rusty_shackleford wrote: ↑ August 24th, 2023, 18:39This isn't an opengl function, did you mean something else?
This might be a better visualization of what's wrong with it. It's with the distance metric removed. Lights are green and red.
It was a glNamedFramebufferDrawBuffer call missing in between the two draws in order to map only one of the buffers for reading purposes, rather than both. It's cheaper to map them than bind them, anyway.