PDA

View Full Version : Creating texture from HDR image



saam_b
01-19-2012, 10:44 AM
Hi, I'm having trouble with setting up a texture which I've read from the OpenEXR file. It's a HDR light probe image and I'm trying to set it as a texture of a big sphere for showing it as my background. The problem is that the color is not right and only a light blue color is visible. I used OpenExr library for reading the image file and stored it like this:



InputFile file(Filename);
Box2i dw = file.header().dataWindow();
m_iWidth = dw.max.x - dw.min.x + 1;
m_iHeight = dw.max.y - dw.min.y + 1;
half *rgb = new half[3 * m_iWidth * m_iHeight];
FrameBuffer frameBuffer;
frameBuffer.insert("R", Slice(HALF, (char *)rgb,
3*sizeof(half), m_iWidth * 3 * sizeof(half), 1, 1, 0.0));
frameBuffer.insert("G", Slice(HALF, (char *)rgb+sizeof(half),
3*sizeof(half), m_iWidth * 3 * sizeof(half), 1, 1, 0.0));
frameBuffer.insert("B", Slice(HALF, (char *)rgb+2*sizeof(half),
3*sizeof(half), m_iWidth * 3 * sizeof(half), 1, 1, 0.0));
file.setFrameBuffer(frameBuffer);
file.readPixels(dw.min.y, dw.max.y);
m_Image = new Color[m_iWidth * m_iHeight];
for (int i = 0; i < m_iWidth * m_iHeight; ++i) {
float frgb[3] = { rgb[3*i], rgb[3*i+1], rgb[3*i+2] };
m_Image[i] = frgb;




where color contains float R, G , B.

and this is how i create a texture:

glGenTextures(1 , &amp;texture);
glBindTexture(GL_TEXTURE_2D,texture);

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, light_tex.m_iWidth, light_tex.m_iHeight, 0, GL_RGBA, GL_FLOAT, light_tex.m_Image);

Alfonse Reinheart
01-19-2012, 11:34 AM
You told OpenGL that you were uploading four components. But your data only seems to have three (unless `Color` is a 4-vector).

Also, your conversion from half to float is... dubious. I mean, you have this line:



float frgb[3] = { rgb[3*i], rgb[3*i+1], rgb[3*i+2] };


`rgb` is a `half*`; an array of `half`. So unless `half` has some implicit conversion to `float`, this isn't going to work. It'd be much easier (and a lot less confusing) to just do this:



m_Image[i][0] = ConvertToFloat(rgb[3*i + 0]);
m_Image[i][1] = ConvertToFloat(rgb[3*i + 1]);
m_Image[i][2] = ConvertToFloat(rgb[3*i + 2]);


Lastly, what's the point of loading half-floats from the file (which is what I assume you're doing), just to convert them to 32-bit floats, all so that you can make OpenGL convert them back to half-floats?

Just give OpenGL half-float data with GL_HALF_FLOAT instead of GL_FLOAT.

saam_b
01-19-2012, 03:21 PM
Just give OpenGL half-float data with GL_HALF_FLOAT instead of GL_FLOAT.

I've tried what you said, and it is still not working, actually the code for reading the file is taken from PBRT and I'm sure it is correct, but I don't know how I have to assign in to a texture. I've attached the result I get with each try.

Rosario Leonardi
01-20-2012, 06:00 PM
I did this last month

GLuint loadTexture(const char *path)
{
Imf::Rgba * pixelBuffer;
try
{
Imf::RgbaInputFile in(path);

Imath::Box2i win = in.dataWindow();
GLuint width = win.max.x - win.min.x+1;
GLuint height = win.max.y - win.min.y+1;
Imath::V2i dim(win.max.x - win.min.x + 1,
win.max.y - win.min.y + 1);

pixelBuffer = new Imf::Rgba[dim.x * dim.y];

int dx = win.min.x;
int dy = win.min.y;

int order = in.lineOrder();

in.setFrameBuffer(pixelBuffer - dx - (dy * dim.x), 1, dim.x);
in.readPixels(win.min.y, win.max.y);
}
catch(Iex::BaseExc & e)
{
std::cerr << e.what() << std::endl;

delete[]pixelBuffer;
return GL_INVALID_VALUE;
}
GLuint texture;
glGenTextures(1, &amp;texture);
glBindTexture(GL_TEXTURE_RECTANGLE, texture);

glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, pixelBuffer);

delete[]pixelBuffer;
return texture;
}

