7
$\begingroup$

Can I turn weight paint(vertex group data) into vertex paint? I would like to take the weight paint value and convert it into a black and white grey scale vertex paint map.

$\endgroup$
6
  • $\begingroup$ you forgot a tag that may help a lot here -> Python! $\endgroup$ Aug 21, 2014 at 17:16
  • $\begingroup$ @MarcClintDion Tag the question, not the answers. Even if the only solution is python, if the OP isn't specifically looking for a python solution, then I think the current tags are fine. $\endgroup$
    – gandalf3
    Aug 21, 2014 at 21:08
  • $\begingroup$ True but for some reason I felt that GiantCowFilms would not have a problem with Python solutions as well. Maybe I'm remembering history wrong. Anyways, the reason I brought it up is because some people may just be skimming the tags only and moving past questions with no Python tag and I was pretty sure there was no built in tool to do this. $\endgroup$ Aug 22, 2014 at 5:42
  • $\begingroup$ I don't mind python if there is no alternative. $\endgroup$ Aug 22, 2014 at 14:37
  • $\begingroup$ @MarcClintDion It seems there are differing opinions on this.. e.g. meta.stackexchange.com/q/59333/238438 and meta.stackexchange.com/q/184658/238438 (the second one has no answers, but links to a bunch of related/duplicate questions with answers) $\endgroup$
    – gandalf3
    Aug 22, 2014 at 19:22

5 Answers 5

10
$\begingroup$

I think you will still have to do a bit of scripting to get the following to work but it does most of what you want. A while back, someone posted a question about how to convert an existing script that turns Weight Paint to Vertex Color Paint, he posted the script link and there are a couple reasons why I decided to post this as an answer instead of only directing you to the link. First, there is an important step necessary to get this to work properly. Second, I'd like to also post the script here directly in case the link to the external site goes down, this way it will still be available here.

To start with, Vertex Color Paint starts off as all white and if you try to use the Addon with the Vertex Colors as all White then it fails weirdly. The first results I got looked like the following. It transitions from white to grey then back to white. It's not black where the blue is in Weight Paint mode.

enter image description here

However, if you first add a Color vertex group and then make that group black, the Addon works fine. Just set the brush color to black and use the Set Vertex Color shift +k

enter image description here

Now you will have all black vertex colors.

So now you can enter weight paint mode and use the Addon which can be found in the spacebar search menu under Weight2VertexCol

You should get results something like the following.

enter image description here

