قم بتحويل الشبكات ذات الألوان الرأسية إلى شبكات مزخرفة مرسومة بالأشعة فوق البنفسجية.
مقدمة
تعد ألوان Vertex طريقة مباشرة لإضافة معلومات الألوان مباشرةً إلى رؤوس الشبكة. غالبًا ما تكون هذه هي الطريقة التي تنتج بها النماذج ثلاثية الأبعاد مثل InstantMesh الشبكات. ومع ذلك، فإن معظم التطبيقات تفضل الشبكات المزخرفة والمعينة بالأشعة فوق البنفسجية.
يشرح هذا البرنامج التعليمي حلاً سريعًا لتحويل الشبكات ذات الألوان الرأسية إلى شبكات مزخرفة مرسومة بالأشعة فوق البنفسجية. يتضمن ذلك الإصدار القصير للحصول على النتائج بسرعة، والإصدار الطويل للحصول على إرشادات تفصيلية.
النسخة القصيرة
قم بتثبيت مكتبة InstantTexture لسهولة التحويل. هذه مكتبة صغيرة قمنا بكتابتها وتقوم بتنفيذ الخطوات الموضحة في الإصدار الطويل أدناه.
pip install git+https://github.com/dylanebert/InstantTexture
الاستخدام
يقوم الكود أدناه بتحويل لون قمة الرأس .obj شبكة للأشعة فوق البنفسجية المعينة ومحكم .glb شبكة وحفظه ل output.glb.
from instant_texture import Converter
input_mesh_path = "https://raw.githubusercontent.com/dylanebert/InstantTexture/refs/heads/main/examples/chair.obj"
converter = Converter()
converter.convert(input_mesh_path)
دعونا تصور شبكة الإخراج.
import trimesh
mesh = trimesh.load("output.glb")
mesh.show()
هذا كل شيء!
للحصول على إرشادات تفصيلية، تابع القراءة.
النسخة الطويلة
تثبيت التبعيات التالية:
- numpy للعمليات العددية
- تريميش لتحميل وحفظ بيانات الشبكة
- xatlas لتوليد خرائط الأشعة فوق البنفسجية
- وسادة لمعالجة الصور
- opencv-بيثون لمعالجة الصور
- httpx لتحميل شبكة الإدخال
pip install numpy trimesh xatlas opencv-python pillow httpx
استيراد التبعيات.
import cv2
import numpy as np
import trimesh
import xatlas
from PIL import Image, ImageFilter
قم بتحميل شبكة الإدخال ذات اللون الرأسي. يجب أن يكون هذا .obj الملف الموجود في input_mesh_path.
إذا كان ملفًا محليًا، فاستخدمه trimesh.load() بدلاً من trimesh.load_remote().
mesh = trimesh.load_remote(input_mesh_path)
mesh.show()
الوصول إلى ألوان قمة الشبكة.
إذا فشل هذا، تأكد من أن الشبكة صالحة .obj ملف بألوان قمة الرأس.
vertex_colors = mesh.visual.vertex_colors
إنشاء خريطة للأشعة فوق البنفسجية باستخدام xatlas.
هذا هو الجزء الأكثر استهلاكا للوقت من العملية.
vmapping, indices, uvs = xatlas.parametrize(mesh.vertices, mesh.faces)
أعد رسم خريطة القمم وألوان القمم إلى خريطة الأشعة فوق البنفسجية.
vertices = mesh.vertices[vmapping]
vertex_colors = vertex_colors[vmapping]
mesh.vertices = vertices
mesh.faces = indices
تحديد حجم الملمس المطلوب.
قم بإنشاء مخزن مؤقت للنسيج يتم ترقيته بواسطة upscale_factor لإنشاء نسيج عالي الجودة.
texture_size = 1024
upscale_factor = 2
buffer_size = texture_size * upscale_factor
texture_buffer = np.zeros((buffer_size, buffer_size, 4), dtype=np.uint8)
املأ نسيج الشبكة المعينة للأشعة فوق البنفسجية باستخدام الاستيفاء مركز الكتلة.
- الاستيفاء Barycentric: يحسب اللون المحرف عند النقطة
pداخل مثلث محدد بالقممv0,v1، وv2مع الألوان المقابلةc0,c1، وc2. - اختبار النقطة في المثلث: يحدد ما إذا كانت نقطة
pتقع داخل مثلث محدد بالقممv0,v1، وv2. - حلقة ملء الملمس:
- كرر على كل وجه من الشبكة.
- استرجاع إحداثيات الأشعة فوق البنفسجية (
uv0,uv1,uv2) والألوان (c0,c1,c2) للوجه الحالي. - تحويل إحداثيات الأشعة فوق البنفسجية إلى إحداثيات المخزن المؤقت.
- حدد المربع المحيط بالمثلث في المخزن المؤقت للنسيج.
- بالنسبة لكل بكسل في المربع المحيط، تحقق مما إذا كان البكسل يقع داخل المثلث باستخدام اختبار النقطة في المثلث.
- إذا كان بالداخل، فاحسب اللون المحرف باستخدام الاستيفاء مركز الكتلة.
- قم بتعيين اللون للبكسل المقابل في المخزن المؤقت للنسيج.
def barycentric_interpolate(v0, v1, v2, c0, c1, c2, p):
v0v1 = v1 - v0
v0v2 = v2 - v0
v0p = p - v0
d00 = np.dot(v0v1, v0v1)
d01 = np.dot(v0v1, v0v2)
d11 = np.dot(v0v2, v0v2)
d20 = np.dot(v0p, v0v1)
d21 = np.dot(v0p, v0v2)
denom = d00 * d11 - d01 * d01
if abs(denom) < 1e-8:
return (c0 + c1 + c2) / 3
v = (d11 * d20 - d01 * d21) / denom
w = (d00 * d21 - d01 * d20) / denom
u = 1.0 - v - w
u = np.clip(u, 0, 1)
v = np.clip(v, 0, 1)
w = np.clip(w, 0, 1)
interpolate_color = u * c0 + v * c1 + w * c2
return np.clip(interpolate_color, 0, 255)
def is_point_in_triangle(p, v0, v1, v2):
def sign(p1, p2, p3):
return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
d1 = sign(p, v0, v1)
d2 = sign(p, v1, v2)
d3 = sign(p, v2, v0)
has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
return not (has_neg and has_pos)
for face in mesh.faces:
uv0, uv1, uv2 = uvs[face]
c0, c1, c2 = vertex_colors[face]
uv0 = (uv0 * (buffer_size - 1)).astype(int)
uv1 = (uv1 * (buffer_size - 1)).astype(int)
uv2 = (uv2 * (buffer_size - 1)).astype(int)
min_x = max(int(np.floor(min(uv0[0], uv1[0], uv2[0]))), 0)
max_x = min(int(np.ceil(max(uv0[0], uv1[0], uv2[0]))), buffer_size - 1)
min_y = max(int(np.floor(min(uv0[1], uv1[1], uv2[1]))), 0)
max_y = min(int(np.ceil(max(uv0[1], uv1[1], uv2[1]))), buffer_size - 1)
for y in range(min_y, max_y + 1):
for x in range(min_x, max_x + 1):
p = np.array([x + 0.5, y + 0.5])
if is_point_in_triangle(p, uv0, uv1, uv2):
color = barycentric_interpolate(uv0, uv1, uv2, c0, c1, c2, p)
texture_buffer[y, x] = np.clip(color, 0, 255).astype(
np.uint8
)
دعونا نتصور كيف يبدو الملمس حتى الآن.
from IPython.display import display
image_texture = Image.fromarray(texture_buffer)
display(image_texture)

