I am implementing a simple center of mass/centroid algorithm for 2D rasters. This is rather trivial on the CPU but has proven difficult to port to the GPU.
My CPU version is something like:
float sumX = 0., sumY = 0., c = 0.
for y in imageH:
for x in imageW:
if image[x, y] > threshold:
sumX += x
sumY += y
c += 1.
Point2D centroid = Point2D( sumX / c, sumY / c )
My GPU version (GLSL with backbuffers/Ping Pong) that currently does not work and basically should sum neighbor pixels if they meet the condition (the input texture could have a bitmask image consisting of black and white shapes, for now one shape would suffice and I will start from there).
vec2 pixSize = 1. / resolution;
vec4 originalTexture = texture( myTex, uv );
if( frame == 0 && originalTex.r > treshold )
{
gl_FragColor = vec4( gl_FragCoord, 0., 0. );
else
{
for( int y = -1; y <= 1; ++y )
for( int x = -1; x <= 1; ++x )
{
vec4 data = texture( backbuffer, uv + pixSize * vec2( x, y );
if( originalTexture.r > threshold )
{
sumCoord += data.xy;
counter += 1.;
}
}
gl_FragColor = vec4( sumCoord / counter, 0., 0. );
}
EDIT: On second thoughts I see that I am not adding the current fragCoord after the first frame so that is something I must take into account.
This is a straight-forward reduction problem.
Create a three-channel texture T with the values
if(image[x,y] > threshold) {
T[x,y,0] = x;
T[x,y,1] = y;
T[x,y,2] = 1;
} else {
T[x,y,0] = T[x,y,1] = T[x,y,2] = 0;
}
Then reduce the texture by a constant factor till you get a single 1x1 pixel. This takes O(log(max(w,h))) GPU friendly steps, thus is extremely efficient. Here are some slides about how to do reduction on GPU. In fact even something like glGenerateMipmap() will work, though the exact filter it uses is implementation-specefic.
Next, after you reduced the texture to a single pixel, you extract the center of mass by
x = T[0,0,0]/T[0,0,2];
y = T[0,0,1]/T[0,0,2];
Done.
EDIT: a simple factor-two reduction with a fragment shader can be done as simple as follows:
layout(binding = 0) uniform sampler2D TEX;
void main() {
vec2 uv = 2*gl_FragCoord.xy/textureSize(TEX, 0);
gl_FragColor = texture(TEX, uv);
}
You need to bind the previous texture to TEX with a bilinear minification filter and (0,0,0,0) clamp-to-border wrapping mode. Setup the next texture layer as the render target.
render ROI 2D texture with unique color for each object
for each object (unique color) render pixel at specified location
Using GLSL shaders compute the CoM for all pixels with the "same" color.
glReadPixels at specified locations for each object
convert color back to 2D position
Here naive GLSL implementation of mine I just busted for fun:
//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
layout(location=0) in vec4 vertex;
layout(location=3) in vec4 color;
flat out vec4 col; // object color
uniform mat4 mvp; // texture resolution
void main()
{
col=color;
gl_Position=mvp*vertex;
}
//---------------------------------------------------------------------------
CPU sise C++/OpenGL/VCL code:
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
flat in vec4 col; // object color
out vec4 gl_FragColor; // fragment output color
uniform sampler2D txr; // texture to blur
uniform float xs,ys; // texture resolution
//---------------------------------------------------------------------------
void main()
{
float x,y,dx,dy,xx,yy,nn;
vec3 dc;
dx=1.0/(xs-1.0); xx=0.0;
dy=1.0/(ys-1.0); yy=0.0; nn=0.0;
for (x=0.0;x<=1.0;x+=dx)
for (y=0.0;y<=1.0;y+=dy)
if (length(texture(txr,vec2(x,y)).rgb-col.rgb)<0.05)
{ xx+=x; yy+=y; nn++; }
if (nn!=0){ xx/=nn; yy/=nn; }
gl_FragColor=vec4(xx,yy,1.0,1.0);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
GLuint txr_img;
//---------------------------------------------------------------------------
struct _obj
{
DWORD col; // color
int x,y; // CoM
};
const int _max_objs=32;
_obj obj[_max_objs];
int objs=0;
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(-1.0,-1.0,0.0);
glScalef(2.0/xs,2.0/ys,1.0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txr_img);
// compute CoM using GLSL
float m[16];
int i,x,y,r;
glUseProgram(prog_id);
glUniform1i(glGetUniformLocation(prog_id,"txr" ),0);
glUniform1f(glGetUniformLocation(prog_id,"xs" ),xs);
glUniform1f(glGetUniformLocation(prog_id,"ys" ),ys);
glGetFloatv(GL_MODELVIEW_MATRIX,m);
glUniformMatrix4fv(glGetUniformLocation(prog_id,"mvp" ),1,false,m);
glBegin(GL_POINTS);
for (x=0,y=0,i=0;i<objs;i++,x++)
{
glColor4ubv((BYTE*)&(obj[i].col));
glVertex2i(x,y);
}
glEnd();
glUseProgram(0);
glFlush();
// obtain data
DWORD dat[_max_objs];
glReadPixels(0,0,_max_objs,1,GL_BGRA,GL_UNSIGNED_BYTE,dat);
for (i=0;i<objs;i++)
{
obj[i].x=(xs*( ((dat[i]>>16)&255)))>>8;
obj[i].y=(ys*(255-((dat[i]>> 8)&255)))>>8;
}
// render image (old API)
glBegin(GL_QUADS);
glColor3f(1.0,1.0,1.0);
glTexCoord2f(0.0,1.0); glVertex2i( 0, 0);
glTexCoord2f(0.0,0.0); glVertex2i( 0,ys);
glTexCoord2f(1.0,0.0); glVertex2i(xs,ys);
glTexCoord2f(1.0,1.0); glVertex2i(xs, 0);
glEnd();
glBindTexture(GL_TEXTURE_2D,0);
glDisable(GL_TEXTURE_2D);
// render computed CoM
r=10;
glLineWidth(5.0);
glColor3f(0.1,0.1,0.1);
glBegin(GL_LINES);
for (i=0;i<objs;i++)
{
x=obj[i].x;
y=obj[i].y;
glVertex2i(x-r,y-r);
glVertex2i(x+r,y+r);
glVertex2i(x-r,y+r);
glVertex2i(x+r,y-r);
}
glEnd();
r=9;
glLineWidth(2.0);
glBegin(GL_LINES);
for (i=0;i<objs;i++)
{
x=obj[i].x;
y=obj[i].y;
glColor4ubv((BYTE*)&obj[i].col);
glVertex2i(x-r,y-r);
glVertex2i(x+r,y+r);
glVertex2i(x-r,y+r);
glVertex2i(x+r,y-r);
}
glEnd();
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
gl_init(Handle);
// textures
Byte q;
unsigned int *pp;
int i,xs,ys,x,y,adr,*txr;
union { unsigned int c32; Byte db[4]; } c;
Graphics::TBitmap *bmp=new Graphics::TBitmap; // new bmp
objs=0;
// image texture
bmp->LoadFromFile("texture.bmp"); // load from file
bmp->HandleType=bmDIB; // allow direct access to pixels
bmp->PixelFormat=pf32bit; // set pixel to 32bit so int is the same size as pixel
xs=bmp->Width; // resolution should be power of 2
ys=bmp->Height;
txr=new int[xs*ys]; // create linear framebuffer
for(adr=0,y=0;y<ys;y++)
{
pp=(unsigned int*)bmp->ScanLine[y];
for(x=0;x<xs;x++,adr++)
{
// rgb2bgr and copy bmp -> txr[]
c.c32=pp[x]; c.db[3]=0;
q =c.db[2];
c.db[2]=c.db[0];
c.db[0]=q;
txr[adr]=c.c32;
for (i=0;i<objs;i++)
if (obj[i].col==c.c32)
{ i=-1; break; }
if (i>=0)
{
obj[objs].col=c.c32;
obj[objs].x=0;
obj[objs].y=0;
objs++;
}
}
}
glGenTextures(1,&txr_img);
glEnable(GL_TEXTURE_2D); // copy it to gfx card
glBindTexture(GL_TEXTURE_2D,txr_img);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
// glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_DECAL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
glDisable(GL_TEXTURE_2D);
delete[] txr;
delete bmp;
int hnd,siz; char vertex[4096],fragment[4096];
hnd=FileOpen("center_of_mass.glsl_vert",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,vertex ,siz); vertex [siz]=0; FileClose(hnd);
hnd=FileOpen("center_of_mass.glsl_frag",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,fragment,siz); fragment[siz]=0; FileClose(hnd);
glsl_init(vertex,NULL,fragment); // old version of gl_simple is without the NULL !!!
hnd=FileCreate("GLSL.txt"); FileWrite(hnd,glsl_log,glsl_logs); FileClose(hnd);
ClientWidth=xs;
ClientHeight=ys;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
glDeleteTextures(1,&txr_img);
gl_exit();
glsl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
gl_resize(ClientWidth,ClientHeight);
gl_draw();
}
//---------------------------------------------------------------------------
it uses gl_simple.h
This is input ROI texture:
And output:
for better precision you can use more pixels per single object output, or use framebuffer with more than 8bit per channel ...
Related
I'm trying to implement a selection-outline feature. This is what I get up to now.
As you can see, the objects are selected correctly when the mouse hovers and a contour is drawn around the selected object.
What I would like to do now is to outline the visible edges of the object in this way
In the image on the left is what I have now, and in the right image is what I want to achieve.
This is the procedure I use now.
void paintGL()
{
/* ... */
int w = geometry().width();
int h = geometry().height();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF);
setClearColor(Qt::GlobalColor::darkGray);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask(0x00);
DrawGeometry();
if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
{
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
DrawOutline(HoveredSphere, 1.0f - 0.025f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
glBindFramebuffer(GL_READ_FRAMEBUFFER, addFBO(FBOIndex::OUTLINE));
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFramebufferObject());
{
// copy stencil buffer
GLbitfield mask = GL_STENCIL_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
glDepthFunc(GL_LEQUAL);
DrawOutline(HoveredSphere, 1.0f);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
}
update();
}
Where DrawGeometry draws all the objects, and DrawOutline draws the selected object scaled by the factor passed as the second parameter.
Thanks for any suggestions.
By following the tips of #MichaelMahn, I found a solution.
First of all, I draw the silhouette of the visible parts of the selected object in a texture.
And then I use this texture to calculate the outline by checking the neighboring pixels to figure out whether or not I stand on the edge of the silhouette.
outline fragment shader
#version 450
uniform sampler2D silhouette;
in FragData
{
smooth vec2 coords;
} frag;
out vec4 PixelColor;
void main()
{
// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);
for (int i = -1; i <= +1; i++)
{
for (int j = -1; j <= +1; j++)
{
if (i == 0 && j == 0)
{
continue;
}
vec2 offset = vec2(i, j) * size;
// and if one of the neighboring pixels is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}
discard;
}
paintgl
void paintGL()
{
int w = geometry().width();
int h = geometry().height();
setClearColor(Qt::GlobalColor::darkGray);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawGeometry();
// if we hover a sphere
if (HoveredSphere != RgbFromColorToString(Qt::GlobalColor::black))
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, addFBO(FBOIndex::SILHOUETTE));
{
// copy depth buffer
GLbitfield mask = GL_DEPTH_BUFFER_BIT;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, mask, GL_NEAREST);
// set clear color
setClearColor(Qt::GlobalColor::white);
// enable depth test
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// clear color buffer
glClear(GL_COLOR_BUFFER_BIT);
// draw silhouette
DrawSilhouette(HoveredSphere);
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
// clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
// draw outline
DrawOutline();
}
}
PROBLEM :: Now I'd like to parameterize the width of the contour, whose thickness is currently fixed at 1 pixel.
Thank you so much for any suggestion!
Thanks to the advice of #Andrea I found the following solution.
outline fragment shader
#version 450
uniform sampler2D silhouette;
in FragData
{
smooth vec2 coords;
} frag;
out vec4 PixelColor;
void main()
{
// outline thickness
int w = 3;
// if the pixel is black (we are on the silhouette)
if (texture(silhouette, frag.coords).xyz == vec3(0.0f))
{
vec2 size = 1.0f / textureSize(silhouette, 0);
for (int i = -w; i <= +w; i++)
{
for (int j = -w; j <= +w; j++)
{
if (i == 0 && j == 0)
{
continue;
}
vec2 offset = vec2(i, j) * size;
// and if one of the pixel-neighbor is white (we are on the border)
if (texture(silhouette, frag.coords + offset).xyz == vec3(1.0f))
{
PixelColor = vec4(vec3(1.0f), 1.0f);
return;
}
}
}
}
discard;
}
Now I still have a small problem when the selected object is at the edge of the window.
As you can see, the outline is cut sharply.
I tried to "play" with glTexParameter on the parameters GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T.
In the image above you can see the effect I get with
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
while in the image below you can see the effect I get with
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &(QVector4D (1.0f, 1.0f, 1.0f, 1.0f)[0]));
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameters(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
I would like the outline to show up at the edge of the window, but only where necessary.
Thanks a lot!
Brief:
I'm making a Powder Toy that makes use of Parallel Processing to do the game physics, its dealing with a 500 x 500 area of powder. The game does mostly everything with the particles on the GPU but it uses the CPU to render the particles (decreases speed by a lot). How would I render the particles on the GPU instead of the CPU? I'm mostly keeping my particle data on the GPU because most of the operations happen there, and cudaMemcpy is quite slow, making by project uncontrollably lag when its on host memory.
Code:
Here's my display function
void display()
{
// Measure performance
mainloopMeasurePerformanceStart(1);
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Copy particle data to render
cudaMemcpy(&particles, d_particles, sizeof(particles), cudaMemcpyDeviceToHost);
// Loop over the sand particles
for(int i=0;i<250000;i++)
{
// Is the sand particle alive
if(particles[i].alive)
{
// Get the position
int pos[2];
id_to_pos(i,pos);
// Draw the pixel
glColor3f(particles[i].color[0],particles[i].color[1],particles[i].color[2]);
glBegin(GL_QUADS);
glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5)*2);
glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5)*2);
glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5+0.002)*2);
glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5+0.002)*2);
glEnd();
}
}
// Get the mouse position
int m_posX, m_posY;
mousePos(&m_posX, &m_posY);
// Draw the cursor
glColor3f(1.0f, 1.0f, 1.0f);
for(int i=0;i<360;i++)
{
// Calculate the position
double pos[2];
pos[0] = sin(2*PI/360*i)*cursor_radius+m_posX;
pos[1] = cos(2*PI/360*i)*cursor_radius+m_posY;
glBegin(GL_QUADS);
glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5)*2);
glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5)*2);
glVertex2d((pos[0]/500.0-0.5+0.002)*2,(pos[1]/500.0-0.5+0.002)*2);
glVertex2d((pos[0]/500.0-0.5)*2,(pos[1]/500.0-0.5+0.002)*2);
glEnd();
}
// Swap the front and back frame buffers
glutSwapBuffers();
// Measure performance
mainloopMeasurePerformanceEnd();
}
And where the processing for the sand happens:
__global__ void do_sand(
Sand *particles, bool *mouseStates, unsigned long seed,
int m_pos_x, int m_pos_y, double cursor_radius
){
// Get the overall ID
int id = blockIdx.x*100+threadIdx.x;
// Convert the ID to a position
int pos[2];
id_to_pos(id,pos);
// Convert the mouse position to an array
int m_pos[2];
m_pos[0] = m_pos_x;
m_pos[1] = m_pos_y;
// Is the sand particle alive
if(particles[id].alive)
{
// Is there sand being cleared and is this particle in range
if(mouseStates[GLUT_RIGHT_BUTTON] && distance_between(pos, m_pos) < cursor_radius)
{
// Delete this particle
particles[id].alive = false;
}
// Do physics
bool done = false;
int check;
switch(particles[id].model)
{
// Powder
case 'P':
{
// Is vertical movement valid
if(pos[1]-1 >= 0 && !done)
{
// Get the ID
check = pos_to_id(pos[0], pos[1]-1);
// Is this space free
if(!particles[check].alive)
{
// Move the particle
particles[check] = particles[id];
particles[id].alive = false;
done = true;
}
}
// Randomly pick the sands course
int choice;
if((seed * id * 5423) % 2 == 0) choice=1;
else choice=-1;
// Check left movement
if(pos[0]-choice < 500 && pos[0]-choice >= 0 && pos[1]-1 >= 0 && !done)
{
// Get the ID
check = pos_to_id(pos[0]-choice,pos[1]-1);
// Is this space free
if(
!particles[check].alive &&
!particles[pos_to_id(pos[0]-choice,pos[1])].alive &&
!(
particles[pos_to_id(pos[0]-choice*2,pos[1])].alive &&
particles[pos_to_id(pos[0]-choice*2,pos[1]-1)].alive
)
){
// Move the particle
particles[check] = particles[id];
particles[id].alive = false;
done = true;
}
}
// Check right movement
if(pos[0]+choice < 500 && pos[0]+choice >= 0 && pos[1]-1 >= 0 && !done)
{
// Get the ID
check = pos_to_id(pos[0]+choice,pos[1]-1);
// Is this space free
if(
!particles[check].alive &&
!particles[pos_to_id(pos[0]+choice,pos[1])].alive &&
!(
particles[pos_to_id(pos[0]+choice*2,pos[1])].alive &&
particles[pos_to_id(pos[0]+choice*2,pos[1]-1)].alive
)
){
// Move the particle
particles[check] = particles[id];
particles[id].alive = false;
done = true;
}
}
}
// Fluid
case 'F':
{
}
}
}
// Is there sand being added and is this particle in range
else if(mouseStates[GLUT_LEFT_BUTTON] && distance_between(pos, m_pos) < cursor_radius)
{
// Make this particle
particles[id].alive = true;
particles[id].color[0] = 0.0f;
particles[id].color[1] = 0.0f;
particles[id].color[2] = 0.6f;
particles[id].model = 'P';
}
}
Since it was first released, CUDA has had support for OpenGL interoperability (Direct3D also). It is well documented, and if you have installed the CUDA examples, you have several compete sample codes you can study.
In short, you can map an existing OpenGL buffet object into the CUDA address space so that a compute kernel can read and write to the OpenGL memory, release the memory from CUDA, and then render from that CUDA modified buffer as normal. There are significant overheads in doing this, but performance may still be better than copying data to the host for rendering.
As suggested, you can read a thorough introduction in this Nvidia supplied presentation.
I worked out how to create textures and render them using CUDA for extra speed, if I create an array of bytes (could also be int or something else) and upload the RGB values using 3 values (or RGBA using 4 values) positioned after each other to form an image I can load it into OpenGL.
GLubyte data[width*height*3] = {
R, G, B,
R, G, B,
R, G, B
}
As talonmies mentioned I could have used an OpenGL buffer object but an image seems to work for displaying each individual pixel on the screen, and I was having trouble finding information about buffer objects online.
Heres a snippet of my display code:
// Setup the pixel varibles
GLubyte *pixels = new GLubyte[sxy[0]*sxy[1]*3]();
// Get the mouse pos
int m_x, m_y;
mousePos(&m_x,&m_y);
// Render on CPU
if(cpu_only) render_pixels_cpu(
particles,pixels,sxy,
m_x,m_y,cursor_radius
);
else
{
// Load the pixels on the GPU
int N = 512;
render_pixels<<<2048,N>>>(
N,d_particles,d_pixels,
d_sxy,m_x,m_y,cursor_radius
);
// Copy the pixel data over
cudaMemcpy(pixels, d_pixels, sizeof(GLubyte)*sxy[0]*sxy[1]*3, cudaMemcpyDeviceToHost);
}
// Generate and bind the texture
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, sxy[0], sxy[1], 0, GL_RGB, GL_UNSIGNED_BYTE, pixels );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
// Free the pixels
delete pixels;
// Draw quads
glBegin(GL_QUADS);
glTexCoord2d( 0.0, 0.0); glVertex2d(-1.0,-1.0);
glTexCoord2d( 1.0, 0.0); glVertex2d( 1.0,-1.0);
glTexCoord2d( 1.0, 1.0); glVertex2d( 1.0, 1.0);
glTexCoord2d( 0.0, 1.0); glVertex2d(-1.0, 1.0);
glEnd();
// Unbind the texture
glBindTexture(GL_TEXTURE_2D, NULL);
// Delete the texture
glDeleteTextures(1, &tex);
Cuda code:
__global__ void render_pixels(
int N, Sand* particles, GLubyte* pixels, int* sxy,
int m_x, int m_y, double m_radius
){
// Get the overall ID
int id = blockIdx.x*N+threadIdx.x;
// Return if out of range
if(i>sxy[0]*sxy[1])return;
// Get the position
int pos[2];
id_to_pos(i,pos,sxy);
// Calculate the image id
int id = (pos[1]*sxy[0])+pos[0];
// Convert the mouse pos to a position
int mpos[2] = {m_x, m_y};
// Calculate the distance
double distance = distance_between(pos, mpos);
// Is the position in range with the mouse
if((int)distance==(int)m_radius&&m_x>-1&&m_y>-1)
{
// Create a circle here
pixels[(id*3)+0] = (GLubyte)255;
pixels[(id*3)+1] = (GLubyte)255;
pixels[(id*3)+2] = (GLubyte)255;
}
else
{
// Set the colours
pixels[(id*3)+0] = (GLubyte)(particles[i].color[0]*255);
pixels[(id*3)+1] = (GLubyte)(particles[i].color[1]*255);
pixels[(id*3)+2] = (GLubyte)(particles[i].color[2]*255);
}
}
How to implement 3d raypicking in an 3d scene with models that contain high poly meshes?
It takes too much time to iterate over all triangles to perform a triangle-line-intersection test. I know that there exist methods like octree etc. and it should be possible to use these for the models in the scene, but I do not know how I should use these concepts at mesh-level. But if you use an octree at mesh-level, how should one cover problems with polygons, that exceed the boundaries of the octree volumes?
Do you have any advice which method is suitable or recommended for 3d ray-intersections with high poly models for real-time OpenGl applications?
For ray picking rendered objects (like by mouse) the best option is to use the already rendered buffers as there is very little cost of reading them in comparison to ray intersection tests on complex scene. The idea is to render each pick-able rendered object to separate buffer per each info you need about them for example like this:
Depth buffer
this will give you the 3D position of the ray intersection with object.
Stencil buffer
if each object rendered to stencil with its ID (or its index in object list) then you can get the picked object directly.
any other
there are also secondary color attachments and FBO's out there. So you can add any other stuff like normal vector or what ever you need.
If coded right all of this will reduce performance only slightly (even not at all) as you do not need to compute anything its just a single write per fragment per buffer.
The picking itself is easy you just read the corresponding pixel from all the buffers you need and convert to wanted format.
Here simple C++/VCL example using fixed pipeline (no shaders)...
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
void matrix_mul_vector(double *c,double *a,double *b,double w=1.0)
{
double q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]*w);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]*w);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]*w);
for(int i=0;i<3;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
class glMouse
{
public:
int sx,sy; // framebuffer position [pixels]
double pos[3]; // [GCS] ray end coordinate (or z_far)
double beg[3]; // [GCS] ray start (z_near)
double dir[3]; // [GCS] ray direction
double depth; // [GCS] perpendicular distance to camera
WORD id; // selected object id
double x0,y0,xs,ys,zFar,zNear; // viewport and projection
double *eye; // camera direct matrix pointer
double fx,fy; // perspective scales
glMouse(){ eye=NULL; for (int i=0;i<3;i++) { pos[i]=0.0; beg[i]=0.0; dir[i]=0.0; } id=0; x0=0.0; y0=0.0; xs=0.0; ys=0.0; fx=0.0; fy=0.0; depth=0.0; }
glMouse(glMouse& a){ *this=a; };
~glMouse(){};
glMouse* operator = (const glMouse *a) { *this=*a; return this; };
// glMouse* operator = (const glMouse &a) { ...copy... return this; };
void resize(double _x0,double _y0,double _xs,double _ys,double *_eye)
{
double per[16];
x0=_x0; y0=_y0; xs=_xs; ys=_ys; eye=_eye;
glGetDoublev(GL_PROJECTION_MATRIX,per);
zFar =0.5*per[14]*(1.0-((per[10]-1.0)/(per[10]+1.0)));
zNear=zFar*(per[10]+1.0)/(per[10]-1.0);
fx=per[0];
fy=per[5];
}
void pick(double x,double y) // test screen x,y [pixels] position
{
int i;
double l;
GLfloat _z;
GLint _id;
sx=x; sy=ys-1.0-y;
// read depth z and linearize
glReadPixels(sx,sy,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&_z); // read depth value
depth=_z; // logarithmic
depth=(2.0*depth)-1.0; // logarithmic NDC
depth=(2.0*zNear)/(zFar+zNear-(depth*(zFar-zNear))); // linear <0,1>
depth=zNear + depth*(zFar-zNear); // linear <zNear,zFar>
// read object ID
glReadPixels(sx,sy,1,1,GL_STENCIL_INDEX,GL_INT,&_id); // read stencil value
id=_id;
// win [pixel] -> GL NDC <-1,+1>
x= (2.0*(x-x0)/xs)-1.0;
y=1.0-(2.0*(y-y0)/ys);
// ray start GL camera [LCS]
beg[2]=-zNear;
beg[1]=(-beg[2]/fy)*y;
beg[0]=(-beg[2]/fx)*x;
// ray direction GL camera [LCS]
for (l=0.0,i=0;i<3;i++) l+=beg[i]*beg[i]; l=1.0/sqrt(l);
for (i=0;i<3;i++) dir[0]=beg[0]*l;
// ray end GL camera [LCS]
pos[2]=-depth;
pos[1]=(-pos[2]/fy)*y;
pos[0]=(-pos[2]/fx)*x;
// convert to [GCS]
matrix_mul_vector(beg,eye,beg);
matrix_mul_vector(pos,eye,pos);
matrix_mul_vector(dir,eye,dir,0.0);
}
};
//---------------------------------------------------------------------------
// camera & mouse
double eye[16],ieye[16]; // direct view,inverse view and perspective matrices
glMouse mouse;
// objects
struct object
{
WORD id; // unique non zero ID
double m[16]; // direct model matrix
object(){}; object(object& a){ *this=a; }; ~object(){}; object* operator = (const object *a) { *this=*a; return this; }; /*object* operator = (const object &a) { ...copy... return this; };*/
};
const int objs=7;
object obj[objs];
// textures
GLuint txr=-1;
//---------------------------------------------------------------------------
void matrix_inv(double *a,double *b) // a[16] = Inverse(b[16])
{
double x,y,z;
// transpose of rotation matrix
a[ 0]=b[ 0];
a[ 5]=b[ 5];
a[10]=b[10];
x=b[1]; a[1]=b[4]; a[4]=x;
x=b[2]; a[2]=b[8]; a[8]=x;
x=b[6]; a[6]=b[9]; a[9]=x;
// copy projection part
a[ 3]=b[ 3];
a[ 7]=b[ 7];
a[11]=b[11];
a[15]=b[15];
// convert origin: new_pos = - new_rotation_matrix * old_pos
x=(a[ 0]*b[12])+(a[ 4]*b[13])+(a[ 8]*b[14]);
y=(a[ 1]*b[12])+(a[ 5]*b[13])+(a[ 9]*b[14]);
z=(a[ 2]*b[12])+(a[ 6]*b[13])+(a[10]*b[14]);
a[12]=-x;
a[13]=-y;
a[14]=-z;
}
//---------------------------------------------------------------------------
void gl_draw()
{
int i; object *o;
double a;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFFFF); // Write to stencil buffer
glStencilFunc(GL_ALWAYS,0,0xFFFF); // Set any stencil to 0
for (o=obj,i=0;i<objs;i++,o++)
{
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(ieye);
glMultMatrixd(o->m);
glStencilFunc(GL_ALWAYS,o->id,0xFFFF); // Set any stencil to object ID
vao_draw();
}
glStencilFunc(GL_ALWAYS,0,0xFFFF); // Set any stencil to 0
glDisable(GL_STENCIL_TEST); // no need fot testing
// render mouse
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(ieye);
a=0.1*mouse.depth;
glColor3f(0.0,1.0,0.0);
glBegin(GL_LINES);
glVertex3d(mouse.pos[0]+a,mouse.pos[1],mouse.pos[2]);
glVertex3d(mouse.pos[0]-a,mouse.pos[1],mouse.pos[2]);
glVertex3d(mouse.pos[0],mouse.pos[1]+a,mouse.pos[2]);
glVertex3d(mouse.pos[0],mouse.pos[1]-a,mouse.pos[2]);
glVertex3d(mouse.pos[0],mouse.pos[1],mouse.pos[2]+a);
glVertex3d(mouse.pos[0],mouse.pos[1],mouse.pos[2]-a);
glEnd();
Form1->Caption=AnsiString().sprintf("%.3lf , %.3lf , %.3lf : %u",mouse.pos[0],mouse.pos[1],mouse.pos[2],mouse.id);
// debug buffer views
if ((Form1->ck_depth->Checked)||(Form1->ck_stencil->Checked))
{
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txr);
GLfloat *f=new GLfloat[xs*ys],z;
if (Form1->ck_depth ->Checked)
{
glReadPixels(0,0,xs,ys,GL_DEPTH_COMPONENT,GL_FLOAT,f);
for (i=0;i<xs*ys;i++) f[i]=1.0-(2.0*mouse.zNear)/(mouse.zFar+mouse.zNear-(((2.0*f[i])-1.0)*(mouse.zFar-mouse.zNear)));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, xs, ys, 0, GL_RED, GL_FLOAT, f);
}
if (Form1->ck_stencil->Checked)
{
glReadPixels(0,0,xs,ys,GL_STENCIL_INDEX,GL_FLOAT,f);
for (i=0;i<xs*ys;i++) f[i]/=float(objs);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, xs, ys, 0, GL_GREEN, GL_FLOAT, f);
}
delete[] f;
glColor3f(1.0,1.0,1.0);
glBegin(GL_QUADS);
glTexCoord2f(1.0,0.0); glVertex2f(+1.0,-1.0);
glTexCoord2f(1.0,1.0); glVertex2f(+1.0,+1.0);
glTexCoord2f(0.0,1.0); glVertex2f(-1.0,+1.0);
glTexCoord2f(0.0,0.0); glVertex2f(-1.0,-1.0);
glEnd();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
int i;
object *o;
gl_init(Handle);
vao_init();
// init textures
glGenTextures(1,&txr);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txr);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COPY);
glDisable(GL_TEXTURE_2D);
// init objects
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(-1.5,4.7,-8.0);
for (o=obj,i=0;i<objs;i++,o++)
{
o->id=i+1; // unique non zero ID
glGetDoublev(GL_MODELVIEW_MATRIX,o->m);
glRotatef(360.0/float(objs),0.0,0.0,1.0);
glTranslatef(-3.0,0.0,0.0);
}
for (o=obj,i=0;i<objs;i++,o++)
{
glLoadMatrixd(o->m);
glRotatef(180.0*Random(),Random(),Random(),Random());
glGetDoublev(GL_MODELVIEW_MATRIX,o->m);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
glDeleteTextures(1,&txr);
gl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
gl_resize(ClientWidth,ClientHeight);
// obtain/init matrices
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0,0,-15.0);
glGetDoublev(GL_MODELVIEW_MATRIX,ieye);
matrix_inv(eye,ieye);
mouse.resize(0,0,xs,ys,eye);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
GLfloat dz=2.0;
if (WheelDelta<0) dz=-dz;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(ieye);
glTranslatef(0,0,dz);
glGetDoublev(GL_MODELVIEW_MATRIX,ieye);
matrix_inv(eye,ieye);
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
mouse.pick(X,Y);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ck_depthClick(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
Here preview of from left RGB,Depth,Stencil:
Here captured GIF:
the first 3 numbers are the 3D position of picked pixel in [GCS] and the last number in caption is the picked ID where 0 means no object.
The example is using gl_simple,h from here:
simple complete GL+VAO/VBO+GLSL+shaders example in C++
You can ignore the VCL stuff as its not important just port the events to your environment...
So what to do:
rendering
You need add stencil buffer to your GL window pixel format so in my case I just add:
pfd.cStencilBits = 16;
into gl_init() function from gl_simple.h. Also add its bit into glClear and set each objects stencil to its ID Like I did in gl_draw().
picking
I wrote a small glMouse class that do all the heavy lifting. On each change of perspective, view, or viewport call its glMouse::resize function. That will prepare all the constants needed for the computations later. Beware it needs direct camera/view matrix !!!
Now on each mouse movement (or click or whatever) call the glMouse::pick function and then use the results like id which will return the ID picked object was rendered with or pos which is the 3D coordinate in global world coordinates ([GCS]) of the ray object intersection.
The function just read the depth and stencil buffers. Linearize depth like here:
depth buffer got by glReadPixels is always 1
and compute the ray beg,dir,pos,depth in [GCS].
Normal
You got 2 options either render your normal as another buffer which is the simplest and most precise. Or read depths of 2 or more neighboring pixels around picked one compute their 3D positions. From that using cross product compute you normal(s) and average if needed. But this can lead to artifacts on edges.
As mentioned in the comments to boost accuracy you should use linear depth buffer instead of linearized logarithmic like this:
Linear depth buffer
Btw I used the same technique in here (in GDI based SW isometric render):
Improving performance of click detection on a staggered column isometric grid
[Edit1] 8bit stencil buffer
Well these days the reliable stencil bitwidth is only 8bit which limits the number of ids to 255. That is in most cases not enough. A workaround is to render the indexes as colors then store the frame into CPU memory and then render colors normaly. Then when needed using the stored frame for picking. Rendering to texture or color attachment is also a possibility.
[Edit2] some related links
objects moving with mouse
objects moving and orienting with mouse
Use an Octree. Make sure it fits in whole of your mesh.
Also, it sounds like you are assigning each to poly to just one leaf/bucket, which is not right. Assign polys to all leafs/buckets they appear in.
I'm trying to implement a program that turns a cube into a sphere based on key presses, and ripples whenever it's clicked. I managed to implement the cube-to-sphere-and-back part, but I have completely no idea where to start on the rippling. I've looked at tons of sources online, I get the math, but I have no idea how to implement it on my vertex shader. Can anyone help me with my dilemma? Thank you!
Here's my cpp, vsh, and fsh: https://drive.google.com/file/d/0B4hkcF9foOTgbUozMjZmSHJhQWM/view?usp=sharing
I'm using GLSL, OpenGL 4.4.0
Here's my code for the vertex shader:
#version 120
attribute vec3 pos;
varying vec4 out_color;
uniform float t;
float PI = 3.14159265357;
int factor = 2; //for determining colors
int num_colors; // = factor * 3 (because RGB)
float currang = 0;
float angfac;
vec4 calculate( float a )
{
//this is just to calculate for the color
}
void main() {
num_colors = factor*3;
angfac = 2*PI/num_colors;
float ang = atan( pos.z, pos.x )+PI;
out_color = calculate(ang);
//rotation
mat3 rotateX = mat3(
vec3( 1, 0, 0),
vec3( 0, cos(t), sin(t)),
vec3( 0, -sin(t), cos(t))
);
mat3 rotateY = mat3(
vec3( cos(t), 0, -sin(t)),
vec3( 0, 1, 0),
vec3( sin(t), 0, cos(t))
);
mat3 rotateZ = mat3(
vec3( cos(t), sin(t), 0),
vec3(-sin(t), cos(t), 0),
vec3( 0, 0, cos(t))
);
gl_Position = gl_ModelViewProjectionMatrix * vec4((pos.xyz*rotateY*rotateX) , 1.0 );
}
and here's parts of my cpp file:
//usual include statements
using namespace std;
enum { ATTRIB_POS };
GLuint mainProgram = 0;
// I use this to indicate the position of the vertices
struct Vtx {
GLfloat x, y, z;
};
const GLfloat PI = 3.14159265357;
const int sideLength = 10;
const size_t nVertices = (sideLength*sideLength*sideLength)-((sideLength-2)*(sideLength-2)*(sideLength-2));
Vtx cube[nVertices];
Vtx sphere[nVertices];
Vtx diff[nVertices];
const double TIME_SPEED = 0.01;
int mI = 4*(sideLength-1);
const int sLCubed = sideLength*sideLength*sideLength;
int indices[nVertices*nVertices];
GLfloat originX = 0.0f; //offset
GLfloat originY = 0.0f; //offset
bool loadShaderSource(GLuint shader, const char *path) {...}
void checkShaderStatus(GLuint shader) {...}
bool initShader() {...}
//in this part of the code, I instantiate an array of indices to be used by glDrawElements()
void transform(int fac)
{
//move from cube to sphere and back by adding/subtracting values and updating cube[].xyz
//moveSpeed = diff[]/speedFac
//fac is to determine direction (going to sphere or going to cube; going to sphere is plus, going back to cube is minus)
for( int i = 0; i<nVertices; i++ )
{
cube[i].x += fac*diff[i].x;
cube[i].y += fac*diff[i].y;
cube[i].z += fac*diff[i].z;
}
}
void initCube() {...} //computation for the vertices of the cube depending on sideLength
void initSphere() {...} //computation for the vertices of the sphere based on the vertices of the cube
void toSphere() {...} //changes the values of the array of vertices of the cube to those of the sphere
void initDiff() {...} //computes for the difference of the values of the vertices of the sphere and the vertices of the cube for the slow transformation
int main() {
//error checking (GLEW, OpenGL versions, etc)
glfwSetWindowTitle("CS177 Final Project");
glfwEnable( GLFW_STICKY_KEYS );
glfwSwapInterval( 1 );
glClearColor(0,0,0,0);
if ( !initShader() ) {
return -1;
}
glEnableVertexAttribArray(ATTRIB_POS);
glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, sizeof(Vtx), cube);
initCube();
initIndices();
initSphere();
initDiff();
glUseProgram(mainProgram);
GLuint UNIF_T = glGetUniformLocation(mainProgram, "t");
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float t = 0;
glUniform1f(UNIF_T, t);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glPointSize(2.0);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glfwOpenWindowHint(GLFW_FSAA_SAMPLES,16);
glEnable(GL_MULTISAMPLE);
do {
int width, height;
glfwGetWindowSize( &width, &height );
glViewport( 0, 0, width, height );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
t += TIME_SPEED;
glUniform1f(UNIF_T, t);
if (glfwGetKey(GLFW_KEY_DEL)) transform(-1);
if (glfwGetKey(GLFW_KEY_INSERT)) transform( 1 );
if (glfwGetKey(GLFW_KEY_HOME)) initCube();
if (glfwGetKey(GLFW_KEY_END)) toSphere();
glDrawElements( GL_TRIANGLES, nVertices*nVertices, GL_UNSIGNED_INT, indices);
glfwSwapBuffers();
} while ( glfwGetKey(GLFW_KEY_ESC) != GLFW_PRESS &&
glfwGetWindowParam(GLFW_OPENED) );
glDeleteProgram(mainProgram);
glfwTerminate();
return 0;
}
I am trying to read from a 3D texture inside a geometry shader:
#version 150
layout(points) in; // origo of cell
layout(points, max_vertices = 1) out;
uniform sampler3D text;
void main (void)
{
for(int i = 0; i < gl_in.length(); ++i)
{
// texture coordinates:
float u, v, w;
// set u, v, and w somehow
...
float value = texture(text, vec3(u, v, w)).r;
bool show;
// set show based on value somehow:
...
if(show) {
gl_Position = gl_in[i].gl_Position;
EmitVertex();
EndPrimitive();
}
}
}
This is how I set up my texure inside my initialize GL code:
int nx = 101;
int ny = 101;
int nz = 101;
float *data = new float[nx*ny*nz];
// set data[] somehow:
...
glEnable(GL_TEXTURE_3D);
glBindTexture(GL_TEXTURE_3D , texture);
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexImage3D( GL_TEXTURE_3D,
0, // level-of-detail number. 0 is the base image level.
GL_RED, // internal format
nx, ny, nz,
0, // border
GL_RED, // pixel format
GL_FLOAT, // data type of the pixel data
data);
But how do I associate the sampler "text" inside the geometry shader with my texture?
So far I have not told OpenGL that there is a sampler named "text" and that it shall sample texture.
EDIT: I tried the following:
GLint textLoc = glGetUniformLocation(program, "text");
glUniform1i(textLoc, 0); // sends 0 to "text" in shader
// why do I not just hardcode 0 inside the geometry shader instead ?
glBindTexture(GL_TEXTURE_3D , texture);
glActiveTexture(GL_TEXTURE0); // same as 0 ?
GLuint sampler_state = 0;
glGenSamplers(1, &sampler_state);
glBindSampler(0, sampler_state); // what does this do?
What is wrong here?