First Person View Model Shader (UPDATED FOR GODOT 4.3+)
FPS viewmodel shader based on Riordan’s shader updated for godot 4.3+
HOW TO USE?
1. Set up your model using the StandardMaterial3D with all the needed maps (eg. albedo, normal map, AO map)
2. Convert StandardMaterial3D to ShaderMaterial
3. Copy the FOV uniform and paste it with the rest of your uniforms
4. Replace the vertex shader with the included code snippet
5. Add fragment shader code to the rest of your fragment shader
The complete shader will look something like this (you can copy this one directly if you only need albedo, roughness, metallic, normal, and AO maps):
// NOTE: Shader automatically converted from Godot Engine 4.3.stable.mono's StandardMaterial3D.
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform vec4 albedo : source_color;
uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable;
uniform float point_size : hint_range(0.1, 128.0, 0.1);
uniform float roughness : hint_range(0.0, 1.0);
uniform sampler2D texture_metallic : hint_default_white, filter_linear_mipmap, repeat_enable;
uniform vec4 metallic_texture_channel;
uniform sampler2D texture_roughness : hint_roughness_r, filter_linear_mipmap, repeat_enable;
uniform float specular : hint_range(0.0, 1.0, 0.01);
uniform float metallic : hint_range(0.0, 1.0, 0.01);
uniform sampler2D texture_emission : source_color, hint_default_black, filter_linear_mipmap, repeat_enable;
uniform vec4 emission : source_color;
uniform float emission_energy : hint_range(0.0, 100.0, 0.01);
uniform sampler2D texture_normal : hint_roughness_normal, filter_linear_mipmap, repeat_enable;
uniform float normal_scale : hint_range(-16.0, 16.0);
uniform sampler2D texture_ambient_occlusion : hint_default_white, filter_linear_mipmap, repeat_enable;
uniform vec4 ao_texture_channel;
uniform float ao_light_affect : hint_range(0.0, 1.0, 0.01);
uniform vec3 uv1_scale;
uniform vec3 uv1_offset;
uniform vec3 uv2_scale;
uniform vec3 uv2_offset;
uniform float FOV : hint_range(20, 120) = 75;
void vertex() {
float scale = 1.0 / tan(FOV * 0.5 * PI / -180.0);
PROJECTION_MATRIX[0][0] = scale / (-VIEWPORT_SIZE.x / VIEWPORT_SIZE.y);
PROJECTION_MATRIX[1][1] = scale;
}
void fragment() {
vec2 base_uv = UV;
vec4 albedo_tex = texture(texture_albedo, base_uv);
ALBEDO = albedo.rgb * albedo_tex.rgb;
float metallic_tex = dot(texture(texture_metallic, base_uv), metallic_texture_channel);
METALLIC = metallic_tex * metallic;
SPECULAR = specular;
vec4 roughness_texture_channel = vec4(1.0, 0.0, 0.0, 0.0);
float roughness_tex = dot(texture(texture_roughness, base_uv), roughness_texture_channel);
ROUGHNESS = roughness_tex * roughness;
// Normal Map: Enabled
NORMAL_MAP = texture(texture_normal, base_uv).rgb;
NORMAL_MAP_DEPTH = normal_scale;
// Emission: Enabled
vec3 emission_tex = texture(texture_emission, base_uv).rgb;
// Emission Operator: Add
EMISSION = (emission.rgb + emission_tex) * emission_energy;
// Ambient Occlusion: Enabled
AO = dot(texture(texture_ambient_occlusion, base_uv), ao_texture_channel);
AO_LIGHT_AFFECT = ao_light_affect;
DEPTH = 1.0 - (1.0 - FRAGCOORD.z) * 0.7;
}
Shader code
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
uniform float FOV : hint_range(20, 120) = 75;
void vertex() {
float scale = 1.0 / tan(FOV * 0.5 * PI / -180.0);
PROJECTION_MATRIX[0][0] = scale / (-VIEWPORT_SIZE.x / VIEWPORT_SIZE.y);
PROJECTION_MATRIX[1][1] = scale;
}
void fragment() {
// For reverse Z, we need to invert the depth scaling
// If you want to scale to 70%, you'd do 1.0 - (1.0 - FRAGCOORD.z) * 0.7
// This maintains the same relative scaling but works with reverse Z
DEPTH = 1.0 - (1.0 - FRAGCOORD.z) * 0.7;
}