Now the fun part, you have to modify the Addon to invert black and white. Fortunately, the math for this is easy, unfortunately I don't know Python scripting very well and I'm mediocre with math. Now if I think correctly(which I don't always do) you can use (x*-1.0)+1.0 to invert black and white. So I assume if we find the place in the script that writes the Vertex Colors and we place that variable where the x is in the above formula then the script should do what you want.

I'll just post the script for now and then maybe someone can figure this last part out. I'll be really surprised if that code works without some indentation reformatting using a program like PyCharm CE since the code window here botched the copy and paste and I had to do a bunch of manual indentation. Anyways, here is the link to the script on the external site so people can just use this copy as a last resort.

https://bitbucket.org/kursad/blender_addons_weight2vertcolortransfer/src/42070d95e0063df441b4f2d617492669b690592c/src/vertex_color_weight_transfer.py?at=master

The script has the filename -> vertex_color_weight_transfer.py in case anyone from the future wants to Google it. His name is listed in the script below.

I put some notes after the script about how I think you can modify it to make Red = 0.0 and Blue = 1.0 instead of what it does now(Unless I'm confused and it already does.)

import bpy 
from mathutils import Color
import random
from bpy.props import *

import bmesh


bl_info = {
    "name": "Weight2VertexCol",
    "author": "Kursad Karatas",
    "version": (0, 1, 0),
    "blender": (2, 6 ,6),
    "location": "View3D > UI panel >CopyWeightColors",
    "description": "Transfers Weights as Vertex Colors",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "3D View"}


class Weight2VertexCol(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.weight2vertexcol"
    bl_label = "Weight2VertexCol"
    bl_space_type = "VIEW_3D"
    bl_options = {'REGISTER', 'UNDO'}

    method=bpy.props.BoolProperty(name="Color", description="Choose the coloring method", default=False)

@classmethod
def poll(cls, context):
    return context.active_object is not None

def execute(self, context):
    transferWeight2VertexCol(context, self.method)
    context.active_object.data.update()
    return {'FINISHED'}


def transferWeight2VertexCol(context, method):
    me=context.active_object
    verts=me.data.vertices

    col=Color()
   col.h=0
    col.s=1
col.v=1

#vcolgrp=bpy.context.active_object.data.vertex_colors.keys()

try:
    assert bpy.context.active_object.vertex_groups
    assert bpy.context.active_object.data.vertex_colors

except AssertionError:
    bpy.ops.error.message('INVOKE_DEFAULT', 
            type = "Error",
            message = 'you need at least one vertex group and one color group')
    return

vgrp=bpy.context.active_object.vertex_groups.keys()

vcolgrp=bpy.context.active_object.data.vertex_colors


#Check to see if we have at least one vertex group and one vertex color group
if len(vgrp) > 0 and len(vcolgrp) > 0: 
    print ("enough parameters")

    #Colored
    if method:
        for poly in me.data.polygons:
            for loop in poly.loop_indices:
                vertindex=me.data.loops[loop].vertex_index        

                #Check to see if the vertex has any geoup association
                try:
                    weight=me.vertex_groups.active.weight(vertindex)
                except:
                   continue

                #col=Color ((r, g, b )) 
                col.h=0.66*weight
                col.s=1
                col.v=1
                me.data.vertex_colors.active.data[loop].color = (col.b, col.g, col.r)


    if not method:
        for poly in me.data.polygons:
            for loop in poly.loop_indices:
                vertindex=me.data.loops[loop].vertex_index        
                #weight=me.vertex_groups['Group'].weight(vertindex)

                #Check to see if the vertex has any geoup association
                try:
                    weight=me.vertex_groups.active.weight(vertindex)
                except:
                    continue

                col.r=weight
                col.g=col.r
                col.b=col.r
                me.data.vertex_colors.active.data[loop].color = (col.b, col.g, col.r)


class MessageOperator(bpy.types.Operator):
    bl_idname = "error.message"
    bl_label = "Message"
    type = StringProperty()
    message = StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.message)
        print(self.message)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=800, height=200)

    def draw(self, context):
        self.layout.label("A message has arrived")
        row = self.layout.split(0.25)
        row.prop(self, "type")
        row.prop(self, "message")
        row = self.layout.split(0.80)
        row.label("") 
        row.operator("error.ok")

#
#   The OK button in the error dialog
#
class OkOperator(bpy.types.Operator):
    bl_idname = "error.ok"
    bl_label = "OK"
    def execute(self, context):
        return {'FINISHED'}


def menu_draw(self, context): 
    self.layout.operator_context = 'INVOKE_REGION_WIN' 
    self.layout.operator(Bevel.bl_idname, "Weight2VertexCol") 

def register():
    bpy.utils.register_class(Weight2VertexCol)
    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_draw) 

    #error window
    bpy.utils.register_class(OkOperator)
    bpy.utils.register_class(MessageOperator)


def unregister():
    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_draw) 
    bpy.utils.unregister_class(Weight2VertexCol)

if __name__ == "Weight2VertexCol":
    register()

The following lines look like where the Red and Blue are being transferred to Black and White. The col.r = weight is a pretty good indication. So is the fact that all three color channels are being assigned the same information. That is how you make a grey scale color which is what we are looking for here.

col.r=weight

col.g=col.r

col.b=col.r

So, as a starting point, I would modify the first line of that snippet to be

col.r= (weight*-1.0)+1.0

and then I would test it. It should do what you want I think.

What would be even better is if you were to find a way to modify the code so that it automatically makes the Vertex Colors all black for you as part of the script which will save you a few steps.

UPDATE: I tested the invert formula and now the script seems to do what you want, here is the result. Also, with the greyscale inversion added, the initial Vertex Colors should start off white instead of black so this saves a bit of setup for you.

enter image description here

$\endgroup$
2
  • 2
    $\begingroup$ This is a really old thread, but I want to add a little correction: the best way to invert a color/alpha value is to simply subtract it from one. It's actually the same math, x*-1 + 1 = 1-x, but one fewer calculation can save time. I do this a lot in shaders, where performance does matter. $\endgroup$ May 9, 2019 at 16:33
  • 2
    $\begingroup$ Comment on old threads all you want, it's not an offense. Your point is valid especially with shaders. I use 1-x in GLSL as well. It slipped past me here, thanks for pointing it out. $\endgroup$ May 25, 2019 at 18:52
5
$\begingroup$

The answer is now super easy for Blender 2.9 (I have only tested with 2.91)

* See bottom of post for reference images

  1. Have your target object selected and relevant vertex group ready
  2. Enter vertex paint mode (Ctrl+Tab, 8)
  3. a. Check that you have the vertex group selected on the right under the "Object Data Properties" menu
    b. Select the top "Paint" menu > "Vertex Color from Weight" (or search it with spacebar)
  4. Voila! You have painted verts from black to white based on 0-to-1 weight

With Images

  1. Have your target object selected and relevant vertex group ready weight-painted object
  2. Enter vertex paint mode (Ctrl+Tab, 8) pie-menu navigation to vert paint mode
  3. a. Check that you have the vertex group selected on the right under the "Object Data Properties" menu
    b. Select the top "Paint" menu > "Vertex Color from Weight" (or search it with spacebar) apply vertex color from vertex group weight
  4. Voila! You have painted verts from black to white based on 0-to-1 weight painted object with varying weights
