Lovely! Interesting what sometimes results out of random comments of mine
float3 flashlight(float distance, float3 viewDir, float3 worldNormal, float falloff = 1)
{
float atten = saturate(falloff / distance); // Determine Light Attenuation (falloff by distance)
//float3 camDir = matView[0]; // This matrix holds the base camera direction inside the index 0.
//camDir.x *= -1; // Flip the Y and swap x and y to rotate it 90 degrees i.e. camDir.yxz
float3 camDir = matView[2]; // This matrix holds the transformed camera direction inside the index 2, matching your actual facing.
float lightCone = saturate(dot(normalize(viewDir), normalize(camDir.xyz))); // Basically does a cone based on Camera's facing
lightCone = pow(lightCone, 15); // This tightens the cone angle
float3 flashlight = saturate(saturate(dot(camDir, worldNormal))) * atten * lightCone; // You could tint the light by multiplying it by a color here
return flashlight;
}
VS_OUTPUT vs_main(uniform const int PcfMode, uniform const bool UseSecondLight, float4 vPosition : POSITION, float3 vNormal : NORMAL, float2 tc : TEXCOORD0, float4 vColor : COLOR0, float4 vLightColor : COLOR1)
{
INITIALIZE_OUTPUT(VS_OUTPUT, Out);
Out.Pos = mul(matWorldViewProj, vPosition);
float4 vWorldPos = (float4)mul(matWorld,vPosition);
float3 vWorldN = normalize(mul((float3x3)matWorld, vNormal)); //normal in world space
Out.Tex0 = tc;
float NdotL = dot(-vSunDir, vWorldN);
float4 diffuse_light = (vAmbientColor + vGroundAmbientColor /*+ ((vSkyLightColor) * smoothstep(0.5, 0.52, NdotL))*/);
Out.ViewDir = normalize(vCameraPos.xyz - vWorldPos.xyz);
Out.WorldNormal = vWorldN;
Out.WorldPos = vWorldPos;
//apply fog
float3 P = mul(matWorldView, vPosition); //position in view space
float d = length(P);
if(bNightVision == 1)
diffuse_light.rgb += flashlight(d, Out.ViewDir, Out.WorldNormal);
Out.Color = (vMaterialColor * vColor * diffuse_light);
//shadow mapping variables
float wNdotSun = saturate(dot(vWorldN, -vSunDir));
Out.SunLight = (wNdotSun) * vSunColor * vMaterialColor * vColor;
if (PcfMode != PCF_NONE)
{
float4 ShadowPos = mul(matSunViewProj, vWorldPos);
Out.ShadowTexCoord = ShadowPos;
Out.ShadowTexCoord.z /= ShadowPos.w;
Out.ShadowTexCoord.w = 1.0f;
Out.ShadowTexelPos = Out.ShadowTexCoord * fShadowMapSize;
//shadow mapping variables end
}
Out.Fog = get_fog_amount_new(d, vWorldPos.z);
return Out;
}
(0, 0, 0, [(key_clicked, key_k),],
[
(try_begin),
(eq, "$NV_on", 1),
(assign, "$NV_on", 0),
(else_try),
(assign, "$NV_on", 1),
(try_end),
(set_shader_param_int, "@bNightVision", "$NV_on"),
]),
(n*6) + 1, n is lamp count
const registers and ps_2 only allows 31 while ps_3 allows 256. This should be fine so long as your target user's PC isn't old enough to gamble in the United States.("spotlight_position", sokf_invisible, "scene_edit_spotlight", "0", [
(ti_on_init_scene_prop,
[
(set_fixed_point_multiplier, 1000),
(store_trigger_param_1, ":instance_no"),
(prop_instance_get_variation_id, ":variance_id", ":instance_no"),
(prop_instance_get_variation_id_2, ":variance_id2", ":instance_no"),
(prop_instance_get_position, pos2, ":instance_no"),
(prop_instance_get_scale, pos3, ":instance_no"),
(position_get_x, reg15, pos2),
(position_get_y, reg16, pos2),
(position_get_z, reg17, pos2),
(position_get_rotation_around_x, reg18, pos2),
(position_get_rotation_around_y, reg19, pos2),
(position_get_rotation_around_z, reg20, pos2),
(convert_to_fixed_point, reg18),
(convert_to_fixed_point, reg19),
(convert_to_fixed_point, reg20),
(store_add, ":string", "str_spotlight_pos0", ":variance_id"),
(str_store_string, s10, ":string"),
(store_add, ":string", "str_spotlight_dir0", ":variance_id"),
(str_store_string, s11, ":string"),
(store_add, ":string", "str_spotlight_col0", ":variance_id"),
(str_store_string, s12, ":string"),
(store_add, ":string", "str_spotlight_fao0", ":variance_id"),
(str_store_string, s13, ":string"),
(store_add, ":string", "str_spotlight_con0", ":variance_id"),
(str_store_string, s14, ":string"),
(set_shader_param_float4, s10, reg15, reg16, reg17, 0),
(set_shader_param_float4, s11, reg18, reg19, reg20, 0),
(try_begin),
(eq, ":variance_id2", 0),
(set_shader_param_float4, s12, 1000, 1000, 1000, 1000), # Using fpm 1000, 1000 is just 1.0, colors are out of percentages inside hlsl
(set_shader_param_float, s13, 1000), # Using fpm 1000, 1000 is just 1.0, falloff of 1 is default
(set_shader_param_float, s14, 15000), # Using fpm 1000, 15000 is just 15.0, 15 is a pretty narrow cone, something like 30degrees
(else_try),
(eq, ":variance_id2", 1),
(set_shader_param_float4, s12, 0, 0, 1000, 1000), # Blue lamp
(set_shader_param_float, s13, 1000),
(set_shader_param_float, s14, 15000),
(else_try),
(eq, ":variance_id2", 2),
(set_shader_param_float4, s12, 0, 1000, 0, 1000), # Green lamp
(set_shader_param_float, s13, 1000),
(set_shader_param_float, s14, 15000),
(else_try),
(eq, ":variance_id2", 3),
(set_shader_param_float4, s12, 1000, 0, 0, 1000), # Red lamp
(set_shader_param_float, s13, 1000),
(set_shader_param_float, s14, 15000),
(try_end),
]),
]),
# After the close of the strings = []
strings.extend(("spotlight_pos%d" %i, "vSpotLightPos[%d]" % i) for i in xrange(0, 11))
strings.extend(("spotlight_dir%d" %i, "vSpotLightDir[%d]" % i) for i in xrange(0, 11))
strings.extend(("spotlight_col%d" %i, "vSpotLightColor[%d]" % i) for i in xrange(0, 11))
strings.extend(("spotlight_fao%d" %i, "vSpotLightFalloff[%d]" % i) for i in xrange(0, 11))
strings.extend(("spotlight_con%d" %i, "vSpotLightCone[%d]" % i) for i in xrange(0, 11))
# Changing the last number of the range will allow you to support as many lamps as you would like +1
# Changing the trigger time will change how quickly movement will update in shader
(0, 0, 0, [],
[
(assign, ":fpm", 1),
(convert_to_fixed_point, ":fpm"),
(scene_prop_get_num_instances, ":spots_count", "spr_spotlight_position"),
(set_shader_param_int, "@iSpotLightCount", ":spots_count"),
(try_for_prop_instances, ":inst", "spr_spotlight_position"),
(set_fixed_point_multiplier, 1000),
(prop_instance_get_variation_id, ":variance_id", ":inst"),
(prop_instance_get_position, pos2, ":inst"),
(prop_instance_get_scale, pos3, ":inst"),
(store_add, ":string", "str_spotlight_pos0", ":variance_id"),
(str_store_string, s10, ":string"),
(store_add, ":string", "str_spotlight_dir0", ":variance_id"),
(str_store_string, s11, ":string"),
(position_get_x, reg15, pos2),
(position_get_y, reg16, pos2),
(position_get_z, reg17, pos2),
(position_get_rotation_around_x, reg18, pos2),
(position_get_rotation_around_y, reg19, pos2),
(position_get_rotation_around_z, reg20, pos2),
(convert_to_fixed_point, reg18),
(convert_to_fixed_point, reg19),
(convert_to_fixed_point, reg20),
(set_shader_param_float4, s10, reg15, reg16, reg17, 0),
(set_shader_param_float4, s11, reg18, reg19, reg20, 0),
(try_end),
(set_fixed_point_multiplier, ":fpm"),
]),
// Spot Light Support
#define NUM_SPOT_LIGHTS 11 // Update this to your max lights
int iSpotLightCount = NUM_SPOT_LIGHTS; // Total Spot Light Count
float3 vSpotLightPos[NUM_SPOT_LIGHTS];
float3 vSpotLightDir[NUM_SPOT_LIGHTS];
float4 vSpotLightColor[NUM_SPOT_LIGHTS];
float vSpotLightFalloff[NUM_SPOT_LIGHTS];
float vSpotLightCone[NUM_SPOT_LIGHTS];
float3 angleToVector(float3 angle)
{
// Convert angle to radians
angle.x = (angle.x) * 3.14159265 / 180;
angle.z = (angle.z - 90) * 3.14159265 / 180;
float sinYaw = sin(angle.z);
float cosYaw = cos(angle.z);
float sinPitch = sin(angle.x);
float cosPitch = cos(angle.x);
float3 direction;
direction.x = cosPitch * cosYaw;
direction.y = cosPitch * sinYaw;
direction.z = -sinPitch;
return direction;
}
float4 calculate_spot_lights_diffuse(const float3 vWorldPos, const float3 vWorldN)
{
float4 total = 0;
for(int j = 0; j < iSpotLightCount; j++)
{
float3 spotLightPos = vSpotLightPos[j];
if(distance(spotLightPos, vCameraPos) < 30) // This is an attempt at making it more performant, I may update as I go on
{
float3 spotLightDir = angleToVector(vSpotLightDir[j]);
float4 spotLightColor = vSpotLightColor[j];
float falloff = vSpotLightFalloff[j];
float angle = vSpotLightCone[j];
float3 L = normalize(spotLightPos - vWorldPos); // Point Light!
falloff /= distance(spotLightPos, vWorldPos) / 5; // Falloff!
float atten = pow(saturate(dot(L, spotLightDir)), angle); // Cone!
total += saturate(dot(normalize(vWorldN), L)) * atten * falloff * spotLightColor; // Light * Cone * Color!
}
}
return total;
}
// In your other Application Constants
int bReticleOffset = 0;
int bDirectionalOffset = 0;
// Amongst the other Font/UI Shaders
PS_OUTPUT ps_main_no_shadow_custom_ui(VS_OUTPUT_FONT_X In)
{
PS_OUTPUT Output;
float2 tile_offset = (In.Tex0.y > 0.5) ? float2((bDirectionalOffset % 8) * 0.125, 0) : float2((bReticleOffset % 4) * 0.25, floor((bReticleOffset % 16) / 4) * 0.125);
// The above code is pretty simple, but it's sort of written weird.
// Basically, if the vertex UV.y is below halfway, use the first formula, since it's a directional arrow, leaving UV.y intact and modifying the UV.x based on the bDirectionalOffset
// Else, it's a reticle, so use the modulo of bReticleOffset to figure out which it's supposed to be.
float4 tex_col = tex2D(MeshTextureSampler, In.Tex0 + tile_offset);
INPUT_TEX_GAMMA(tex_col.rgb);
// This half is nearly exclusively to address shortcomings in my own texture sheet, and is totally optional.
float3 hsv = RGBtoHSV(tex_col.rgb);
tex_col = (hsv.y > 0.5 && hsv.z > 0.25 && In.Tex0.y < 0.5)? In.Color : tex_col;
tex_col.a = (tex_col.r > 0.05 || tex_col.g > 0.05 || tex_col.b > 0.01) ? tex_col.a : 0;
// Basically I used a specific shade of blurple to mark the center of the reticle, and if that's there, change it to the reticle mesh's Vertex Color. I did this to get the R, G, and B dots inside the Native Gadgets.
Output.RGBColor = (In.Tex0.y > 0.5) ? In.Color * tex_col : tex_col; // But, again, only if it's a reticle.
OUTPUT_GAMMA(Output.RGBColor.rgb);
return Output;
}
technique simple_shading_custom_ui //Uses gamma
{
pass P0
{
VertexShader = vs_font_compiled_2_0;
PixelShader = compile ps_2_a ps_main_no_shadow_custom_ui();
}
}
("reticle_chooser", prsntf_manual_end_only, 0,
[
(ti_on_presentation_load, [
(set_fixed_point_multiplier, 1000),
(presentation_set_duration, 99999),
(create_text_overlay, "$g_presentation_load_in_text", "@Modify Your Reticle", tf_title_text),
(overlay_set_color, "$g_presentation_load_in_text", 0xFFFFFF),
(init_position, pos19),
(init_position, pos20),
(position_set_x, pos19, 500),
(position_set_y, pos19, 650),
(position_set_x, pos20, 1000),
(position_set_y, pos20, 1000),
(overlay_set_position, "$g_presentation_load_in_text", pos19),
(overlay_set_size, "$g_presentation_load_in_text", pos20),
(position_set_x, pos19, 200),
(position_set_y, pos19, 550),
(create_text_overlay, "$g_top_reticle_text", "@Reticle Select", tf_with_outline),
(overlay_set_color, "$g_top_reticle_text", 0xFFFFFF),
(overlay_set_position, "$g_top_reticle_text", pos19),
(position_set_x, pos19, 220),
(position_set_y, pos19, 500),
(create_number_box_overlay, "$g_reticle_choice", 0, 16),
(overlay_set_position, "$g_reticle_choice", pos19),
(position_set_x, pos19, 200),
(position_set_y, pos19, 450),
(create_text_overlay, "$g_attack_dir_text", "@Direction Attack Select", tf_with_outline),
(overlay_set_color, "$g_attack_dir_text", 0xFFFFFF),
(overlay_set_position, "$g_attack_dir_text", pos19),
(position_set_x, pos19, 220),
(position_set_y, pos19, 400),
(create_number_box_overlay, "$g_attack_dir_choice", 0, 8),
(overlay_set_position, "$g_attack_dir_choice", pos19),
(position_set_x, pos19, 500),
(position_set_y, pos19, 550),
(create_text_overlay, "$g_left_reticle_text", "@Preview", tf_center_justify),
(overlay_set_position, "$g_left_reticle_text", pos19),
(position_set_y, pos19, 500),
(create_mesh_overlay, "$g_reticle_preview_t", "mesh_crosshair_combined"),
(overlay_set_position, "$g_reticle_preview_t", pos19),
(create_mesh_overlay, "$g_attack_dir_preview_t", "mesh_arrow_up"),
(position_set_y, pos19, 400),
(overlay_set_position, "$g_attack_dir_preview_t", pos19),
]),
(ti_on_presentation_event_state_change,
[
(set_fixed_point_multiplier, 1000),
(store_trigger_param_1, ":object"),
(store_trigger_param_2, ":value"),
(try_begin),
(eq, ":object", "$g_reticle_choice"),
(set_shader_param_int, "@bReticleOffset", ":value"),
(else_try),
(eq, ":object", "$g_attack_dir_choice"),
(set_shader_param_int, "@bDirectionalOffset", ":value"),
(try_end),
]
),
(ti_on_presentation_run,
[
(presentation_set_duration, 99999),
(this_or_next|key_clicked, key_escape),
(this_or_next|key_clicked, key_tilde),
(this_or_next|key_clicked, key_k),
(key_clicked, key_tab),
(presentation_set_duration, 0),
]),
]),
Yet another amazing thing accomplished with shaders. Very nice.Swappable Aiming Reticle / Directional Markers
float3 get_envmap_coords( float3 eye, float3 normal, float sun)
{
float3 coords = reflect(eye, normal) * 0.5 + 0.5; // I forget why I added the adjustments? It seems necessary though
coords.x = sun != 0.0 ? coords.x * 0.5 : coords.x * 0.5 + 0.5; // read as: if sun is not 0 use left panel, else use right panel
// Since reflect gives values between 0.0 and 1.0 we need to shrink the UV.x by half to take into account for the rectangular image
return coords;
}
They're best thought of as a way to add depth and variation in the specular reflections, to keep them from being one flat tone.Nice little one! Envmaps are mostly for plate armours or are there other items at which they can be useful?