How Can I Use a Procedural Bump Map to Replicate a Natural Pattern?
To align the texture along the direction of the mesh requires a UV map and this needs to be unwrapped in such a way that each face is oriented along the line of the antler. Further, to avoid a 'seam' along the length of the mesh requires that a seamless texture is used and that the UV map unwrapping ensures that the edges of the UV map align. The right UV map and a seamless texture can produce the following :

Manually unwrapping such a mesh while keeping the faces correctly aligned and joined at the seams would be quite complicated and time consuming - unwrapping the splits while keeping the orientation and the seams would be particularly difficult. Fortunately, Python can do the unwrapping for us.
Starting with a mesh created using the Skin modifier :

The 'texture' needs to run from one end of the antler (the 'root) to the tips. Create two new vertex groups (one for the 'start' and the other for the 'end') and assign the relevant vertices to each. For the example above I marked the vertices around the 'root' of the antler as 'START' and those at the tips as 'END'. The texture will be spread over the mesh using those points as anchors.


Next, create another vertex group ('seam') and assign vertices along the length of each 'branch' to it. This will be the 'seam' of the texture as it's wrapped around the mesh. You can experiment with different seams to get twists and different patterns for the split but the pattern shown below works quite well.

To give further control at the boundaries of branches you can allocate vertices to a 'crease' vertex group. This will effectively block the influence of the 'seam' to prevent one branch's seam from affecting the next. You may need some experimentation for this - depending on your geometry and chosen seam. In my case I simply 'capped' the end of each seam.

Open a Text Editor window and paste the following code (Blender versions prior to 2.80 - for later versions, see EDIT further down post) :
import bpy
import bmesh

group_vertex_cache = []

def color_vertices_rgb(obj, numpasses, target_delta, grp_start, grp_end, grp_seam, grp_crease):
    &quot;&quot;&quot;Uses the Vertex Groups to assign each vertex UV coordinates, blended across the mesh&quot;&quot;&quot;

    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='DESELECT')
    obj.select = True
    bpy.context.scene.objects.active = obj
    bpy.ops.object.mode_set(mode='EDIT')
    
    obj.update_from_editmode()

    mesh = obj.data 
    scn = bpy.context.scene

    scn.objects.active = obj

    start_vertices = []     # Start RED
    end_vertices = []       # End RED
    seam_vertices = []      # GREEN
    crease_vertices = []    # Block influence
    vert_colors = {}
    vert_links = {}
    
    global group_vertex_cache
    init_group_vertex_cache(obj)
 
    # Get a count of loop verts and create an array to hold the details
    loopvert_count = len(obj.data.loops)
    loopverts = [0] * loopvert_count
  
    print('Capturing...') 
    # Process each loop vert and collect all the data we require
    for polygon in obj.data.polygons:
        
        loopidxpos = -1
        for loopidx in polygon.loop_indices:
            loopidxpos = loopidxpos + 1
        
            loopvertrec = {}
            
            # Determine which loop verts are to the left and right of this one
            if loopidxpos == 0:
                left  = polygon.loop_indices[len(polygon.loop_indices) - 1]
                right = polygon.loop_indices[loopidxpos + 1]
            elif loopidxpos == (len(polygon.loop_indices)-1):
                left  = polygon.loop_indices[loopidxpos - 1]
                right = polygon.loop_indices[0]
            else:
                left  = polygon.loop_indices[loopidxpos - 1]
                right = polygon.loop_indices[loopidxpos + 1]
              
            loopvertrec['left']  = left
            loopvertrec['right'] = right
            loopvertrec['left-dist'] = get_edge_length(obj, loopidx, left)
            loopvertrec['right-dist'] = get_edge_length(obj, loopidx, right)
             
            # get list of loops verts that make up the same vert as this one
            loopvertrec['shared-loop-verts'] = get_loop_verts(obj, loopidx)
               
            #...get whether it's in each of the 'groups' (ie, start, end, seam, crease) - and the associated weight (assume weight 0 if not in group)
            loopvertrec['start-weight'] = get_group_weight(obj, grp_start, loopidx)
            loopvertrec['end-weight'] = get_group_weight(obj, grp_end, loopidx)
            loopvertrec['seam-weight'] = get_group_weight(obj, grp_seam, loopidx)
            loopvertrec['crease-weight'] = get_group_weight(obj, grp_crease, loopidx)
            
            #loopvertrec['color'] = get_loopvert_color(obj, loopidx)
            
            loopverts[loopidx] = loopvertrec
            print(str(loopidx))
            
            vert_colors[loopidx] = [0.5,0.5,0.5]
              
    print('Captured.')
    
    new_colors = {}
    print('Processing....')
    for p in range(0, numpasses):
        print(&quot;Pass &quot;+str(p+1)+&quot; of &quot;+str(numpasses))

        maxdelta = 0    
        
        for v in range(0, len(loopverts)):
            
            loopvertrec = loopverts[v]
            
            start_weight  = loopvertrec['start-weight'] 
            end_weight    = loopvertrec['end-weight']
            seam_weight   = loopvertrec['seam-weight']
            crease_weight = loopvertrec['crease-weight']
            
            # Generate Red, Green, Blue based on weights and spread over mesh. Red will be used to generate U, Green/Blue to generate V
            
            # Average colors with neighbours
            r = (vert_colors[v][0] + vert_colors[loopvertrec['left']][0] + vert_colors[loopvertrec['right']][0]) / 3
            g = (vert_colors[v][1] + vert_colors[loopvertrec['left']][1] + vert_colors[loopvertrec['right']][1]) / 3
            b = (vert_colors[v][2] + vert_colors[loopvertrec['left']][2] + vert_colors[loopvertrec['right']][2]) / 3
            
            average_this_loop = [r, g, b]
            
            for v2 in loopvertrec['shared-loop-verts']:
                r = r + (vert_colors[v][0] + vert_colors[loopverts[v2]['left']][0] + vert_colors[loopverts[v2]['right']][0]) / 3
                g = g + (vert_colors[v][1] + vert_colors[loopverts[v2]['left']][1] + vert_colors[loopverts[v2]['right']][1]) / 3
                b = b + (vert_colors[v][2] + vert_colors[loopverts[v2]['left']][2] + vert_colors[loopverts[v2]['right']][2]) / 3

            average_all_loops = [r / (len(loopvertrec['shared-loop-verts'])+1),g / (len(loopvertrec['shared-loop-verts'])+1),b / (len(loopvertrec['shared-loop-verts'])+1)]
                
            # CREASE
            r = average_all_loops[0]
            g = average_this_loop[1] * ( crease_weight ) + average_all_loops[1] * ( 1.0 - crease_weight )
            b = average_this_loop[2] * ( crease_weight ) + average_all_loops[2] * ( 1.0 - crease_weight )
            
            r = r * (1.0 - end_weight)                          # END
            r = r * (1.0 - start_weight) + 1.0 * start_weight   # START
            
            # Determine the left/right side of the seam based on direction of 'red'
            if (vert_colors[loopvertrec['left']][0] &gt; vert_colors[loopvertrec['right']][0]):
                leftness = 1.0
            elif (vert_colors[loopvertrec['left']][0] &lt; vert_colors[loopvertrec['right']][0]):
                leftness = 0.0
            else:
                leftness = 0.5
                
            g = g * (1.0 - seam_weight) + leftness * seam_weight
            
            # store it
            if len(new_colors)&gt;v:
                vertdelta = abs(new_colors[v][0]-r) + abs(new_colors[v][1]-g) + abs(new_colors[v][2]-b)
            else:
                vertdelta = abs(r) + abs(g) + abs(b)
                
            if vertdelta &gt; maxdelta:
                maxdelta = vertdelta
                
            new_colors[v] = (r,g,b)

        print(maxdelta) 
        if maxdelta &lt; target_delta:
            break           
            
        #Copy new colors back to vert_colors 
        for i in range(0,len(loopverts)):
            vert_colors[i] = new_colors[i]
    
    # Generate UV map - must be in EDIT mode for this
    
    bm = bmesh.from_edit_mesh(obj.data)
    uv_layer = bm.loops.layers.uv.verify()
    bm.faces.layers.tex.verify()
    
    for f in bm.faces:
        for l in f.loops:
            luv = l[uv_layer]
            
            r = vert_colors[l.index][0]
            g = vert_colors[l.index][1]
            b = vert_colors[l.index][2]
            
            luv.uv = (r, g)
            
    bmesh.update_edit_mesh(obj.data)
    bpy.ops.object.mode_set(mode='OBJECT')
   
