Creating New Textures from Scratch
The PNMImage Class
This class is how Panda3D handles regular images (.gif, .jpg, and the like).
This class allows you to manipulate individual pixels of the image. You can load
existing images using the function read(filename)
where
filename
is the path to the image file. Or, you can create a brand new image
from scratch, by passing the x, y size to the constructor.
myImage = PNMImage()
myImage.read("testImg.png")
myEmptyImage = PNMImage(256, 256)
You can get the size of the image you have read using the
get_x_size()
and get_y_size()
methods.
Although you cannot change the size of an image directly, you can rescale an
image by filtering it into a larger or smaller PNMImage:
fullSize = PNMImage(Filename("testImg.png"))
reduced = PNMImage(256, 256)
reduced.gaussianFilterFrom(1.0, fullSize)
You can get individual RGB values using the
get_red(x, y)
,
get_green(x, y)
,
get_blue(x, y)
or
get_red_val(x, y)
,
get_green_val(x, y)
,
get_blue_val(x, y)
methods, where x and y are
the coordinates of the pixel to sample (the upper-left corner is 0, 0
whereas the lower-right corner is size.x - 1, size.y - 1
).
The difference between these functions is that the regular getters functions
return a number between 0.0 and 1.0, while the ones marked with “val” return
their raw value as an integer. For example, if your image uses 8-bit-per-channel
color, calling get_green_val()
will return 255 for a fully
green pixel whereas calling get_green()
will return 1.0.
You can also get all the RGB information at the same time using
get_xel(x, y)
and
get_xel_val(x, y)
, which return a 3-component
vector containing the red, green and blue channels, respectively.
# The pixel at 0,0 is red and we're using 8-bit color
myImage.getRedVal(0, 0) # Returns 255
myImage.getRed(0, 0) # Returns 1
colors = myImage.getXelVal(0,0) # Returns (255,0,0)
colorVal = myImage.getXel(0,0) # Returns (1,0,0)
The methods for setting pixel information are
set_red(x, y, value)
,
set_green(x, y, value)
,
set_blue(x, y, value)
,
set_xel(x, y, color)
, or
set_red_val(x, y, value)
,
set_green_val(x, y, value)
,
set_blue_val(x, y, value)
,
set_xel_val(x, y, color)
.
The same as above applies regarding the dichotomy between the regular setters
and the ones marked with “val”. You can also fill an image with a color by using
fill(r, g, b)
and
fill_val(r, g, b)
.
myImage.setGreenVal(0, 0, 255) # If pixel (0, 0) was red before, now it is yellow
myImage.setBlue(0, 0, 1) # Pixel (0, 0) is now white
gray = Vec3(0.5, 0.5, 0.5)
# Both of these set the origin to gray
myImage.setXelVal(0, 0, gray * 255)
myImage.setXel(0, 0, gray)
# Makes every pixel red
myImage.fillVal(255, 0, 0)
# Makes every pixel green
myImage.fill(0, 1, 0)
There are also gets and sets for the alpha channel using the same interface as
above. However, if you use them on an image that doesn’t have an alpha channel
you will cause a crash. To see if an image has an alpha channel use
has_alpha()
which returns True if there is an alpha channel
and False otherwise. You can add an alpha channel using
add_alpha()
. You can also remove it using
remove_alpha()
.
You can also make an image grayscale using make_grayscale()
.
To set or get a grayscale value, you can use get_gray()
and
set_gray()
. (Using these functions on a color image will just
affect the value in the blue channel.) If you want to get the grayscale
value of a pixel regardless of whether the image is a grayscale or a color
image, you can use get_bright(x, y)
, which works
equally well on color or on grayscale images. If you want to weight the colors
use get_bright(x, y, r, g, b)
, where r, g, b are
the weights for the respective channels.
There are several other useful functions in the class, which are described on
the PNMImage
page in the API Reference.
Storing a Texture into an Image
The Panda Texture
class does not allow for pixel manipulation. But the
PNMImage
class does. Therefore, if you want to change the image in a
Texture
object you must call store(image)
,
which saves the image of the texture into the given image object.
myImage = PNMImage()
myTexture = loader.loadTexture("myTex.jpg")
# After this call, myImage now holds the same image as the texture
myTexture.store(myImage)
Loading a PNMImage into a Texture
Once you have changed all the data in the image you can now load it into a
texture using the texture’s load(myImage)
method, where
myImage
is the PNMImage to make the texture from.
# Assume we already have myImage which is our modified PNMImage
myTexture = Texture("texture name")
# This texture now contains the data from myImage
myTexture.load(myImage)
Remember however, that most graphics cards require that the dimensions of
texture have to be a power of two. PNMImage
does not have this
restriction and Panda will not automatically scale the image when you put it
into a texture.