Page 1 of 1
gamedev megathread
Posted: June 10th, 2023, 13:37
by twig
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.
gamedev megathread
Posted: June 10th, 2023, 14:19
by twig
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:
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);
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
# one-liner for testing
>>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation)
0.4980392156862745
If you find this less counterintuitive than it is for me then I'd be grateful for help.
-twig
gamedev megathread
Posted: June 10th, 2023, 17:13
by Maggot
twig wrote: ↑
June 10th, 2023, 14:19
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:
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);
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
# one-liner for testing
>>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation)
0.4980392156862745
If you find this less counterintuitive than it is for me then I'd be grateful for help.
-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.
gamedev megathread
Posted: June 10th, 2023, 17:31
by Maggot
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.
gamedev megathread
Posted: June 10th, 2023, 23:23
by rusty_shackleford
twig wrote: ↑
June 10th, 2023, 14:19
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:
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);
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
# one-liner for testing
>>> I = 255; dist=128; attenuation=1 - dist / I; print(attenuation)
0.4980392156862745
If you find this less counterintuitive than it is for me then I'd be grateful for help.
-twig
Linear:
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
gamedev megathread
Posted: June 13th, 2023, 04:29
by twig
Yes. Thank you.
Maggot wrote: ↑
June 10th, 2023, 17:13
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
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 to
Code: Select all
color = vec3(color_intensity.rgb * A);
Maggot wrote: ↑
June 10th, 2023, 17:31
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.
Seems like a lot more work than making a hostile fork from a known good revision and cherry-picking the following commits.
gamedev megathread
Posted: August 24th, 2023, 18:30
by twig
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.
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);
The shader is conditionally outputing depending on a uniform value.
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);
} [...]
}
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'?
gamedev megathread
Posted: August 24th, 2023, 18:39
by rusty_shackleford
This isn't an opengl function, did you mean something else?
gamedev megathread
Posted: August 24th, 2023, 19:33
by twig
glNamedFramebufferReadBuffer.
This might be a better visualization of what's wrong with it. It's with the distance metric removed. Lights are green and red.

gamedev megathread
Posted: August 24th, 2023, 19:44
by rusty_shackleford
twig wrote: ↑
August 24th, 2023, 19:33
glNamedFramebufferReadBuffer.
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 honestly don't know, I think I need to see more of the code.
gamedev megathread
Posted: August 24th, 2023, 21:24
by twig
I posted the clone URL on IRC. You can do basically:
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}
gamedev megathread
Posted: August 24th, 2023, 21:41
by rusty_shackleford
twig wrote: ↑
August 24th, 2023, 21:24
I posted the clone URL on IRC. You can do basically:
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}
cmake fails at trying to find sdl2(it is installed)
I see no issues with the code tbh
gamedev megathread
Posted: August 24th, 2023, 22:44
by Shillitron
Redaxium 3 sneak peak? Looks ready to ship IMO.
gamedev megathread
Posted: August 27th, 2023, 06:32
by Tweed
twig wrote: ↑
August 24th, 2023, 19:33
glNamedFramebufferReadBuffer.
This might be a better visualization of what's wrong with it. It's with the distance metric removed. Lights are green and red.
The watermelon dimension.
gamedev megathread
Posted: August 30th, 2023, 00:58
by twig
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.
gamedev megathread
Posted: August 31st, 2023, 23:25
by twig