###########################################################################################################################

def get_edge_length(obj, loopidx1, loopidx2):

    vert1 = obj.data.loops[loopidx1].vertex_index
    vert2 = obj.data.loops[loopidx2].vertex_index
    
    for edge in obj.data.edges:
        if (edge.vertices[0] == vert1 and edge.vertices[1] == vert2) or (edge.vertices[0] == vert2 and edge.vertices[1] == vert1):
            
            OWMatrix = obj.matrix_world
            v1Pos = OWMatrix * obj.data.vertices[vert1].co
            v2Pos = OWMatrix * obj.data.vertices[vert2].co
            
            dist = (v1Pos - v2Pos).length
            
            return dist            
            
    return 0.0

###########################################################################################################################

def get_loop_verts(obj, loopidx):
    loopverts = []
    vertidx = obj.data.loops[loopidx].vertex_index
    
    idx = -1
    for loop in obj.data.loops:
        idx = idx + 1
        if loop.vertex_index == vertidx and idx != loopidx:
            loopverts.append(idx)
    
    return loopverts

###########################################################################################################################

def get_group_weight(obj, group, loopidx):
    
    groupindex = obj.vertex_groups[group].index
    vertidx = obj.data.loops[loopidx].vertex_index
    
    global group_vertex_cache
    
    for v in group_vertex_cache:
        if v.index == vertidx:
            for g in v.groups:
                if g.group == groupindex:
                    return g.weight
    
    return 0.0

