InsaneBump - very handy texture map generator for gimp!

amazing plug in for gimp that i found, generates 3 different normal maps, ao maps, height maps, diffuse, specular etc.
Lets you get amazing results with your textures within minutes!
in the video i show you how to install and export out a texture, also how to implement it into bge using a simple plane, and how to use 4 of the 9 textures on it.
very helpful plug-in, thought you guys might like it as well :D!

can be used for any engine, even cycles providing you set up the nodes correctly!

plugin download + instructions:
.blend preview:

thanks for watching your support is appreciated!

(the preview image is of a 4 vertice plane, no displacement used!)

That’s really great, with nice results! Thank you.

Beautiful plugin. It made me think if image manipulation could be done straight in blender and I spent half a day - that I should have spent on my game - trying to do image filtering in blender. So I’ll blame that plugin if my game doesn’t come out anytime soon (a pitiful excuse).
It turns out the answer is yes, you can do it. It is a bit slow but I went for clarity over performances.
So, in theory, one could build the entire stack of transformations made by that plugin AND apply the images to the active object.
It won’t be the solution to all problems but it would be cool. Drop image, press button X and (after three hours) voilà, your shaded model is served.

Here’s the test i made (generic kernel filtering framework with a 3x3 sharpening matrix used for the test)

import bpy

class DataBuffer():
    def __init__(self, size):
        self.buffer = [None] * size
    def get(self, index):
        return self.buffer[index]
    def set(self, index, value):
        self.buffer[index] = value

class DataMatrix():
    def __init__(self, rowCount, colCount):
        self.rowCount = rowCount
        self.colCount = colCount
        self.buffer = DataBuffer(rowCount * colCount)
    def getRowCount(self):
        return self.rowCount
    def getColCount(self):
        return self.colCount
    def subscript(self, row, column):
        return (self.colCount * row) + column
    def get(self, row, column):
        index = self.subscript(row, column)
        return self.buffer.get(index)
    def set(self, row, column, value):
        index = self.subscript(row, column)
        self.buffer.set(index, value)
        return self

class Pixel():
    def __init__(self, r, g, b, a):
        self.r = r
        self.g = g
        self.b = b
        self.a = a
    def getRed(self): return self.r
    def getGreen(self): return self.g
    def getBlue(self): return self.b
    def getAlpha(self): return self.a
    def scale(self, value, output):
        if output == None: output = Pixel(0,0,0,0)
        output.r = self.r * value
        output.g = self.g * value
        output.b = self.b * value
        output.a = self.a * value
        return output
    def add(self, that, output):
        if output == None: output = Pixel(0,0,0,0)
        output.r = self.r + that.r
        output.g = self.g + that.g
        output.b = self.b + that.b
        output.a = self.a + that.a
        return output
    def clamp(self, output):
        if output == None: output = Pixel(0,0,0,0)
        output.r = max(0, min(self.r, 1))
        output.b = max(0, min(self.b, 1))
        output.g = max(0, min(self.g, 1))
        output.a = max(0, min(self.a, 1))
        return output
    def __str__(self):
        return "RGBA(%d,%d,%d,%d)" % (self.r, self.g, self.b, self.a)

class ImageData():
    def __init__(self, blenderImage):
        self.image = blenderImage
        self.channels = 4
    def getChannels(self):
        return self.channels
    def getRowCount(self):
        return self.image.size[1]
    def getColCount(self):
        return self.image.size[0]
    def getPixelAt(self, row, col):
        raster = self.image.pixels
        index = (self.getColCount() * row * self.getChannels()) + (col * self.getChannels())
        red = raster[index]
        green = raster[index+1]
        blue = raster[index+2]
        alpha = raster[index+3]
        return Pixel(red, green, blue, alpha)
    def setPixelAt(self, row, col, pixel):
        raster = self.image.pixels
        index = (self.getColCount() * row * self.getChannels()) + (col * self.getChannels())
        raster[index] = pixel.getRed()
        raster[index+1] = pixel.getGreen()
        raster[index+2] = pixel.getBlue()
        raster[index+3] = pixel.getAlpha()
    def getSubImage(self, startrow, startcol, rowcount, colcount,extend=True):
        data = DataMatrix(rowcount, colcount)
        for row in range(startrow, startrow+rowcount):
            for col in range(startcol, startcol+colcount):
                if extend:
                    r = min(self.getRowCount() - 1, max(0, row))
                    c = min(self.getColCount() - 1, max(0, col))
                    pixel = self.getPixelAt(r, c)
                    data.set(row-startrow, col-startcol, pixel)
                    pixel = self.getPixelAt(row, col)
                    data.set(row-startrow, col-startcol, pixel)
        return data
    def setSubImage(self, startrow, startcol, pixelDataMatrix):
        for row in range(startrow, startrow + pixelDataMatrix.getRowCount()):
            for col in range(startcol, startcol + pixelDataMatrix.getColCount()):
                inputPixel = pixelDataMatrix.get(row, col)
                if (row >= 0) and (row < self.getRowCount()) and (col >= 0) and (col < self.getColCount()):
                    self.setPixelAt(row, col, inputPixel)
    def filter(self, filterFun, destinationImage):
        if destinationImage == None:
            destinationImage = generateImageData(, self.getRowCount(), self.getColCount())
        filterFun(self, destinationImage)
        return destinationImage

