Using 3D texture in OpenGL/openFrameworks with programmable pipeline
Here we explain how to work with 3D textures using modern OpenGL, verson >= 3.2, using C++ code with openFrameworks.
For preparing this text we used several sources:
We will performs the folliwing steps to create and 3D texture for openFrameworks 0.10.1:
1. Enable programmable pipeline
2. Define the function for printing GL errors
3. Create some volume data
4. Create and upload 3D texture
5. Setting 3D texture to shader
6. Create shader files
1. Enable programmable pipeline
Be sure you are using programmable pipeline, so in main.cpp, main() use the following code:
int main() {
ofGLWindowSettings settings;
settings.setGLVersion(3, 2); //GL Programmable Renderer
settings.setSize(1024, 768);
ofCreateWindow(settings); //setup the GL context
ofRunApp(new ofApp());
settings.setGLVersion(3, 2); //GL Programmable Renderer
settings.setSize(1024, 768);
ofCreateWindow(settings); //setup the GL context
ofRunApp(new ofApp());
}
2. Define the function for printing GL errors
To write GL-related errors during creating 3D texture, please define in ofApp.cpp the following function:
GLenum res = glGetError();
if (res != GL_NO_ERROR) {
cout << "GL error at " << pass << ": " << res << endl;
}
}
if (res != GL_NO_ERROR) {
cout << "GL error at " << pass << ": " << res << endl;
}
}
3. Create some volume data
We created RGBA volume of size 256x256x256.
Note: size of 3D texture may be artitrary (not cube, not power of 2), but several GPUs want size not less 256.
I checked it's working with the size 800x800x800 on GPU with memory 6 Gb.
Codefor creating 3D volume data, put it into for setup():
const unsigned int D = 256;
const unsigned int DX = D, DY = D, DZ = D;
const unsigned int Size = DX * DY * DZ * 4; //RGBA
cout << "Cube size: " << D << endl;
cout << "Memory size, bytes: " << Size << endl;
unsigned char *volumeData = new unsigned char[Size];
for (unsigned int z = 0; z < DZ; z++) {
for (unsigned int y = 0; y < DY; y++) {
for (unsigned int x = 0; x < DX; x++) {
unsigned int i = 4 * (x + DX * (y + DY * z));
float r = ofPoint(float(x) - DX / 2, float(y) - DY / 2, float(z) - DZ / 2).length();
float value = ofMap(r, 0, DX/2 * 1, 1, 0, true); //smooth step
volumeData[i] = float(x) / DX * 255; //value * 255; //float(z) / DX * 1;// +0.5;
volumeData[i + 1] = value * 255; //float(z) / DX * 1;// +0.5;
volumeData[i + 2] = 0.3 * 255;// value;// float(y) / DX * 1;;
volumeData[i + 3] = (sin(x*0.05)*0.5+0.5)*255;
}
}
}
const unsigned int Size = DX * DY * DZ * 4; //RGBA
cout << "Cube size: " << D << endl;
cout << "Memory size, bytes: " << Size << endl;
unsigned char *volumeData = new unsigned char[Size];
for (unsigned int z = 0; z < DZ; z++) {
for (unsigned int y = 0; y < DY; y++) {
for (unsigned int x = 0; x < DX; x++) {
unsigned int i = 4 * (x + DX * (y + DY * z));
float r = ofPoint(float(x) - DX / 2, float(y) - DY / 2, float(z) - DZ / 2).length();
float value = ofMap(r, 0, DX/2 * 1, 1, 0, true); //smooth step
volumeData[i] = float(x) / DX * 255; //value * 255; //float(z) / DX * 1;// +0.5;
volumeData[i + 1] = value * 255; //float(z) / DX * 1;// +0.5;
volumeData[i + 2] = 0.3 * 255;// value;// float(y) / DX * 1;;
volumeData[i + 3] = (sin(x*0.05)*0.5+0.5)*255;
}
}
}
4. Create and upload 3D texture
Define textureID as a ofApp class member or just global variable:
GLuint textureID;
Add the following code of creating 3D texture to setup():
glGenTextures(1, &textureID);
errorCheck("glGenTextures(1, &textureID);");
glBindTexture(GL_TEXTURE_3D, textureID);
errorCheck("glBindTexture(GL_TEXTURE_3D, textureID);");
if (!(glIsTexture(textureID) == GL_TRUE)) cout << "Texture Binding Failed." << endl;
errorCheck("glGenTextures(1, &textureID);");
glBindTexture(GL_TEXTURE_3D, textureID);
errorCheck("glBindTexture(GL_TEXTURE_3D, textureID);");
if (!(glIsTexture(textureID) == GL_TRUE)) cout << "Texture Binding Failed." << endl;
//Wrap modes
//https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexParameter.xml
//GL_CLAMP, GL_REPEAT, GL_MIRRORED_REPEAT (GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
//Interpolation
//https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml
//GL_LINEAR,GL_NEAREST
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexParameter.xml
//GL_CLAMP, GL_REPEAT, GL_MIRRORED_REPEAT (GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
//Interpolation
//https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml
//GL_LINEAR,GL_NEAREST
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//Upload texture data to GPU
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, DX, DY, DZ, 0, GL_RGBA, GL_UNSIGNED_BYTE, volumeData);
errorCheck("glTexImage3D");
errorCheck("glTexImage3D");
glBindTexture(GL_TEXTURE_3D, 0);
errorCheck(" glBindTexture(GL_TEXTURE_3D, 0) ");
Detele volume data if it's not required anymore:
delete[] volumeData;
5. Setting 3D texture to shader
Here we illustrate how to use 3D texture in shader by rendering it's slices.
Define shader object as ofApp class member of as a global variable:
Define shader object as ofApp class member of as a global variable:
ofShader shader;
Load shader in setup() (we will give shader's code later)
shader.load("3dtexture");
Now, we will draw on the screen 3D texture's slices using the shader, so add the following code to draw():
//Bind the shader
shader.begin();
//Set 3D texture to shader
int textureLocation = 1; // Location an be 1,2,3,... - but unique for each used texture
glActiveTexture(GL_TEXTURE0 + textureLocation);
glBindTexture(GL_TEXTURE_3D, textureID);
shader.setUniform1i("volume", textureLocation); glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE0 + textureLocation);
glBindTexture(GL_TEXTURE_3D, textureID);
shader.setUniform1i("volume", textureLocation); glActiveTexture(GL_TEXTURE0);
//Also we pass the time in seconds to the shader to scan through 3D texture slices
shader.setUniform1f("time", ofGetElapsedTimef());
//Draw rectangle (passed through the shader
ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
//Unbind the shader
shader.end();
6. Create shader files
Vertex shader computes rectangle's output position gl_Position in normalized device coordinates [-1,1]x[-1,1]x[-1,1], and computes pos - 2D vector with uniform coordinates [0,1]x[0,1], which we will use for rendering slice of the 3D texture in fragment shader.
Put the following code to data/3dtexture.vert file:
#version 330
precision mediump float;
uniform mat4 modelViewProjectionMatrix;
in vec4 position;
out vec2 pos; //[0..1,0..1]
void main(){
gl_Position = modelViewProjectionMatrix * position;
pos = gl_Position.xy / gl_Position.w;
pos = pos*0.5 + vec2(0.5,0.5);
}
precision mediump float;
uniform mat4 modelViewProjectionMatrix;
in vec4 position;
out vec2 pos; //[0..1,0..1]
void main(){
gl_Position = modelViewProjectionMatrix * position;
pos = gl_Position.xy / gl_Position.w;
pos = pos*0.5 + vec2(0.5,0.5);
}
Fragment shader computes FragColorby sampling it from 3D texture volume.
Put the following code to data/3dtexture.vert file:
#version 330
precision mediump float;
in vec2 pos; //[0..1,0..1]
uniform sampler3D volume; //3D texture
uniform float time; //time in seconds
precision mediump float;
in vec2 pos; //[0..1,0..1]
uniform sampler3D volume; //3D texture
uniform float time; //time in seconds
out vec4 FragColor; //resulted pixel size
void main( void ) {
//Important note: coordinates for 3D texture are uniform [0,1]x[0,1]x[0,1]
//Get Z as a slice, changing over time
float Z = sin(time)*0.5+0.5;
//(when creating texture we set warp mode in CPU code to GL_REPEAT)
FragColor = texture(volume,vec3(pos.x*3,pos.y*3,Z));
}
}
In result, you will see on the screen some animated picture, which shows scanning through Z-slices of the created 3D texture.
Comments
Post a Comment