In my case i didn't do any conversion. I know that the exr image was stored as float16. In your code you are doing a lot of useless conversion.
you read the image as float16, then you convert it as float32, then you convert the float32 to Color (can I see the layout of Color?) and then you use as internal format float16, so you are asking to openGl :
"I have this float data in RGBA, to convert it again in float16."

saam_b
01-21-2012, 03:12 AM
In my case i didn't do any conversion. I know that the exr image was stored as float16. In your code you are doing a lot of useless conversion.
you read the image as float16, then you convert it as float32, then you convert the float32 to Color (can I see the layout of Color?) and then you use as internal format float16, so you are asking to openGl :
"I have this float data in RGBA, to convert it again in float16."


First tnx for your help! I also tried your code but I can't see the image any more, I know I'm mixing sth with these conversions...

this is my Color class:



class Color
{
public:

float R , G , B/* , A*/;
Color(const Color&amp; col);
Color();
virtual ~Color(void);
Color(const float r,const float g, const float b);
void Set(const float r, const float g, const float b);

friend Color operator * (const float s, const Color c);
Color&amp; operator=(const Color&amp; c);
Color&amp; operator=(const float&amp; c);
Color&amp; operator=(const float c[3]);
//Color&amp; operator=(const Lib3dsByte&amp; c);
Color operator*(const Color&amp; c);
Color operator*(float d) const;
Color operator/(float d) const;
void operator*=(const float s);
void operator/=(const float s);
Color&amp; operator+=(const Color&amp; c);
Color operator+(const Color&amp; vec3) const;
bool operator==(const float c) const;
//Color mult(const Color&amp; c) const;
void normalize(void);
Color ColorNorm(void)const;
void Bind();

Color Mult(const Color &amp;c);
void MultScalar(const float a);
};




and I want to pass this texture to the shader and use latitude-longitude for mapping it on a sphere.

this is how i pass it to the shader in my display function:



qobj = gluNewQuadric();
gluQuadricTexture(qobj,GL_TRUE);


glEnable(GL_CULL_FACE);

glFrontFace(GL_CW /* or GL_CCW */); //default is GL_CCW

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0f, 20.0f, 50.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

glPushMatrix();
glTranslatef(tx,ty,0);
glRotatef(rotx,1,0,0);
glRotatef(roty,0,1,0);

LShader.bind();
glEnable(GL_TEXTURE_2D );
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D , texture);

int texture_location = glGetUniformLocation(LShader.GetID(), "color_texture");
glUniform1i(texture_location, texture_location);

gluSphere(qobj,10000,30,30);
gluDeleteQuadric(qobj);

glDisable(GL_TEXTURE_2D);
LShader.unbind();
glPopMatrix();


glutSwapBuffers();


and this is my fragment shader :



varying vec3 oVertexP;


uniform sampler2D color_texture;

void main()
{

float pi = 3.1415;
float x , y, z;
vec3 viewVec = normalize(oVertexP);
x = viewVec.x;
y = viewVec.y;
z = viewVec.z;

//Latitude-longitude
float u = 1.0 + (1.0/pi) * atan(x,-z); u /= 2.0;
float v = acos(y) / pi;

vec4 color = vec4( texture2D(color_texture, vec2(u,v)) );
gl_FragColor = vec4(pow(color.xyz,1.0/2.2),1.0); //Tone mapping
}




one other thing is that I changed GL_TEXTURE_RECTANGLE to GL_TEXTURE_2D in your code, but still I can't see any result! :(

saam_b
01-21-2012, 03:20 AM
Problem solved!! I made a stupid mistake by creating the texture before initializing glut!! you code works perfectly Rosario Leonardi! tnx a lot! :)

Rosario Leonardi
01-22-2012, 07:05 AM
No problem. :)
Your code didn't work cause your Color class have only RGB information.
So in memory you have
RGBRGBRGBRGBRGBRGBRGBRGB...
but when you use

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, light_tex.m_iWidth, light_tex.m_iHeight, 0, GL_RGBA, GL_FLOAT, light_tex.m_Image);

you are telling openGl that your image data are GL_RGBA
so, openGL expect data as RGBARGBARGBARGBA... messing up everything.

Passing the data in the right format also avoid a lot of work to openGl speed up the code.

If you are going to use my code, that was a fast test to check the best HDR format for my after effect, so, no mipmap, no filter. If you are going to use that as a texture probably you need some filter. Also keep in mind that float16 picture use an huge amount of memory.

saam_b
01-25-2012, 05:22 AM
it's always good to know where you're making mistake! tnx a lot for your explanation! :)