class ConvolutionFilter():
    def __init__(self, kernelDataMatrix, opaque=True):
        self.kernel = kernelDataMatrix
        self.opaque = opaque
    def __call__(self, source, destination):
        kernelRows = self.kernel.getRowCount()
        kernelCols = self.kernel.getColCount()
        halfKernelRows = int(kernelRows / 2)
        halfKernelCols = int(kernelCols / 2)
        def applyKernel(region):
            tot = Pixel(0,0,0,0)
            for row in range(0, kernelRows):
                for col in range(0, kernelCols):
                    kernelFactor = self.kernel.get(row, col)
                    regionPixel = region.get(row, col)
                    transformedPixel = regionPixel.scale(kernelFactor, None)
                    tot.add(transformedPixel, tot)
            kernelSize = kernelRows * kernelCols
            return tot
        for row in range(0, source.getRowCount()):
            for col in range(0, source.getColCount()):
                firstRow = row - halfKernelRows
                firstCol = col - halfKernelCols
                sourceRegion = source.getSubImage(firstRow, firstCol, kernelRows, kernelCols)
                convolutionResultPixel = applyKernel(sourceRegion)
                if self.opaque: convolutionResultPixel.a = 1
                destination.setPixelAt(row, col, convolutionResultPixel)

def getFirstBlenderImage():
    screen = bpy.context.screen
    areas = screen.areas
    for area in areas:
        areaType = area.type
        if areaType == "IMAGE_EDITOR":
            for space in area.spaces:
                if space.__class__.__name__ == "SpaceImageEditor":
                    return (area, space, ImageData(space.image))
    return None

def generateImageUid(prefix):
    names = [ for x in]
    newname = prefix
    index = 0
    broken = 1000
    while (newname in names) and (index < broken):
        newname = prefix + str(index)
        index += 1
    if index == broken:
        print("generateImageUid: can't find a uid for an image (that's weird)")
        return None
        return newname

def generateImageData(prefix, rowcount, colcount):
    uid = generateImageUid(prefix), width=colcount, height=rowcount)
    return ImageData(

Filter the first image available in the image panel with a 3x3 sharpening Kernel.
Takes ages to complete.
def test():
    imageData = getFirstBlenderImage()
    if imageData == None:
        print("no image found")
    area = imageData[0]
    space = imageData[1]
    image = imageData[2]
    kernel = DataMatrix(3,3)
    conv = ConvolutionFilter(kernel)
    newimage = image.filter(conv, None)


This is great. I would also recommend BIMP

Batch Image Manipulation Plugin, very useful for editing lightmaps and other things!

I can not get to gimp plugin website

Website is down ;/

Up again :wink:

I love this plugin! Here is a screenshot of my basic world for a cave game I am making:
Thanks for the plugin!


I found this very useful and I am using linux os

Yes i agree, however i found one flaw in the program that is expressed when the texture is very linear, for example andrew prices realistic texturing tutorial. The brick texture he uses when put through the insane bump plugin leaves a diamond shaped blur across the whole texture. This doesn’t affect the diffuse texture but it does change the normal map making some parts of the texture bumpy when there isn’t meant to be any.

I have problem with IsaneBump on Ubuntu 12.04_amd64

I download the binary: Extrated and place the file /home/serviteur/.gimp-2.8/plug-ins
When select Filters and then Map, InsaneBump DOSEN’T EXIST

How to solved it?

‘’ excuses me for my bad english’’

Check to make sure you have the execute bit set. (e.g. chmod 755

I verified it is already executable !

The plugin for Ubuntu (extrated) is: gimp-plugin-insanebump (74.0 ko) not

I wrote jdrussell51 and he have compiled from source for amd 64 bit.
The 64bit Ubuntu 12.04 version of the gimp-plugin-insanebump work now !