A Project I am working on involves me using glScissor, in some cases i need to perform a scissor on an area twice (or more), with the goal of only rendering what is within both scissor boxes.
The issue im running into is that the second scissor box just overrides the previous one, meaning only the last box set is used instead of both.
I have tried existing solutions such as setting scissor1, push matrix, enable scissor_test, set scissor2, disable scissor_test, popmatrix, disable scissor_test. As proposed here: glScissor() call inside another glScissor()
I could not get these to produce any difference, I had also tried glPushAttrib instead of matrix but still no difference.
Here is an example program I wrote for scissor testing, its compiled by g++ and uses freeglut, the scissoring takes place in display():
/*
Compile: g++ .\scissor.cpp -lglu32 -lfreeglut -lopengl32
*/
#include <GL/gl.h>//standard from mingw, already in glut.h - header library
#include <GL/glu.h>//standard from mingw, already in glut.h - utility library
#include <GL/glut.h>//glut/freeglut - more utilities, utility tool kit
void display();
void reshape(int, int);
void timer(int);
void init(){
glClearColor(0, 0, 0, 1);
}
int main(int argc, char **argv){
glutInit(&argc, argv);//init glut
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);//init display mode, add double buffer mode
//init window
glutInitWindowPosition(200, 100);//if not specified, it will display in a random spot
glutInitWindowSize(500, 500);//size
//create window
glutCreateWindow("Window 1");
//give glut a function pointer so it can call that function later
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutTimerFunc(0, timer, 0);//call certain function after a specified amount of time
init();
glutMainLoop();//once this loop runs your program has started running, when the loop ends the program terminates
}
float xPos = -10;
int state = 1;//1 = right, -1 = left
//our rendering happens here
void display(){
//clear previous frame
glClear(GL_COLOR_BUFFER_BIT);//pass in flag of frame buffer
//draw next frame below
glLoadIdentity();//reset rotations, transformations, ect. (resets coordinate system)
//we are using a model view matrix by default
//TEST
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 100, 1000);
glPushMatrix();
glEnable(GL_SCISSOR_TEST);
glScissor(50, 0, 1000, 1000);
//assuming both scissors intersect, we should only see the square between 50 and 100 pixels
//draw
glBegin(GL_QUADS);//every set of 3 verticies is a triangle
//GL_TRIANGLES = 3 points
//GL_QUADS = 4 points
//GL_POLYGON = any amount of points
glVertex2f(xPos, 1);//the 2 is the amount of args we pass in, the f means theyr floats
glVertex2f(xPos, -1);
glVertex2f(xPos+2, -1);
glVertex2f(xPos+2, 1);
glEnd();//tell opengl your done drawing verticies
glDisable(GL_SCISSOR_TEST);
glPopMatrix();
glDisable(GL_SCISSOR_TEST);
//display frame buffer on screen
//glFlush();
glutSwapBuffers();//if double buffering, call swap buffers instead of flush
}
//gets called when window is reshaped
void reshape(int width, int hight){
//set viewport and projection
//viewport is a rectangle where everything is drawn, like its the window
glViewport(0, 0, width, hight);
//matrix modes: there is model view and projection, projection has depth
glMatrixMode(GL_PROJECTION);
glLoadIdentity();//reset current matrix after changing matrix mode
gluOrtho2D(-10, 10, -10, 10);//specify 2d projection, set opengl's coordinate system
glMatrixMode(GL_MODELVIEW);//change back to model view
}
//this like makes a loop
void timer(int a){
glutPostRedisplay();//opengl will call the display function the next time it gets the chance
glutTimerFunc(1000/60, timer, 0);
//update positions and stuff
//this can be done here or in the display function
switch(state){
case 1:
if(xPos < 8)
xPos += 0.15;
else
state = -1;
break;
case -1:
if(xPos > -10)
xPos -= 0.15;
else
state = 1;
break;
}
}
I tried following example solutions, such as push/pop matrix/attrib, but couldnt get anything to work
There is no first or second scissor box. There is just the scissor box. You can change the scissor box and that change will affect subsequent rendering. But at any one time, there is only one.
What you want is to use the stencil buffer to discard fragments outside of an area defined by rendering certain values into the stencil buffer.
Related
I have an interesting bug that has been "bugging" me for a few days now.
I am currently using OpenGL to draw text on a screen. I am utilizing the OGLFT library to assist the drawing. This library actually uses the freetype2 library. I am actually not doing anything special with the text. I am only looking for monochromatic text.
Anyways, after implementing the library, I noticed that the text is only drawn correct when I have glStipple enabled. I believe that there is some interference issue between the OGLFT library and what I am enabling.
I was wondering if there is anyone out there with some experience on using the OGLFT library. I am posting a minimalist example of my code to demonstrate what is going on:
(Please note that there are some variables that are used to st the zoom factor of my glCanvas and the position of the camera and that this is only for 2D)
double _zoomX = 1.0;
double _zoomY = 1.0;
double _cameraX = 0;
double _cameraY = 0;
/* This function gets called everytime a draw routine is needed */
void modelDefinition::onPaintCanvas(wxPaintEvent &event)
{
wxGLCanvas::SetCurrent(*_geometryContext);// This will make sure the the openGL commands are routed to the wxGLCanvas object
wxPaintDC dc(this);// This is required for drawing
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT);
updateProjection();
OGLFT::Monochrome *testface = new OGLFT::Monochrome( "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 8);
testface->draw(0, 0, "test");
glEnable(GL_LINE_STIPPLE);// WHen I comment out this line, the text is unable to be drawn
glLineStipple(1, 0b0001100011000110);
glBegin(GL_LINES);
glVertex2d(_startPoint.x, _startPoint.y);
glVertex2d(_endPoint.x, _endPoint.y);
glEnd();
glDisable(GL_LINE_STIPPLE);
SwapBuffers();
}
void modelDefinition::updateProjection()
{
// First, load the projection matrix and reset the view to a default view
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-_zoomX, _zoomX, -_zoomY, _zoomY, -1.0, 1.0);
//Reset to modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, (double)this->GetSize().GetWidth(), (double)this->GetSize().GetHeight());
/* This section will handle the translation (panning) and scaled (zooming).
* Needs to be called each time a draw occurs in order to update the placement of all the components */
if(_zoomX < 1e-9 || _zoomY < 1e-9)
{
_zoomX = 1e-9;
_zoomY = _zoomX;
}
if(_zoomX > 1e6 || _zoomY > 1e6)
{
_zoomX = 1e6;
_zoomY = _zoomX;
}
glTranslated(-_cameraX, -_cameraY, 0.0);
}
Also one thing to note is that the code below the glEnable(GL_LINE_STIPPLE); is required. It is as if the glStipple needs to be drawn correctly for the text to be displayed correctly.
Looking through your code, I believe that your intention is to render it as a greyscale? If so, then you can simply use the OGLFT::Grayscale *testface = new OGLFT::Grayscale( "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 8);
This will get what you need without having to worry about the issue that you posted. In fact, I recommend doing it this way too.
I've been working around to make a little light shader.
It works perfectly, I mean, the light fades as it's supposed to, it's a circle around my character moving with it.
It could be perfect only if that resizing event wasn't existing.
When SFML resizes the window, it enlarges everything, but in a strange way. It enlarged everything but shaders.
I tried to resize my window (I love resizing pixel graph games, I find it most beautiful. So I don't want to prevent the resizing event).
Here's my shader :
uniform vec3 light;
void main(void) {
float distance = sqrt(pow(gl_FragCoord.x - light.x, 2) + pow(gl_FragCoord.y - light.y, 2));
float alpha = 1.;
if (distance <= light.z) {
alpha = (1.0 / light.z) * distance;
}
gl_FragColor = vec4(0., 0., 0., alpha);
}
So, the problem is, my window is showed at 1280 x 736 (to fit with 32x32 textures), and I have a 1920 x 1080 monitor. When I enlarge the window to fit in 1920 x 1080 (title bar included), the whole thing resizes correctly, everything's fine, but the shader is now 1920x1080 (minus the title bar). So the shader needs different coordinates (what's supposed to be in x = 32, y = 0 is, for the shader, in x = 48 y = 0).
So I was wondering, is it possible to enlarge the shader with the whole window ? Should I use events or something like that ?
Thanks for your answers ^^
EDIT : Here's some pics :
So this is the light shader before it resizes (it's dark everywhere but on the player, like it's supposed to be).
Then I resize the window, the player doesn't move, the textures fit the entire window, but the light moved.
So, to explain correctly, when I resize the window, I want everything to fit the window, so it's full of textures, but when I do that, the coordinates given to my shader are the ones before resizing, and if I move it moves as if I didn't resize the window, so the light is never on my player again.
I'm not sure it's clearer, but I tried my best.
EDIT2 : Here's my code which calls the shader :
void Graphics::UpdateLight() {
short radius = 65; // 265 on the pictures
int x = m_game->GetPlayer()->GetSprite()->getPosition().x + CASE_LEN / 2; // Setting on the middle of the player sprite (CASE_LEN is a const which contains the size of a case (here 32))
int y = HEIGHT - (m_game->GetPlayer()->GetSprite()->getPosition().y + CASE_LEN / 2); // (the "HEIGHT -" part was set because it seems that y = 0 is on the bottom of the texture for GLSL)
sf::Vector3f shaderLight;
shaderLight.x = x;
shaderLight.y = y;
shaderLight.z = radius;
m_lightShader.setParameter("light", shaderLight);
}
The code snippet you're showing really only updates the shader coordinates (and from a quick glimpse it looks fine). The bug most likely happens somewhere where you're actually drawing things.
I'd use a completely different approach, because your shader approach might get rather tedious once you're rendering multiple things, other light sources, etc.
As such I'd suggest you render a light map to a render texture (which would essentially be like "black = no light, color = light of that color").
Rather than trying to explain everything in text, I've written a quick commented example program which will draw a window on screen and move some light sources over a background image (I've used the one that comes with SFML's shader example):
There are no requirements other than having a file called "background.jpg" in your startup path.
Feel free to copy this code or use it for inspiration. Just keep in mind this isn't optimized and really just a quick edit to show the general idea.
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>
const float PI = 3.1415f;
struct Light
{
sf::Vector2f position;
sf::Color color;
float radius;
};
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Lights");
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(60);
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
// Setup everything for the lightmap
sf::RenderTexture lightmapTex;
// We're using a 512x512 render texture for max. compatibility
// On modern hardware it could match the window resolution of course
lightmapTex.create(512, 512);
sf::Sprite lightmap(lightmapTex.getTexture());
// Scale the sprite to fill the window
lightmap.setScale(640 / 512.f, 480 / 512.f);
// Set the lightmap's view to the same as the window
lightmapTex.setView(window.getDefaultView());
// Drawable helper to draw lights
// We'll just have to adjust the first vertex's color to tint it
sf::VertexArray light(sf::PrimitiveType::TriangleFan);
light.append({sf::Vector2f(0, 0), sf::Color::White});
// This is inaccurate, but for demo purposes…
// This could be more elaborate to allow better graduation etc.
for (float i = 0; i <= 2 * PI; i += PI * .125f)
light.append({sf::Vector2f(std::sin(i), std::cos(i)), sf::Color::Transparent});
// Setup some lights
std::vector<Light> lights;
lights.push_back({sf::Vector2f(50.f, 50.f), sf::Color::White, 100.f });
lights.push_back({sf::Vector2f(350.f, 150.f), sf::Color::Red, 150.f });
lights.push_back({sf::Vector2f(150.f, 250.f), sf::Color::Yellow, 200.f });
lights.push_back({sf::Vector2f(250.f, 450.f), sf::Color::Cyan, 100.f });
// RenderStates helper to transform and draw lights
sf::RenderStates rs(sf::BlendAdd);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
bool flip = false; // simple toggle to animate differently
// Draw the light map
lightmapTex.clear(sf::Color::Black);
for(Light &l : lights)
{
// Apply all light attributes and render it
// Reset the transformation
rs.transform = sf::Transform::Identity;
// Move the light
rs.transform.translate(l.position);
// And scale it (this could be animated to create flicker)
rs.transform.scale(l.radius, l.radius);
// Adjust the light color (first vertex)
light[0].color = l.color;
// Draw the light
lightmapTex.draw(light, rs);
// To make things a bit more interesting
// We're moving the lights
l.position.x += flip ? 2 : -2;
flip = !flip;
if (l.position.x > 640)
l.position.x -= 640;
else if (l.position.x < 0)
l.position.x += 640;
}
lightmapTex.display();
window.clear(sf::Color::White);
// Draw the background / game
window.draw(background);
// Draw the lightmap
window.draw(lightmap, sf::BlendMultiply);
window.display();
}
}
I've been wanting to experiment with platforming physics using freeglut, but before I would allow myself to start, I had an old problem to take care of.
You see, I want to write a reshape handler that not only maintains the scale and eliminates any distortion of the view, but also allows all of the onscreen shapes to maintain their size even while the window is too small to contain them (i.e. let them be clipped).
I've almost got all three parts solved, but when I scale my window, the circle I have drawn onto it scales just slightly. Otherwise, I got the clipping, and I have eliminated the distortion. Update: What I want to achieve is a program that maintains scale and aspect ratio independent of window size.
Here's my code:
void reshape(int nwidth,int nheight)
{
glViewport(0,0,nwidth,nheight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//here begins the code
double bound = 1.5;
double aspect = double(nwidth)/nheight;
//so far, I get the best results by normalizing the dimensions
double norm = sqrt(bound*bound+aspect*aspect);
double invnorm = sqrt(bound*bound+(1/aspect)*(1/aspect));
if(nwidth <= nheight)
glOrtho(-bound/invnorm,bound/invnorm,-bound/aspect/invnorm,bound/aspect/invnorm,-1,1);
else
glOrtho(-bound*aspect/norm,bound*aspect/norm,-bound/norm,bound/norm,-1,1);
//without setting the modelview matrix to the identity form,
//the circle becomes an oval, and does not clip when nheight > nwidth
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Update: As per Mr. Coleman's suggestion, I've tried switching out single precision for double. The scaling issue has improved along the vertical axis, but whenever I drag the horizontal axis in either direction, the shape still scales by a noticeable amount. It's still the same shape throughout, but a visual inspection tells me that the shape is not the same size when the window is 150x300 as it is when the window is 600x800, regardless of which glOrtho is being executed.
I've got it. Here's how I changed my code:
//at the top of the source file, in global scope:
int init_width;//the initial width
int init_height;//the initial height
void reshape(int new_width, int new_height)
{
//moved the glViewport call further down (it was part of an earlier idea that didn't work out)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();//these two lines are unchanged
double bound = 1.0; //I reduced the edge distance to make the shape larger in the window
double scaleX = double(new_width)/init_width;
double scaleY = double(new_height)/init_height;
glOrtho( -bound*scaleX/2, bound*scaleX/2, //these are halved in order to un-squash the shape
-bound*scaleY, bound*scaleY, -1,1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0,0,new_width,new_height);
}
That is what my code looks like now. It maintains the scale and shape of what I have on screen, and allows it to go offscreen when the window is too small to contain the entire shape.
I am working on a small animation of a sinewave being generated by a moving circle similar to this gif.
However I'm having a weird issue with using glutWireTorus for the circle. Even when I comment out all the other drawing code and leave the torus moving along the z axis alone, I get flashing lines on the inside of it:
This is the display function where everything is drawn. I use the display function as my idle function, also, I checked if that was the issue by creating an idle function and putting my update code there but it still persisted.
void display(void)
{
glClearColor(0.f,0.f,0.f,0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the color buffer
and the depth buffer
glLoadIdentity();
glTranslatef(0.0f,0.0f,-5.0f); //Push everything back 5 units into the scene,
otherwise we won't see the primitive
camera();
//yPrev = myCircle.y;
glPushMatrix();
glTranslatef(0.0f,0.0f,t);
glutWireTorus(0.02,1,100,100);
glPopMatrix();
DrawAxes();
glutSwapBuffers(); //swap the buffers
glFlush();//Flush the OpenGL buffers to the window
t-=0.002;
if (t < -T)
{
t = 0;
}
}
Uh, interesting, I run into the same problem.
Here is the picture using parameter
glutWireTorus(/*innerRadius*/0.3, /*outerRadius*/0.5, /*nsides*/32, /*rings*/32);
another picture using parameter
glutWireTorus(/*innerRadius*/0.3, /*outerRadius*/0.5, /*nsides*/64, /*rings*/128);
We don't know why without glutWireTorus function implementation, but we can implements our own torus, or maybe someone can explain it?
I am having a very tough time sorting out this strange clipping bug in my app.
Basically, for some reason, OpenGL is clipping (using the scissor test) my call to glClear(), but not the rendering I do afterwards.
The real problem, however, is that the problem goes away when I resize my window. I can guarantee that resizing the window doesn't change anything in my app or run any code. It is very stange. Worse still, simply putting
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
where I need to disable the scissor test, instead of having just one call to glDisable() solves the problem. So does removing the code all together (the scissor test is already disabled in this test case, but the code is there for when it wasn't left to disabled in previous code). It even solves the problem to put:
glEnable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
There are only two explanations I can think of. Either I am somehow calling UB (which I doubt, because opengl doesn't have UB AFAIK), or there is an implementation bug, because calling glDisable() twice with the same parameter consecutively SHOULD be the same as calling it once... if I'm not mistaken.
JUST incase it is of interest, here is the function for which the problem is happening:
void gle::Renderer::setup3DCamera(gle::CameraNode& cam, gle::Colour bkcol,
int clrmask, int skymode, gle::Texture* skytex, bool uselight) {
// Viewport
Rectangle wr(cam.getViewport()?*cam.getViewport():Rectangle(0,0,1,1));
if (cam.isRatioViewport()||(!cam.getViewport())) {
if (i_frameBind==NULL)
wr.scale(selectedWindow->getWidth(),selectedWindow->getHeight());
else wr.scale(i_frameBind->getWidth(),i_frameBind->getHeight());
}
gle::Rectangle_t<int> iport; iport.set(wr);
int winHei;
if (i_frameBind==NULL)
winHei = selectedWindow->getHeight();
else
winHei = i_frameBind->getHeight();
glViewport(iport.x1(),winHei-iport.y2(),iport.wid(),iport.hei());
// Viewport Clipping
if (cam.isClipping()) {
/* This is never executed in the test case */
glEnable(GL_SCISSOR_TEST);
glScissor(iport.x1(),winHei-iport.y2(),iport.wid(),iport.hei());
} else {
/* This is where I disable the scissor test */
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
}
float w=wr.wid()/2, h=wr.hei()/2;
// Projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Projection proj = cam.getProjection();
gluPerspective(proj.fov,proj.aspect*(w/h),proj.cnear,proj.cfar);
// Camera
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float m[] = { 1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1 };
glMultMatrixf(m);
static gle::Mesh *skyBox = NULL;
// Screen Clearing
switch (clrmask&GLE_CLR_COLOUR&0x00F?skymode:GLE_SKYNONE) {
case GLE_SKYNONE:
clear(clrmask&(~GLE_CLR_COLOUR)); break;
case GLE_SKYCOLOUR:
clearColour(clrmask,bkcol); break;
case GLE_SKYBOX:
glDisable(GL_DEPTH_TEST);
if (!(clrmask&GLE_CLR_DEPTH&0x00F)) glDepthMask(0);
float m = (cam.getProjection().cnear+cam.getProjection().cfar)/2.0f;
if (skyBox==NULL) skyBox = gle::createStockMesh(GLE_MESHSKYBOX,GLE_WHITE,0,m);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
skytex->flush();
glBindTexture(GL_TEXTURE_2D,skytex->getID());
glDisable(GL_LIGHTING);
glPushMatrix();
float m3[16];
Orientation::matrixSet(m3,cam.pos().getMatrix(GLE_ROTMATRIX));
Orientation::matrixTranspose(m3);
glMultMatrixf(m3);
if (i_reflectionOn) glMultMatrixf(Orientation::matrixGet3x3(i_reflectionTransform));
renderMesh(*skyBox,NULL,1);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
if (clrmask&GLE_CLR_DEPTH) glClear(GL_DEPTH_BUFFER_BIT);
else glDepthMask(1);
glAble(GL_DEPTH_TEST,depthmode!=GLE_ALWAYS);
break;
}
// Camera
glMultMatrixf(cam.getAbsInverseMatrix());
if (i_reflectionOn) glMultMatrixf(i_reflectionTransform);
// Lighting
i_lightOn = uselight;
glAble(GL_LIGHTING,i_lightOn);
}
This looks like a driver bug to me. However, there are two cases where this may actually be a bug in your code.
First, you might be in the middle of the glBegin() / glEnd() block when calling that glDisable(), causing some error and also ending the block, effectively making the second call to glDisable() legit and effective. Note that this is a dumb example with glBegin() / glEnd(), it could be pretty much any case of OpenGL error being caught. Insert glGetError() calls throughout your code to be sure. My guess is the first call to glDisable() generates GL_INVALID_OPERATION.
Second, you are not scissor testing, but you are still calling glViewport() with the same values. This would have the opposite effect (not clip glClear() and clip drawing) on NVIDIA, but it might very well be it does the opposite on some other driver / GL implementation.