I'm working with OpenGL and I am trying to create a sphere that has a reflective surface. I have it reflecting but the reflection isn't correct. The object in the reflection should be bent and deformed according to the curve of the surface, instead I'm getting only a straight reflection. I haven't used GL_STENCIL much so help would be very much appreciated. I have provided pieces of code such as the creation of the sphere and the draw method. If anyone needs more let me know.
Creation:
sphere = gluNewQuadric();
gluQuadricDrawStyle(sphere, GLU_FILL);
gluQuadricNormals(sphere, GLU_SMOOTH);
gluSphere(sphere, 1, 100, 100);
gluDeleteQuadric(sphere);
Drawing:
glClearColor (0.0,0.0,0.0,1);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0, 0, -10);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); //disable the color mask
glDepthMask(GL_FALSE); //disable the depth mask
glEnable(GL_STENCIL_TEST); //enable the stencil testing
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); //set the stencil buffer to replace our data
sphereDraw(); //the mirror surface
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //enable the color mask
glDepthMask(GL_TRUE); //enable the depth mask
glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //set the stencil buffer to keep our next lot of data
glPushMatrix();
glScalef(1.0f, -1.0f, 1.0f); //flip the reflection vertically
glTranslatef(0,2,-20); //translate the reflection onto the drawing plane
glRotatef(angle,0,1,0); //rotate the reflection
//draw object as our reflection
glPopMatrix();
glDisable(GL_STENCIL_TEST); //disable the stencil testing
glEnable(GL_BLEND); //enable alpha blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //set the blending function
sphereDraw(); //draw our bench
glDisable(GL_BLEND); //disable alpha blending
//draw object
Since I'm new to using GL_STENCIL I wasn't sure if it's just something small or if much more needs to be done to detect that angle of reflection.
Have you considered using reflection/environment mapping?
There are 2 main forms. Spherical environment mapping usually works by having a pre-calculated environment map. It can, however, been done dynamically. Its main drawback is that it is view dependent.
The other system is Cubic Environment mapping. Cubic is very easy to set up and involves simply rendering your scene 6 times in 6 different direction (ie on to each face of the cube). Cubic env mapping is view independent.
There is another system that sits between spherical and cubic. Its called dual paraboloid environment mapping. It has the draw back that generating the dual paraboloids is quite complex (like spherical) but (like cubic) it is view independent.
Related
In order to implement deferred lightning, I render light sources as a sphere with a radius of light.
I rendered only actual positions of light to screen. The result is below.
so I thought maybe if I can render the intersection of this spheres then I can get rid of for loop for each light in the deferred shader.
by using the following document in the link https://kayru.org/articles/deferred-stencil/
I implemented the intersection of light spheres with my actual scene, and I save it to the texture below.
The problem is the result is a bit different than what I expect. Intersection also includes other spheres. Light volumes should not intersect with each other. How can I implement a proper intersection method?
int k = 0;
for(GLXlight & glxlightdata : entitySystem->glxlights){
lBufferShader->uniform4f(lBufferShader->lightPos[k], &glxlightdata.lightPos[0]);
k++;
}
lBufferShader->uniformMatrix4(lBufferShader->ViewMatrix, &entitySystem->view[0][0]); //ViewMatrix
lBufferShader->uniform3f(lBufferShader->viewPos, &(*entitySystem->viewpos)[0]); //viewPos
lBufferShader->uniformMatrix4(lBufferShader->ProjectionMatrix, &entitySystem->projection[0][0]); //ProjectionMatrix
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lightObject->glxobject.elementBufferID);
glCullFace(GL_BACK); //Front (near) faces only
glColorMask(GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE); //Colour write is disabled
glDepthMask(GL_FALSE); //Z-write is disabled
glDepthFunc(GL_LEQUAL); //Z function is 'Less/Equal'//
glStencilFunc(GL_ALWAYS, 0, 0x00); //Stencil test result does not modify Stencil buffer
glStencilMask(0xFF);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); //Z-Fail writes non-zero value to Stencil buffer
glDrawElementsInstanced(GL_TRIANGLES, lightObject->glxobject.size, GL_UNSIGNED_SHORT, (void *) 0, lightObject->glxinstances.size());
glCullFace(GL_FRONT); //Back (far) faces only
glColorMask(GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE); //Colour write is enabled
glDepthMask(GL_FALSE); //Z-write is disabled
glDepthFunc(GL_GEQUAL); //Z function is 'Greater/Equal'
glStencilFunc(GL_EQUAL, 0, 0xFF); //Stencil test result does not modify Stencil buffer
glStencilMask(0xFF);
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP); //Z-Fail writes non-zero value to Stencil buffer
glDrawElementsInstanced(GL_TRIANGLES, lightObject->glxobject.size, GL_UNSIGNED_SHORT, (void *) 0, lightObject->glxinstances.size());
I solved this problem.
After rendering geometry to buffer, instead of drawing quad to screen, I rendered spheres in the following.
First, render light spheres in the null pass with the following states.
glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDepthFunc(GL_LESS);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0, 0);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
glDepthMask(GL_FALSE);//disable writing to depth buffer
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);//!! disable writing to color buffer
Render light spheres again now with the following states.
glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
This image is rendered using three passes.
In the first pass, I render a three axis.
in the second pass a transparent cylinder is rendered (glEnable(GL_BLEND)) alpha = 0.5f.
finally the golden and grey spheres are rendered in the third pass(glEnable(GL_BLEND)).
The alpha value of the golden spheres = 1.0f and the grey sphere = 0.2f.
The problem:
As you can see,
the cylinder overlaps the spheres even though we enable blending.
the axes overlap the cylinder and the spheres!
Here is my code:
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClearDepthf(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
here the data is prepared and sent to shaders (first pass)
glDrawElements(GL_POINTS, 256, GL_UNSIGNED_INT, reinterpret_cast<void*>(0));
ps: a geometry shader is used to render lines from the given points.
Then we prepare and pass the cylinder data
glEnable(GL_BLEND);
glCullFace(GL_FRONT);
glDrawElements(GL_POINTS, 256, GL_UNSIGNED_INT, reinterpret_cast<void*>(0));
glCullFace(GL_BACK);
glDrawElements(GL_POINTS, 256, GL_UNSIGNED_INT, reinterpret_cast<void*>(0));
glDisable( GL_BLEND);
ps: a geometry shader, also, is used to render the mesh of the cylinders from the given points.
Finally, I render the golden sphere and the grey sphere in one pass
glEnable(GL_BLEND);
glDrawElements(GL_LINE_STRIP, goldenSphereNumber, GL_UNSIGNED_INT, (void*)0);
glDrawElements(GL_LINE_STRIP, sphereIndexCount, GL_UNSIGNED_INT, (void*)0);
glDisable( GL_BLEND);
ps: here also a geometry shader is used to render the mesh of the cylinders from the given lines.
Do you see any wrong? Could you help, please?
With the same code as in my previous question Rendering quad with tiling image? I don't understand why the triangle is not being rendered on top of the textured quad.
Can someone point out what am I missing?
You have depth test enabled which defaults to less (only pixels that are closer get drawn).
If you want a background then disable depth writing during the first pass.
void GLViewer::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
m_backgroundShader.bind();
glBindVertexArray(m_backgroundVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textureID);
glUniform1i(glGetUniformLocation(m_backgroundShader.programId(),"tex"),0);
glDrawArrays(GL_TRIANGLE_STRIP,0,4);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
m_triangleShader.bind();
glBindVertexArray(m_VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
update();
}
I am trying to reflect a bunch of trees from a forest into a nearby lake.I followed all the standard steps to draw a reflection using the Stencil Buffer.But the "water" doesn't seem to allow the objects below to be seen.This is my function: draw_sea() - draws the plan on which to reflect and drawFigTree(fig_tree) - draws the trees below. Where's the problem?
void draw_mirror()
{
glEnable(GL_STENCIL_TEST);
//glColorMask(0, 0, 0, 0);
glDisable(GL_DEPTH_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw_sea();
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glEnable(GL_DEPTH_TEST);
glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPushMatrix();
glTranslatef(0.0, SEA_LEVEL, 0.0);
glEnable(GL_CLIP_PLANE0);
glClipPlane(GL_CLIP_PLANE0, clip_plane0 );
glPopMatrix();
glPushMatrix();
glTranslatef(-25.0, 2*(-1), 0.0);
glScalef(1.0, -1.0, 1.0);
drawFigTree(fig_tree);
glPopMatrix();
glDisable(GL_CLIP_PLANE0);
glDisable(GL_STENCIL_TEST);
}
Blending doesn't work together with depth testing. For blending to work you must sort visible surfaces far-to-near. In case of the water you must draw the reflection first (it's like looking "through" the water surface at the reflected scene) and then the water.
glDisable(GL_DEPTH_TEST);
You are correct in turning off depth tests. But you must also turn off depth writes; otherwise, you'll have polluted the depth buffer when it comes time to render the reflected geometry.
glDepthMask(GL_FALSE);
Note: Don't forget to turn the depth writes back on after rendering the lake.
I am working on a n-body code which models the dynamics of a stellar disk. In the rendering, there are two types of particles : "classic" particles ( whites in the below image) and "dark matter" particles (in blue).
Here's this image at the start of simulation :
Everything seems to be ok with transparency but if I zoom during the run, I notice that actually, some particles keeps the same intermediate color, i.e there are purple.
Here's an example on this image ( which is the stellar disk seen from the side) :
My main problem is so that I don't understand why the color doesn't change as a function of others particles which are behind. For example, I would like a white/blue particle to be partially shaded by the others blue/white particles, and in real-time.
I show you my drawPoints() function where I use transparency :
void drawPoints()
{
glEnable(GL_POINT_SPRITE);
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_NV);
glEnable(GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glEnable( GL_DEPTH_TEST );
glUseProgram(m_program);
glUniform1f( glGetUniformLocation(m_program, "pointRadius"), m_particleRadius );
glUniform1f( glGetUniformLocation(m_program, "pointScale"), m_pointScale );
GLuint vbo_disk;
glBindBuffer(GL_ARRAY_BUFFER, vbo_disk);
glVertexPointer(4, GL_DOUBLE, 4*sizeof(double), pos);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glDrawArrays(GL_POINTS, 0, numBodies_disk);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
GLuint vbo_halo;
glBindBuffer(GL_ARRAY_BUFFER, vbo_halo);
glVertexPointer(4, GL_DOUBLE, 4*sizeof(double), &pos[numBodies_disk]);
glEnableClientState(GL_VERTEX_ARRAY);
glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
glDrawArrays(GL_POINTS, 0, numBodies_halo);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_BLEND);
glDisable(GL_POINT_SPRITE);
}
I tried to use glEnable( GL_DEPTH_TEST ) but it draws 2D texture with black background squares.
Could you give me some clues to have this cumulated and partial transparency in real-time ?
Make sure you disable depth testing:
glDisable( GL_DEPTH_TEST );
Then, you may want to try different blending modes such as additive blending:
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
While additive blending is really pretty, it may produce too much white which could defeat the purpose of this visualization. You may try lowering alpha values in glColor4f. Another solution would be to use blue and red particles to accentuate the difference.