"""Render authored, periodic 4D wood grain at native 4096px in Blender. This is a procedural material bake, not an enlargement of a bitmap. The torus coordinate mapping makes the material seamless on all four edges. """ import bpy, math, json from pathlib import Path PROJECT=Path(__file__).resolve().parents[2] out=PROJECT/'assets/materials/phase10' scene=bpy.data.scenes.new('Bark_PBR_4K');bpy.context.window.scene=scene scene.render.engine='CYCLES';scene.cycles.samples=1 scene.render.resolution_x=4096;scene.render.resolution_y=4096;scene.render.resolution_percentage=100 scene.render.image_settings.file_format='PNG';scene.render.image_settings.color_mode='RGB';scene.render.image_settings.color_depth='8' scene.render.image_settings.compression=90;scene.render.dither_intensity=0 scene.view_settings.view_transform='Standard';scene.view_settings.look='None' scene.render.film_transparent=False bpy.ops.mesh.primitive_plane_add(size=2) plane=bpy.context.object;plane.name='Bark_material_bake_surface' mat=bpy.data.materials.new('SW_4K_Sculpted_Oak');mat.use_nodes=True;plane.data.materials.append(mat) n=mat.node_tree.nodes;n.clear();links=mat.node_tree.links def node(kind): return n.new(kind) def mathnode(op,a,b=None): obj=node('ShaderNodeMath');obj.operation=op if isinstance(a,(float,int)): obj.inputs[0].default_value=a else: links.new(a,obj.inputs[0]) if b is not None: if isinstance(b,(float,int)):obj.inputs[1].default_value=b else:links.new(b,obj.inputs[1]) return obj.outputs[0] uv=node('ShaderNodeTexCoord');split=node('ShaderNodeSeparateXYZ');links.new(uv.outputs['UV'],split.inputs[0]) u=mathnode('MULTIPLY',split.outputs['X'],math.tau) v=mathnode('MULTIPLY',split.outputs['Y'],math.tau) co=node('ShaderNodeCombineXYZ') links.new(mathnode('MULTIPLY',mathnode('COSINE',u),10),co.inputs['X']) links.new(mathnode('MULTIPLY',mathnode('SINE',u),10),co.inputs['Y']) links.new(mathnode('MULTIPLY',mathnode('COSINE',v),.7),co.inputs['Z']) grain=node('ShaderNodeTexNoise');grain.noise_dimensions='4D' links.new(co.outputs[0],grain.inputs['Vector']);links.new(mathnode('MULTIPLY',mathnode('SINE',v),.7),grain.inputs['W']) grain.inputs['Scale'].default_value=2.6;grain.inputs['Detail'].default_value=7;grain.inputs['Roughness'].default_value=.73 height=node('ShaderNodeValToRGB') height.color_ramp.elements[0].position=.27;height.color_ramp.elements[0].color=(.07,.07,.07,1) height.color_ramp.elements[1].position=.67;height.color_ramp.elements[1].color=(.8,.8,.8,1) height.color_ramp.elements.new(.43).color=(.14,.14,.14,1) height.color_ramp.elements.new(.51).color=(.55,.55,.55,1) links.new(grain.outputs['Fac'],height.inputs[0]) emit=node('ShaderNodeEmission');output=node('ShaderNodeOutputMaterial');links.new(emit.outputs[0],output.inputs['Surface']) links.new(height.outputs[0],emit.inputs['Color']) bpy.ops.object.camera_add(location=(0,0,3));camera=bpy.context.object;camera.data.type='ORTHO';camera.data.ortho_scale=2;scene.camera=camera scene.render.filepath=str(out/'oak-height-4k.png');bpy.ops.render.render(write_still=True) colors=node('ShaderNodeValToRGB') def linear(hexcolor): rgb=[int(hexcolor[i:i+2],16)/255 for i in (0,2,4)] return tuple(c/12.92 if c<=.04045 else ((c+.055)/1.055)**2.4 for c in rgb)+(1,) colors.color_ramp.elements[0].position=.0;colors.color_ramp.elements[0].color=linear('302B23') colors.color_ramp.elements[1].position=1;colors.color_ramp.elements[1].color=linear('B09A6D') colors.color_ramp.elements.new(.18).color=linear('4B3C2A') colors.color_ramp.elements.new(.51).color=linear('766041') colors.color_ramp.elements.new(.74).color=linear('94855D') links.new(height.outputs[0],colors.inputs[0]);links.new(colors.outputs[0],emit.inputs['Color']) scene.render.filepath=str(out/'oak-albedo-4k.png');bpy.ops.render.render(write_still=True) bpy.data.libraries.write(str(PROJECT/'assets-source/phase10/oak-material-4k.blend'),{scene},fake_user=True,compress=True) print('PBR_BAKE_PASS: two native 4096 x 4096 8-bit runtime maps; original seamless procedural material.')