Can I map a vector image on a mesh?

I often run into a situation where I create Image Maps in Illustrator, export them as PNG, and when I apply them into my mesh and zoom in, pixelation from the PNG appears. This annoys me a lot in 3D and games (see this model imported from a Wii U game, which gets pixelated as soon as you zoom in)

Wouldn't it make more sense to be able to import an image map as a vector format, since Blender accepts (in some instances) SVG files?

I haven't been able to do this...

EDIT: There are a lot of great answers which are also very tedious and can't really be incorporated in a regular workflow. The question was more about mapping SVG like this one on a cup object like this one to show a client an idea quickly, etc.

• Raster will always pixellate if you get close enough, but it's not an issue if you limit how close the camera can get. If it gets close enough to see the pixels, then modelling the geometry instead of using textures is needed anyway. – dixiepig Apr 5 '18 at 19:32
• You'd be better off creating UVs and assigning materials directly on the geometry. If you are going to use SVGs, make sure that you convert the images to paths before saving. Paths can be imported as curves into blender. – cegaton Jun 11 '18 at 23:45
• You can plot every graph with formulas. If you like maths, you could create a script which converts your curves to node graphs. For fun I recreated the keyboard base layout with nodes. – Leander Jun 13 '18 at 22:34
• For testing purposes here is a free SVG of a keyboard (click the blue arrow under the thumbnail) - you can try to reproduce the image in the original post. – MicroMachine Jun 14 '18 at 17:41

The requested feature doesn't natively exist within Blender, hence recreating the behaviour is very complex and tedious.

Creating shapes with material nodes

If a shape can be recreated with mathematical functions (plotted shapes) it can be created with cycles math nodes.

Let's create a procedural rounded rectangle group. (If you know Blender and math, skip to step 3.)

1. On a large plane, add a material with an Object Texture Coordinate input.

We're going to focus on the X, Y positive area (marked blue) first.
2. Let's by defining the bounds for the rectangle, separate the XYZ channels and add two greather than nodes for X an. Add them together clamped.

The black is the interiour of the rectangle in the XY positive area. Make sure your object's scale is applied as we're using local coordinates. Strangely enough, we don't need these nodes, discard them!
3. Create a circular shape using the unit circle functions. In our case it is simple: radius^2 = x^2 + y^2
This means, that for every point inside the circle, the following is true: sqrt(x^2 + y^2) < radius.
In nodes, add a Power node for each x and y, then an add node to add them together, then another Power node for the square root. Finally a greather than to determine if the point is outside of the circle (= if the radius is to great).

The 2nd power node with a value of 0.5 is missing in the image. Don't forget, the effect of the nodes is seen in material view and rendered view only.
4. Offset the X and Y coordinate by rectangle-"radius" minus rounded corner radius.

Regardless of the value of the CornerRadius, the circles bounds are clamped to the RectangleSize bounds.
5. Clamp the values left and below of the circle respective X and Y diameter.

These are the desired areas. To get the red area we multiply x < rectangleRadius - circleRadius with y < rectangle radius, and for the blue area switch the inputs. Then add both results together.

This is the node graph, after multiplying this rectangle restriction onto the circle, a neatly rounded corner.
6. To make the rounded corner XY symmetrical, add an abs node each to the X and Y outputs form the separate RGB node.
7. Before the abs node, add a subtract node, to be able to shift the x and y coordinates.
8. Make the end result influence a color mix node.
9. Make it a group with an input for inputColor, Vector Coordinates, offsetX, offsetY, Size and CornerSize.

Nodegraph before grouping
10. But wait, add support for a different height and width. Figure it out yourself or download the blend.

Go wild, this is rather slow.

Creating shapes based on functions with nodes

Let's recreate this easter egg.

1. The first function will be converted as we did with the first example. I will interpret
(x-10)^2 / 300 + y^2 / 600 = 1
as
2. I added some arbitrary sine functions.

Creating a shape with OSL

