Rock Generator 2.8.0: Cavity in Vertex-Farben (texturfreier Weg)

Idee kam vom User: die Steine ueber "smart materials" in UE loesen statt ueber
gebackene Texturen. Prozedurales Noise/Voronoi im Shader waere zu teuer (laeuft
pro Pixel), aber der Kern der Idee traegt - deshalb dieser Weg:

Neue Option "Cavity in Vertex-Farben": schreibt Cavity/AO plus optionalen
Hoehen-Verlauf in das Farb-Attribut "Cavity" (vertex_color_dirt + eigene
Nachbearbeitung fuer Staerke und Verlauf). Das Material braucht damit KEINE
Textur:  Base Color = Farbton x VertexColor, dazu Kantenlicht und Schattentint.

Gegenueber Triplanar sitzen die dunklen Fugen dadurch auf den ECHTEN Kanten des
Meshes statt darueberzuschweben - genau das passiert in der Genshin-Referenz.

Verifiziert: Attribut wird angelegt, Wertebereich 0.250..0.947 (echter Kontrast),
und es ueberlebt den FBX-Roundtrip UNVERAENDERT (gleicher Bereich nach Import).
Regler: Cavity-Staerke, Hoehen-Verlauf, Weichzeichnen.
Neuer Test tests/test_vertex_cavity.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 09:10:54 +02:00
co-authored by Claude Opus 4.8
parent 1df70c9e50
commit de2acdd381
7 changed files with 152 additions and 6 deletions
+93 -1
View File
@@ -1,7 +1,7 @@
bl_info = {
"name": "Stylized Rock Generator",
"author": "D4rkst3r",
"version": (2, 7, 0),
"version": (2, 8, 0),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > Rock Gen",
"description": (
@@ -60,6 +60,7 @@ PRESET_KEYS = (
"shading_mode", "autosmooth_angle",
"randomize_shape", "noise_scale_max", "displace_strength_max", "nonuniform_scale",
"flatten_bottom", "flatten_ratio", "set_origin_bottom", "keep_size",
"bake_vertex_cavity", "cavity_strength", "cavity_blur", "cavity_gradient",
"use_class_size", "base_size",
)
@@ -453,6 +454,65 @@ def _apply_random_transform(context, obj, settings, rng):
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
def _bake_vertex_cavity(context, obj, settings):
"""Cavity + Hoehen-Verlauf in die VERTEX-FARBEN backen.
Warum: das Material braucht damit KEINE Textur. Base Color = Farbton x
Vertex-Farbe, dazu Kantenlicht und Schattentint im Shader - genau der
Genshin-Aufbau. Ausserdem sitzen die dunklen Fugen auf den ECHTEN Kanten
des Meshes, statt per Triplanar darueberzuschweben.
Aufloesung haengt an der Vertex-Dichte; fuer flaechige Platten reicht das,
fuer feine Struktur nicht (dann Textur nehmen).
"""
me = obj.data
layer = me.color_attributes.get("Cavity")
if layer is None:
layer = me.color_attributes.new(name="Cavity", type='BYTE_COLOR', domain='CORNER')
me.color_attributes.active_color = layer
me.color_attributes.render_color_index = me.color_attributes.find("Cavity")
_deselect_all(context)
context.view_layer.objects.active = obj
obj.select_set(True)
try:
bpy.ops.paint.vertex_color_set(color=(1.0, 1.0, 1.0, 1.0))
except RuntimeError:
pass
try:
bpy.ops.paint.vertex_color_dirt(
blur_strength=1.0,
blur_iterations=settings.cavity_blur,
clean_angle=3.14159,
dirt_angle=0.0,
dirt_only=False,
normalize=True,
)
except RuntimeError as exc:
raise RockGenError("Vertex-Cavity fehlgeschlagen: %s" % exc)
# Staerke regeln + optionaler Hoehen-Verlauf (unten dunkler, wie in der
# Referenz). Beides direkt auf den Farbwerten, das ist schnell genug.
zs = [v.co.z for v in me.vertices]
if not zs:
return
zmin, zmax = min(zs), max(zs)
span = max(zmax - zmin, 1e-6)
amt = settings.cavity_strength
grad = settings.cavity_gradient
data = layer.data
for poly in me.polygons:
for li in poly.loop_indices:
vi = me.loops[li].vertex_index
c = data[li].color
val = 1.0 - (1.0 - c[0]) * amt # Cavity abschwaechen
if grad > 0.0:
t = (me.vertices[vi].co.z - zmin) / span
val *= (1.0 - grad) + grad * (0.35 + 0.65 * t)
val = max(0.0, min(1.0, val))
data[li].color = (val, val, val, 1.0)
def _apply_shading(context, obj, settings):
"""Setzt den Shading-Look: FLAT (facettiert), SMOOTH oder AUTO (nach Winkel)."""
_deselect_all(context)
@@ -607,6 +667,8 @@ def make_one_rock(context, settings, index, batch_count=1):
_apply_hardsurface(context, obj, settings)
_apply_random_transform(context, obj, settings, rng)
_apply_shading(context, obj, settings)
if settings.bake_vertex_cavity:
_bake_vertex_cavity(context, obj, settings)
if settings.set_origin_bottom:
_set_origin_bottom(context, obj)
if settings.assign_material:
@@ -730,6 +792,27 @@ class RockGenSettings(PropertyGroup):
description="Setzt den Origin auf die Unterseiten-Mitte (sauberes Aufstellen in UE)",
)
# --- Vertex-Cavity (texturfreier Weg) ---
bake_vertex_cavity: BoolProperty(
name="Cavity in Vertex-Farben", default=False,
description=("Backt Cavity/AO in die Vertex-Farben (Attribut 'Cavity'). "
"Das Material braucht dann KEINE Textur: Base Color = Farbton "
"x Vertex-Farbe, Rest ueber Kantenlicht und Schattentint"),
)
cavity_strength: FloatProperty(
name="Cavity-Staerke", default=0.7, min=0.0, max=1.0,
description="0 = alles weiss, 1 = volle Abdunklung in den Vertiefungen",
)
cavity_blur: IntProperty(
name="Weichzeichnen", default=1, min=0, max=5,
description="Glaettet den Cavity-Verlauf ueber benachbarte Vertices",
)
cavity_gradient: FloatProperty(
name="Hoehen-Verlauf", default=0.35, min=0.0, max=1.0,
description=("Dunkelt den Stein nach unten hin ab (wie in der Referenz). "
"0 = aus"),
)
# --- Material ---
assign_material: BoolProperty(
name="Material zuweisen", default=False,
@@ -1567,6 +1650,15 @@ class VIEW3D_PT_rock_generator(Panel):
sub.prop(settings, "lod_count")
sub.prop(settings, "lod_ratio_step")
box = layout.box()
box.label(text="Vertex-Cavity (ohne Textur)")
box.prop(settings, "bake_vertex_cavity")
sub = box.column(align=True)
sub.enabled = settings.bake_vertex_cavity
sub.prop(settings, "cavity_strength")
sub.prop(settings, "cavity_gradient")
sub.prop(settings, "cavity_blur")
box = layout.box()
box.label(text="Material")
box.prop(settings, "assign_material")