feat(draw): support linear lights

This commit is contained in:
yvt 2021-05-01 23:58:53 +09:00
parent a35dab4e61
commit 8ac141daf4
9 changed files with 98 additions and 10 deletions

View File

@ -227,6 +227,7 @@ namespace spades {
class DynamicLightParam {
DynamicLightType type;
Vector3 origin;
Vector3 point2;
float radius;
Vector3 color;

View File

@ -23,6 +23,10 @@
uniform vec3 dynamicLightOrigin;
uniform mat4 dynamicLightSpotMatrix;
uniform bool dynamicLightIsLinear;
uniform vec3 dynamicLightLinearDirection;
uniform float dynamicLightLinearLength;
void PrepareForShadow_Map(vec3 vertexCoord) ;
@ -32,8 +36,18 @@ varying vec3 lightTexCoord;
void PrepareForDynamicLightNoBump(vec3 vertexCoord, vec3 normal) {
PrepareForShadow_Map(vertexCoord);
lightPos = dynamicLightOrigin - vertexCoord;
vec3 lightPosition = dynamicLightOrigin;
if (dynamicLightIsLinear) {
// Linear light approximation - choose the closest point on the light
// geometry as the representative light source
float d = dot((vertexCoord - dynamicLightOrigin), dynamicLightLinearDirection);
d = clamp(d, 0.0, dynamicLightLinearLength);
lightPosition += dynamicLightLinearDirection * d;
}
lightPos = lightPosition - vertexCoord;
lightNormal = normal;
// projection

View File

@ -107,8 +107,12 @@ namespace spades {
void StartScene(const SceneDefinition &) { OnProhibitedAction(); }
void AddLight(const client::DynamicLightParam &light) {
Vector3 rad(light.radius, light.radius, light.radius);
if (CheckVisibility(AABB3(light.origin - rad, light.origin + rad))) {
AABB3 aabb{light.origin, light.origin};
if (light.type == DynamicLightTypeLinear) {
aabb += light.point2;
}
if (CheckVisibility(aabb.Inflate(light.radius))) {
base->AddLight(light);
}
}

View File

@ -62,7 +62,11 @@ namespace spades {
float opacity = 1.0;
};
enum DynamicLightType { DynamicLightTypePoint, DynamicLightTypeSpotlight };
enum DynamicLightType {
DynamicLightTypePoint,
DynamicLightTypeSpotlight,
DynamicLightTypeLinear
};
struct DynamicLightParam {
DynamicLightType type = DynamicLightTypePoint;
@ -72,7 +76,13 @@ namespace spades {
* is unaffected by the light. */
float radius;
Vector3 color;
/**
* The second position of the light.
*
* For `DyanmicLightTypeLinear`, this specifies the second endpoint's position. For
* other light types, this value is ignored.
*/
Vector3 point2;
/** The basis vectors specifying the orientation of a spotlight.
* See the existing code for usage. */
std::array<Vector3, 3> spotAxis;

View File

@ -61,9 +61,15 @@ namespace spades {
clipPlanes[i] = Plane3::PlaneWithPointOnPlane(param.origin, planeN[i]);
}
}
if (param.type == client::DynamicLightTypeLinear) {
poweredLength = (param.point2 - param.origin).GetPoweredLength();
}
}
bool GLDynamicLight::Cull(const spades::AABB3 &box) const {
const client::DynamicLightParam &param = GetParam();
if (param.type == client::DynamicLightTypeSpotlight) {
for (const Plane3 &plane : clipPlanes) {
if (!PlaneCullTest(plane, box)) {
@ -72,8 +78,19 @@ namespace spades {
}
}
const client::DynamicLightParam &param = GetParam();
return box.Inflate(param.radius) && param.origin;
AABB3 inflatedBox = box.Inflate(param.radius);
if (param.type == client::DynamicLightTypeLinear) {
Vector3 intersection;
// TODO: using `OBB3` here is overkill, but `AABB3` doesn't have `RayCast`
if (!OBB3(inflatedBox)
.RayCast(param.origin, param.point2 - param.origin, &intersection)) {
return false;
}
return (intersection - param.origin).GetPoweredLength() <= poweredLength;
}
return inflatedBox && param.origin;
}
bool GLDynamicLight::SphereCull(const spades::Vector3 &center, float radius) const {
@ -85,6 +102,9 @@ namespace spades {
return false;
}
}
} else if (param.type == client::DynamicLightTypeLinear) {
return Line3::MakeLineSegment(param.origin, param.point2).GetDistanceTo(center) <
radius + param.radius;
}
float maxDistance = radius + param.radius;

View File

@ -33,6 +33,9 @@ namespace spades {
/** World-space clip planes (spotlight only) */
std::array<Plane3, 4> clipPlanes;
/** `(point2 - origin).GetPoweredLength()` (linear light only) */
float poweredLength;
public:
GLDynamicLight(const client::DynamicLightParam &param);
const client::DynamicLightParam &GetParam() const { return param; }

View File

@ -32,7 +32,10 @@ namespace spades {
dynamicLightRadius("dynamicLightRadius"),
dynamicLightRadiusInversed("dynamicLightRadiusInversed"),
dynamicLightSpotMatrix("dynamicLightSpotMatrix"),
dynamicLightProjectionTexture("dynamicLightProjectionTexture")
dynamicLightProjectionTexture("dynamicLightProjectionTexture"),
dynamicLightIsLinear("dynamicLightIsLinear"),
dynamicLightLinearDirection("dynamicLightLinearDirection"),
dynamicLightLinearLength("dynamicLightLinearLength")
{
lastRenderer = NULL;
@ -70,6 +73,9 @@ namespace spades {
dynamicLightRadiusInversed(program);
dynamicLightSpotMatrix(program);
dynamicLightProjectionTexture(program);
dynamicLightIsLinear(program);
dynamicLightLinearDirection(program);
dynamicLightLinearLength(program);
dynamicLightOrigin.SetValue(param.origin.x, param.origin.y, param.origin.z);
dynamicLightColor.SetValue(param.color.x, param.color.y, param.color.z);
@ -90,7 +96,9 @@ namespace spades {
device.TexParamater(IGLDevice::Texture2D, IGLDevice::TextureWrapT,
IGLDevice::ClampToEdge);
} else {
dynamicLightIsLinear.SetValue(0);
} else if (param.type == client::DynamicLightTypePoint ||
param.type == client::DynamicLightTypeLinear) {
device.ActiveTexture(texStage);
whiteImage->Bind(IGLDevice::Texture2D);
dynamicLightProjectionTexture.SetValue(texStage);
@ -100,6 +108,24 @@ namespace spades {
// UV is in a valid range so the fragments are not discarded.
dynamicLightSpotMatrix.SetValue(Matrix4::Translate(0.5, 0.5, 0.0) *
Matrix4::Scale(0.0));
if (param.type == client::DynamicLightTypeLinear) {
// Convert two endpoints to one endpoint + direction + length.
// `Vector3::Normalize` is no-op when the length is zero,
// therefore the zero-length case is handled.
Vector3 direction = param.point2 - param.origin;
float length = direction.GetLength();
direction = direction.Normalize();
dynamicLightLinearDirection.SetValue(direction.x, direction.y, direction.z);
dynamicLightLinearLength.SetValue(length);
dynamicLightIsLinear.SetValue(1);
} else {
dynamicLightIsLinear.SetValue(0);
}
} else {
SPUnreachable();
}
device.ActiveTexture(texStage);

View File

@ -42,6 +42,9 @@ namespace spades {
GLProgramUniform dynamicLightRadiusInversed;
GLProgramUniform dynamicLightSpotMatrix;
GLProgramUniform dynamicLightProjectionTexture;
GLProgramUniform dynamicLightIsLinear;
GLProgramUniform dynamicLightLinearDirection;
GLProgramUniform dynamicLightLinearLength;
public:
GLDynamicLightShader();

View File

@ -288,6 +288,9 @@ namespace spades {
r = eng->RegisterEnumValue("DynamicLightType", "Spotlight",
DynamicLightTypeSpotlight);
manager->CheckError(r);
r = eng->RegisterEnumValue("DynamicLightType", "Linear",
DynamicLightTypeLinear);
manager->CheckError(r);
r = eng->RegisterObjectBehaviour("ModelRenderParam",
asBEHAVE_CONSTRUCT,
@ -343,6 +346,10 @@ namespace spades {
"Vector3 origin",
asOFFSET(DynamicLightParam, origin));
manager->CheckError(r);
r = eng->RegisterObjectProperty("DynamicLightParam",
"Vector3 point2",
asOFFSET(DynamicLightParam, point2));
manager->CheckError(r);
r = eng->RegisterObjectProperty("DynamicLightParam",
"float radius",
asOFFSET(DynamicLightParam, radius));