The open shading language enable us to use a custom script node. Knowlegde of C and Python is required for the following part. (Since I don't really know any C or Python, the code samples are messy.)

You might want to head to BlenderSushi and complete their OSL series.

We'll create a stop sign polygon octagon.

1. I'll hard code some coordinates. Store the points in a string and convert them to a c style array with this python snippet. I just manually copied and pasted the coordinates separated by spaces and commas and the manually copied and pasted the generated code. Execute the snippet in any python interpreter.
pts = "-1,2 1,2 2,1 2,-1 1,-2 -1,-2 -2,-1 -2,1"
pts = [[float(n) for n in m.split(",")] for m in pts.split(" ")]
pts.append(pts[0])

pt_array = "point pts[" + str(len(pts)) + "] = {"
for pt in pts:
pt_array += " point(" + str(pt[0]) + ", " + str(pt[1]) + ", 0),"
pt_array = pt_array[:-1] + "};"

print(pt_array)


This generates the following output.

point pts[9] = { point(-1.0, 2.0, 0), point(1.0, 2.0, 0), point(2.0, 1.0, 0), point(2.0, -1.0, 0), point(1.0, -2.0, 0), point(-1.0, -2.0, 0), point(-2.0, -1.0, 0), point(-2.0, 1.0, 0), point(-1.0, 2.0, 0)};

1. In Blender, create a new text block and start off with a basic osl shader

shader simpleColor( point Vector = P, output color col = color(0.0)) { // insert our custom point array here }

2. Select the textblock in a script-node in the material nodes. Activate Open Shading Language in the Render Settings.

3. Now we got our polygon in our osl shader. To detect if a point lies in the polygon, we are going to implement a simple version of the Ray Casting algorithm. There are plenty of resources on point-in-polgyon topics on stackoverflow. I'll cast a ray from the y minimum and check with how many polylines it intersects. If the number is a multiple of two, the point must lie outside the polygon.
4. Create a simple function, which will take two points of a linear mathematical function and an x value and return the corresponding y value.
float linear_function(float x, point p1, point p2) {
float x1 = p1[0];
float y1 = p1[1];
float x2 = p2[0];
float y2 = p2[1];
if (x1 == x2)
return 0.0;

float m = ((y2 - y1) / (x2 - x1));
float n = y1 - m * x1;

return (m*x+n);
}


7. Now, we'll add another function to detect if a point lies above a polyline. First we'll check if the x coordinate is inbetween the two points of the line, then we'll compare the y value of the function between the two lines with the y value of our point.

int hit(point p, point p1, point p2) {
float x = p[0];
if (p1[0] == p2[0])
return 0;
if (p1[0] < p2[0])
if (p1[0] > x || p2[0] < x)
return 0;
if (p1[0] > p2[0])
if (p1[0] < x || p2[0] > x)
return 0;

if (linear_function(x, p1, p2) > p[1])
return 1;
return 0;
}

1. With these two functions added, the shader can be tested with 2 points. Note, that osl shaders are only visible in rendered preview mode.
2. We can also use our stop sign coordinates.

Notes on svgs

With these tools you are equipped to start writing your svg plugin. The hit function has to be implemented for curveto, smooth curveto, quadratic Bézier curve, smooth quadratic Bézier curveto and elliptical Arc and the python script has to convert all types of the svg path. It's actually not that hard to compute further intersections, but parsing the svg is going to be a lot of work.

The final goal is to write a python script, which parses an svg (with python's xml parser) and converts is to a osl shader script. The points and curves would be hardcoded in the shader.
Unfortunately, we have to first implement the most basic collision detection for beziers (see the previous link), because most svgs will use them, and possibly for all other svg elements (see the previous2 link).

• I like the answer a lot but I don't think it could be incorporated in a regular workflow. The question was more about mapping SVG like this one on a cup object like this one to show a client an idea, etc. – MicroMachine Jun 20 '18 at 21:42
• @MicroMachine Did you actually go through the answer, do all steps and read all links? No, because the blend file is broken (I just noticed and will fix it: OSL is disabled and the code is wrong). You've also supplied three different svgs. My answer works well with the images in the question. If I extend my answer to supply an easy way with the next sample svg, will you change it again? (Read the last two links! This answer isn't the solution, but the path to the solution: your own addon to import svgs.) – Leander Jun 21 '18 at 6:32
• @Leander no prob, I'll go through it in detail again and get back to you! – MicroMachine Jun 21 '18 at 6:37
• @MicroMachine I have recreated this svg and will start writing the converter myself, as soon as I find time. The script mentioned in the edit at the end, will still be easier to create than altering the source code and creating your own build with svg support. – Leander Jun 21 '18 at 7:50
• @Leander thanks for the time and effort you put into this, it's way beyond my skills but I'm excited to see the converter you make! Keep me posted. I accepted your answer based on your dedication and the potential of that converter – MicroMachine Jun 21 '18 at 17:29

One option is to import your SVG using the SVG import addon. We'll use this test image. Select File> Import > Scalable Vector Graphic

In my case, not a lot appears to have changed, but I have some curves now in the outliner.

If I zoom in, I can see the image I imported, but there are some problems. Note that blender has automatically created a set of basic diffuse materials (called "SVGMat.001...") for each color in the image. Editing these can be used to change the look of the final result.

I'll start by increasing the scale of all of the curves by 100, then applying that scale with ctrl + a.

Right now all of the curves are intersecting with each other, because they all have the same Z position. I'll go through each curve, and move it up or down by 0.001 or so, to keep them from clipping. In my case, all the layers were imported in order, so it was pretty easy to tell which should be on top, because it showed up last in the outliner.

To keep all of these curves together, first I'm going to add an empty, then parent all the curves to that empty.

There's still one more major problem that needs to be fixed: Cartman's eyes and mouth are not filled in. This is because Blender automatically filled in his feet, but not the other shapes on the same layer. We can fix this by entering edit mode and adding polyfaces for each shape that isn't filled. press tab to enter edit mode with the eyes/mouth/feet layer selected.

Select and single vertex.

Press ctrl + L to select all the vertices on the same curve.

Then press F to fill the shape. Repeat for all of the shapes in the layer.

This now gives us a final image that can be moved around and scaled normally by the empty, that has no pixel aliasing. You might notice that some of the larger curves (such as Cartman's face) seem a bit low resolution. For these curves, we can adjust the curve resolution to make bezier curves and arcs render more smoothly.

To do this, select the curve you want to change, and increase the resolution value (when Render U Resolution is 0 it copies from the Preview U Resolution, so I changed that value, but pick whichever you want).

One final note: the lines on Cartman's chin and jacket will not show up in a render without some more tuning, or using freestyle rendering. This is because they aren't actually faces, just lines, which have no thickness, and therefore don't show up when rendering.

• This is great, thanks! Would the elements stay in place when you use it against a mesh? Can you detail the mapping process? It seems this technique would work best with some manageable vector images (no gradients, limited nb of elements...) – MicroMachine Jun 13 '18 at 21:54
• Yeah, this is not sustainable for anything particularly complex. There is no (UV) mapping technique because the imported objects are curves, not meshes (curves can't have UV data). It's basically the same as creating objects and applying materials to the entire thing, rather than creating UV maps and applying textures. The elements are all children of the empty, so as long as you just move/rotate/scale the empty around, all the layers should stay in place. – Tavi Kohn Jun 13 '18 at 22:12
• I'm just wondering why it's possible to interpret/import SVG data into blender as editable objects, but not possible to import them as a 2d image in the UV mapping tools (without editing the data). Would make more sense for UV Mapping than limited bitmap data. Something similar is how InDesign handles an Illustrator image that you place into it. – MicroMachine Jun 13 '18 at 22:34
• UV maps are for mapping pixels to mesh faces. Because Vector graphics don't have pixels, the UV mapping workflow doesn't really apply. You could consider this a limitation of Blender, but I don't really blame them for not wanting to implement this functionality; it sounds difficult. – Tavi Kohn Jun 14 '18 at 1:29
• Curves are converted to flat polygons for render in Blender anyways. Keeping these as curves may be the more memory-efficient way to store some data, and may be easier to edit than textures, but there will still be pixellation until you edit the curve to have sufficient resolution for your render-- just like there will be pixellation from a texture until you change the texture to have sufficient resolution for your render. To eliminate pixellation fully, you either need 4 vertices per render pixel or 4 texels per render pixel, your choice. – Nathan Jun 17 '18 at 18:02

Make curves in the keyboard in shape of all keys, modifiers>solidify. Move slightly above all the base layer textures. Done. Crisp and detailed because it will be a mesh. To make it a "constant" color, use an emmissive node directly to surface on the shading tree, and check intensity. You´re ready to go.

• Your answer only works for the keyboard situation, not to put eyes on a character in a videogame for example – MicroMachine Dec 19 '18 at 18:36