Big Guide: Texture Transitions and "Multishader", part 1

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:

  1. After creating the desired shape, be sure to reset all transforms by pressing Ctrl + A: All Transforms.
  2. Do not modify the UV.
  3. Do not use Extrude.
  4. 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:

  1. Name your materials using only numbers within the range of 0 to 99.
  2. 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:

1 Like

For that, a very special thanks :beers: :smiley_cat:

A small addition to the guide.
When copying the Blender code, pay attention to these three lines. They automatically select suitable materials for places where errors occur that the Blender code could not calculate. This code does not correct these errors, but only masks them.
But if you want to visually observe the problem areas, then comment out these lines. In my case, the errors are displayed as a red texture.

Screenshot_70
Screenshot_71

To eliminate these errors, you simply need to slightly adjust the material placement – reduce the occupied area in some places, increase it in others, or move the materials around through trial and error until the errors are gone. The code has a limitation: for proper operation, there must be at least 2 polygons separating the structures of materials. Otherwise, these errors may occur.

1 Like

Maybe it’s like this

ΠΠ΅ΠΏΡ€ΠΎΡˆΠ΅Π΄ΡˆΠΈΠ΅ Π½ΠΈ ΠΏΠΎ ΠΎΠ΄Π½ΠΎΠΌΡƒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΡƒ. НулСвыС

:red_question_mark:

Although perhaps it doesn’t really matter here.

Big, good job :+1:

I’ve corrected the addition to the guide, it’s now much clearer what those three lines of code do.