Much nicer point light formulas

This commit is contained in:
bloeys
2024-05-14 07:36:14 +04:00
parent dcfe254052
commit f13db47918
2 changed files with 118 additions and 60 deletions

108
main.go
View File

@ -82,18 +82,14 @@ func (d *DirLight) GetProjViewMat() gglm.Mat4 {
return *projMat.Mul(&viewMat) return *projMat.Mul(&viewMat)
} }
// Check https://wiki.ogre3d.org/tiki-index.php?page=-Point+Light+Attenuation for values // Based on: https://lisyarus.github.io/blog/posts/point-light-attenuation.html
type PointLight struct { type PointLight struct {
Pos gglm.Vec3 Pos gglm.Vec3
DiffuseColor gglm.Vec3 DiffuseColor gglm.Vec3
SpecularColor gglm.Vec3 SpecularColor gglm.Vec3
// @TODO
Radius float32 Radius float32
Falloff float32
Constant float32
Linear float32
Quadratic float32
FarPlane float32 FarPlane float32
} }
@ -278,38 +274,32 @@ var (
Pos: gglm.NewVec3(0, 2, -2), Pos: gglm.NewVec3(0, 2, -2),
DiffuseColor: gglm.NewVec3(1, 0, 0), DiffuseColor: gglm.NewVec3(1, 0, 0),
SpecularColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1),
// These values are for 50m range Falloff: 1.0,
Constant: 1.0, Radius: 20,
Linear: 0.09,
Quadratic: 0.032,
FarPlane: 25, FarPlane: 25,
}, },
{ {
Pos: gglm.NewVec3(0, -5, 0), Pos: gglm.NewVec3(0, -5, 0),
DiffuseColor: gglm.NewVec3(0, 1, 0), DiffuseColor: gglm.NewVec3(0, 1, 0),
SpecularColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1),
Constant: 1.0, Falloff: 1.0,
Linear: 0.09, Radius: 20,
Quadratic: 0.032,
FarPlane: 25, FarPlane: 25,
}, },
{ {
Pos: gglm.NewVec3(5, 0, 0), Pos: gglm.NewVec3(5, 0, 0),
DiffuseColor: gglm.NewVec3(1, 1, 1), DiffuseColor: gglm.NewVec3(1, 1, 1),
SpecularColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1),
Constant: 1.0, Falloff: 1.0,
Linear: 0.09, Radius: 20,
Quadratic: 0.032,
FarPlane: 25, FarPlane: 25,
}, },
{ {
Pos: gglm.NewVec3(-3, 4, 3), Pos: gglm.NewVec3(-3, 4, 3),
DiffuseColor: gglm.NewVec3(1, 1, 1), DiffuseColor: gglm.NewVec3(1, 1, 1),
SpecularColor: gglm.NewVec3(1, 1, 1), SpecularColor: gglm.NewVec3(1, 1, 1),
Constant: 1.0, Falloff: 1.0,
Linear: 0.09, Radius: 10,
Quadratic: 0.032,
FarPlane: 25, FarPlane: 25,
}, },
} }
@ -750,20 +740,15 @@ func (g *Game) updateLights() {
groundMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) groundMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor)
palleteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor) palleteMat.SetUnifVec3(indexString+".specularColor", &p.SpecularColor)
whiteMat.SetUnifFloat32(indexString+".constant", p.Constant) whiteMat.SetUnifFloat32(indexString+".falloff", p.Falloff)
containerMat.SetUnifFloat32(indexString+".constant", p.Constant) containerMat.SetUnifFloat32(indexString+".falloff", p.Falloff)
groundMat.SetUnifFloat32(indexString+".constant", p.Constant) groundMat.SetUnifFloat32(indexString+".falloff", p.Falloff)
palleteMat.SetUnifFloat32(indexString+".constant", p.Constant) palleteMat.SetUnifFloat32(indexString+".falloff", p.Falloff)
whiteMat.SetUnifFloat32(indexString+".linear", p.Linear) whiteMat.SetUnifFloat32(indexString+".radius", p.Radius)
containerMat.SetUnifFloat32(indexString+".linear", p.Linear) containerMat.SetUnifFloat32(indexString+".radius", p.Radius)
groundMat.SetUnifFloat32(indexString+".linear", p.Linear) groundMat.SetUnifFloat32(indexString+".radius", p.Radius)
palleteMat.SetUnifFloat32(indexString+".linear", p.Linear) palleteMat.SetUnifFloat32(indexString+".radius", p.Radius)
whiteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic)
containerMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic)
groundMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic)
palleteMat.SetUnifFloat32(indexString+".quadratic", p.Quadratic)
whiteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) whiteMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane)
containerMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane) containerMat.SetUnifFloat32(indexString+".farPlane", p.FarPlane)
@ -832,10 +817,6 @@ func (g *Game) Update() {
g.updateCameraPos() g.updateCameraPos()
g.showDebugWindow() g.showDebugWindow()
if input.KeyClicked(sdl.K_F4) {
logging.InfoLog.Printf("Pos: %s; Forward: %s; |Forward|: %f\n", cam.Pos.String(), cam.Forward.String(), cam.Forward.Mag())
}
} }
func (g *Game) showDebugWindow() { func (g *Game) showDebugWindow() {
@ -957,24 +938,53 @@ func (g *Game) showDebugWindow() {
indexString := "pointLights[" + indexNumString + "]" indexString := "pointLights[" + indexNumString + "]"
if imgui.DragFloat3("Pos", &pl.Pos.Data) { if imgui.DragFloat3("Pos", &pl.Pos.Data) {
whiteMat.SetUnifVec3(indexString+".pos", &pl.Pos)
containerMat.SetUnifVec3(indexString+".pos", &pl.Pos) posStr := indexString + ".pos"
groundMat.SetUnifVec3(indexString+".pos", &pl.Pos)
palleteMat.SetUnifVec3(indexString+".pos", &pl.Pos) whiteMat.SetUnifVec3(posStr, &pl.Pos)
containerMat.SetUnifVec3(posStr, &pl.Pos)
groundMat.SetUnifVec3(posStr, &pl.Pos)
palleteMat.SetUnifVec3(posStr, &pl.Pos)
} }
if imgui.ColorEdit3("Diffuse Color", &pl.DiffuseColor.Data) { if imgui.ColorEdit3("Diffuse Color", &pl.DiffuseColor.Data) {
whiteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor)
containerMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) diffStr := indexString + ".diffuseColor"
groundMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor)
palleteMat.SetUnifVec3(indexString+".diffuseColor", &pl.DiffuseColor) whiteMat.SetUnifVec3(diffStr, &pl.DiffuseColor)
containerMat.SetUnifVec3(diffStr, &pl.DiffuseColor)
groundMat.SetUnifVec3(diffStr, &pl.DiffuseColor)
palleteMat.SetUnifVec3(diffStr, &pl.DiffuseColor)
} }
if imgui.ColorEdit3("Specular Color", &pl.SpecularColor.Data) { if imgui.ColorEdit3("Specular Color", &pl.SpecularColor.Data) {
whiteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor)
containerMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) specularStr := indexString + ".specularColor"
groundMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor)
palleteMat.SetUnifVec3(indexString+".specularColor", &pl.SpecularColor) whiteMat.SetUnifVec3(specularStr, &pl.SpecularColor)
containerMat.SetUnifVec3(specularStr, &pl.SpecularColor)
groundMat.SetUnifVec3(specularStr, &pl.SpecularColor)
palleteMat.SetUnifVec3(specularStr, &pl.SpecularColor)
}
if imgui.DragFloatV("Falloff", &pl.Falloff, 0.1, 0.001, 100, "%.3f", imgui.SliderFlagsNone) {
falloffStr := indexString + ".falloff"
whiteMat.SetUnifFloat32(falloffStr, pl.Falloff)
containerMat.SetUnifFloat32(falloffStr, pl.Falloff)
groundMat.SetUnifFloat32(falloffStr, pl.Falloff)
palleteMat.SetUnifFloat32(falloffStr, pl.Falloff)
}
if imgui.DragFloatV("Radius", &pl.Radius, 0.2, 0, 500, "%.3f", imgui.SliderFlagsNone) {
falloffStr := indexString + ".radius"
whiteMat.SetUnifFloat32(falloffStr, pl.Radius)
containerMat.SetUnifFloat32(falloffStr, pl.Radius)
groundMat.SetUnifFloat32(falloffStr, pl.Radius)
palleteMat.SetUnifFloat32(falloffStr, pl.Radius)
} }
imgui.TreePop() imgui.TreePop()

