Important: I have to work with the fixed pipeline (I have no voice in this matter).
I have to modify some existing OpenGL code (a panoramic picture viewer, where the panorama is split into the six faces of a cube) so we're able to draw lines/points on top of the loaded textures, where the points are the mouse coordinates unprojected to object coordinates.
I wrote a test program with a coloured cube just to try the line painting on top of it:
I got this with the code pushing the GL_DEPTH_BUFFER_BIT attribute to the stack, disabling it before painting the points and poping the stack attribute after I have done with the painting.
I tried to use that same approach in the existing application, but I got these results (here, I'm trying only to paint a point):
I specified red as the color for the point but, as you can see, it doesn't have the desired one. I thought it might be due to blending and that it might be mixing its color with the underlying texture, so I pushed the GL_BLEND attribute to the stack as well and disabled it before painting, but the point isn't getting the desired color anyway.
What is happening here? Is there a way to "force" the pipeline to paint the point red?
initCube() : this is call before updating the GL scene.
void panoViewer::initCube() {
makeCurrent();
if(texture){
glDisable( texture );
textName = 0;
texture = 0;
}
glDisable( GL_TEXTURE_GEN_S );
glDisable( GL_TEXTURE_GEN_T );
glDisable( GL_TEXTURE_GEN_R );
glFrontFace( GL_CCW );
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
texture = GL_TEXTURE_CUBE_MAP;
textName = texnms[1];
glEnableClientState(GL_NORMAL_ARRAY);
glTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R );
// Add the textures to the cube faces.
// ...
}
initializeGL() :
void panoViewer::initializeGL() {
qglClearColor(Qt::black);
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable( GL_DEPTH_TEST );
// create texture objects
glGenTextures( 1, textName );
glBindTexture( GL_TEXTURE_CUBE_MAP, textName );
// find the largest feasible textures
maxTex2Dsqr = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d );
maxTex2Drec = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d / 2 );
maxTexCube = maxTexSize( GL_PROXY_TEXTURE_CUBE_MAP, maxcube, maxcube );
// constant texture mapping parameters...
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
// for cube maps...
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// enable alpha blending for overlay
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glDisable(GL_LIGHTING);
// Create display list: dispList
// ...
}
paintGL() :
void panoViewer::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(texture) {
glBindTexture(texture, textName);
glEnable( texture );
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotated( 180, 0, 1, 0 ); // camera looks at the front of the van
glRotated( 180, 0, 0, 1 ); // van's roof points to the sky
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// double hFOV, vFOV; // angular size at sphere center (deg)
// double minFOV, maxFOV; // limits on vFOV
// double wFOV; // vert angle at eye (deg) sets magnification
double hhnear = Znear * tan( 0.5 * RAD(wFOV) ),
hwnear = hhnear * aspectRatio,
dxnear = 2 * hwnear * fcompx,
dynear = 2 * hhnear * fcompy;
glFrustum( -(hwnear + dxnear), hwnear - dxnear,
-(hhnear + dynear), hhnear - dynear,
Znear, Zfar
);
glRotated( 180, 0, 1, 0 );
glTranslated( eyex, eyey, eyez );
glRotated( tiltAngle, 1, 0, 0 );
glRotated( panAngle, 0, 1, 0 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glCallList(dispList);
glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
// Paint the point in red
// ...
glPopAttrib();
}
UPDATE: I forgot to mention that the code is based in Qt. It uses the QtOpenGL module extensively.
UPDATE #2: I've added some code.
In the fixed function pipeline, there are many states which could lead to the vertex color beeing completely ignored.
As Reto Koradi pointed out in the comments, when lighting is enabled, the colors have no effect (unless GL_COLOR_MATERIAL is enabled, in which case the color value is used to update the material parameters used for the lighting equation.)
As I pointed out in the comments, another case is texturing. Depending on the GL_TEX_ENV_MODE selected, the fragment's color (as determined by lighting, or directly interpolated from the vertex colors) is modulated by the texture color, or completely replaced. In that case, disabling texturing for every texture unit in use can solve the issue.
Related
I have got several meshes (~100) of the same complex object in various poses with slightly different rotation and translation parameters. The object consists of multiple rigid components like arms and legs.
The goal is to generate a unique grayscale picture showing the accumulation of these poses for a particular body part. The heat-map obtained gives an idea of probable pixel locations for the body part, where white represents maximum probability, and black minimum (the lighter the higher probability). Say I'm interested in the accumulation of the legs. If many leg pose samples lie on the same (x,y) pixel location, than I expect to see light pixels there. Ultimately the leg poses might not exactly overlap, so I also expect to see a smooth transition to the black low probability around the leg silhouette boundaries.
To solve this task I have decided to use rendering in OpenGL frame buffers as these are known to be computationally cheap, and because I need to run this accumulation procedure very often.
What I did is the following. I accumulate the corresponding renderings of the body part I'm interested in (let's still keep the leg example) on the same frame buffer 'fboLegsId' using GL_BLEND. In order to discriminate between the legs
and the rest of the body, I texture the mesh with two colors:
rgba(gray,gray,gray,255) for the legs, where gray = 255 / Number of samples = 255/100
rgba(0,0,0,0) for the rest of the body
Then I accumulate the 100 renderings (which for the leg should sum up to white = 255) by doing the following:
glBindFramebuffer(GL_FRAMEBUFFER, fboLegsId);
glClearColor(0,0,0,255);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
for each sample s = 0...100
mesh.render(pose s);
end
glReadPixels(...)
This performs almost as I expected. I do obtain the smooth grayscale heat-map I wanted. However there are self-occlusion problems
which arise even when I use only 1 sample. Say for a single pose sample, one of the arms moved before the leg, partially occluding them. I expect the influence of the occluded leg parts to be cancelled during rendering. However it renders as if the arm is invisible/translucent, allowing for pixels behind to be fully shown. This leads to wrong renderings and therefore wrong accumulations.
If I simple disable blending, I see the correct self-occlusion aware result. So, apparently the problem lies somewhere at blending time.
I also tried different blending functions, and so far the following one produced the closer results to a self-occlusion aware accumulation approach:
glBlendFunc(GL_ONE, GL_SRC_ALPHA);
Anyway there is still a problem here: one single sample looks now correct; two or more accumulated samples instead show overlapping artefacts with other samples. It looks like each accumulation replaces the current buffer pixel if the pixel is not part of the legs. And if the leg was found many times in front of the (let's say) the arm, than it becomes darker and darker, instead of lighter and lighter.
I tried to fix this by clearing depth buffer at each rendering iteration enabling depth computations, but this did not solve the problem.
I feel like there is either something conceptually wrong in my approach, or a small mistake somewhere.
I've tried a different approach based on the suggestions which performs as expected. Now I'm working with 2 frame buffers. The first one (SingleFBO) is used to render single samples with correct self-occlusion handling. The second (AccFBO) is used to accumulate the 2D textures from the first buffer using blending. Please, check my code below:
// clear the accumulation buffer
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for each sample s = 0...100
{
// set rendering destination to SingleFBO
glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
mesh->render(pose s);
glDisable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
// set rendering destination to the accumulation buffer
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glClear(GL_DEPTH_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
// draw texture from previous buffer to a quad
glBindTexture(GL_TEXTURE_2D, textureLeg);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glBegin( GL_QUADS );
{
glTexCoord2f(0,0); glVertex2f(-1.0f, -1.0f);
glTexCoord2f(1,0); glVertex2f(1.0f, -1.0f);
glTexCoord2f(1,1); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0,1); glVertex2f(-1.0f, 1.0f);
}
glEnd();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
// restore
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
}
glBindFramebuffer(GL_FRAMEBUFFER, AccFBO);
glReadPixels(...)
Please, check also my (standard) code for initializing the SingleFBO (similarly for AccFBO):
// create a texture object
glGenTextures(1, &textureLeg);
glBindTexture(GL_TEXTURE_2D, textureLeg);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create a renderbuffer object to store depth info
glGenRenderbuffers(1, &rboLeg);
glBindRenderbuffer(GL_RENDERBUFFER, rboLeg);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a framebuffer object
glGenFramebuffers(1, &SingleFBO);
glBindFramebuffer(GL_FRAMEBUFFER, SingleFBO);
// attach the texture to FBO color attachment point
glFramebufferTexture2D(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_COLOR_ATTACHMENT0, // 2. attachment point
GL_TEXTURE_2D, // 3. tex target: GL_TEXTURE_2D
textureLeg, // 4. tex ID
0); // 5. mipmap level: 0(base)
// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_DEPTH_ATTACHMENT, // 2. attachment point
GL_RENDERBUFFER, // 3. rbo target: GL_RENDERBUFFER
rboLeg); // 4. rbo ID
// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
error(...);
// switch back to window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Here's a different approach:
Create two frame buffers: normal and acc. normal frame buffer should have a texture storage (with glFramebufferTexture2D).
Here's the basic algorithm:
Clear acc to black
Bind normal, clear to black, and render scene with white legs, and other parts black
Bind acc, render a full screen rectangle, with normal texture on it, with blend mode GL_ONE, GL_ONE
Forward the animation, and if it haven't finished, goto 2.
You have the result in acc
So, basically, acc will contain the individual frames summed.
I am attempting to procedurally generate a star-filled background in OpenGL.
The approach I am taking is to create a skybox with a cubemap texture. Each side of the cubemap texture essentially consists of a 2048x2048 black image with randomly selected texels set to White. Here is the result:
I'm not sure how obvious it is from the image, but when moving around a very distinct box shape can be made out as stars close to the edge of the box appear smaller and closer together. How can I prevent this? Do I need to abandon the skybox approach and use something like a skysphere instead?
EDIT: here is how I am mapping the cubemap onto the sky.
// Create and bind texture.
glGenTextures(1, &texture_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture_);
for (unsigned int i = 0; i < 6; ++i) {
std::vector<std::uint8_t> image = generateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, TEXTURE_WIDTH, TEXTURE_HEIGHT,
0, GL_RGB, GL_UNSIGNED_BYTE, image.data());
}
// Set texture parameters.
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
Here is the definition of the generateTexture function:
std::vector<std::uint8_t> Stars::generateTexture(GLsizei width, GLsizei height) {
std::vector<std::uint8_t> image(static_cast<std::size_t>(3 * width * height));
add_stars(image, NUM_STARS);
return image;
}
void Stars::add_stars(std::vector<std::uint8_t>& image, unsigned int nStars) {
std::default_random_engine eng;
std::uniform_int_distribution<std::size_t> dist(0, image.size() / 3 - 1);
while (nStars--) {
std::size_t index = 3 * dist(eng);
image[index++] = 255;
image[index++] = 255;
image[index++] = 255;
}
}
EDIT2: here is the draw function used to render the sky.
void Stars::draw(const Camera& camera) const {
// Skybox will be rendered last. In order to ensure that the stars are rendered at the back of
// the scene, the depth buffer is filled with values of 1.0 for the skybox -- this is done in
// the vertex shader. We need to make sure that the skybox passes the depth te3t with values
// less that or equal to the depth buffer.
glDepthFunc(GL_LEQUAL);
program_.enable();
// Calculate view-projection matrix and set the corresponding uniform. The view matrix must be
// stripped of translation components so that the skybox follows the camera.
glm::mat4 view = glm::mat4(glm::mat3(camera.viewMatrix()));
glm::mat4 projection = camera.projectionMatrix();
glm::mat4 VP = projection * view;
glUniformMatrix4fv(program_.uniformLocation("VP"), 1, GL_FALSE, glm::value_ptr(VP));
// Bind buffer objects and texture to current context and draw.
glBindVertexArray(vao_);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture_);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(INDICES.size()), GL_UNSIGNED_INT,
reinterpret_cast<GLvoid *>(0));
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
program_.disable();
glDepthFunc(GL_LESS);
}
generate stars uniformly in some cubic volume
x=2.0*Random()-1.0; // <-1,+1>
y=2.0*Random()-1.0; // <-1,+1>
z=2.0*Random()-1.0; // <-1,+1>
project them on unit sphere
So just compute the length of vector (x,y,z) and divide the coordinates by it.
project the result onto the cube map
Each side of cube is defined by the plane so find intersection of ray casted from (0,0,0) through Cartesian star position and the planes. Take the intersection with shortest distance to (0,0,0) and use that as final star position.
The implementation could be something like this OpenGL&C++ code:
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int i,n=10000;
float a,b,x,y,z;
//RandSeed=8123456789;
n=obj.pnt.num; // triangulated sphere point list
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE,GL_ONE);
glPointSize(2.0);
glBegin(GL_POINTS);
for (i=0;i<n;i++)
{
// equidistant points instead of random to test this
x=obj.pnt[i].p[0];
y=obj.pnt[i].p[1];
z=obj.pnt[i].p[2];
/*
// random star spherical position
a=2.0*M_PI*Random();
b=M_PI*(Random()-0.5);
// spherical 2 cartessian r=1;
x=cos(a)*cos(b);
y=sin(a)*cos(b);
z= sin(b);
*/
// redish sphere map
glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
// cube half size=1 undistort // using similarities like: yy/xx = y/x
if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=x; z/=x; if (x>=0) x=1.0; else x=-1.0; }
else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=y; z/=y; if (y>=0) y=1.0; else y=-1.0; }
else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=z; y/=z; if (z>=0) z=1.0; else z=-1.0; }
// bluish cube map
glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
}
glEnd();
glPointSize(1.0);
glDisable(GL_BLEND);
glFlush();
SwapBuffers(hdc);
Looks like it works as it should here preview (of the blended sphere/cube map):
Although it looks like there are holes but there are none (it is may be some blend error) if I disable the sphere map render then there are no visible holes or distortions in the mapping.
The sphere triangulation mesh obj used to test this is taken from here:
Sphere triangulation
[Edit1] yes there was a silly blending error
I repaired the code ... but the problem persists anyway. does not matter this mapping is working as should here the updated code result:
So just adapt the code to your texture generator ...
[Edit2] Random stars
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int i;
float x,y,z,d;
RandSeed=8123456789;
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE,GL_ONE);
glPointSize(2.0);
glBegin(GL_POINTS);
for (i=0;i<1000;i++)
{
// uniform random cartesian stars inside cube
x=(2.0*Random())-1.0;
y=(2.0*Random())-1.0;
z=(2.0*Random())-1.0;
// project on unit sphere
d=sqrt((x*x)+(y*y)+(z*z));
if (d<1e-3) { i--; continue; }
d=1.0/d;
x*=d; y*=d; z*=d;
// redish sphere map
glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
// cube half size=1 undistort using similarities like: y/x = y'/x'
if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=x; z/=x; if (x>=0) x=1.0; else x=-1.0; }
else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=y; z/=y; if (y>=0) y=1.0; else y=-1.0; }
else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=z; y/=z; if (z>=0) z=1.0; else z=-1.0; }
// bluish cube map
glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
}
glEnd();
glPointSize(1.0);
glDisable(GL_BLEND);
glFlush();
SwapBuffers(hdc);
Here Blend of booth (1000 stars):
And Here only the cube-map (10000 stars)
[Edit3] The Blend problem solved
It was caused by Z-fighting and occasional changing of sign for some coordinates during the projection due to forgotten fabs here fixed code:
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int i;
float x,y,z,d;
RandSeed=8123456789;
glDepthFunc(GL_ALWAYS);
// glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE,GL_ONE);
glPointSize(2.0);
glBegin(GL_POINTS);
for (i=0;i<25000;i++)
{
// uniform random cartesian stars inside cube
x=(2.0*Random())-1.0;
y=(2.0*Random())-1.0;
z=(2.0*Random())-1.0;
// project on unit sphere
d=sqrt((x*x)+(y*y)+(z*z));
if (d<1e-3) { i--; continue; }
d=1.0/d;
x*=d; y*=d; z*=d;
// redish sphere map
glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
// cube half size=1 undistort using similarities like: y/x = y'/x'
if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=fabs(x); z/=fabs(x); if (x>=0) x=1.0; else x=-1.0; }
else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=fabs(y); z/=fabs(y); if (y>=0) y=1.0; else y=-1.0; }
else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=fabs(z); y/=fabs(z); if (z>=0) z=1.0; else z=-1.0; }
// bluish cube map
glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
}
glEnd();
glPointSize(1.0);
glDisable(GL_BLEND);
glFlush();
SwapBuffers(hdc);
And here the Blend result finally the colors are as should be so the sphere and cube stars overlaps perfectly (white) while viewing from (0,0,0):
The Problem
I have been trying to implement shadows in OpenGL for some time. I have finally gotten it to a semi-working state in that the shadow appears but covers the scene in strange places [i.e - it is not relative to the light]
To further explain the above gif: As I move the light-source further away from the scene (to the left) - the shadow stretches further. Why? If anything, it should show more of the scene.
Update - I messed around with the lights position and am now being given this result (confusing):
Depth Map
Here it is:
The Code
Because this is a difficult issue to pinpoint - I will post a large chunk of the code I am using in this application.
The Framebuffer and Depth Texture - The first thing I needed was a framebuffer to record the depth values of all the drawn objects and then I needed to dump these values into a depth texture (the shadow-map):
// Create Framebuffer
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
// Create and Load Depth Texture
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
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_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
//Attach Texture To Framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
//Check for errors
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
Falcon::Debug::error("ShadowBuffer [Framebuffer] could not be initialized.");
Rendering The Scene - First I do the shadow-pass which just runs through some basic shaders and outputs to the framebuffer and then I do a second, regular pass that actually draws the scene and does GLSL shadow-map sampling:
//Clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Select Main Shader
normalShader->useShader();
//Bind + Update + Draw
/* Render Shadows */
shadowShader->useShader();
glBindFramebuffer(GL_FRAMEBUFFER, Shadows::framebuffer());
//Viewport
glViewport(0,0,640,480);
//GLM Matrix Definitions
glm::mat4 shadow_matrix_view;
glm::mat4 shadow_matrix_projection;
//View And Projection Calculations
shadow_matrix_view = glm::lookAt(glm::vec3(light.x,light.y,light.z), glm::vec3(0,0,0), glm::vec3(0,1,0));
shadow_matrix_projection = glm::perspective(45.0f, 1.0f, 0.1f, 1000.0f);
//Calculate MVP(s)
glm::mat4 shadow_depth_mvp = shadow_matrix_projection * shadow_matrix_view * glm::mat4(1.0);
glm::mat4 shadow_depth_bias = glm::mat4(0.5,0,0,0,0,0.5,0,0,0,0,0.5,0,0.5,0.5,0.5,1) * shadow_depth_mvp;
//Send Data To The GPU
glUniformMatrix4fv(glGetUniformLocation(shadowShader->getShader(),"depth_matrix"), 1, GL_FALSE, &shadow_depth_mvp[0][0]);
glUniformMatrix4fv(glGetUniformLocation(normalShader->getShader(),"depth_matrix_bias"), 1, GL_FALSE, &shadow_depth_bias[0][0]);
renderScene();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
/* Clear */
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* Shader */
normalShader->useShader();
/* Shadow-map */
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Shadows::shadowmap());
glUniform1f(glGetUniformLocation(normalShader->getShader(),"shadowMap"),0);
/* Render Scene */
glViewport(0,0,640,480);
renderScene();
Fragment Shader - This is where I calculate the final color to be output and do the depth texture / shadow-map sampling. It could be the source of where I am going wrong:
//Shadows
uniform sampler2DShadow shadowMap;
in vec4 shadowCoord;
void main()
{
//Lighting Calculations...
//Shadow Sampling:
float visibility = 1.0;
if (texture(shadowMap, shadowCoord.xyz) < shadowCoord.z){
visibility = 0.1;
}
//Final Output
outColor = finalColor * visibility;
}
Edits
<1> AMD Hardware Issue - It was also suggested that this could be an issue of the GPU but I find this hard to believe given that it's a Radeon HD 6670. Would it be worth putting in a Nvidia card in to test this theory?
<2> Suggest Changes - I made some suggested changes from the comments and answers:
Firstly, I changed the light's perspective projection to an ortho one which gave me the accuracy I needed in the shadow-map so that now I can see the depth clearly (i.e -> it's not all white). In addition, it removes the need for the perspective division so I am using 3-dimensional coordinates for testing this. Below is a screenshot:
Secondly, I changed my texture sampling to this: visibility = texture(shadowMap,shadowCoord.xyz); which now always returns 0 and thus I cannot see the scene as it is considered ENTIRELY shadowed.
Thirdly and finally, I made a swap from GL_LEQUAL to GL_LESS as suggested an no changes occurred.
There is something fundamentally wrong with your shader:
uniform sampler2DShadow shadowMap; // NOTE: Shadow samplers perform comparison !!
...
if (texture(shadowMap, shadowCoord.xyz) < shadowCoord.z)
You have texture compare vs. reference enabled. That means that the 3rd texture coordinate is going to be compared by the texture (...) function and the returned value is going to be the result of the test function (GL_LEQUAL in this case).
In other words, texture (...) will return either 0.0 (fail) or 1.0 (pass) by comparing the looked up depth at shadowCoord.xy to the value of shadowCoord.z. You are doing this test twice.
Consider using this altered code instead:
float visibility = texture(shadowMap, shadowCoord.xyz);
That is not going to produce quite the results you want because your comparison function is GL_LEQUAL, but it is a start. Consider changing the comparison function to GL_LESS to get an exact functional match.
I have an OpenGL context on which I draw successfully using OpenGL.
I need to draw a specific rectangle of an IOSurface to this context.
What is the best way to do this on 10.8?
NOTE:
I know how to do this on 10.9 using CoreImage (by createing a CIImage from the IOSurface, and render it with [CIContext drawImage:inRect:fromRect]).
However, this does not work well for me on 10.8 (each raw of the image is displayed with a different offset, and the image is distorted diagonally).
Edit: Here is the code that works on 10.9 but not on 10.8:
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CIImage* ciImage = [[CIImage alloc] initWithIOSurface:surface plane:0 format:kCVPixelFormatType_32BGRA options:#{kCIImageColorSpace : (__bridge id)colorSpace}];
NSRect flippedFromRect = fromRect;
// Flip rect before passing to CoreImage:
{
flippedFromRect.origin.y = IOSurfaceGetHeight(surface) - fromRect.origin.y - fromRect.size.height;
}
[ciContext drawImage:ciImage inRect:inRect fromRect:flippedFromRect];
CGColorSpaceRelease(colorSpace);
Here is the solution by wrapping the IOSurface with an OpenGL texture and draw the texture to the screen. This assumes a similar API to [CIContext render:toIOSurface:bounds:colorSpace:] but a vertically flipped OpenGL coordinate system.
// Draw surface on OpenGL context
{
// Enable the rectangle texture extenstion
glEnable(GL_TEXTURE_RECTANGLE_EXT);
// 1. Create a texture from the IOSurface
GLuint name;
{
CGLContextObj cgl_ctx = ...
glGenTextures(1, &name);
GLsizei surface_w = (GLsizei)IOSurfaceGetWidth(surface);
GLsizei surface_h = (GLsizei)IOSurfaceGetHeight(surface);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, name);
CGLError cglError =
CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE_EXT, GL_RGBA, surface_w, surface_h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface, 0);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
}
// 2. Draw the texture to the current OpenGL context
{
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, name);
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
glColor4f(0.f, 0.f, 1.0f, 1.0f);
glTexCoord2f( (float)NSMinX(fromRect), (float)(NSMinY(fromRect)));
glVertex2f( (float)NSMinX(inRect), (float)(NSMinY(inRect)));
glTexCoord2f( (float)NSMaxX(fromRect), (float)NSMinY(fromRect));
glVertex2f( (float)NSMaxX(inRect), (float)NSMinY(inRect));
glTexCoord2f( (float)NSMaxX(fromRect), (float)NSMaxY(fromRect));
glVertex2f( (float)NSMaxX(inRect), (float)NSMaxY(inRect));
glTexCoord2f( (float)NSMinX(fromRect), (float)NSMaxY(fromRect));
glVertex2f( (float)NSMinX(inRect), (float)NSMaxY(inRect));
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
}
glDeleteTextures(1, &name);
}
If you need to draw in the display's color profile, you can explicitly call ColorSync and pass it your source profile and destination profile. It will return to you a “recipe” to perform the color correction. That recipe actually has a linearization, a color conversion (a 3x3 conversion matrix) and a gamma.
FragmentInfo = ColorSyncTransformCopyProperty (transform, kColorSyncTransformFullConversionData, NULL);
If you like, you can combine all those operations into a 3D lookup table. That's actually what happens in the color management of many of the OS X frameworks and applications.
References:
Apple TextureUpload sample code
Draw IOSurfaces to another IOSurface
OpenGL Options for Advanced Color Management
I'm trying to take a concave polygon and apply an image to it as a texture. The polygon can have multiple contours, both internal holes and external "islands". It can be any shape, but will be smaller than the image and will fit inside it. It does not necessarily touch the edges of the image.
I've successfully displayed the tessellated polygon, and textured a simple square, but can't get the two to work together.
Here's how I'm loading the texture:
GLuint texture;
int width, height;
BYTE * data;
FILE * file;
// open texture data
file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
// allocate buffer
width = 256;
height = 256;
data = (BYTE *)malloc( width * height * 3 );
// read texture data
fread( data, width * height * 3, 1, file );
fclose( file );
glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap ? GL_REPEAT : GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap ? GL_REPEAT : GL_CLAMP );
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width,height, GL_RGB, GL_UNSIGNED_BYTE, data );
free( data );
return texture;
Here's the tessellation function:
GLuint tessellate1()
{
GLuint id = glGenLists(1); // create a display list
if(!id) return id; // failed to create a list, return 0
GLUtesselator *tess = gluNewTess(); // create a tessellator
if(!tess) return 0; // failed to create tessellation object, return 0
GLdouble quad1[4][3] = { {-1,3,0}, {0,0,0}, {1,3,0}, {0,2,0} };
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, GLTexture::LoadTextureRAW("texture.raw", true));
// register callback functions
gluTessCallback(tess, GLU_TESS_BEGIN, (void (CALLBACK *)())tessBeginCB);
gluTessCallback(tess, GLU_TESS_END, (void (CALLBACK *)())tessEndCB);
gluTessCallback(tess, GLU_TESS_ERROR, (void (CALLBACK *)())tessErrorCB);
gluTessCallback(tess, GLU_TESS_VERTEX, (void (CALLBACK *)())tessVertexCB);
glNewList(id, GL_COMPILE);
glColor3f(1,1,1);
gluTessBeginPolygon(tess, 0); // with NULL data
gluTessBeginContour(tess);
gluTessVertex(tess, quad1[0], quad1[0]);
gluTessVertex(tess, quad1[1], quad1[1]);
gluTessVertex(tess, quad1[2], quad1[2]);
gluTessVertex(tess, quad1[3], quad1[3]);
gluTessEndContour(tess);
gluTessEndPolygon(tess);
glEndList();
gluDeleteTess(tess); // delete after tessellation
glDisable(GL_TEXTURE_2D);
setCamera(0, 0, 5, 0, 0, 0);
return id; // return handle ID of a display list
}
Here's the tessellation vertex callback function:
// cast back to double type
const GLdouble *ptr = (const GLdouble*)data;
double dImageX = -1, dImageY = -1;
//hardcoded extents of the polygon for the purposes of testing
int minX = 607011, maxX = 616590;
int minY = 4918219, maxY = 4923933;
//get the % coord of the texture for a poly vertex. Assumes image and poly bounds are the same for the purposes of testing
dImageX = (ptr[0] - minX) / (maxX - minX);
dImageY = (ptr[1] - minY) / (maxY - minY);
glTexCoord2d(dImageX, dImageY);
glVertex2d(ptr[0], ptr[1]);
And here's the display callback:
void displayCB()
{
// clear buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// save the initial ModelView matrix before modifying ModelView matrix
glPushMatrix();
// tramsform camera
glTranslatef(0, 0, cameraDistance);
glRotatef(cameraAngleX, 1, 0, 0); // pitch
glRotatef(cameraAngleY, 0, 1, 0); // heading
// draw meshes
glCallList(listId1); //id of the tessellated poly
// draw info messages
showInfo();
glPopMatrix();
glutSwapBuffers();
}
The results of this are a correctly drawn polygon with no texture applied.
// init
glGenTextures( 1, &texture );
// vertex callback
glBindTexture(GL_TEXTURE_2D, 1);
I don't think the first ID returned by glGenTextures() is required to be 1.
Try using texture instead of 1 in your glBindTexture() call.
Also, there's really no reason to enable texturing and re-bind the texture for every vertex. Just do it once before you call into the tesselator.
You're not capturing the texture binding and Enable inside the display list, so it's not going to be taken into account when you replay it. So, either:
Capture the BindTexture and Enable inside the display list, or
BindTexture and Enable(TEXTURE_2D) before calling CallList
The problem was the glDisable(GL_TEXTURE_2D) call in the tessellation function. After removing it, the texture was applied correctly.