победа над водопадами, шейдеры m2
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
extends RefCounted
|
||||
class_name WowWMOMaterial
|
||||
|
||||
const WMOBLEND_OPAQUE := 0
|
||||
const WMOBLEND_ALPHA_KEY := 1
|
||||
const WMOBLEND_ALPHA := 2
|
||||
const SHADER_VERSION := 3
|
||||
|
||||
static var _shader_cache: Dictionary = {}
|
||||
|
||||
|
||||
static func clear_cache() -> void:
|
||||
_shader_cache.clear()
|
||||
|
||||
|
||||
static func _ensure_wow_shader_globals() -> void:
|
||||
pass
|
||||
|
||||
|
||||
static func build(
|
||||
texture: Texture2D,
|
||||
texture2: Texture2D,
|
||||
material_flags: int,
|
||||
shader_id: int,
|
||||
blend_mode: int,
|
||||
texture_path: String = "",
|
||||
texture2_path: String = "",
|
||||
diffuse_color: Color = Color.WHITE,
|
||||
emissive_color: Color = Color.BLACK,
|
||||
secondary_color: Color = Color.WHITE,
|
||||
texture3: Texture2D = null,
|
||||
texture3_path: String = "") -> ShaderMaterial:
|
||||
|
||||
var mode := _blend_shader_mode(blend_mode)
|
||||
var effective_diffuse := diffuse_color
|
||||
if maxf(maxf(effective_diffuse.r, effective_diffuse.g), effective_diffuse.b) <= 0.001:
|
||||
effective_diffuse = Color.WHITE
|
||||
|
||||
_ensure_wow_shader_globals()
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.resource_name = texture_path.get_file().get_basename()
|
||||
var pixel_shader := _wmo_pixel_shader_id(shader_id)
|
||||
mat.shader = _get_shader(mode, texture2 != null, texture3 != null)
|
||||
mat.render_priority = 0 if blend_mode <= WMOBLEND_ALPHA_KEY else 1
|
||||
mat.set_shader_parameter("albedo_tex", texture)
|
||||
mat.set_shader_parameter("detail_tex", texture2)
|
||||
mat.set_shader_parameter("extra_tex", texture3)
|
||||
mat.set_shader_parameter("use_texture", texture != null)
|
||||
mat.set_shader_parameter("use_detail_texture", texture2 != null)
|
||||
mat.set_shader_parameter("use_extra_texture", texture3 != null)
|
||||
mat.set_shader_parameter("pixel_shader", pixel_shader)
|
||||
mat.set_shader_parameter("alpha_ref", _alpha_ref(blend_mode, pixel_shader))
|
||||
mat.set_shader_parameter("alpha_output_strength", _alpha_output_strength(blend_mode, pixel_shader))
|
||||
mat.set_shader_parameter("vertex_color_strength", 0.72)
|
||||
mat.set_shader_parameter("ambient_floor", 0.22)
|
||||
mat.set_shader_parameter("diffuse_color", effective_diffuse)
|
||||
mat.set_shader_parameter("emissive_color", emissive_color)
|
||||
mat.set_shader_parameter("material_emissive_strength", _material_emissive_strength(material_flags, emissive_color, texture_path))
|
||||
mat.set_shader_parameter("secondary_color", _effective_secondary_color(secondary_color))
|
||||
mat.set_shader_parameter("detail_strength", 1.0 if texture2 != null else 0.0)
|
||||
mat.set_meta("texture0_path", texture_path)
|
||||
mat.set_meta("texture1_path", texture2_path)
|
||||
mat.set_meta("texture2_path", texture3_path)
|
||||
mat.set_meta("wow_flags", material_flags)
|
||||
mat.set_meta("wow_shader", shader_id)
|
||||
mat.set_meta("wow_wmo_pixel_shader", pixel_shader)
|
||||
mat.set_meta("wow_blend_mode", blend_mode)
|
||||
return mat
|
||||
|
||||
|
||||
static func _wmo_pixel_shader_id(shader_id: int) -> int:
|
||||
match shader_id:
|
||||
0:
|
||||
return 0 # Diffuse
|
||||
1:
|
||||
return 1 # Specular
|
||||
2:
|
||||
return 2 # Metal
|
||||
3:
|
||||
return 3 # Env
|
||||
4:
|
||||
return 4 # Opaque
|
||||
5:
|
||||
return 5 # EnvMetal
|
||||
6:
|
||||
return 6 # TwoLayerDiffuse
|
||||
7:
|
||||
return 7 # TwoLayerEnvMetal
|
||||
8:
|
||||
return 8 # TwoLayerTerrain
|
||||
9:
|
||||
return 9 # DiffuseEmissive
|
||||
11:
|
||||
return 10 # MaskedEnvMetal
|
||||
12:
|
||||
return 11 # EnvMetalEmissive
|
||||
13:
|
||||
return 12 # TwoLayerDiffuseOpaque
|
||||
15:
|
||||
return 13 # TwoLayerDiffuseEmissive
|
||||
16:
|
||||
return 0 # DiffuseTerrain
|
||||
17:
|
||||
return 14 # AdditiveMaskedEnvMetal
|
||||
18:
|
||||
return 15 # TwoLayerDiffuseMod2x
|
||||
19:
|
||||
return 16 # TwoLayerDiffuseMod2xNA
|
||||
20:
|
||||
return 17 # TwoLayerDiffuseAlpha
|
||||
21:
|
||||
return 18 # Lod
|
||||
22:
|
||||
return 19 # Parallax
|
||||
23:
|
||||
return 20 # DF shader
|
||||
_:
|
||||
return 0
|
||||
|
||||
|
||||
static func _shader_alpha_is_opacity(pixel_shader: int) -> bool:
|
||||
match pixel_shader:
|
||||
0, 1, 2, 4, 18, 19:
|
||||
return true
|
||||
_:
|
||||
return false
|
||||
|
||||
|
||||
static func _alpha_ref(blend_mode: int, pixel_shader: int) -> float:
|
||||
if blend_mode > WMOBLEND_OPAQUE:
|
||||
return 0.501960814
|
||||
return 0.0
|
||||
|
||||
|
||||
static func _alpha_output_strength(blend_mode: int, pixel_shader: int) -> float:
|
||||
if blend_mode == WMOBLEND_ALPHA and _shader_alpha_is_opacity(pixel_shader):
|
||||
return 1.0
|
||||
return 0.0
|
||||
|
||||
|
||||
static func _effective_secondary_color(color: Color) -> Color:
|
||||
if maxf(maxf(color.r, color.g), color.b) <= 0.001:
|
||||
return Color.WHITE
|
||||
return color
|
||||
|
||||
|
||||
static func _material_emissive_strength(material_flags: int, color: Color, texture_path: String) -> float:
|
||||
var color_strength := maxf(maxf(color.r, color.g), color.b) * color.a
|
||||
if color_strength <= 0.001:
|
||||
return 0.0
|
||||
var strength := 0.12
|
||||
if (material_flags & 0x10) != 0:
|
||||
strength = 0.26
|
||||
var lower_path := texture_path.to_lower()
|
||||
if lower_path.contains("window") or lower_path.contains("_wnd_") or lower_path.contains("wnd_"):
|
||||
strength = minf(strength, 0.18)
|
||||
return strength
|
||||
|
||||
|
||||
static func _blend_shader_mode(blend_mode: int) -> String:
|
||||
match blend_mode:
|
||||
WMOBLEND_ALPHA_KEY:
|
||||
return "cutout"
|
||||
WMOBLEND_ALPHA:
|
||||
return "alpha"
|
||||
_:
|
||||
return "opaque"
|
||||
|
||||
|
||||
static func _get_shader(mode: String, has_detail: bool, has_extra: bool) -> Shader:
|
||||
var key := "v=%d|%s|detail=%s|extra=%s" % [SHADER_VERSION, mode, str(has_detail), str(has_extra)]
|
||||
if _shader_cache.has(key):
|
||||
return _shader_cache[key]
|
||||
|
||||
var shader := Shader.new()
|
||||
shader.code = _shader_code(mode, has_detail, has_extra)
|
||||
_shader_cache[key] = shader
|
||||
return shader
|
||||
|
||||
|
||||
static func _shader_code(mode: String, has_detail: bool, has_extra: bool) -> String:
|
||||
var render_modes: Array[String] = ["blend_mix", "cull_disabled", "unshaded"]
|
||||
if mode == "opaque" or mode == "cutout":
|
||||
render_modes.append("depth_draw_opaque")
|
||||
else:
|
||||
render_modes.append("depth_prepass_alpha")
|
||||
|
||||
return """
|
||||
shader_type spatial;
|
||||
render_mode %s;
|
||||
|
||||
uniform sampler2D albedo_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2D detail_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2D extra_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform bool use_texture = false;
|
||||
uniform bool use_detail_texture = false;
|
||||
uniform bool use_extra_texture = false;
|
||||
uniform int pixel_shader = 0;
|
||||
uniform float alpha_ref = 0.0;
|
||||
uniform float alpha_output_strength = 0.0;
|
||||
uniform float vertex_color_strength = 0.88;
|
||||
uniform float ambient_floor = 0.20;
|
||||
uniform vec4 diffuse_color : source_color = vec4(1.0);
|
||||
uniform vec4 emissive_color : source_color = vec4(0.0);
|
||||
uniform float material_emissive_strength = 0.0;
|
||||
uniform vec4 secondary_color : source_color = vec4(1.0);
|
||||
uniform float detail_strength = 0.0;
|
||||
|
||||
global uniform vec4 wow_ambient_color;
|
||||
global uniform vec4 wow_light_color;
|
||||
global uniform vec3 wow_light_dir;
|
||||
global uniform vec4 wow_fog_color;
|
||||
global uniform vec2 wow_fog_range;
|
||||
global uniform float wow_fog_density;
|
||||
global uniform float wow_sun_elevation;
|
||||
|
||||
varying vec3 world_normal;
|
||||
varying vec3 world_pos;
|
||||
varying vec3 view_pos;
|
||||
|
||||
void vertex() {
|
||||
world_normal = normalize(MODEL_NORMAL_MATRIX * NORMAL);
|
||||
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
view_pos = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||
}
|
||||
|
||||
vec3 apply_wow_fog(vec3 color) {
|
||||
float dist = length(view_pos);
|
||||
float range_fog = smoothstep(wow_fog_range.x, wow_fog_range.y, dist);
|
||||
float fog_amount = range_fog * clamp(wow_fog_density, 0.0, 0.55);
|
||||
return mix(color, wow_fog_color.rgb, fog_amount);
|
||||
}
|
||||
|
||||
float color_luma(vec3 color) {
|
||||
return dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
}
|
||||
|
||||
vec3 textured_material_tint(vec3 color) {
|
||||
float luma = max(color_luma(color), 0.001);
|
||||
vec3 hue = clamp(color / luma, vec3(0.58), vec3(1.20));
|
||||
return mix(vec3(1.0), hue, 0.28);
|
||||
}
|
||||
|
||||
vec3 untextured_material_color(vec3 color) {
|
||||
float luma = color_luma(color);
|
||||
vec3 muted = mix(vec3(luma), color, 0.42);
|
||||
return clamp(muted * 0.82, vec3(0.08), vec3(0.72));
|
||||
}
|
||||
|
||||
void resolve_wmo_pixel(vec4 tex1, vec4 tex2, vec4 tex3, bool has_detail, bool has_extra, out vec3 diffuse, out vec3 emissive) {
|
||||
float layer_weight = clamp(secondary_color.a, 0.0, 1.0);
|
||||
diffuse = tex1.rgb;
|
||||
emissive = vec3(0.0);
|
||||
if (!has_detail) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pixel_shader == 3) {
|
||||
emissive = tex2.rgb * tex1.a;
|
||||
} else if (pixel_shader == 5) {
|
||||
emissive = (tex1.rgb * tex1.a) * tex2.rgb;
|
||||
} else if (pixel_shader == 6) {
|
||||
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
|
||||
diffuse = mix(layer2, tex1.rgb, layer_weight);
|
||||
} else if (pixel_shader == 8 || pixel_shader == 12) {
|
||||
diffuse = mix(tex2.rgb, tex1.rgb, layer_weight);
|
||||
} else if (pixel_shader == 9) {
|
||||
emissive = tex2.rgb * tex2.a * layer_weight;
|
||||
} else if (pixel_shader == 10 && has_extra) {
|
||||
float mix_factor = clamp(tex3.a * layer_weight, 0.0, 1.0);
|
||||
diffuse = mix(mix(tex1.rgb * tex2.rgb * 2.0, tex3.rgb, mix_factor), tex1.rgb, tex1.a);
|
||||
} else if (pixel_shader == 11 && has_extra) {
|
||||
emissive = ((tex1.rgb * tex1.a) * tex2.rgb) + ((tex3.rgb * tex3.a) * layer_weight);
|
||||
} else if (pixel_shader == 13) {
|
||||
vec3 layer = tex2.rgb * (1.0 - tex2.a);
|
||||
diffuse = mix(layer, tex1.rgb, layer_weight);
|
||||
emissive = (tex2.rgb * tex2.a) * (1.0 - layer_weight);
|
||||
} else if (pixel_shader == 14 && has_extra) {
|
||||
diffuse = mix(
|
||||
(tex1.rgb * tex2.rgb * 2.0) + (tex3.rgb * clamp(tex3.a * layer_weight, 0.0, 1.0)),
|
||||
tex1.rgb,
|
||||
tex1.a
|
||||
);
|
||||
} else if (pixel_shader == 15) {
|
||||
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
|
||||
vec3 layer3 = mix(layer2, tex1.rgb, layer_weight);
|
||||
diffuse = has_extra ? layer3 * tex3.rgb * 2.0 : layer3;
|
||||
} else if (pixel_shader == 16) {
|
||||
vec3 mod_layer = tex1.rgb * tex2.rgb * 2.0;
|
||||
diffuse = mix(tex1.rgb, mod_layer, layer_weight);
|
||||
} else if (pixel_shader == 17) {
|
||||
vec3 layer2 = mix(tex1.rgb, tex2.rgb, tex2.a);
|
||||
vec3 layer3 = mix(layer2, tex1.rgb, has_extra ? tex3.a : layer_weight);
|
||||
diffuse = has_extra ? layer3 * tex3.rgb * 2.0 : layer3;
|
||||
}
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec4 tex1 = use_texture ? texture(albedo_tex, UV) : vec4(1.0);
|
||||
vec4 tex2 = use_detail_texture ? texture(detail_tex, UV) : vec4(1.0);
|
||||
vec4 tex3 = use_extra_texture ? texture(extra_tex, UV) : vec4(1.0);
|
||||
if (alpha_ref > 0.0 && tex1.a < alpha_ref) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec3 mat_diffuse;
|
||||
vec3 shader_emissive;
|
||||
resolve_wmo_pixel(tex1, tex2, tex3, use_detail_texture, use_extra_texture, mat_diffuse, shader_emissive);
|
||||
|
||||
vec3 baked = max(COLOR.rgb, vec3(ambient_floor));
|
||||
vec3 baked_lit = mix(vec3(1.0), baked, clamp(vertex_color_strength, 0.0, 1.0));
|
||||
vec3 n = normalize(world_normal);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
float ndl = max(dot(n, normalize(wow_light_dir)), 0.0);
|
||||
vec3 zone_ambient = mix(vec3(0.78), max(wow_ambient_color.rgb, vec3(0.08)), 0.16);
|
||||
vec3 zone_light = mix(vec3(1.0), wow_light_color.rgb, 0.10);
|
||||
float day_strength = mix(0.48, 0.92, clamp(wow_sun_elevation, 0.0, 1.0));
|
||||
vec3 lit = baked_lit * (zone_ambient * 0.86 + zone_light * ndl * 0.24 * day_strength);
|
||||
lit = clamp(lit, vec3(0.46), vec3(1.05));
|
||||
vec3 material_color = untextured_material_color(diffuse_color.rgb);
|
||||
if (use_texture) {
|
||||
material_color = mat_diffuse * textured_material_tint(diffuse_color.rgb);
|
||||
}
|
||||
float emissive_mask = smoothstep(0.35, 0.92, color_luma(mat_diffuse));
|
||||
vec3 material_emissive = mat_diffuse * emissive_color.rgb * emissive_color.a * material_emissive_strength * emissive_mask;
|
||||
vec3 emissive = shader_emissive * 0.35 + material_emissive;
|
||||
vec3 color = clamp(material_color * lit + emissive, vec3(0.0), vec3(1.0));
|
||||
ALBEDO = apply_wow_fog(color);
|
||||
%s
|
||||
}
|
||||
""" % [
|
||||
", ".join(render_modes),
|
||||
"" if mode == "opaque" or mode == "cutout" else "\tALPHA = mix(diffuse_color.a, tex1.a * diffuse_color.a, clamp(alpha_output_strength, 0.0, 1.0));",
|
||||
]
|
||||
Reference in New Issue
Block a user