diff --git a/include/reign.h b/include/reign.h index 90c30ad..1ec6e91 100644 --- a/include/reign.h +++ b/include/reign.h @@ -40,6 +40,11 @@ enum RE_motion_state { RE_MOTION_STATE_LOOP = 2, }; +enum RE_fog_type { + RE_FOG_LINEAR = 1, + RE_FOG_LIGHT_SCATTERING = 2, +}; + struct RE_options { int anti_aliasing; int wait_vsync; @@ -74,16 +79,26 @@ struct RE_plugin { struct RE_back_cg back_cg[RE_NR_BACK_CGS]; - vec3 shadow_map_light_dir; - float shadow_bias; - // Settings. int render_mode; int shadow_mode; + vec3 shadow_map_light_dir; + float shadow_bias; int bump_mode; int bloom_mode; int glare_mode; int fog_mode; + enum RE_fog_type fog_type; + float fog_near; + float fog_far; + vec3 fog_color; + float ls_beta_r; + float ls_beta_m; + float ls_g; + float ls_distance; + vec3 ls_light_dir; + vec3 ls_light_color; + vec3 ls_sun_color; int specular_mode; int light_map_mode; int soft_fog_edge_mode; diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl index e2dad09..4b6210c 100644 --- a/shaders/reign.f.glsl +++ b/shaders/reign.f.glsl @@ -15,6 +15,8 @@ */ #define NR_DIR_LIGHTS 3 +#define FOG_LINEAR 1 +#define FOG_LIGHT_SCATTERING 2 struct dir_light { vec3 dir; @@ -41,15 +43,22 @@ uniform vec3 rim_color; uniform bool use_light_map; uniform bool use_shadow_map; uniform float shadow_bias; +uniform int fog_type; +uniform float fog_near; +uniform float fog_far; +uniform vec3 fog_color; in vec2 tex_coord; in vec2 light_tex_coord; in vec3 frag_pos; in vec4 shadow_frag_pos; +in float dist; in vec3 eye; in vec3 normal; in vec3 light_dir[NR_DIR_LIGHTS]; in vec3 specular_dir; +in vec3 ls_ex; +in vec3 ls_in; out vec4 frag_color; void main() { @@ -92,6 +101,14 @@ void main() { frag_rgb += pow(1.0 - max(dot(normalize(normal), view_dir), 0.0), rim_exponent) * rim_color; } + // Fog + if (fog_type == FOG_LINEAR) { + float fog_factor = clamp((dist - fog_near) / (fog_far - fog_near), 0.0, 1.0); + frag_rgb = mix(frag_rgb, fog_color, fog_factor); + } else if (fog_type == FOG_LIGHT_SCATTERING) { + frag_rgb = frag_rgb * ls_ex + ls_in; + } + // Shadow mapping if (use_shadow_map) { vec3 shadow_coords = shadow_frag_pos.xyz / shadow_frag_pos.w * 0.5 + 0.5; diff --git a/shaders/reign.v.glsl b/shaders/reign.v.glsl index be7a33d..f1e9d23 100644 --- a/shaders/reign.v.glsl +++ b/shaders/reign.v.glsl @@ -15,6 +15,9 @@ */ #define NR_DIR_LIGHTS 3 +#define FOG_LIGHT_SCATTERING 2 + +const float PI = 3.14159265358979323846; struct dir_light { vec3 dir; @@ -34,10 +37,15 @@ uniform mat4 bone_matrices[MAX_BONES]; uniform bool use_normal_map; -uniform vec3 view_pos; +uniform vec3 camera_pos; uniform dir_light dir_lights[NR_DIR_LIGHTS]; uniform vec3 specular_light_dir; uniform mat4 shadow_transform; +uniform int fog_type; +uniform vec4 ls_params; // (beta_r, beta_m, g, distance) +uniform vec3 ls_light_dir; +uniform vec3 ls_light_color; +uniform vec3 ls_sun_color; in vec3 vertex_pos; in vec3 vertex_normal; @@ -51,10 +59,13 @@ out vec2 tex_coord; out vec2 light_tex_coord; out vec3 frag_pos; out vec4 shadow_frag_pos; +out float dist; out vec3 eye; out vec3 normal; out vec3 light_dir[NR_DIR_LIGHTS]; out vec3 specular_dir; +out vec3 ls_ex; +out vec3 ls_in; void main() { mat4 local_bone_transform = local_transform; @@ -83,19 +94,39 @@ void main() { } vec4 world_pos = local_bone_transform * vec4(vertex_pos, 1.0); - gl_Position = proj_transform * view_transform * world_pos; + vec4 view_pos = view_transform * world_pos; + gl_Position = proj_transform * view_pos; tex_coord = vertex_uv; light_tex_coord = vertex_light_uv; - + dist = -view_pos.z; shadow_frag_pos = shadow_transform * world_pos; // These are in tangent-space if use_normal_map is true, in world-space // otherwise. frag_pos = TBN * vec3(world_pos); - eye = TBN * view_pos; + eye = TBN * camera_pos; light_dir[0] = TBN * dir_lights[0].dir; light_dir[1] = TBN * dir_lights[1].dir; light_dir[2] = TBN * dir_lights[2].dir; specular_dir = TBN * specular_light_dir; + + if (fog_type == FOG_LIGHT_SCATTERING) { + float beta_r = ls_params.x; + float beta_m = ls_params.y; + float g = ls_params.z; + float distance = dist / ls_params.w; + + vec3 view_dir = normalize(camera_pos - vec3(world_pos)); + float cos_theta = dot(view_dir, normalize(ls_light_dir)); + // Note: `* PI` in the two assignments below should be `/ PI` (see the + // definitions of Rayleigh / Mie phase functions in [1]), but this is + // how TT3's shader works. + // [1] http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/ATI-LightScattering.pdf + float phase_r = 3.0 / 16.0 * PI * (1.0 + cos_theta * cos_theta); + float phase_m = 1.0 / 4.0 * PI * (1.0 - g) * (1.0 - g) / pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5); + float f_ex = exp((beta_r + beta_m) * -distance); + ls_in = (phase_r * beta_r + phase_m * beta_m) / (beta_r + beta_m) * (1.0 - f_ex) * ls_sun_color; + ls_ex = ls_light_color * f_ex; + } } diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index 384afca..79963c7 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -121,7 +121,7 @@ struct RE_renderer { GLint specular_texture; GLint rim_exponent; GLint rim_color; - GLint view_pos; + GLint camera_pos; GLint use_light_map; GLint light_texture; GLint use_normal_map; @@ -130,6 +130,14 @@ struct RE_renderer { GLint shadow_transform; GLint shadow_texture; GLint shadow_bias; + GLint fog_type; + GLint fog_near; + GLint fog_far; + GLint fog_color; + GLint ls_params; + GLint ls_light_dir; + GLint ls_light_color; + GLint ls_sun_color; // Attribute variable locations GLint vertex_normal; diff --git a/src/3d/debug.c b/src/3d/debug.c index 8cb14dc..cd436cb 100644 --- a/src/3d/debug.c +++ b/src/3d/debug.c @@ -47,6 +47,15 @@ static const char *motion_state_name(enum RE_motion_state state) return "UNKNOWN"; } +static const char *fog_type_name(enum RE_fog_type type) +{ + switch (type) { + case RE_FOG_LINEAR: return "LINEAR"; + case RE_FOG_LIGHT_SCATTERING: return "LIGHT_SCATTERING"; + } + return "UNKNOWN"; +} + static void print_motion(const char *name, struct motion *m, int indent) { if (m->instance->type == RE_ITYPE_BILLBOARD) { @@ -119,6 +128,24 @@ void RE_debug_print(struct sact_sprite *sp, int indent) indent_printf(indent, "name = \"%s\",\n", p->plugin.name); indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n", SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw); + indent_printf(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir)); + indent_printf(indent, "shadow_bias = %f,\n", p->shadow_bias); + + indent_printf(indent, "fog_type = %s,\n", fog_type_name(p->fog_type)); + switch (p->fog_type) { + case RE_FOG_LINEAR: + indent_printf(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far); + indent_printf(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color)); + break; + case RE_FOG_LIGHT_SCATTERING: + indent_printf(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m); + indent_printf(indent, "ls_g = %f,\n", p->ls_g); + indent_printf(indent, "ls_distance = %f,\n", p->ls_distance); + indent_printf(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir)); + indent_printf(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color)); + indent_printf(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color)); + break; + } for (int i = 0; i < p->nr_instances; i++) print_instance(p->instances[i], i, indent); diff --git a/src/3d/reign.c b/src/3d/reign.c index db9d236..8b8c173 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -182,6 +182,7 @@ struct RE_plugin *RE_plugin_new(void) plugin->aar = aar; for (int i = 0; i < RE_NR_BACK_CGS; i++) RE_back_cg_init(&plugin->back_cg[i]); + plugin->fog_type = RE_FOG_LINEAR; return plugin; } diff --git a/src/3d/renderer.c b/src/3d/renderer.c index e36eda9..7817f44 100644 --- a/src/3d/renderer.c +++ b/src/3d/renderer.c @@ -172,7 +172,7 @@ struct RE_renderer *RE_renderer_new(struct texture *texture) r->specular_texture = glGetUniformLocation(r->program, "specular_texture"); r->rim_exponent = glGetUniformLocation(r->program, "rim_exponent"); r->rim_color = glGetUniformLocation(r->program, "rim_color"); - r->view_pos = glGetUniformLocation(r->program, "view_pos"); + r->camera_pos = glGetUniformLocation(r->program, "camera_pos"); r->use_light_map = glGetUniformLocation(r->program, "use_light_map"); r->light_texture = glGetUniformLocation(r->program, "light_texture"); r->use_normal_map = glGetUniformLocation(r->program, "use_normal_map"); @@ -181,6 +181,14 @@ struct RE_renderer *RE_renderer_new(struct texture *texture) r->shadow_transform = glGetUniformLocation(r->program, "shadow_transform"); r->shadow_texture = glGetUniformLocation(r->program, "shadow_texture"); r->shadow_bias = glGetUniformLocation(r->program, "shadow_bias"); + r->fog_type = glGetUniformLocation(r->program, "fog_type"); + r->fog_near = glGetUniformLocation(r->program, "fog_near"); + r->fog_far = glGetUniformLocation(r->program, "fog_far"); + r->fog_color = glGetUniformLocation(r->program, "fog_color"); + r->ls_params = glGetUniformLocation(r->program, "ls_params"); + r->ls_light_dir = glGetUniformLocation(r->program, "ls_light_dir"); + r->ls_light_color = glGetUniformLocation(r->program, "ls_light_color"); + r->ls_sun_color = glGetUniformLocation(r->program, "ls_sun_color"); r->vertex_normal = glGetAttribLocation(r->program, "vertex_normal"); r->vertex_uv = glGetAttribLocation(r->program, "vertex_uv"); r->vertex_light_uv = glGetAttribLocation(r->program, "vertex_light_uv"); @@ -507,7 +515,7 @@ static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct R static void setup_lights(struct RE_plugin *plugin) { struct RE_renderer *r = plugin->renderer; - glUniform3fv(r->view_pos, 1, plugin->camera.pos); + glUniform3fv(r->camera_pos, 1, plugin->camera.pos); int light_index = 0; for (int i = 0; i < plugin->nr_instances; i++) { struct RE_instance *inst = plugin->instances[i]; @@ -587,6 +595,24 @@ void RE_render(struct sact_sprite *sp) glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]); glUniformMatrix4fv(r->shadow_transform, 1, GL_FALSE, shadow_transform[0]); glUniform1f(r->shadow_bias, plugin->shadow_bias); + if (plugin->fog_mode) { + glUniform1i(r->fog_type, plugin->fog_type); + switch (plugin->fog_type) { + case RE_FOG_LINEAR: + glUniform1f(r->fog_near, plugin->fog_near); + glUniform1f(r->fog_far, plugin->fog_far); + glUniform3fv(r->fog_color, 1, plugin->fog_color); + break; + case RE_FOG_LIGHT_SCATTERING: + glUniform4f(r->ls_params, plugin->ls_beta_r, plugin->ls_beta_m, plugin->ls_g, plugin->ls_distance); + glUniform3fv(r->ls_light_dir, 1, plugin->ls_light_dir); + glUniform3fv(r->ls_light_color, 1, plugin->ls_light_color); + glUniform3fv(r->ls_sun_color, 1, plugin->ls_sun_color); + break; + } + } else { + glUniform1i(r->fog_type, 0); + } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index d89ca8f..423bf2b 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -773,29 +773,203 @@ static bool ReignEngine_SetShadowMapResolutionLevel(int plugin, int level) } //bool ReignEngine_SetShadowSplitDepth(int plugin, int num, float fDepth); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetFogType, int plugin, int type); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetFogNear, int plugin, float near); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetFogFar, int plugin, float far); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetFogColor, int plugin, float r, float g, float b); -//int ReignEngine_GetFogType(int plugin); -//float ReignEngine_GetFogNear(int plugin); -//float ReignEngine_GetFogFar(int plugin); -//void ReignEngine_GetFogColor(int plugin, float *r, float *g, float *b); + +static bool ReignEngine_SetFogType(int plugin, int type) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + if (type != RE_FOG_LINEAR && type != RE_FOG_LIGHT_SCATTERING) { + WARNING("unknown fog type %d", type); + return false; + } + p->fog_type = type; + return true; +} + +static bool ReignEngine_SetFogNear(int plugin, float near) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->fog_near = near; + return true; +} + +static bool ReignEngine_SetFogFar(int plugin, float far) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->fog_far = far; + return true; +} + +static bool ReignEngine_SetFogColor(int plugin, float r, float g, float b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->fog_color[0] = r; + p->fog_color[1] = g; + p->fog_color[2] = b; + return true; +} + +static int ReignEngine_GetFogType(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->fog_type : 0; +} + +static float ReignEngine_GetFogNear(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->fog_near : 0.0; +} + +static float ReignEngine_GetFogFar(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->fog_far : 0.0; +} + +static void ReignEngine_GetFogColor(int plugin, float *r, float *g, float *b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return; + *r = p->fog_color[0]; + *g = p->fog_color[1]; + *b = p->fog_color[2]; +} + HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, LoadLightScatteringSetting, int plugin, struct string *filename /* e.g. "Data\PolyData\BG\bg001\bg001.ls2" */); -//float ReignEngine_GetLSBetaR(int plugin); -//float ReignEngine_GetLSBetaM(int plugin); -//float ReignEngine_GetLSG(int plugin); -//float ReignEngine_GetLSDistance(int plugin); -//void ReignEngine_GetLSLightDir(int plugin, float *pfX, float *pfY, float *pfZ); -//void ReignEngine_GetLSLightColor(int plugin, float *r, float *g, float *b); -//void ReignEngine_GetLSSunColor(int plugin, float *r, float *g, float *b); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSBetaR, int plugin, float beta_r); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSBetaM, int plugin, float beta_m); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSG, int plugin, float g); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSDistance, int plugin, float distance); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSLightDir, int plugin, float x, float y, float z); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSLightColor, int plugin, float r, float g, float b); -HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetLSSunColor, int plugin, float r, float g, float b); + +static float ReignEngine_GetLSBetaR(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->ls_beta_r : 0.0; +} + +static float ReignEngine_GetLSBetaM(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->ls_beta_m : 0.0; +} + +static float ReignEngine_GetLSG(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->ls_g : 0.0; +} + +static float ReignEngine_GetLSDistance(int plugin) +{ + struct RE_plugin *p = get_plugin(plugin); + return p ? p->ls_distance : 0.0; +} + +static void ReignEngine_GetLSLightDir(int plugin, float *x, float *y, float *z) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return; + *x = p->ls_light_dir[0]; + *y = p->ls_light_dir[1]; + *z = -p->ls_light_dir[2]; +} + +static void ReignEngine_GetLSLightColor(int plugin, float *r, float *g, float *b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return; + *r = p->ls_light_color[0]; + *g = p->ls_light_color[1]; + *b = p->ls_light_color[2]; +} + +static void ReignEngine_GetLSSunColor(int plugin, float *r, float *g, float *b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return; + *r = p->ls_sun_color[0]; + *g = p->ls_sun_color[1]; + *b = p->ls_sun_color[2]; +} + +static bool ReignEngine_SetLSBetaR(int plugin, float beta_r) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_beta_r = beta_r; + return true; +} + +static bool ReignEngine_SetLSBetaM(int plugin, float beta_m) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_beta_m = beta_m; + return true; +} + +static bool ReignEngine_SetLSG(int plugin, float g) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_g = g; + return true; +} + +static bool ReignEngine_SetLSDistance(int plugin, float distance) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_distance = distance; + return true; +} + +static bool ReignEngine_SetLSLightDir(int plugin, float x, float y, float z) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_light_dir[0] = x; + p->ls_light_dir[1] = y; + p->ls_light_dir[2] = -z; + glm_vec3_normalize(p->ls_light_dir); + return true; +} + +static bool ReignEngine_SetLSLightColor(int plugin, float r, float g, float b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_light_color[0] = r; + p->ls_light_color[1] = g; + p->ls_light_color[2] = b; + return true; +} + +static bool ReignEngine_SetLSSunColor(int plugin, float r, float g, float b) +{ + struct RE_plugin *p = get_plugin(plugin); + if (!p) + return false; + p->ls_sun_color[0] = r; + p->ls_sun_color[1] = g; + p->ls_sun_color[2] = b; + return true; +} + //bool ReignEngine_SetDrawTextureFog(int plugin, bool bDraw); //bool ReignEngine_GetDrawTextureFog(int plugin); @@ -1494,18 +1668,18 @@ HLL_LIBRARY(ReignEngine, HLL_EXPORT(SetFogNear, ReignEngine_SetFogNear), HLL_EXPORT(SetFogFar, ReignEngine_SetFogFar), HLL_EXPORT(SetFogColor, ReignEngine_SetFogColor), - HLL_TODO_EXPORT(GetFogType, ReignEngine_GetFogType), - HLL_TODO_EXPORT(GetFogNear, ReignEngine_GetFogNear), - HLL_TODO_EXPORT(GetFogFar, ReignEngine_GetFogFar), - HLL_TODO_EXPORT(GetFogColor, ReignEngine_GetFogColor), + HLL_EXPORT(GetFogType, ReignEngine_GetFogType), + HLL_EXPORT(GetFogNear, ReignEngine_GetFogNear), + HLL_EXPORT(GetFogFar, ReignEngine_GetFogFar), + HLL_EXPORT(GetFogColor, ReignEngine_GetFogColor), HLL_EXPORT(LoadLightScatteringSetting, ReignEngine_LoadLightScatteringSetting), - HLL_TODO_EXPORT(GetLSBetaR, ReignEngine_GetLSBetaR), - HLL_TODO_EXPORT(GetLSBetaM, ReignEngine_GetLSBetaM), - HLL_TODO_EXPORT(GetLSG, ReignEngine_GetLSG), - HLL_TODO_EXPORT(GetLSDistance, ReignEngine_GetLSDistance), - HLL_TODO_EXPORT(GetLSLightDir, ReignEngine_GetLSLightDir), - HLL_TODO_EXPORT(GetLSLightColor, ReignEngine_GetLSLightColor), - HLL_TODO_EXPORT(GetLSSunColor, ReignEngine_GetLSSunColor), + HLL_EXPORT(GetLSBetaR, ReignEngine_GetLSBetaR), + HLL_EXPORT(GetLSBetaM, ReignEngine_GetLSBetaM), + HLL_EXPORT(GetLSG, ReignEngine_GetLSG), + HLL_EXPORT(GetLSDistance, ReignEngine_GetLSDistance), + HLL_EXPORT(GetLSLightDir, ReignEngine_GetLSLightDir), + HLL_EXPORT(GetLSLightColor, ReignEngine_GetLSLightColor), + HLL_EXPORT(GetLSSunColor, ReignEngine_GetLSSunColor), HLL_EXPORT(SetLSBetaR, ReignEngine_SetLSBetaR), HLL_EXPORT(SetLSBetaM, ReignEngine_SetLSBetaM), HLL_EXPORT(SetLSG, ReignEngine_SetLSG),