3

Using C++ and OSG I'm trying to upload a float texture to my shader, but somehow it does not seem to work. At the end I posted some part of my code. Main question is how to create an osg::Image object using data from a float array. In OpenGL the desired code would be

glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, width, height, 0, 
             GL_LUMINANCE, GL_FLOAT, data);

but in this case I have to use OSG.

The code runs fine when using

Image* image = osgDB::readImageFile("someImage.jpg");  

instead of

image = new Image;  

but I need to upload generated float data. It's also not possible to switch to unsigned char arrays as I need the GL_LUMINANCE32F_ARB data range in the shader code.

I hope someone can help me here as Google couldn't help me with it (googled for eg: osg float image). So here's my code.

using namespace std;
using namespace osg;
//...
float* data = new float[width*height];
fill_n(data, size, 1.0); // << I actually do this for testing purposes
Texture2D* texture = new Texture2D;
Image* image = new Image;
osg::State* state = new osg::State;
Uniform* uniform = new Uniform(Uniform::SAMPLER_2D, "texUniform");
texture->setInternalFormat(GL_LUMINANCE32F_ARB);
texture->setDataVariance(osg::Object::DYNAMIC);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
if (data == NULL)
  cout << "texdata null" << endl; // << this is not printed
image->setImage(width, height, 1, GL_LUMINANCE32F_ARB,
                GL_LUMINANCE, GL_FLOAT,
                (unsigned char*)data, osg::Image::USE_NEW_DELETE);
if (image->getDataPointer() == NULL)
  cout << "datapointernull" << endl; // << this is printed
if (!image->valid())
  exit(1); //  << here the code exits (hard exit just for testing purposes)
osgDB::writeImageFile(*image, "blah.png");
texture->setInternalFormat(GL_LUMINANCE32F_ARB);
texture->setImage(image);
camera->getOrCreateStateSet()->setTextureAttributeAndModes(4, texture);
state->setActiveTextureUnit(4);
texture->apply(*state);
uniform->set(4);
addProgrammUniform(uniform);

I found another way on the web, letting osg::Image create the data and fill it afterwards. But somehow this also does not work. I inserted this just after the new XYZ; lines.

image->setInternalTextureFormat(GL_LUMINANCE32F_ARB);
image->allocateImage(width,height,1,GL_LUMINANCE,GL_FLOAT);
if (image->data() == NULL)
  cout << "null here?!" << endl; // << this is printed.
Bart
  • 19,692
  • 7
  • 68
  • 77
Nero
  • 1,295
  • 1
  • 16
  • 23
  • 1
    Surely that should either be `Image image;` or `Image *image = new Image`. That same goes for `state`, `uniform` and the like. Does your code even compile? – Bart Jul 31 '12 at 09:11
  • Oops, yes, you're right. The code was taken from my class where texture, image, state and uniform are defined in the header as pointers. I just forgot the * when adding the types in that snippet. I'll correct that. Thanks – Nero Jul 31 '12 at 10:16
  • I added one more way I found to solve the problem, but this also does not work. Although allocateImage(.) is just called the data pointer is NULL. – Nero Aug 01 '12 at 10:43

2 Answers2

1

I use the following (simplified) code to create and set a floating-point texture:

// Create texture and image
osg::Texture* texture = new osg::Texture2D;
osg::Image* image = new osg::Image();
image->allocateImage(size, size, 1, GL_LUMINANCE, GL_FLOAT);
texture->setInternalFormat(GL_LUMINANCE32F_ARB);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setImage(image);

// Set texture to node
osg::StateSet* stateSet = node->getOrCreateStateSet();
stateSet->setTextureAttributeAndModes(TEXTURE_UNIT_NUMBER, texture);

// Set data
float* data = reinterpret_cast<float*>(image->data());
/* ...data processing... */
image->dirty();

You may want to change some of the parameters, but this should give you a start. I believe that in your case TEXTURE_UNIT_NUMBER should be set to 4.

kshahar
  • 10,423
  • 9
  • 49
  • 73
0

but I need to upload generated float data. It's also not possible to switch to unsigned char arrays as I need the GL_LUMINANCE32F_ARB data range in the shader code.

osgDB::writeImageFile(*image, "blah.png");

png files don't support 32bit per channel data, so you can not write your texture to file this way. See the libpng book:

PNG grayscale images support the widest range of pixel depths of any image type. Depths of 1, 2, 4, 8, and 16 bits are supported, covering everything from simple black-and-white scans to full-depth medical and raw astronomical images.[63]

[63] Calibrated astronomical image data is usually stored as 32-bit or 64-bit floating-point values, and some raw data is represented as 32-bit integers. Neither format is directly supported by PNG, although one could, in principle, design an ancillary chunk to hold the proper conversion information. Conversion of data with more than 16 bits of dynamic range would be a lossy transformation, however--at least, barring the abuse of PNG's alpha channel or RGB capabilities.

For 32 bit per channel, check out the OpenEXR format.

If however 16bit floating points (i.e. half floats) suffice, then you can go about it like so:

osg::ref_ptr<osg::Image> heightImage = new osg::Image;
int pixelFormat = GL_LUMINANCE;
int type = GL_HALF_FLOAT;
heightImage->allocateImage(tex_width, tex_height, 1, pixelFormat, type);

Now to actually use and write half floats, you can use the GLM library. You get the half float type by including <glm/detail/type_half.hpp>, which is then called hdata. You now need to get the data pointer from your image and cast it to said format:

glm::detail::hdata *data = reinterpret_cast<glm::detail::hdata*>(heightImage->data());

This you can then access like you would a one dimensional array, so for example

data[currentRow*tex_width+ currentColumn] = glm::detail::toFloat16(3.1415f);

Not that if you write this same data to a bmp or tif file (using the osg plugins), the result will be incorrect. In my case I just got the left half of the intended image stretched onto the full width and not in grayscale, but in some strange color encoding.

Community
  • 1
  • 1
Tare
  • 482
  • 1
  • 9
  • 25