كما نرى، فإن الملمس يحتوي على الكثير من الثقوب.
لتصحيح ذلك، سنجمع بين 4 تقنيات:
- في الرسم: املأ الثقوب باستخدام متوسط لون وحدات البكسل المحيطة.
- مرشح متوسط: قم بإزالة التشويش عن طريق استبدال كل بكسل باللون المتوسط لوحدات البكسل المحيطة به.
- طمس غاوسي: قم بتنعيم النسيج لإزالة أي ضوضاء متبقية.
- الاختزال: تغيير الحجم إلى
texture_sizeمع إعادة تشكيل LANCZOS.
image_bgra = texture_buffer.copy()
mask = (image_bgra[:, :, 3] == 0).astype(np.uint8) * 255
image_bgr = cv2.cvtColor(image_bgra, cv2.COLOR_BGRA2BGR)
inpainted_bgr = cv2.inpaint(
image_bgr, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA
)
inpainted_bgra = cv2.cvtColor(inpainted_bgr, cv2.COLOR_BGR2BGRA)
texture_buffer = inpainted_bgra[::-1]
image_texture = Image.fromarray(texture_buffer)
image_texture = image_texture.filter(ImageFilter.MedianFilter(size=3))
image_texture = image_texture.filter(ImageFilter.GaussianBlur(radius=1))
image_texture = image_texture.resize((texture_size, texture_size), Image.LANCZOS)
display(image_texture)

كما نرى، أصبح الملمس الآن أكثر سلاسة ولا يحتوي على ثقوب.
يمكن تحسين ذلك بشكل أكبر باستخدام تقنيات أكثر تقدمًا أو التحرير اليدوي للنسيج.
أخيرًا، يمكننا إنشاء شبكة جديدة بإحداثيات الأشعة فوق البنفسجية والملمس الذي تم إنشاؤه.
material = trimesh.visual.material.PBRMaterial(
baseColorFactor=[1.0, 1.0, 1.0, 1.0],
baseColorTexture=image_texture,
metallicFactor=0.0,
roughnessFactor=1.0,
)
visuals = trimesh.visual.TextureVisuals(uv=uvs, material=material)
mesh.visual = visuals
mesh.show()

وها هو! الشبكة مُعيَّنة بالأشعة فوق البنفسجية ومزخرفة.
لتصديره عند التشغيل محليا، اتصل mesh.export("output.glb").
القيود
كما ترون، لا تزال الشبكة تحتوي على العديد من القطع الأثرية الصغيرة.
كما أن جودة خريطة الأشعة فوق البنفسجية والملمس أقل بكثير من معايير الشبكة الجاهزة للإنتاج.
ومع ذلك، إذا كنت تبحث عن حل سريع للتعيين من شبكة ملونة قمة الرأس إلى شبكة معيّنة بالأشعة فوق البنفسجية، فقد يكون هذا الأسلوب مفيدًا لك.
خاتمة
تناول هذا البرنامج التعليمي كيفية تحويل شبكة ذات ألوان قمة الرأس إلى شبكة مزخرفة مرسومة بالأشعة فوق البنفسجية.
إذا كانت لديك أي أسئلة أو تعليقات، فلا تتردد في فتح مشكلة على GitHub أو على المساحة.
شكرا لك على القراءة!