mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 22:31:05 +00:00
[vulkan] Implement all the extended light models
Standard quake has just linear, but the modding community added inverse, inverse-square (raw and offset (1/(r^2+1)), infinite (sun), and ambient (minlight). Other than the lack of shadows, marcher now looks really good.
This commit is contained in:
parent
52168da93e
commit
ae231319ea
3 changed files with 281 additions and 64 deletions
|
@ -41,7 +41,7 @@ typedef struct qfv_light_s {
|
|||
vec3_t color;
|
||||
int data;
|
||||
vec3_t position;
|
||||
float radius;
|
||||
float light;
|
||||
vec3_t direction;
|
||||
float cone;
|
||||
} qfv_light_t;
|
||||
|
@ -55,9 +55,8 @@ typedef struct qfv_lightvisset_s DARRAY_TYPE (byte) qfv_lightvisset_t;
|
|||
|
||||
typedef struct qfv_light_buffer_s {
|
||||
float intensity[NUM_STYLES + 4];
|
||||
int cascadeCount;
|
||||
int planeCount;
|
||||
int cubeCount;
|
||||
float distFactor1;
|
||||
float distFactor2;
|
||||
int lightCount;
|
||||
qfv_light_t lights[NUM_LIGHTS] __attribute__((aligned(16)));
|
||||
mat4f_t shadowMat[NUM_LIGHTS];
|
||||
|
|
|
@ -10,12 +10,28 @@ struct LightData {
|
|||
vec3 color;
|
||||
int data;// bits 0-6: intensity key (however, values 0-66)
|
||||
vec3 position;
|
||||
float radius;
|
||||
float light; // doubles as radius for linear
|
||||
vec3 direction;
|
||||
float cone;
|
||||
};
|
||||
|
||||
#define MaxLights 256
|
||||
#define MaxLights 256
|
||||
|
||||
#define StyleMask 0x07f
|
||||
#define ModelMask 0x380
|
||||
#define ShadowMask 0xc00
|
||||
|
||||
#define LM_LINEAR (0 << 7) // light - dist (or radius + dist if -ve)
|
||||
#define LM_INVERSE (1 << 7) // distFactor1 * light / dist
|
||||
#define LM_INVERSE2 (2 << 7) // distFactor2 * light / (dist * dist)
|
||||
#define LM_INFINITE (3 << 7) // light
|
||||
#define LM_AMBIENT (4 << 7) // light
|
||||
#define LM_INVERSE3 (5 << 7) // distFactor2 * light / (dist + distFactor2)**2
|
||||
|
||||
#define ST_NONE (0 << 10) // no shadows
|
||||
#define ST_CASCADE (1 << 10) // cascaded shadow maps
|
||||
#define ST_PLANE (2 << 10) // single plane shadow map (small spotlight)
|
||||
#define ST_CUBE (3 << 10) // cubemap (omni, large spotlight)
|
||||
|
||||
layout (set = 2, binding = 0) uniform sampler2DArrayShadow shadowCascade[MaxLights];
|
||||
layout (set = 2, binding = 0) uniform sampler2DShadow shadowPlane[MaxLights];
|
||||
|
@ -23,7 +39,9 @@ layout (set = 2, binding = 0) uniform samplerCubeShadow shadowCube[MaxLights];
|
|||
|
||||
layout (set = 1, binding = 0) uniform Lights {
|
||||
vec4 intensity[17]; // 68 floats
|
||||
ivec4 lightCounts;
|
||||
float distFactor1; // for inverse
|
||||
float distFactor2; // for inverse2 and inverse2
|
||||
int lightCount;
|
||||
LightData lights[MaxLights];
|
||||
mat4 shadowMat[MaxLights];
|
||||
vec4 shadowCascale[MaxLights];
|
||||
|
@ -31,45 +49,80 @@ layout (set = 1, binding = 0) uniform Lights {
|
|||
|
||||
layout (location = 0) out vec4 frag_color;
|
||||
|
||||
vec3
|
||||
calc_light (LightData light, vec3 position, vec3 normal)
|
||||
float
|
||||
spot_cone (LightData light, vec3 incoming)
|
||||
{
|
||||
float r = light.radius;
|
||||
vec3 dist = light.position - position;
|
||||
float d = sqrt (dot (dist, dist));
|
||||
if (d > r) {
|
||||
// the light is too far away
|
||||
return vec3 (0);
|
||||
}
|
||||
|
||||
vec3 incoming = dist / d;
|
||||
float spotdot = dot (incoming, light.direction);
|
||||
float lightdot = dot (incoming, normal);
|
||||
|
||||
int style = light.data & 0x7f;
|
||||
// deliberate array index error: access intensity2 as well
|
||||
float i = intensity[style / 4][style % 4];
|
||||
i *= step (d, r) * clamp (lightdot, 0, 1);
|
||||
i *= smoothstep (spotdot, 1 - (1 - spotdot) * 0.995, light.cone);
|
||||
return light.color * i * (r - d);
|
||||
return smoothstep (spotdot, 1 - (1 - spotdot) * 0.995, light.cone);
|
||||
}
|
||||
|
||||
vec3
|
||||
float
|
||||
diffuse (vec3 incoming, vec3 normal)
|
||||
{
|
||||
float lightdot = dot (incoming, normal);
|
||||
return clamp (lightdot, 0, 1);
|
||||
}
|
||||
|
||||
float
|
||||
light_linear (LightData light, float d)
|
||||
{
|
||||
float l = light.light;
|
||||
if (l < 0) {
|
||||
return min (l + d, 0);
|
||||
} else {
|
||||
return max (l - d, 0);
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
light_inverse (LightData light, float d)
|
||||
{
|
||||
float l = light.light;
|
||||
return l / (distFactor1 * d);
|
||||
}
|
||||
|
||||
float
|
||||
light_inverse2 (LightData light, float d)
|
||||
{
|
||||
float l = light.light;
|
||||
return l / (distFactor2 * d);
|
||||
}
|
||||
|
||||
float
|
||||
light_infinite (LightData light)
|
||||
{
|
||||
return light.light;
|
||||
}
|
||||
|
||||
float
|
||||
light_ambient (LightData light)
|
||||
{
|
||||
return light.light;
|
||||
}
|
||||
|
||||
float
|
||||
light_inverse3 (LightData light, float d)
|
||||
{
|
||||
float l = light.light;
|
||||
return l / (distFactor2 * d + 1);
|
||||
}
|
||||
|
||||
float
|
||||
shadow_cascade (sampler2DArrayShadow map)
|
||||
{
|
||||
return vec3(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vec3
|
||||
float
|
||||
shadow_plane (sampler2DShadow map)
|
||||
{
|
||||
return vec3(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vec3
|
||||
float
|
||||
shadow_cube (samplerCubeShadow map)
|
||||
{
|
||||
return vec3(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -83,23 +136,59 @@ main (void)
|
|||
vec3 light = vec3 (0);
|
||||
|
||||
if (MaxLights > 0) {
|
||||
int i = 0;
|
||||
while (i < lightCounts.x) {
|
||||
shadow_cascade (shadowCascade[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < lightCounts.y) {
|
||||
shadow_plane (shadowPlane[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < lightCounts.z) {
|
||||
shadow_cube (shadowCube[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < lightCounts.w) {
|
||||
light += calc_light (lights[i], p, n);
|
||||
i++;
|
||||
vec3 minLight = vec3 (0);
|
||||
for (int i = 0; i < lightCount; i++) {
|
||||
vec3 dist = lights[i].position - p;
|
||||
float d = dot (dist, dist);
|
||||
int model = lights[i].data & ModelMask;
|
||||
|
||||
if (model != LM_INFINITE
|
||||
&& d > lights[i].light * lights[i].light) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float l = 0;
|
||||
if (model == LM_LINEAR) {
|
||||
d = sqrt (d);
|
||||
l = light_linear (lights[i], d);
|
||||
} else if (model == LM_INVERSE) {
|
||||
d = sqrt (d);
|
||||
l = light_inverse (lights[i], d);
|
||||
} else if (model == LM_INVERSE2) {
|
||||
l = light_inverse2 (lights[i], d);
|
||||
d = sqrt (d);
|
||||
} else if (model == LM_INFINITE) {
|
||||
l = light_infinite (lights[i]);
|
||||
dist = lights[i].direction;
|
||||
d = -1;
|
||||
} else if (model == LM_AMBIENT) {
|
||||
l = light_ambient (lights[i]);
|
||||
} else if (model == LM_INVERSE3) {
|
||||
l = light_inverse3 (lights[i], d);
|
||||
d = sqrt (d);
|
||||
}
|
||||
|
||||
int style = lights[i].data & StyleMask;
|
||||
l *= intensity[style / 4][style % 4];
|
||||
|
||||
int shadow = lights[i].data & ShadowMask;
|
||||
if (shadow == ST_CASCADE) {
|
||||
l *= shadow_cascade (shadowCascade[i]);
|
||||
} else if (shadow == ST_PLANE) {
|
||||
l *= shadow_plane (shadowPlane[i]);
|
||||
} else if (shadow == ST_CUBE) {
|
||||
l *= shadow_cube (shadowCube[i]);
|
||||
}
|
||||
|
||||
if (model == LM_AMBIENT) {
|
||||
minLight = max (l * lights[i].color, minLight);
|
||||
} else {
|
||||
vec3 incoming = dist / d;
|
||||
l *= spot_cone (lights[i], incoming) * diffuse (incoming, n);
|
||||
light += l * lights[i].color;
|
||||
}
|
||||
}
|
||||
light = max (light, minLight);
|
||||
}
|
||||
frag_color = vec4 (c * light + e, 1);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,8 @@ find_visible_lights (vulkan_ctx_t *ctx)
|
|||
memset (lframe->lightvis.a, 0, lframe->lightvis.size * sizeof (byte));
|
||||
for (size_t i = 0; i < lctx->lightleafs.size; i++) {
|
||||
int l = lctx->lightleafs.a[i];
|
||||
if (lframe->pvs[l / 8] & (1 << (l % 8))) {
|
||||
//FIXME -1 needs check for sky
|
||||
if (l == -1 || lframe->pvs[l / 8] & (1 << (l % 8))) {
|
||||
lframe->lightvis.a[i] = 1;
|
||||
visible++;
|
||||
}
|
||||
|
@ -132,9 +133,9 @@ update_lights (vulkan_ctx_t *ctx)
|
|||
light_data->intensity[66] = 1 / 16.0;
|
||||
light_data->intensity[67] = 1 / 16.0;
|
||||
|
||||
light_data->cascadeCount = 0;
|
||||
light_data->planeCount = 0;
|
||||
light_data->cubeCount = 0;
|
||||
light_data->distFactor1 = 1 / 128.0;
|
||||
light_data->distFactor2 = 1 / 16384.0;
|
||||
|
||||
light_data->lightCount = 0;
|
||||
R_FindNearLights (r_origin, NUM_LIGHTS - 1, lights);
|
||||
for (int i = 0; i < NUM_LIGHTS - 1; i++) {
|
||||
|
@ -144,7 +145,7 @@ update_lights (vulkan_ctx_t *ctx)
|
|||
light_data->lightCount++;
|
||||
VectorCopy (lights[i]->color, light_data->lights[i].color);
|
||||
VectorCopy (lights[i]->origin, light_data->lights[i].position);
|
||||
light_data->lights[i].radius = lights[i]->radius;
|
||||
light_data->lights[i].light = lights[i]->radius;
|
||||
light_data->lights[i].data = 64; // default dynamic light
|
||||
VectorZero (light_data->lights[i].direction);
|
||||
light_data->lights[i].cone = 1;
|
||||
|
@ -406,11 +407,121 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
|
|||
free (lctx);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_light (qfv_light_t *light, int leaf)
|
||||
{
|
||||
Sys_MaskPrintf (SYS_vulkan,
|
||||
"[%g, %g, %g] %d %d %d, "
|
||||
"[%g %g %g] %g, [%g %g %g] %g, %d\n",
|
||||
VectorExpand (light->color),
|
||||
(light->data & 0x07f),
|
||||
(light->data & 0x380) >> 7,
|
||||
(light->data & 0xc00) >> 10,
|
||||
VectorExpand (light->position), light->light,
|
||||
VectorExpand (light->direction), light->cone,
|
||||
leaf);
|
||||
}
|
||||
|
||||
static float
|
||||
parse_float (const char *str, float defval)
|
||||
{
|
||||
float val = defval;
|
||||
if (str) {
|
||||
char *end;
|
||||
val = strtof (str, &end);
|
||||
if (end == str) {
|
||||
val = defval;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_vector (const char *str, vec_t *val)
|
||||
{
|
||||
if (str) {
|
||||
int num = sscanf (str, "%f %f %f", VectorExpandAddr (val));
|
||||
while (num < 3) {
|
||||
val[num++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
ecos (float ang)
|
||||
{
|
||||
if (ang == 90 || ang == -90) {
|
||||
return 0;
|
||||
}
|
||||
if (ang == 180 || ang == -180) {
|
||||
return -1;
|
||||
}
|
||||
if (ang == 0 || ang == 360) {
|
||||
return 1;
|
||||
}
|
||||
return cos (ang * M_PI / 180);
|
||||
}
|
||||
|
||||
static float
|
||||
esin (float ang)
|
||||
{
|
||||
if (ang == 90) {
|
||||
return 1;
|
||||
}
|
||||
if (ang == -90) {
|
||||
return -1;
|
||||
}
|
||||
if (ang == 180 || ang == -180) {
|
||||
return 0;
|
||||
}
|
||||
if (ang == 0 || ang == 360) {
|
||||
return 0;
|
||||
}
|
||||
return sin (ang * M_PI / 180);
|
||||
}
|
||||
|
||||
static void
|
||||
sun_vector (const vec_t *ang, vec_t *vec)
|
||||
{
|
||||
// ang is yaw, pitch (maybe roll, but ignored
|
||||
vec[0] = ecos (ang[1]) * ecos (ang[0]);
|
||||
vec[1] = ecos (ang[1]) * esin (ang[0]);
|
||||
vec[2] = esin (ang[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_sun (lightingctx_t *lctx, plitem_t *entity)
|
||||
{
|
||||
qfv_light_t light = {};
|
||||
float sunlight;
|
||||
//float sunlight2;
|
||||
vec3_t sunangle = { 0, -90, 0 };
|
||||
|
||||
sunlight = parse_float (PL_String (PL_ObjectForKey (entity,
|
||||
"_sunlight")), 0);
|
||||
//sunlight2 = parse_float (PL_String (PL_ObjectForKey (entity,
|
||||
// "_sunlight2")), 0);
|
||||
parse_vector (PL_String (PL_ObjectForKey (entity, "_sun_mangle")),
|
||||
sunangle);
|
||||
if (sunlight <= 0) {
|
||||
return;
|
||||
}
|
||||
VectorSet (1, 1, 1, light.color);
|
||||
light.data = 3 << 7; //FIXME magic number (LM_INFINITE)
|
||||
light.light = sunlight;
|
||||
sun_vector (sunangle, light.direction);
|
||||
light.cone = 1;
|
||||
DARRAY_APPEND (&lctx->lights, light);
|
||||
DARRAY_APPEND (&lctx->lightleafs, -1);
|
||||
dump_light (&light, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_light (qfv_light_t *light, const plitem_t *entity,
|
||||
const plitem_t *targets)
|
||||
{
|
||||
const char *str;
|
||||
int model = 0;
|
||||
|
||||
/*Sys_Printf ("{\n");
|
||||
for (int i = PL_D_NumKeys (entity); i-- > 0; ) {
|
||||
|
@ -422,7 +533,7 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
|
|||
|
||||
light->cone = 1;
|
||||
light->data = 0;
|
||||
light->radius = 300;
|
||||
light->light = 300;
|
||||
VectorSet (1, 1, 1, light->color);
|
||||
|
||||
if ((str = PL_String (PL_ObjectForKey (entity, "origin")))) {
|
||||
|
@ -449,16 +560,34 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
|
|||
|
||||
if ((str = PL_String (PL_ObjectForKey (entity, "light_lev")))
|
||||
|| (str = PL_String (PL_ObjectForKey (entity, "_light")))) {
|
||||
light->radius = atof (str);
|
||||
light->light = atof (str);
|
||||
}
|
||||
|
||||
if ((str = PL_String (PL_ObjectForKey (entity, "style")))) {
|
||||
light->data = atoi (str) & 0x3f;
|
||||
}
|
||||
|
||||
if ((str = PL_String (PL_ObjectForKey (entity, "delay")))) {
|
||||
model = atoi (str) & 0x7;
|
||||
if (model == 2) model = 5; //FIXME for marcher (need a map)
|
||||
light->data |= model << 7;
|
||||
}
|
||||
|
||||
if ((str = PL_String (PL_ObjectForKey (entity, "color")))
|
||||
|| (str = PL_String (PL_ObjectForKey (entity, "_color")))) {
|
||||
sscanf (str, "%f %f %f", VectorExpandAddr (light->color));
|
||||
VectorScale (light->color, 1/255.0, light->color);
|
||||
}
|
||||
|
||||
//FIXME magic numbers
|
||||
if (model == 3) { // infinite
|
||||
light->data |= 1 << 10; // cascade
|
||||
} else if (model != 4) {// ambient
|
||||
if (light->cone > -0.5) {
|
||||
light->data |= 3 << 10; // cube
|
||||
} else {
|
||||
light->data |= 2 << 10; // plane
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,11 +629,17 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx)
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < PL_A_NumObjects (entities); i++) {
|
||||
for (int i = 0; i < PL_A_NumObjects (entities); i++) {
|
||||
plitem_t *entity = PL_ObjectAtIndex (entities, i);
|
||||
const char *classname = PL_String (PL_ObjectForKey (entity,
|
||||
"classname"));
|
||||
if (classname && strnequal (classname, "light", 5)) {
|
||||
if (!classname) {
|
||||
continue;
|
||||
}
|
||||
if (strequal (classname, "worldspawn")) {
|
||||
// parse_sun can add many lights
|
||||
parse_sun (lctx, entity);
|
||||
} else if (strnequal (classname, "light", 5)) {
|
||||
qfv_light_t light = {};
|
||||
|
||||
parse_light (&light, entity, targets);
|
||||
|
@ -512,13 +647,7 @@ Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx)
|
|||
mleaf_t *leaf = Mod_PointInLeaf (&light.position[0],
|
||||
model);
|
||||
DARRAY_APPEND (&lctx->lightleafs, leaf - model->brush.leafs);
|
||||
Sys_MaskPrintf (SYS_vulkan,
|
||||
"[%g, %g, %g] %d, "
|
||||
"[%g %g %g] %g, [%g %g %g] %g, %zd\n",
|
||||
VectorExpand (light.color), light.data,
|
||||
VectorExpand (light.position), light.radius,
|
||||
VectorExpand (light.direction), light.cone,
|
||||
leaf - model->brush.leafs);
|
||||
dump_light (&light, leaf - model->brush.leafs);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < ctx->frames.size; i++) {
|
||||
|
|
Loading…
Reference in a new issue