###########################################################################################################################

def init_group_vertex_cache(obj):
    
    global group_vertex_cache
    
    for v in obj.data.vertices:
        if len(v.groups) &gt; 0:
            group_vertex_cache.append(v)
            


###########################################################################################################################

#Invoke it
color_vertices_rgb(bpy.context.scene.objects[&quot;Antler&quot;], 1250, 0.0001,'START', 'END', 'SEAM', 'CREASE')

Note the last line of the script which invokes the function on your mesh. This should be amended to specify the name of your mesh and the other arguments as follows :
color_vertices_rgb(bpy.context.scene.objects[&quot;&lt;meshname&gt;&quot;], &lt;iterations&gt;, &lt;mindelta&gt;, '&lt;start&gt;', '&lt;end&gt;', '&lt;seam&gt;', '&lt;crease&gt;')

Where meshname is the name of your mesh, iterations is the number of steps to run the 'average' spread algorithm, mindelta is the minimum acceptable change in UV coordinates (if it reaches that then it will decide it's finished regardless of iterations - if unsure, set to 0), start, end, seam, crease are the names of the 4 vertex groups mentioned above. Run the script - it might take a while depending on the complexity of your mesh and the chosen number of iterations (more will produce better results but will take longer). If you have a command-line console window open it will output progress and diagnostics.
This works by allocating UV coordinates (which can be represented as 'colors' - Red being equivalent to 'U' and Green being equivalent to 'V') to each of the vertices in the groups and spreading them across the mesh by averaging them with their immediate neighbours.

Note that the Red is strongest at the root and fades out towards the tips while the green is strongest at the left-hand edge of the seam and fades around the circumference of the mesh.
Once the processing is complete you should have a UV Map that you can use for your texture and this can be used with a seamless image texture if desired - although depending on the texture used, this isn't strictly necessary if the texture is already quite linear since it effectively hides the seam. For example, here's a simple Musgrave texture, scaled in one direction using a Mapping node.


Combine multiple textures at different scales for a more interesting surface.


This can now be used to drive the Bump node and adjusted to get the desired effect.

Blend file attached 

EDIT for Blender 2.80+ (changes to API): At later Blender versions the API function calls for some operations has changed. Here's an updated script that works at later releases (tested on 2.91).
import bpy
import bmesh

group_vertex_cache = []

def color_vertices_rgb(obj, numpasses, target_delta, grp_start, grp_end, grp_seam, grp_crease):
    &quot;&quot;&quot;Uses the Vertex Groups to assign each vertex UV coordinates, blended across the mesh&quot;&quot;&quot;

    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='DESELECT')

    #For 2.80+, changed API for selecting object
    #obj.select = True
    obj.select_set(state = True)

    #For 2.80+, changed API to set active object 
    #bpy.context.scene.objects.active = obj
    bpy.context.view_layer.objects.active = obj

    bpy.ops.object.mode_set(mode='EDIT')

    obj.update_from_editmode()

    mesh = obj.data 
    scn = bpy.context.scene

    #2.80+ this isn't necessary (already made it 'active')
    #scn.objects.active = obj

    start_vertices = []     # Start RED
    end_vertices = []       # End RED
    seam_vertices = []      # GREEN
    crease_vertices = []    # Block influence
    vert_colors = {}
    vert_links = {}

    global group_vertex_cache
    init_group_vertex_cache(obj)

    # Get a count of loop verts and create an array to hold the details
    loopvert_count = len(obj.data.loops)
    loopverts = [0] * loopvert_count

    print('Capturing...') 
    # Process each loop vert and collect all the data we require
    for polygon in obj.data.polygons:

        loopidxpos = -1
        for loopidx in polygon.loop_indices:
            loopidxpos = loopidxpos + 1

            loopvertrec = {}

            # Determine which loop verts are to the left and right of this one
            if loopidxpos == 0:
                left  = polygon.loop_indices[len(polygon.loop_indices) - 1]
                right = polygon.loop_indices[loopidxpos + 1]
            elif loopidxpos == (len(polygon.loop_indices)-1):
                left  = polygon.loop_indices[loopidxpos - 1]
                right = polygon.loop_indices[0]
            else:
                left  = polygon.loop_indices[loopidxpos - 1]
                right = polygon.loop_indices[loopidxpos + 1]

            loopvertrec['left']  = left
            loopvertrec['right'] = right
            loopvertrec['left-dist'] = get_edge_length(obj, loopidx, left)
            loopvertrec['right-dist'] = get_edge_length(obj, loopidx, right)

            # get list of loops verts that make up the same vert as this one
            loopvertrec['shared-loop-verts'] = get_loop_verts(obj, loopidx)

            #...get whether it's in each of the 'groups' (ie, start, end, seam, crease) - and the associated weight (assume weight 0 if not in group)
            loopvertrec['start-weight'] = get_group_weight(obj, grp_start, loopidx)
            loopvertrec['end-weight'] = get_group_weight(obj, grp_end, loopidx)
            loopvertrec['seam-weight'] = get_group_weight(obj, grp_seam, loopidx)
            loopvertrec['crease-weight'] = get_group_weight(obj, grp_crease, loopidx)

            #loopvertrec['color'] = get_loopvert_color(obj, loopidx)

            loopverts[loopidx] = loopvertrec
            print(str(loopidx))

            vert_colors[loopidx] = [0.5,0.5,0.5]

    print('Captured.')

    new_colors = {}
    print('Processing....')
    for p in range(0, numpasses):
        print(&quot;Pass &quot;+str(p+1)+&quot; of &quot;+str(numpasses))

        maxdelta = 0    

        for v in range(0, len(loopverts)):

            loopvertrec = loopverts[v]

            start_weight  = loopvertrec['start-weight'] 
            end_weight    = loopvertrec['end-weight']
            seam_weight   = loopvertrec['seam-weight']
            crease_weight = loopvertrec['crease-weight']

            # Generate Red, Green, Blue based on weights and spread over mesh. Red will be used to generate U, Green/Blue to generate V

            # Average colors with neighbours
            r = (vert_colors[v][0] + vert_colors[loopvertrec['left']][0] + vert_colors[loopvertrec['right']][0]) / 3
            g = (vert_colors[v][1] + vert_colors[loopvertrec['left']][1] + vert_colors[loopvertrec['right']][1]) / 3
            b = (vert_colors[v][2] + vert_colors[loopvertrec['left']][2] + vert_colors[loopvertrec['right']][2]) / 3

            average_this_loop = [r, g, b]

            for v2 in loopvertrec['shared-loop-verts']:
                r = r + (vert_colors[v][0] + vert_colors[loopverts[v2]['left']][0] + vert_colors[loopverts[v2]['right']][0]) / 3
                g = g + (vert_colors[v][1] + vert_colors[loopverts[v2]['left']][1] + vert_colors[loopverts[v2]['right']][1]) / 3
                b = b + (vert_colors[v][2] + vert_colors[loopverts[v2]['left']][2] + vert_colors[loopverts[v2]['right']][2]) / 3

            average_all_loops = [r / (len(loopvertrec['shared-loop-verts'])+1),g / (len(loopvertrec['shared-loop-verts'])+1),b / (len(loopvertrec['shared-loop-verts'])+1)]

            # CREASE
            r = average_all_loops[0]
            g = average_this_loop[1] * ( crease_weight ) + average_all_loops[1] * ( 1.0 - crease_weight )
            b = average_this_loop[2] * ( crease_weight ) + average_all_loops[2] * ( 1.0 - crease_weight )

            r = r * (1.0 - end_weight)                          # END
            r = r * (1.0 - start_weight) + 1.0 * start_weight   # START

            # Determine the left/right side of the seam based on direction of 'red'
            if (vert_colors[loopvertrec['left']][0] &gt; vert_colors[loopvertrec['right']][0]):
                leftness = 1.0
            elif (vert_colors[loopvertrec['left']][0] &lt; vert_colors[loopvertrec['right']][0]):
                leftness = 0.0
            else:
                leftness = 0.5

            g = g * (1.0 - seam_weight) + leftness * seam_weight

            # store it
            if len(new_colors)&gt;v:
                vertdelta = abs(new_colors[v][0]-r) + abs(new_colors[v][1]-g) + abs(new_colors[v][2]-b)
            else:
                vertdelta = abs(r) + abs(g) + abs(b)

            if vertdelta &gt; maxdelta:
                maxdelta = vertdelta

            new_colors[v] = (r,g,b)

        print(maxdelta) 
        if maxdelta &lt; target_delta:
            break           

        #Copy new colors back to vert_colors 
        for i in range(0,len(loopverts)):
            vert_colors[i] = new_colors[i]

    # Generate UV map - must be in EDIT mode for this

    bm = bmesh.from_edit_mesh(obj.data)
    uv_layer = bm.loops.layers.uv.verify()
    
    #Removed for 2.80+ since no longer valid and not needed
    #bm.faces.layers.tex.verify()

    for f in bm.faces:
        for l in f.loops:
            luv = l[uv_layer]

            r = vert_colors[l.index][0]
            g = vert_colors[l.index][1]
            b = vert_colors[l.index][2]

            luv.uv = (r, g)

    bmesh.update_edit_mesh(obj.data)
    bpy.ops.object.mode_set(mode='OBJECT')