$\endgroup$
3
$\begingroup$

Here's a script for Blender that does the same thing, works in 2.8. It borrows some from the other script, but it's mostly my own. It's a little rough around the edges, I haven't made it into an addon yet. It'll be a while before I bother to improve it, though, so I'll post it here for anyone that needs it.

How to use:

Select the object you want to copy WP to VP or VP to WP. The active WP or VP layer will be copied to VP or WP (respectively) and take the same name + '_Copy'. If you want to copy VP to WP, run the script as it is. If you want to copy WP to VP, comment out line 56 that says CopyVPaintToVWeight(obAct) and uncomment the line beneath it that says CopyVWeightToVPaint(obAct)

At some point, I'll polish this, add features, and update it. It's not a priority for me at the moment, though!

#feel free to use this code for any purpose, anywhere
import bpy
from mathutils import Color

def CopyVWeightToVPaint(ob):
    values = GetListFromVertexDataLayer( "VW", ob)
    vCol = (ob.vertex_groups.active.name + "_Copy")
    ob.data.vertex_colors.new(name = vCol)
    for l in ob.data.loops:
        i = l.vertex_index
        val = values[i]
        ob.data.vertex_colors[vCol].data[l.index].color =  (val, val, val, val)


def CopyVPaintToVWeight(ob):
    values = GetListFromVertexDataLayer( "VP", ob)
    grp = (ob.data.vertex_colors.active.name + "_Copy")
    ob.vertex_groups.new(name = grp)
    for key in values:
        ob.vertex_groups[grp].add([key], values[key], 'REPLACE')

def DesaturateColor(col):
    #just a simple average is good enough for now
    noAlpha = []
    for i in (range(len(col)-1)):
        noAlpha.append(col[i])
    return (sum(noAlpha)/len(noAlpha))

def GetListFromVertexDataLayer(layerType, ob):
    outList = {}
    if (layerType == "VP"):
        print ("Getting " + str((len(ob.data.loops))) + " values from paint layer:", ob.data.vertex_colors.active.name)
        for l in (ob.data.loops):
            i = l.vertex_index
            #should check if there's already an i in outList 
            j = l.index
            col=[]
            for channel in (ob.data.vertex_colors.active.data[j].color):
                col.append(channel)
            val = DesaturateColor(col)
            outList[i] = val      
    elif (layerType == "VW"):
        print ("Getting " + str((len(ob.data.vertices))) + " values from weight layer:", ob.vertex_groups.active.name)
        for v in ob.data.vertices:
            try:
                weight = ob.vertex_groups.active.weight(v.index)
                #I'm sure there's a better way
            except:
                weight = 0
            outList[v.index] = weight
    print ("Length of output:", len(outList), "Expected Length:", len(ob.data.vertices))
    return outList


obAct = bpy.context.active_object
obActData = obAct.data
CopyVPaintToVWeight(obAct)
#CopyVWeightToVPaint(obAct)
$\endgroup$
2
$\begingroup$

Tissue addon 0.3.38 for Blender 2.8 can convert weights to vertex color.

enter image description here

$\endgroup$
1
  • 4
    $\begingroup$ Your answer would be much better if you added how to convert vertex colors with the addon and not just simply said that it can do such. $\endgroup$
    – David
    Dec 25, 2019 at 12:58
1
$\begingroup$

FJSnyman's answer is great and simple. Its only drawback is that its not dynamic. Here's a scriptless solution, that requires no addons, that can be used with dynamic vertex groups (and works even in older versions of Blender):

  1. Create a plane (or anything). Give it a vertex color layer and paint it all 1.0 white. Make it non-rendering.

  2. Create a vertex color layer for your rendering object. Give it the same name as your plane's vertex color layer. Paint it all 0.0 black.

  3. Give your rendering object a data transfer modifier copying face-corner data, vertex color. I've specified the layer in case you have multiple. I'll use "nearest corner of nearest face" as the copy mode, but a few other options will work (and the ones that don't, might just be a bug with Blender.) Finally, specify the vertex group you wish to visualize for the modifier.

You're just blending from black to white on the basis of the vertex group. Here it is with a vertex weight proximity modifier preceding it:

enter image description here

Depending on what you want to do with this, you might consider that you're using vertex color here to represent non-color data, but that Blender assumes that all vertex color is sRGB color. That might impact what you want to do with the vertex color in nodes. Just a warning that a linear vertex group falloff, interpreted as vertex color, will lead to a non-linear falloff once Blender does the color space conversion.

$\endgroup$
1
  • $\begingroup$ I can also verify this dynamic option works, and was exactly what I was looking for. Here is an example blend file, this is using version 2.91: ufile.io/3phks2rr $\endgroup$ Feb 24, 2021 at 19:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .