новый рендер
This commit is contained in:
@@ -9,6 +9,7 @@ const IGNORE_TERRAIN_HOLES := false
|
||||
const USE_GEOMETRY_NORMALS := true
|
||||
|
||||
static var _terrain_shader: Shader
|
||||
static var _control_splat_shader: Shader
|
||||
static var _single_texture_shader: Shader
|
||||
static var _empty_alpha_texture: Texture2D
|
||||
static var _native_adt_baker: Object
|
||||
@@ -110,6 +111,35 @@ static func build_baked_tile_render_payload(
|
||||
return _build_baked_tile_payload(data, image_cache, extracted_dir, lod, texture_size)
|
||||
|
||||
|
||||
static func build_control_splat_tile_render_payload(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String = "",
|
||||
lod: int = 0,
|
||||
texture_size: int = 256) -> Dictionary:
|
||||
return _build_control_splat_tile_payload(data, image_cache, extracted_dir, lod, texture_size)
|
||||
|
||||
|
||||
static func apply_control_splat_material(
|
||||
mesh: Mesh,
|
||||
texture_images: Array,
|
||||
alpha_atlas: Texture2D,
|
||||
layer_index_map: Texture2D) -> Mesh:
|
||||
|
||||
if mesh == null or texture_images.is_empty() or alpha_atlas == null or layer_index_map == null:
|
||||
return mesh
|
||||
var texture_array := _build_terrain_texture_array_from_images(texture_images)
|
||||
if texture_array == null:
|
||||
return mesh
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
if array_mesh == null:
|
||||
return mesh
|
||||
array_mesh.surface_set_material(
|
||||
0,
|
||||
_build_control_splat_material(texture_array, alpha_atlas, layer_index_map))
|
||||
return array_mesh
|
||||
|
||||
|
||||
static func build_baked_tile_render_payload_pair(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -128,6 +158,35 @@ static func build_baked_tile_render_payload_pair(
|
||||
coarse_texture_size)
|
||||
|
||||
|
||||
static func upgrade_cached_baked_mesh_materials(
|
||||
mesh: Mesh,
|
||||
uv_scale: float = 1.0,
|
||||
mip_bias: float = -1.25,
|
||||
sharpen_strength: float = 0.35) -> Mesh:
|
||||
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
if array_mesh == null:
|
||||
return mesh
|
||||
|
||||
for surface_idx in array_mesh.get_surface_count():
|
||||
var mat := array_mesh.surface_get_material(surface_idx)
|
||||
if not (mat is ShaderMaterial):
|
||||
continue
|
||||
var shader_mat := mat as ShaderMaterial
|
||||
var tex: Texture2D = shader_mat.get_shader_parameter("tex0")
|
||||
if tex == null:
|
||||
continue
|
||||
var upgraded := ShaderMaterial.new()
|
||||
upgraded.shader = _get_single_texture_shader()
|
||||
upgraded.set_shader_parameter("uv_scale", uv_scale)
|
||||
upgraded.set_shader_parameter("mip_bias", mip_bias)
|
||||
upgraded.set_shader_parameter("sharpen_strength", sharpen_strength)
|
||||
upgraded.set_shader_parameter("tex0", tex)
|
||||
array_mesh.surface_set_material(surface_idx, upgraded)
|
||||
|
||||
return array_mesh
|
||||
|
||||
|
||||
static func get_tile_origin(data: Dictionary) -> Vector3:
|
||||
var found := false
|
||||
var min_x := 0.0
|
||||
@@ -469,6 +528,105 @@ static func _build_baked_tile_payload(
|
||||
return payload
|
||||
|
||||
|
||||
static func _build_control_splat_tile_payload(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String,
|
||||
lod: int,
|
||||
texture_size: int) -> Dictionary:
|
||||
|
||||
var payload := _build_baked_tile_geometry_payload(data, lod)
|
||||
if payload.is_empty():
|
||||
return {}
|
||||
|
||||
var tex_names: PackedStringArray = data.get("textures", PackedStringArray())
|
||||
var texture_images := _build_terrain_texture_images(tex_names, image_cache, extracted_dir, texture_size)
|
||||
var texture_array := _build_terrain_texture_array_from_images(texture_images)
|
||||
if texture_array == null:
|
||||
return payload
|
||||
|
||||
payload = _build_control_splat_tile_geometry_payload(data, lod, tex_names.size())
|
||||
if payload.is_empty():
|
||||
return {}
|
||||
|
||||
var alpha_atlas := _build_tile_alpha_atlas(data)
|
||||
var layer_index_map := _build_tile_layer_index_map(data, tex_names.size())
|
||||
var material := _build_control_splat_material(texture_array, alpha_atlas, layer_index_map)
|
||||
var mesh: Mesh = payload.get("mesh", null)
|
||||
if mesh != null:
|
||||
mesh.surface_set_material(0, material)
|
||||
|
||||
payload["texture_array"] = texture_array
|
||||
payload["texture_images"] = texture_images
|
||||
payload["alpha_atlas"] = alpha_atlas
|
||||
payload["layer_index_map"] = layer_index_map
|
||||
payload["texture_names"] = tex_names
|
||||
return payload
|
||||
|
||||
|
||||
static func _build_control_splat_tile_geometry_payload(data: Dictionary, lod: int, texture_count: int) -> Dictionary:
|
||||
var tile_origin := get_tile_origin(data)
|
||||
var verts := PackedVector3Array()
|
||||
var nrms := PackedVector3Array()
|
||||
var uvs_arr := PackedVector2Array()
|
||||
var colors := PackedColorArray()
|
||||
var indices := PackedInt32Array()
|
||||
var max_id := mini(maxi(texture_count - 1, 0), 255)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
|
||||
var heights: PackedFloat32Array = chunk.get("heights", PackedFloat32Array())
|
||||
var positions := _build_chunk_positions(heights)
|
||||
if positions.is_empty():
|
||||
continue
|
||||
|
||||
var geometry := _build_chunk_geometry(
|
||||
positions,
|
||||
chunk.get("normals", PackedVector3Array()),
|
||||
chunk.get("holes", 0),
|
||||
lod)
|
||||
|
||||
var local_verts: PackedVector3Array = geometry["verts"]
|
||||
if local_verts.is_empty():
|
||||
continue
|
||||
|
||||
var layer_color := _get_chunk_layer_color(chunk, max_id)
|
||||
var offset: Vector3 = chunk.get("origin", Vector3.ZERO) - tile_origin
|
||||
var base := verts.size()
|
||||
|
||||
for v: Vector3 in local_verts:
|
||||
var tv := v + offset
|
||||
verts.append(tv)
|
||||
uvs_arr.append(Vector2(tv.x / TILE_SIZE, tv.z / TILE_SIZE))
|
||||
colors.append(layer_color)
|
||||
for n: Vector3 in geometry["nrms"]:
|
||||
nrms.append(n)
|
||||
for idx: int in geometry["indices"]:
|
||||
indices.append(base + idx)
|
||||
|
||||
if verts.is_empty():
|
||||
return {}
|
||||
|
||||
var arrays := []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
arrays[Mesh.ARRAY_NORMAL] = nrms
|
||||
arrays[Mesh.ARRAY_TEX_UV] = uvs_arr
|
||||
arrays[Mesh.ARRAY_COLOR] = colors
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
|
||||
return {
|
||||
"mesh": mesh,
|
||||
"position": Vector3.ZERO,
|
||||
"name": "ControlSplatTileLOD%d" % lod,
|
||||
}
|
||||
|
||||
|
||||
static func _build_baked_tile_payload_pair(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -874,6 +1032,8 @@ static func _build_baked_texture_material(tex: Texture2D) -> Material:
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_single_texture_shader()
|
||||
mat.set_shader_parameter("uv_scale", 1.0)
|
||||
mat.set_shader_parameter("mip_bias", -1.25)
|
||||
mat.set_shader_parameter("sharpen_strength", 0.35)
|
||||
mat.set_shader_parameter("tex0", tex)
|
||||
if tex:
|
||||
_baked_texture_material_cache[tex.get_instance_id()] = mat
|
||||
@@ -887,6 +1047,8 @@ static func _build_single_texture_material(tex: Texture2D) -> Material:
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_single_texture_shader()
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
mat.set_shader_parameter("sharpen_strength", 0.0)
|
||||
mat.set_shader_parameter("tex0", tex)
|
||||
if tex:
|
||||
_single_texture_material_cache[tex.get_instance_id()] = mat
|
||||
@@ -897,6 +1059,7 @@ static func _build_layered_material(textures: Array, alpha_maps: Array) -> Shade
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_terrain_shader()
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
mat.set_shader_parameter("layer_count", textures.size())
|
||||
var base_tex: Texture2D = textures[0]
|
||||
|
||||
@@ -939,6 +1102,234 @@ static func _get_empty_alpha_texture() -> Texture2D:
|
||||
return _empty_alpha_texture
|
||||
|
||||
|
||||
static func _build_terrain_texture_images(
|
||||
tex_names: PackedStringArray,
|
||||
image_cache: Dictionary,
|
||||
extracted_dir: String,
|
||||
texture_size: int) -> Array[Image]:
|
||||
|
||||
if tex_names.is_empty():
|
||||
var fallback := Image.create(maxi(16, texture_size), maxi(16, texture_size), false, Image.FORMAT_RGBA8)
|
||||
fallback.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
fallback.generate_mipmaps()
|
||||
return [fallback]
|
||||
|
||||
var target_size := maxi(16, texture_size)
|
||||
var images: Array[Image] = []
|
||||
for tex_name in tex_names:
|
||||
var source_img := _load_image(tex_name, image_cache, extracted_dir)
|
||||
var img: Image = null
|
||||
if source_img != null:
|
||||
img = source_img.duplicate()
|
||||
else:
|
||||
img = Image.create(target_size, target_size, false, Image.FORMAT_RGBA8)
|
||||
img.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
|
||||
if img.get_format() != Image.FORMAT_RGBA8:
|
||||
img.convert(Image.FORMAT_RGBA8)
|
||||
if img.get_width() != target_size or img.get_height() != target_size:
|
||||
img.resize(target_size, target_size, Image.INTERPOLATE_LANCZOS)
|
||||
img.generate_mipmaps()
|
||||
images.append(img)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
static func _build_terrain_texture_array_from_images(images: Array) -> Texture2DArray:
|
||||
if images.is_empty():
|
||||
return null
|
||||
|
||||
var fallback: Image = null
|
||||
for image_variant in images:
|
||||
var candidate := image_variant as Image
|
||||
if candidate != null and candidate.get_width() > 0 and candidate.get_height() > 0:
|
||||
fallback = candidate
|
||||
break
|
||||
if fallback == null:
|
||||
fallback = Image.create(16, 16, false, Image.FORMAT_RGBA8)
|
||||
fallback.fill(Color(0.4, 0.55, 0.3, 1.0))
|
||||
fallback.generate_mipmaps()
|
||||
|
||||
var valid_images: Array[Image] = []
|
||||
for image_variant in images:
|
||||
var img := image_variant as Image
|
||||
if img != null and img.get_width() > 0 and img.get_height() > 0:
|
||||
valid_images.append(img)
|
||||
continue
|
||||
valid_images.append(fallback)
|
||||
|
||||
if valid_images.is_empty():
|
||||
return null
|
||||
|
||||
var texture_array := Texture2DArray.new()
|
||||
var err := texture_array.create_from_images(valid_images)
|
||||
if err != OK:
|
||||
return null
|
||||
return texture_array
|
||||
|
||||
|
||||
static func _build_tile_alpha_atlas(data: Dictionary) -> Texture2D:
|
||||
var atlas_size := 16 * 64
|
||||
var image := Image.create(atlas_size, atlas_size, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color(0.0, 0.0, 0.0, 1.0))
|
||||
var tile_origin := get_tile_origin(data)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var chunk_coord := _get_chunk_grid_coord(chunk, tile_origin)
|
||||
var base_x := chunk_coord.x * 64
|
||||
var base_y := chunk_coord.y * 64
|
||||
var alpha_maps: Array = chunk.get("alpha_maps", [])
|
||||
for py in range(64):
|
||||
for px in range(64):
|
||||
var a0 := 0
|
||||
var a1 := 0
|
||||
var a2 := 0
|
||||
if alpha_maps.size() > 0:
|
||||
var bytes0: PackedByteArray = alpha_maps[0]
|
||||
if bytes0.size() == 64 * 64:
|
||||
a0 = bytes0[py * 64 + px]
|
||||
if alpha_maps.size() > 1:
|
||||
var bytes1: PackedByteArray = alpha_maps[1]
|
||||
if bytes1.size() == 64 * 64:
|
||||
a1 = bytes1[py * 64 + px]
|
||||
if alpha_maps.size() > 2:
|
||||
var bytes2: PackedByteArray = alpha_maps[2]
|
||||
if bytes2.size() == 64 * 64:
|
||||
a2 = bytes2[py * 64 + px]
|
||||
image.set_pixel(
|
||||
base_x + px,
|
||||
base_y + py,
|
||||
Color8(a0, a1, a2, 255))
|
||||
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
|
||||
static func _build_tile_layer_index_map(data: Dictionary, texture_count: int) -> Texture2D:
|
||||
var image := Image.create(16, 16, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color8(0, 0, 0, 255))
|
||||
var tile_origin := get_tile_origin(data)
|
||||
var max_id := mini(maxi(texture_count - 1, 0), 255)
|
||||
|
||||
for chunk in data.get("chunks", []) as Array:
|
||||
if chunk.is_empty():
|
||||
continue
|
||||
var layers: Array = chunk.get("layers", [])
|
||||
if layers.is_empty():
|
||||
continue
|
||||
var ids := PackedByteArray([0, 0, 0, 0])
|
||||
for i in range(mini(4, layers.size())):
|
||||
var layer: Dictionary = layers[i]
|
||||
ids[i] = clampi(int(layer.get("texture_id", 0)), 0, max_id)
|
||||
|
||||
var chunk_coord := _get_chunk_grid_coord(chunk, tile_origin)
|
||||
image.set_pixel(
|
||||
chunk_coord.x,
|
||||
chunk_coord.y,
|
||||
Color8(ids[0], ids[1], ids[2], ids[3]))
|
||||
|
||||
return ImageTexture.create_from_image(image)
|
||||
|
||||
|
||||
static func _get_chunk_layer_color(chunk: Dictionary, max_id: int) -> Color:
|
||||
var layers: Array = chunk.get("layers", [])
|
||||
if layers.is_empty():
|
||||
return Color(0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
var ids := PackedFloat32Array([0.0, 0.0, 0.0, 0.0])
|
||||
for i in range(mini(4, layers.size())):
|
||||
var layer: Dictionary = layers[i]
|
||||
ids[i] = float(clampi(int(layer.get("texture_id", 0)), 0, max_id)) / 255.0
|
||||
return Color(ids[0], ids[1], ids[2], ids[3])
|
||||
|
||||
|
||||
static func _get_chunk_grid_coord(chunk: Dictionary, tile_origin: Vector3) -> Vector2i:
|
||||
var origin: Vector3 = chunk.get("origin", Vector3.ZERO)
|
||||
var local := origin - tile_origin
|
||||
return Vector2i(
|
||||
clampi(int(round(local.x / CHUNK_SIZE)), 0, 15),
|
||||
clampi(int(round(local.z / CHUNK_SIZE)), 0, 15))
|
||||
|
||||
|
||||
static func _build_control_splat_material(
|
||||
texture_array: Texture2DArray,
|
||||
alpha_atlas: Texture2D,
|
||||
layer_index_map: Texture2D) -> ShaderMaterial:
|
||||
|
||||
var mat := ShaderMaterial.new()
|
||||
mat.shader = _get_control_splat_shader()
|
||||
mat.set_shader_parameter("terrain_tex", texture_array)
|
||||
mat.set_shader_parameter("alpha_atlas", alpha_atlas)
|
||||
mat.set_shader_parameter("layer_index_map", layer_index_map)
|
||||
mat.set_shader_parameter("uv_scale", 8.0)
|
||||
mat.set_shader_parameter("mip_bias", -0.75)
|
||||
return mat
|
||||
|
||||
|
||||
static func _get_control_splat_shader() -> Shader:
|
||||
if _control_splat_shader:
|
||||
return _control_splat_shader
|
||||
|
||||
_control_splat_shader = Shader.new()
|
||||
_control_splat_shader.code = """
|
||||
shader_type spatial;
|
||||
render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform sampler2DArray terrain_tex : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform sampler2D alpha_atlas : filter_linear, repeat_disable;
|
||||
uniform sampler2D layer_index_map : filter_nearest, repeat_disable;
|
||||
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
vec3 sample_layer(float layer, vec2 local_uv) {
|
||||
return texture(terrain_tex, vec3(local_uv * uv_scale, layer), mip_bias).rgb;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 tile_uv = clamp(UV, vec2(0.0), vec2(0.999999));
|
||||
vec2 chunk_f = floor(tile_uv * 16.0);
|
||||
vec2 local_uv = fract(tile_uv * 16.0);
|
||||
|
||||
vec4 packed_ids = COLOR;
|
||||
vec4 layers = floor(packed_ids * 255.0 + vec4(0.5));
|
||||
vec3 alpha = texture(alpha_atlas, (chunk_f * 64.0 + local_uv * 64.0 + vec2(0.5)) / 1024.0).rgb;
|
||||
|
||||
float w1 = alpha.r;
|
||||
float w2 = alpha.g;
|
||||
float w3 = alpha.b;
|
||||
float w0 = max(0.0, 1.0 - w1 - w2 - w3);
|
||||
float sum = max(w0 + w1 + w2 + w3, 0.0001);
|
||||
w0 /= sum;
|
||||
w1 /= sum;
|
||||
w2 /= sum;
|
||||
w3 /= sum;
|
||||
|
||||
vec3 albedo =
|
||||
sample_layer(layers.x, local_uv) * w0 +
|
||||
sample_layer(layers.y, local_uv) * w1 +
|
||||
sample_layer(layers.z, local_uv) * w2 +
|
||||
sample_layer(layers.w, local_uv) * w3;
|
||||
|
||||
vec3 n = normalize(NORMAL);
|
||||
if (!FRONT_FACING) {
|
||||
n = -n;
|
||||
}
|
||||
vec3 l = normalize(light_dir);
|
||||
float ndl = max(dot(n, l), 0.0);
|
||||
float hemi_term = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
float light = ambient + diffuse * ndl + hemi * hemi_term;
|
||||
ALBEDO = albedo * light;
|
||||
}
|
||||
"""
|
||||
return _control_splat_shader
|
||||
|
||||
|
||||
static func _build_tile_baked_albedo(
|
||||
data: Dictionary,
|
||||
image_cache: Dictionary,
|
||||
@@ -1106,6 +1497,7 @@ render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform int layer_count = 1;
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
@@ -1114,10 +1506,10 @@ uniform float hemi = 0.12;
|
||||
void fragment() {
|
||||
vec2 tiled_uv = UV * uv_scale;
|
||||
|
||||
vec4 c0 = texture(tex0, tiled_uv);
|
||||
vec4 c1 = texture(tex1, tiled_uv);
|
||||
vec4 c2 = texture(tex2, tiled_uv);
|
||||
vec4 c3 = texture(tex3, tiled_uv);
|
||||
vec4 c0 = texture(tex0, tiled_uv, mip_bias);
|
||||
vec4 c1 = texture(tex1, tiled_uv, mip_bias);
|
||||
vec4 c2 = texture(tex2, tiled_uv, mip_bias);
|
||||
vec4 c3 = texture(tex3, tiled_uv, mip_bias);
|
||||
|
||||
float w1 = layer_count > 1 ? texture(alpha1, UV).r : 0.0;
|
||||
float w2 = layer_count > 2 ? texture(alpha2, UV).r : 0.0;
|
||||
@@ -1165,11 +1557,29 @@ render_mode unshaded, cull_disabled, depth_draw_opaque;
|
||||
|
||||
uniform sampler2D tex0 : source_color, filter_linear_mipmap_anisotropic, repeat_enable;
|
||||
uniform float uv_scale = 8.0;
|
||||
uniform float mip_bias = -0.75;
|
||||
uniform float sharpen_strength = 0.0;
|
||||
uniform vec3 light_dir = vec3(-0.35, 0.82, -0.45);
|
||||
uniform float ambient = 0.62;
|
||||
uniform float diffuse = 0.38;
|
||||
uniform float hemi = 0.12;
|
||||
|
||||
vec3 sample_sharp(sampler2D tex, vec2 uv) {
|
||||
vec3 center = texture(tex, uv, mip_bias).rgb;
|
||||
if (sharpen_strength <= 0.001) {
|
||||
return center;
|
||||
}
|
||||
vec2 texel = 1.0 / vec2(textureSize(tex, 0));
|
||||
vec2 min_uv = vec2(0.001);
|
||||
vec2 max_uv = vec2(0.999);
|
||||
vec3 side =
|
||||
texture(tex, clamp(uv + vec2(texel.x, 0.0), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv - vec2(texel.x, 0.0), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv + vec2(0.0, texel.y), min_uv, max_uv), mip_bias).rgb +
|
||||
texture(tex, clamp(uv - vec2(0.0, texel.y), min_uv, max_uv), mip_bias).rgb;
|
||||
return clamp(center * (1.0 + 4.0 * sharpen_strength) - side * sharpen_strength, vec3(0.0), vec3(1.0));
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec3 n = normalize(NORMAL);
|
||||
if (!FRONT_FACING) {
|
||||
@@ -1179,7 +1589,7 @@ void fragment() {
|
||||
float ndl = max(dot(n, l), 0.0);
|
||||
float hemi_term = clamp(n.y * 0.5 + 0.5, 0.0, 1.0);
|
||||
float light = ambient + diffuse * ndl + hemi * hemi_term;
|
||||
ALBEDO = texture(tex0, UV * uv_scale).rgb * light;
|
||||
ALBEDO = sample_sharp(tex0, UV * uv_scale) * light;
|
||||
}
|
||||
"""
|
||||
return _single_texture_shader
|
||||
|
||||
Reference in New Issue
Block a user