Hello.
I want to share my developments in the field of texture transitions and the resulting solution, which I call the Multishader. This approach allows for managing a large number of materials within a single shader, while also achieving high-quality transitions between them and a high level of optimization.
If you follow the instructions, you can achieve the same results as shown in the screenshots:
This is the visual result with textures:
The texture transitions are procedural, thus offering infinite quality.
This tutorial assumes you have some familiarity with Texture Arrays. If this is a new concept for you, please take a moment to read this article:
Part 1: Blender
I am using Blender v.4.1, but I believe this code should also be applicable to later versions.
First, itβs important to understand that this method is specifically designed for planes and will not work with other mesh types such as cubes, spheres, pyramids or torus.
To be clear, these too are fundamentally planes. The crucial understanding is that any form that was initially a plane and whose UV layout maintains the properties of a uniform plane will be compatible.
Remember these rules for creating your object to avoid errors:
- After creating the desired shape, be sure to reset all transforms by pressing Ctrl + A: All Transforms.
- Do not modify the UV.
- Do not use Extrude.
- Do not use Loop Cuts.
I know, these are very restrictive rules!
In practice, this method is primarily suitable for creating landscapes. And yes, I specifically developed this βtechnologyβ for landscapes because Iβm currently creating a landscape for my game, hence the unexpected limitations.
So, we have our figure created. Next, we need to assign materials.
Rules for using and assigning materials:
- Name your materials using only numbers within the range of 0 to 99.
- If you donβt want a material to have a transition, name it using letters.
Now, in more detail:
Create your texture atlas beforehand, and name the materials so that the name corresponds to the index of the texture within the atlas. I recommend making the first texture red, because it will appear when there are errors.
Moving on to the code.
Proceed to the Scripting workspace and save your script in a convenient location.
Iβll say right away that Iβm not a code master, so if thereβs some magic one-line code that would make me feel like I wasted a lot of 1.5 months, please do share!
I should also mention that this code was developed in collaboration with AI
And one more thing β I was really lazy about translating the comments to English, so apologies for that. Feel free to tackle the translation yourself if youβre up for it. The comments are in Russian.
import bpy
from mathutils import Vector
from collections import Counter
import time
import cProfile
import os
import math
# Π€ΡΠ½ΠΊΡΠΈΡ ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ Π² ΡΠΎΡΠΌΠ°ΡΠ΅.exr Π΄Π»Ρ Π·Π°ΠΏΠ΅ΡΠ°ΡΠ»Π΅Π½ΠΈΡ ΠΈΠ½Π΄Π΅ΠΊΡΠΎΠ² ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΡΠΎΡΠ΅Π΄Π΅ΠΉ ΠΈ ΡΠ°Π±Π»ΠΎΠ½Π° ΠΏΠΎΠ³ΡΠ°Π½ΠΈΡΠ½ΠΎΠ³ΠΎ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Π°
def create_texture(pixel_colors, width, height):
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΠΏΡΡΡΠΎΠΉ ΠΌΠ°ΡΡΠΈΠ² ΠΏΠΈΠΊΡΠ΅Π»Π΅ΠΉ (RGBA) Π΄Π»Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ
flat_pixels = [0.0] * (width * height * 4)
# ΠΠ°ΠΏΠΎΠ»Π½ΡΠ΅ΠΌ ΠΌΠ°ΡΡΠΈΠ² ΠΏΠΈΠΊΡΠ΅Π»Π΅ΠΉ Π½Π°ΠΏΡΡΠΌΡΡ ΠΈΠ· `pixel_colors`
for i in range(len(pixel_colors)):
index = i * 4
flat_pixels[index:index + 4] = pixel_colors[i] # ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ RGBA
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ Π½ΠΎΠ²ΠΎΠ΅ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π² Blender
model_name = bpy.context.active_object.name
img = bpy.data.images.new(model_name, width=width, height=height, alpha=True, float_buffer=True)
img.colorspace_settings.name = "Non-Color"
img.pixels.foreach_set(flat_pixels) # ΠΠ°ΠΏΠΎΠ»Π½ΡΠ΅ΠΌ ΠΏΠΈΠΊΡΠ΅Π»ΠΈ
# Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅
file_path = ""
texture_path = os.path.join(file_path, f"{model_name}.exr")
img.filepath_raw = texture_path
img.file_format = 'OPEN_EXR'
img.save()
print(f" Π’Π΅ΠΊΡΡΡΡΠ° ΡΠΎΡ
ΡΠ°Π½Π΅Π½Π°: {texture_path}")
#---------------------------------------------------------------------
# Π€ΡΠ½ΠΊΡΠΈΡ ΡΠ°ΡΡΠ΅ΡΠ° ΡΠΎΡΠ΅Π΄Π΅ΠΉ ΠΏΠΎΠ»ΠΈΠ³ΠΎΠ½ΠΎΠ² ΠΈ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΠ΅ ΡΠΎΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΌΠ°ΡΡΠΈΠ²Π°
def border_material():
# ΠΠ°ΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠ°ΠΉΠΌΠ΅Ρ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ
start_time = time.time()
#--------------------------------------------------------------------------
# ΠΠ°ΠΏΠΈΡΡΠ²Π°ΠΉ ΠΏΠΎΠ»ΠΈΠ³ΠΎΠ½Π°Π»ΡΠ½ΡΡ ΡΠ΅ΡΠΊΡ Π² ΠΌΠ°ΡΡΠΈΠ² ΠΈ ΠΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅ΠΌ ΠΏΡΠΈ ΡΡΠΎΠΌ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ, ΡΠ°ΠΊ ΠΊΠ°ΠΊ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ ΠΎΠ½ΠΈ ΡΠ΄Π²ΠΈΠ½ΡΡΡ
obj = bpy.context.active_object
uv_layer = obj.data.uv_layers.active.data
# ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠ°Π·ΠΌΠ΅ΡΡ ΠΌΠ°ΡΡΠΈΠ²Π° ΠΏΠΎ Π΄Π»ΠΈΠ½Π΅ ΡΠ΅ΡΠΊΠΈ UV
all_uvs_x = [round(uv_layer[loop_index].uv.x,3) for face in obj.data.polygons for loop_index in face.loop_indices]
all_uvs_y = [round(uv_layer[loop_index].uv.y, 3) for face in obj.data.polygons for loop_index in face.loop_indices]
unique_u = sorted(list(set(all_uvs_x)))
unique_v = sorted(list(set(all_uvs_y)))
GRID_SIZE_X = len(unique_u) - 1
GRID_SIZE_Y = len(unique_v) - 1
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΠΏΡΡΡΡΠ΅ ΠΌΠ°ΡΡΠΈΠ²Ρ
polygon_grid = [["None"] * GRID_SIZE_X for _ in range(GRID_SIZE_Y)]
# Π€ΠΎΡΠΌΠΈΡΡΠ΅ΠΌ ΠΌΠ°ΡΡΠΈΠ²
for poly_index, polygon in enumerate(obj.data.polygons):
# ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ UV-ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ Π²Π΅ΡΡΠΈΠ½Ρ ΠΏΠΎΠ»ΠΈΠ³ΠΎΠ½Π°
uv_coords = uv_layer[polygon.loop_start].uv # ΠΠ΅ΡΡΠΌ UV ΠΏΠ΅ΡΠ²ΠΎΠΉ Π²Π΅ΡΡΠΈΠ½Ρ
x = int(round(uv_coords.x * GRID_SIZE_X)) # ΠΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΡ UV β ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΡΠ΅ΡΠΊΠΈ
y = int(round(uv_coords.y * GRID_SIZE_Y))
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, ΡΡΠΎ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ Π² ΠΏΡΠ΅Π΄Π΅Π»Π°Ρ
ΠΌΠ°ΡΡΠΈΠ²Π°
if 0 <= x < GRID_SIZE_X and 0 <= y < GRID_SIZE_Y:
material_name = obj.data.materials[polygon.material_index].name
if material_name.isdigit():
polygon_grid[y][x] = int(material_name)
else:
polygon_grid[y][x] = -1 # ΠΡΠ»ΠΈ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π» Π½Π΅ ΡΠΈΡΠ»ΠΎΠ²ΠΎΠΉ, Π·Π°ΠΌΠ΅Π½ΡΠ΅ΠΌ Π½Π° -1
#--------------------------------------------------------------------------
#ΠΠ°Ρ
ΠΎΠ΄ΠΈΠΌ ΡΡΡΠΊΠΈ ΠΈ "Π½Π°Π·Π½Π°ΡΠ°Π΅ΠΌ" ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π» 100, Π±ΡΠΊΠ²Π΅Π½Π½ΡΠ΅ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Ρ ΠΈΠ³Π½ΠΎΡΠΈΡΡΡΡΡΡ
offsets = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]
for y in range(GRID_SIZE_Y):
for x in range(GRID_SIZE_X):
current_value = polygon_grid[y][x]
# ΠΡΠΎΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΆΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΡΠ½Π½ΡΠ΅ ΡΡΠ΅ΠΉΠΊΠΈ (100 ΠΈΠ»ΠΈ -1)
if current_value != "None" and current_value != 100 and current_value != -1:
for dy, dx in offsets:
ny, nx = y + dy, x + dx
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π³ΡΠ°Π½ΠΈΡΡ ΠΌΠ°ΡΡΠΈΠ²Π°
if 0 <= ny < GRID_SIZE_Y and 0 <= nx < GRID_SIZE_X:
neighbor_value = polygon_grid[ny][nx]
# ΠΡΠΎΠΏΡΡΠΊΠ°Π΅ΠΌ ΡΠΎΡΠ΅Π΄Π΅ΠΉ Ρ 100 ΠΈΠ»ΠΈ -1
if neighbor_value != "None" and neighbor_value != 100 and neighbor_value != -1:
if current_value < neighbor_value:
polygon_grid[y][x] = 100 # ΠΠ΅Π½ΡΠ΅ΠΌ ΡΠ΅ΠΊΡΡΡΡ ΡΡΠ΅ΠΉΠΊΡ
#ΠΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ Π°Π»Π³ΠΎΡΠΈΡΠΌΡ
polygon_grid = array_editing_algorithms(polygon_grid, GRID_SIZE_X, GRID_SIZE_Y)
#ΠΡΠ·ΡΠ²Π°Π΅ΠΌ ΡΠΈΠ½Π°Π»ΡΠ½ΡΠ΅ Π°Π»Π³ΠΎΡΠΈΡΠΌΡ
algorithms(polygon_grid, GRID_SIZE_X, GRID_SIZE_Y)
# ΠΠΎΠ½Π΅Ρ ΠΎΡΡΡΠ΅ΡΠ° ΡΠ°Π±ΠΎΡΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠ°
end_time = time.time()
# ΠΡΠ²ΠΎΠ΄ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ
execution_time = end_time - start_time
print(f"ΠΡΠ΅ΠΌΡ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠ°: {execution_time:.2f} ΡΠ΅ΠΊΡΠ½Π΄")
#---------------------------------------------------------------------
#Π€ΡΠ½ΠΊΡΠΈΡ ΡΠ΄Π°Π»Π΅Π½ΠΈΡ Π»ΠΈΡΠ½ΠΈΡ
100 ΠΈΠ· ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠ³ΠΎ ΠΌΠ°ΡΡΠΈΠ²Π°
def array_editing_algorithms(array, width, height):
for y in range(height):
for x in range(width):
if array[y][x] == 100: # ΠΡΠ»ΠΈ Π½Π°ΠΉΠ΄Π΅Π½Π° ΡΡΠ΅ΠΉΠΊΠ° 100
# ΠΠ°Ρ
ΠΎΠ΄ΠΈΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΡΠΎΡΠ΅Π΄Π½ΠΈΡ
ΠΏΠΎΠ»ΠΈΠ³ΠΎΠ½ΠΎΠ²
top = array[y + 1][x] if y + 1 < len(array) else None
bottom = array[y - 1][x] if y - 1 >= 0 else None
left = array[y][x - 1] if x - 1 >= 0 else None
right = array[y][x + 1] if x + 1 < len(array[0]) else None
top_left = array[y + 1][x - 1] if y + 1 < len(array) and x - 1 >= 0 else None
top_right = array[y + 1][x + 1] if y + 1 < len(array) and x + 1 < len(array[0]) else None
bottom_left = array[y - 1][x - 1] if y - 1 >= 0 and x - 1 >= 0 else None
bottom_right = array[y - 1][x + 1] if y - 1 >= 0 and x + 1 < len(array[0]) else None
top_2 = array[y + 2][x] if y + 2 < len(array) else None
bottom_2 = array[y - 2][x] if y - 2 >= 0 else None
left_2 = array[y][x - 2] if x - 2 >= 0 else None
right_2 = array[y][x + 2] if x + 2 < len(array[0]) else None
# ΠΠ»Π³ΠΎΡΠΈΡΠΌ ΠΊΠ²Π°Π΄ΡΠ°Ρ
#1
if top == 100 and left == 100 and top_left == 100 and right != 100 and left_2 != 100 and left_2 != bottom:
array[y][x] = bottom
#2
if bottom == 100 and right == 100 and bottom_right == 100 and left != 100 and right_2 != 100 and right_2 != top:
array[y][x] = top
#3
if left == 100 and bottom == 100 and bottom_left == 100 and top != 100 and bottom_2 != 100 and bottom_2 != right:
array[y][x] = right
#4
if right == 100 and top == 100 and top_right == 100 and bottom != 100 and top_2 != 100 and top_2 != left:
array[y][x] = left
#5
if top == 100 and right == 100 and top_right == 100 and left != 100 and right_2 != 100 and right_2 != bottom:
array[y][x] = bottom
#6
if bottom == 100 and left == 100 and bottom_left == 100 and right != 100 and left_2 != 100 and left_2 != top:
array[y][x] = top
#7
if left == 100 and top == 100 and top_left == 100 and bottom != 100 and top_2 != 100 and top_2 != right:
array[y][x] = right
#8
if right == 100 and bottom == 100 and bottom_right == 100 and top != 100 and bottom_2 != 100 and bottom_2 != left:
array[y][x] = left
return array
#---------------------------------------------------------------------
#Π€ΡΠ½ΠΊΡΠΈΡ ΡΠ°ΡΡΠ΅ΡΠ° ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄ΠΎΠ²
def algorithms(polygon_grid, width, height):
# ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π°ΠΊΡΠΈΠ²Π½ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ (ΠΌΠ΅Ρ)
obj = bpy.context.active_object
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΠΌΠ°ΡΡΠΈΠ², Π³Π΄Π΅ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΏΠΈΠΊΡΠ΅Π»Ρ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ ΠΊΠ°ΠΊ [R, G, B, A]
pixel_array = [[0.0, 0.0, 0.0, 1.0] for _ in range(len(obj.data.polygons))]
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΠΊΠΎΠΏΠΈΡ ΠΌΠ°ΡΡΠΈΠ²Π° polygon_grid
material_grid = [row[:] for row in polygon_grid] # ΠΠ»ΡΠ±ΠΎΠΊΠ°Ρ ΠΊΠΎΠΏΠΈΡ
# ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π»Ρ 8 ΡΠΎΡΠ΅Π΄Π΅ΠΉ
offsets = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]
#ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ Π³Π΄Π΅ Π² ΡΡΡΡΠΊΡΡΡΠ°Ρ
Π±Π°Π³ΡΠ΅Ρ min ΠΈ max
for y in range(height):
for x in range(width):
if polygon_grid[y][x] == 100: # ΠΡΠ»ΠΈ Π½Π°ΠΉΠ΄Π΅Π½Π° ΡΡΠ΅ΠΉΠΊΠ° 100
neighbor_values = [] # Π‘ΠΏΠΈΡΠΎΠΊ ΡΠΎΡΠ΅Π΄Π΅ΠΉ
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π²ΡΠ΅Ρ
ΡΠΎΡΠ΅Π΄Π΅ΠΉ
for dy, dx in offsets:
ny, nx = y + dy, x + dx
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π³ΡΠ°Π½ΠΈΡΡ ΠΌΠ°ΡΡΠΈΠ²Π°
if 0 <= ny < height and 0 <= nx < width:
neighbor_value = polygon_grid[ny][nx]
# ΠΠ³Π½ΠΎΡΠΈΡΡΠ΅ΠΌ 100 ΠΈ -1
if neighbor_value != 100 and neighbor_value != -1:
neighbor_values.append(neighbor_value)
# ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ min/max ΡΡΠ΅Π΄ΠΈ ΡΠΎΡΠ΅Π΄Π΅ΠΉ
if neighbor_values:
material_min = min(neighbor_values)
material_max = max(neighbor_values)
# ΠΡΠ»ΠΈ min ΠΈ max ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡΡ, Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ None
if material_min == material_max:
material_min, material_max = -2, -2
else:
material_min, material_max = -2, -2
# ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ min/max (ΠΈΠ»ΠΈ None) Π² material_grid
material_grid[y][x] = [material_max, material_min]
# ΠΡΡ
ΠΎΠ΄Ρ ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΡ
Π΄Π°Π½Π½ΡΡ
Π½Π°Ρ
ΠΎΠ΄ΠΈΠΌ Π²Π΅ΡΠ½ΡΠ΅ min ΠΈ max
for y in range(height):
for x in range(width):
# ΠΡΠ΅ΠΌ ΡΡΠ΅ΠΉΠΊΠΈ Ρ [-2, -2]
if material_grid[y][x] == [-2, -2]:
min_value = []
max_value = []
# ΠΠ°ΡΠΈΠ½Π°Π΅ΠΌ Ρ Π±Π»ΠΈΠΆΠ°ΠΉΡΠΈΡ
ΡΠΎΡΠ΅Π΄Π΅ΠΉ
search_range = 1
while not min_value or not max_value: # ΠΡΠ»ΠΈ ΡΠΏΠΈΡΠΎΠΊ ΠΏΡΡΡ, ΡΠ°ΡΡΠΈΡΡΠ΅ΠΌ Π·ΠΎΠ½Ρ ΠΏΠΎΠΈΡΠΊΠ°
for dy, dx in offsets:
ny, nx = y + dy * search_range, x + dx * search_range # Π Π°ΡΡΠΈΡΡΠ΅ΠΌ ΠΏΠΎΠΈΡΠΊ
# ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ Π³ΡΠ°Π½ΠΈΡΡ ΠΌΠ°ΡΡΠΈΠ²Π°
if 0 <= ny < height and 0 <= nx < width:
neighbor_value = material_grid[ny][nx]
# ΠΡΠ»ΠΈ ΡΠΎΡΠ΅Π΄ β Π΄Π²ΠΎΠΉΠ½Π°Ρ ΡΡΠ΅ΠΉΠΊΠ°, ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅ΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΡ
if isinstance(neighbor_value, list) and len(neighbor_value) == 2 and neighbor_value != [-2, -2]:
max_value.append(neighbor_value[0]) # ΠΠ΅ΡΠ²ΠΎΠ΅ ΡΠΈΡΠ»ΠΎ β max
min_value.append(neighbor_value[1]) # ΠΡΠΎΡΠΎΠ΅ ΡΠΈΡΠ»ΠΎ β min
search_range += 1 # Π£Π²Π΅Π»ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΠ°Π΄ΠΈΡΡ ΠΏΠΎΠΈΡΠΊΠ°
# ΠΠ³ΡΠ°Π½ΠΈΡΠΈΠ²Π°Π΅ΠΌ ΡΠ°Π·ΠΌΠ΅Ρ ΠΏΠΎΠΈΡΠΊΠ°, ΡΡΠΎΠ±Ρ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠ³ΠΎ ΡΠΈΠΊΠ»Π°
if search_range > max(height, width):
break
# ΠΡΠ»ΠΈ Π½Π°ΡΠ»ΠΈ Ρ
ΠΎΡΡ Π±Ρ ΠΎΠ΄Π½ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅, ΠΎΠ±Π½ΠΎΠ²Π»ΡΠ΅ΠΌ `material_grid[y][x]`**
if min_value and max_value:
material_grid[y][x] = [max(max_value), min(min_value)]
# ΠΠ°Ρ
ΠΎΠ΄ΠΈ ΡΠΎΡΠ΅Π΄Π΅ΠΉ ΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠΎΠ² ΠΊΠΎΠ΄ΠΈΡΡΠ΅ΠΌ Π² ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π½ΠΎΠΌΠ΅ΡΠ° ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄ΠΎΠ² ΠΈ ΠΈΠ½Π΄Π΅ΠΊΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ²
for y in range(height):
for x in range(width):
#ΠΡΠΎΠ²Π΅ΡΠΊΠ° Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠ°
completed = False
# ΠΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅ΠΌ ΠΈΠ½Π΄Π΅ΠΊΡ ΠΌΠ°ΡΡΠΈΠ²Π° polygon_grid Π² ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ ΠΌΠ°ΡΡΠΈΠ²Π° pixel_array
index = y * width + x
if polygon_grid[y][x] == 100: # ΠΡΠ»ΠΈ Π½Π°ΠΉΠ΄Π΅Π½Π° ΡΡΠ΅ΠΉΠΊΠ° 100
# Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΡΠΏΠΈΡΠΎΠΊ Π΄Π»Ρ Ρ
ΡΠ°Π½Π΅Π½ΠΈΡ ΡΠΎΡΠ΅Π΄Π½ΠΈΡ
Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ
neighbor_values = []
# ΠΠ°Ρ
ΠΎΠ΄ΠΈΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΡΠΎΡΠ΅Π΄Π½ΠΈΡ
ΠΏΠΎΠ»ΠΈΠ³ΠΎΠ½ΠΎΠ²
top = polygon_grid[y + 1][x] if y + 1 < len(polygon_grid) else None
bottom = polygon_grid[y - 1][x] if y - 1 >= 0 else None
left = polygon_grid[y][x - 1] if x - 1 >= 0 else None
right = polygon_grid[y][x + 1] if x + 1 < len(polygon_grid[0]) else None
top_left = polygon_grid[y + 1][x - 1] if y + 1 < len(polygon_grid) and x - 1 >= 0 else None
top_right = polygon_grid[y + 1][x + 1] if y + 1 < len(polygon_grid) and x + 1 < len(polygon_grid[0]) else None
bottom_left = polygon_grid[y - 1][x - 1] if y - 1 >= 0 and x - 1 >= 0 else None
bottom_right = polygon_grid[y - 1][x + 1] if y - 1 >= 0 and x + 1 < len(polygon_grid[0]) else None
# ΠΠ°Ρ
ΠΎΠ΄ΠΈΠΌ Π½Π΅ΠΊΠΎΡΠΎΡΡΡ
Π΄Π°Π»ΡΠ½ΠΈΡ
ΡΠΎΡΠ΅Π΄Π΅ΠΉ
top_2_left = polygon_grid[y + 2][x - 1] if y + 2 < len(polygon_grid) and x - 1 >= 0 else None
top_2_right = polygon_grid[y + 2][x + 1] if y + 2 < len(polygon_grid) and x + 1 < len(polygon_grid[0]) else None
bottom_2_right = polygon_grid[y - 2][x + 1] if y - 2 >= 0 and x + 1 < len(polygon_grid[0]) else None
bottom_2_left = polygon_grid[y - 2][x - 1] if y - 2 >= 0 and x - 1 >= 0 else None
top_right_2 = polygon_grid[y + 1][x + 2] if y + 1 < len(polygon_grid) and x + 2 < len(polygon_grid[0]) else None
top_left_2 = polygon_grid[y + 1][x - 2] if y + 1 < len(polygon_grid) and x - 2 >= 0 else None
bottom_left_2 = polygon_grid[y - 1][x - 2] if y - 1 >= 0 and x - 2 >= 0 else None
bottom_right_2 = polygon_grid[y - 1][x + 2] if y - 1 >= 0 and x + 2 < len(polygon_grid[0]) else None
# Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π» ΡΠΎΡΠ΅Π΄Π΅ΠΉ
material_max = material_grid[y][x][0]
material_min = material_grid[y][x][1]
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (1,2,3,4) Π‘ΡΠΎΡΠΎΠ½Ρ
#1
if bottom == material_max:
pixel_array[index][0] = (material_max * 100 + 1) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if top == material_max:
pixel_array[index][0] = (material_max * 100 + 2) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#3
if right == material_max:
pixel_array[index][0] = (material_max * 100 + 3) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#4
if left == material_max:
pixel_array[index][0] = (material_max * 100 + 4) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (5,6,7,8) ΠΠ½Π΅ΡΠ½ΠΈΠ΅ ΡΠ³Π»Ρ
#1
if bottom == 100 and right == 100 and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 5) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if bottom == 100 and left == 100 and bottom_left == material_max:
pixel_array[index][0] = (material_max * 100 + 6) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#3
if top == 100 and right == 100 and top_right == material_max:
pixel_array[index][0] = (material_max * 100 + 7) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#4
if top == 100 and left == 100 and top_left == material_max:
pixel_array[index][0] = (material_max * 100 + 8) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (9,10,11,12) ΠΠ½ΡΡΡΠ΅Π½Π½ΠΈΠ΅ ΡΠ³Π»Ρ
#1
if bottom == material_max and right == material_max and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 9) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if bottom == material_max and left == material_max and bottom_left == material_max:
pixel_array[index][0] = (material_max * 100 + 10) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#3
if top == material_max and right == material_max and top_right == material_max:
pixel_array[index][0] = (material_max * 100 + 11) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#4
if top == material_max and left == material_max and top_left == material_max:
pixel_array[index][0] = (material_max * 100 + 12) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (1,2,3,4) ΠΠ°Π΄ ΠΏΡΡΡΡΠΌ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠΌ
#1
if bottom == 100 and left == 100 and right == 100 and bottom_left == material_max and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 1) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if top == 100 and left == 100 and right == 100 and top_left == material_max and top_right == material_max:
pixel_array[index][0] = (material_max * 100 + 2) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#3
if right == 100 and top == 100 and bottom == 100 and top_right == material_max and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 3) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#4
if left == 100 and top == 100 and bottom == 100 and top_left == material_max and bottom_left == material_max:
pixel_array[index][0] = (material_max * 100 + 4) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (0) ΠΡΡΡΠΎΠΉ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»
#1
if top == 100 and bottom == 100 and left == 100 and right == 100 and top_left == material_max and top_right == material_max and bottom_left == material_max and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 0) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if left == material_max and right == material_max or top == material_max and bottom == material_max:
pixel_array[index][0] = (material_max * 100 + 0) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ (9,10,11,12) ΠΠ΅ΠΎΠ±ΡΡΠ½ΡΠ΅
#1
if bottom == material_max and top == 100 and left == 100 and right == 100 and top_left == material_max:
pixel_array[index][0] = (material_max * 100 + 10) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#2
if top == material_max and bottom == 100 and left == 100 and right == 100 and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 11) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#3
if right == material_max and left == 100 and top == 100 and bottom == 100 and bottom_left == material_max:
pixel_array[index][0] = (material_max * 100 + 9) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#4
if left == material_max and right == 100 and top == 100 and bottom == 100 and top_right == material_max:
pixel_array[index][0] = (material_max * 100 + 12) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#5
if bottom == material_max and top == 100 and left == 100 and right == 100 and top_right == material_max:
pixel_array[index][0] = (material_max * 100 + 9) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#6
if top == material_max and bottom == 100 and left == 100 and right == 100 and bottom_left == material_max:
pixel_array[index][0] = (material_max * 100 + 12) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#7
if right == material_max and left == 100 and top == 100 and bottom == 100 and top_left == material_max:
pixel_array[index][0] = (material_max * 100 + 11) / 10000
pixel_array[index][1] = material_min / 255
completed = True
#8
if left == material_max and right == 100 and top == 100 and bottom == 100 and bottom_right == material_max:
pixel_array[index][0] = (material_max * 100 + 10) / 10000
pixel_array[index][1] = material_min / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ ΠΏΠ΅ΡΠ΅ΡΠ΅ΡΠ΅Π½ΠΈΡ 3-Ρ
ΡΠ°Π·Π½ΡΡ
ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ². ΠΡΠΎΡΡΡΠ΅
#1
if top != 100 and bottom_left != 100 and bottom_right != 100 and top != bottom_left and bottom_left != bottom_right and bottom_right != top:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if top is None else (top * 100 + 13) / 10000
pixel_array[index][1] = material_min / 255 if bottom_left is None else bottom_left / 255
pixel_array[index][2] = material_max / 255 if bottom_right is None else bottom_right / 255
completed = True
#2
if bottom != 100 and top_left != 100 and top_right != 100 and bottom != top_left and top_left != top_right and top_right != bottom:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if bottom is None else (bottom * 100 + 14) / 10000
pixel_array[index][1] = material_min / 255 if top_left is None else top_left / 255
pixel_array[index][2] = material_max / 255 if top_right is None else top_right / 255
completed = True
#3
if left != 100 and top_right != 100 and bottom_right != 100 and left != top_right and top_right != bottom_right and bottom_right != left:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if left is None else (left * 100 + 15) / 10000
pixel_array[index][1] = material_min / 255 if top_right is None else top_right / 255
pixel_array[index][2] = material_max / 255 if bottom_right is None else bottom_right / 255
completed = True
#4
if right != 100 and top_left != 100 and bottom_left != 100 and right != top_left and top_left != bottom_left and bottom_left != right:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if right is None else (right * 100 + 16) / 10000
pixel_array[index][1] = material_min / 255 if top_left is None else top_left / 255
pixel_array[index][2] = material_max / 255 if bottom_left is None else bottom_left / 255
completed = True
# ΠΠ»Π³ΠΎΡΠΈΡΠΌΡ ΠΏΠ΅ΡΠ΅ΡΠ΅ΡΠ΅Π½ΠΈΡ 3-Ρ
ΡΠ°Π·Π½ΡΡ
ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ². Π‘Π»ΠΎΠΆΠ½ΡΠ΅
#1
if top == 100 and bottom == 100 and left == 100 and right == material_min and bottom_left == material_max and top_left == 100 and bottom_left != top_2_left < right:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if top_2_left is None else (top_2_left * 100 + 17) / 10000
pixel_array[index][1] = material_min / 255 if bottom_left is None else bottom_left / 255
pixel_array[index][2] = material_max / 255 if right is None else right / 255
completed = True
#2
if top == 100 and bottom == 100 and right == 100 and left == material_min and top_right == material_max and bottom_right == 100 and top_right != bottom_2_right < left:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if bottom_2_right is None else (bottom_2_right * 100 + 18) / 10000
pixel_array[index][1] = material_min / 255 if top_right is None else top_right / 255
pixel_array[index][2] = material_max / 255 if left is None else left / 255
completed = True
#3
if left == 100 and right == 100 and bottom == 100 and top == material_min and bottom_right == material_max and bottom_left == 100 and bottom_right != bottom_left_2 < top:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if bottom_left_2 is None else (bottom_left_2 * 100 + 19) / 10000
pixel_array[index][1] = material_min/ 255 if bottom_right is None else bottom_right / 255
pixel_array[index][2] = material_max / 255 if top is None else top / 255
completed = True
#4
if left == 100 and right == 100 and top == 100 and bottom == material_min and top_left == material_max and top_right == 100 and top_left != top_right_2 < bottom:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if top_right_2 is None else (top_right_2 * 100 + 20) / 10000
pixel_array[index][1] = material_min / 255 if top_left is None else top_left / 255
pixel_array[index][2] = material_max / 255 if bottom is None else bottom / 255
completed = True
#5
if top == 100 and bottom == 100 and right == 100 and left == material_min and bottom_right == material_max and top_right == 100 and bottom_right != top_2_right < left:
pixel_array[index][0] = (material_min * 100 + 0) / 10000 if top_2_right is None else (top_2_right * 100 + 21) / 10000
pixel_array[index][1] = material_min / 255 if bottom_right is None else bottom_right / 255
pixel_array[index][2] = material_max / 255 if left is None else left / 255
completed = True
Part 2: