feat(draw): support linear lights
This commit is contained in:
parent
a35dab4e61
commit
8ac141daf4
@ -227,6 +227,7 @@ namespace spades {
|
||||
class DynamicLightParam {
|
||||
DynamicLightType type;
|
||||
Vector3 origin;
|
||||
Vector3 point2;
|
||||
float radius;
|
||||
Vector3 color;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 ¶m = GetParam();
|
||||
|
||||
if (param.type == client::DynamicLightTypeSpotlight) {
|
||||
for (const Plane3 &plane : clipPlanes) {
|
||||
if (!PlaneCullTest(plane, box)) {
|
||||
@ -72,8 +78,19 @@ namespace spades {
|
||||
}
|
||||
}
|
||||
|
||||
const client::DynamicLightParam ¶m = 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 ¢er, 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;
|
||||
|
@ -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 ¶m);
|
||||
const client::DynamicLightParam &GetParam() const { return param; }
|
||||
|
@ -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);
|
||||
|
@ -42,6 +42,9 @@ namespace spades {
|
||||
GLProgramUniform dynamicLightRadiusInversed;
|
||||
GLProgramUniform dynamicLightSpotMatrix;
|
||||
GLProgramUniform dynamicLightProjectionTexture;
|
||||
GLProgramUniform dynamicLightIsLinear;
|
||||
GLProgramUniform dynamicLightLinearDirection;
|
||||
GLProgramUniform dynamicLightLinearLength;
|
||||
|
||||
public:
|
||||
GLDynamicLightShader();
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user