###########################################################################################################################

def get_edge_length(obj, loopidx1, loopidx2):

    vert1 = obj.data.loops[loopidx1].vertex_index
    vert2 = obj.data.loops[loopidx2].vertex_index

    for edge in obj.data.edges:
        if (edge.vertices[0] == vert1 and edge.vertices[1] == vert2) or (edge.vertices[0] == vert2 and edge.vertices[1] == vert1):

            OWMatrix = obj.matrix_world

            #In 2.80+, use the '@' operator for matrix mulitplication
            #v1Pos = OWMatrix * obj.data.vertices[vert1].co
            #v2Pos = OWMatrix * obj.data.vertices[vert2].co
            v1Pos = OWMatrix @ obj.data.vertices[vert1].co
            v2Pos = OWMatrix @ obj.data.vertices[vert2].co

            dist = (v1Pos - v2Pos).length

            return dist            

    return 0.0

###########################################################################################################################

def get_loop_verts(obj, loopidx):
    loopverts = []
    vertidx = obj.data.loops[loopidx].vertex_index

    idx = -1
    for loop in obj.data.loops:
        idx = idx + 1
        if loop.vertex_index == vertidx and idx != loopidx:
            loopverts.append(idx)

    return loopverts

###########################################################################################################################

def get_group_weight(obj, group, loopidx):

    groupindex = obj.vertex_groups[group].index
    vertidx = obj.data.loops[loopidx].vertex_index

    global group_vertex_cache

    for v in group_vertex_cache:
        if v.index == vertidx:
            for g in v.groups:
                if g.group == groupindex:
                    return g.weight

    return 0.0

###########################################################################################################################

def init_group_vertex_cache(obj):

    global group_vertex_cache

    for v in obj.data.vertices:
        if len(v.groups) &gt; 0:
            group_vertex_cache.append(v)



###########################################################################################################################

#Invoke it
color_vertices_rgb(bpy.context.scene.objects[&quot;Suzanne&quot;], 1250, 0.0001,'START', 'END', 'SEAM', 'CREASE')