View File

@ -35,9 +35,8 @@ struct PointLight {
vec3 pos; vec3 pos;
vec3 diffuseColor; vec3 diffuseColor;
vec3 specularColor; vec3 specularColor;
float constant; float falloff;
float linear; float radius;
float quadratic;
float farPlane; float farPlane;
}; };
uniform PointLight pointLights[NUM_POINT_LIGHTS]; uniform PointLight pointLights[NUM_POINT_LIGHTS];
@ -162,9 +161,8 @@ struct PointLight {
vec3 pos; vec3 pos;
vec3 diffuseColor; vec3 diffuseColor;
vec3 specularColor; vec3 specularColor;
float constant; float falloff;
float linear; float radius;
float quadratic;
float farPlane; float farPlane;
}; };
uniform PointLight pointLights[NUM_POINT_LIGHTS]; uniform PointLight pointLights[NUM_POINT_LIGHTS];
@ -272,10 +270,60 @@ float CalcPointShadow(int lightIndex, vec3 worldLightPos, vec3 tangentLightDir,
return shadow; return shadow;
} }
//
// The following point light attenuation formulas
// are from https://lisyarus.github.io/blog/posts/point-light-attenuation.html
//
// I found them more intuitive than the standard implementation and it also ensures
// we have zero light at the selected distance.
//
float sqr(float x)
{
return x * x;
}
// This version doesn't have a harsh cutoff at radius
float AttenuateNoCusp(float dist, float radius, float falloff)
{
// Since we only use this as attenuation and max intensity defines
// the max output value, anything more than 1 would increase
// the output of the light, which I don't think makes sense for
// our attenuation purposes.
//
// Seems to me this can be done simply by increasing color values above 255.
//
// Forcing to 1 for now.
#define MAX_INTENSITY 1
float s = dist / radius;
if (s >= 1.0)
return 0.0;
float s2 = sqr(s);
return MAX_INTENSITY * sqr(1 - s2) / (1 + falloff * s2);
}
// This version has a harsh/immediate cutoff at radius
float AttenuateCusp(float dist, float radius, float falloff)
{
#define MAX_INTENSITY 1
float s = dist / radius;
if (s >= 1.0)
return 0.0;
float s2 = sqr(s);
return MAX_INTENSITY * sqr(1 - s2) / (1 + falloff * s);
}
vec3 CalcPointLight(PointLight pointLight, int lightIndex) vec3 CalcPointLight(PointLight pointLight, int lightIndex)
{ {
// Ignore unset lights // Ignore inactive lights
if (pointLight.constant == 0){ if (pointLight.radius == 0){
return vec3(0); return vec3(0);
} }
@ -293,7 +341,7 @@ vec3 CalcPointLight(PointLight pointLight, int lightIndex)
// Attenuation // Attenuation
float distToLight = length(tangentLightPos - tangentFragPos); float distToLight = length(tangentLightPos - tangentFragPos);
float attenuation = 1 / (pointLight.constant + pointLight.linear * distToLight + pointLight.quadratic * (distToLight * distToLight)); float attenuation = AttenuateNoCusp(distToLight, pointLight.radius, pointLight.falloff);
// Shadow // Shadow
float shadow = CalcPointShadow(lightIndex, pointLight.pos, tangentLightDir, pointLight.farPlane); float shadow = CalcPointShadow(lightIndex, pointLight.pos, tangentLightDir, pointLight.farPlane);