I have a viewport that is half of the screen size/framebuffer.
x, y, w, h = 0, 0, 512, 512
And my scissor region is the full framebuffer
x, y, w, h = 0, 0, 0 1024, 512
I am drawing a line, that is far outside of the viewport, from left to right. I am expecting the line to be drawn only inside the viewport. I have tested this on three different graphic cards, and on two of them, I get the result I am I expecting. However on the third one, the line is drawn outside the viewport, but inside the scissor region.
Which one of the results is correct here? As far as I understand it, the lines' two vertices should be moved to outer viewport positions. It should not be drawn outside of it.
If you read pitfall nr 10 on this site:
https://www.opengl.org/archives/resources/features/KilgardTechniques/oglpitfall/
They are talking about drawing outside the viewport, but that is just some special cases, for example where you have a really thick line, my line width is 1
EDIT: After a discussion in the KhronosGroup:
From the spec Vulkan 1.0.68:
If either of a line segment’s vertices lie outside of the clip volume,
the line segment may be clipped, with new vertex coordinates computed
for each vertex that lies outside the clip volume. A clipped line
segment endpoint lies on both the original line segment and the
boundary of the clip volume.
Nvidia:
We intentionally made this change for maintenance2, to allow for
pop-free points and lines. Point clipping behavior is queriable but
line clipping behavior is not, though I believe the "preferred"
behavior for lines is to be pop-free. We changed the NVIDIA driver
last year from tight clipping to pop-free, and even changed CTS tests
to allow this new behavior. So this is all working as designed.
Old answer:
The viewport defines a transformation from normalized device coordinates to window coordinates. The viewport transformation does not do any clipping.
However, the clipping does happen before NDC space, the view frustum clipping does guarantee that no vertex can fall outside the viewport. And if you are using orthogonal projection, clip space and NDC space are the same. So everything outside [-1,1] will be clipped. And if you are not doing any of the special cases the opengl link talks about the vertices should be clipped.
If two of your graphics cards draws inside the viewport and one outside, it's probably a driver bug. If you are using Vulkan, which is fairly new, that is most likely the case.
Edit: My answer below is technically correct but not useful. The viewport transform itself doesn't define a clip volume or scissor. But because of clipping to the view frustum ([-w,+w] for x and y, [0,+w] for z), all the (x,y) values entering the viewport transform will be in [-1,1], and will not be transformed to fall outside of the rectangle that defined the viewport.
Like in GL, the viewport only defines a transform from normalized device coordinates to framebuffer coordinates. The transform is based on the width/height/depth/origin of the viewport volume, but that volume doesn't define a clipping volume. The math is described in section 23.5 Controlling the Viewport.
In Vulkan you can also specify a scissor rectangle in framebuffer coordinates for each viewport, in VkPipelineViewportStateCreateInfo:: pScissors or vkCmdSetScissor. Pixels outside this rectangle are dropped. But you have to set the scissor rectangle yourself, and it doesn't have to match the viewport volume.
Related
Is there a way to set a transformation for NDC to window, but separately specify the clipping region so it matches the actual window size?
Background: I have a bunch of openGL code that renders a 2D map to a window. It's a lot of complex code, because I use both the GPU and the CPU to draw on the map, so it's important that I keep to a consistent coordinate system in both places. To keep that simple, I use glViewport(0,0,mapSizeX, mapSizeY), and now map coordinates correspond well to pixel coordinates in the frame buffer, exactly what I need. I can use GLSL to draw some of the map, call glReadPixels and use the CPU to draw on top of that, and glDrawPixels to send that back to the frame buffer, all of that using the same coordinate system. Finally I use GLSL to draw a few final things over that (that I don't want zoomed). That all works, except...
The window isn't the same size as the map, and glViewport doesn't just set up the transformation. It also sets up clipping. So now when I go to draw a few last items, and the window is larger than the map, things I draw near the top of the screen get clipped away. Is there a workaround?
glViewport doesn't just set up the transformation. It also sets up clipping.
No, it just sets up the transformation. By the time the NDC-to-window space transform happens, clipping has already been done. That happened immediately after vertex processing; your vertex shader (or whatever you're doing to transform vertices) handled that based on how it transformed vertices into clip-space.
You should use the viewport to set up how you want the NDC box to visibly appear in the window. Your VS needs to handle the transformation into the clipping area. So it effectively decides how much of the world gets put into the NDC box that things get clipped to.
Basically, you have map space (the coordinates used by your map) and clip-space (the coordinates after vertex transformations). And you have some notion of which part of the map you want to actually draw to the window. You need to transform the region of your map that you want to see such that the corners of this region appear in the corners of the clipping box (for orthographic projections, this is typically [-1, 1]).
In compatibility OpenGL, this might be defined by using glOrtho for othographic projections to transform from you. In a proper vertex shader, you'll need to provide an appropriate orthographic matrix.
I have four arbitrary points (lt,rt,rb,lb) in 3d space and I would like these points to define my near clipping plane (lt stands for left-top, rt for right-top and so on).
Unfortunately, these points are not necessarily a rectangle (in screen space). They are however a rectangle in world coordinates.
The context is that I want to have a mirror surface by computing the mirrored world into a texture. The mirror is an arbitary translated and rotated rectangle in 3d space.
I do not want to change the texture coordinates on the vertices, because that would lead to ugly pixelisation when you e.g. look at the mirror from the side. When I would do that, also culling would not work correctly which would lead to huge performance impacts in my case (small mirror, huge world).
I also cannot work with the stencil buffer, because in some scenarios I have mirrors facing each other which would also lead to a huge performance drop. Furthermore, I would like to keep my rendering pipeline simple.
Can anyone tell me how to compute the according projection matrix?
Edit: Of cause I already have moved my camera accordingly. That is not the problem here.
Instead of tweaking the projection matrix (which I don't think can be done in the general case), you should define an additional clipping plane. You do that by enabling:
glEnable(GL_CLIP_DISTANCE0);
And then set gl_ClipDistance vertex shader output to be the distance of the vertex from the mirror:
gl_ClipDistance[0] = dot(vec4(vertex_position, 1.0), mirror_plane);
I heard clipping should be done in clipping coordinate system.
The book suggests a situation that a line is laid from behind camera to in viewing volume. (We define this line as PQ, P is behind camera point)
I cannot understand why it can be a problem.
(The book says after finishing normalizing transformation, the P will be laid in front of camera.)
I think before making clipping coordinate system, the camera is on original point (0, 0, 0, 1) because we did viewing transformation.
However, in NDCS, I cannot think about camera's location.
And I have second question.
In vertex shader, we do model-view transformation and then projection transformation. Finally, we output these vertices to rasterizer.
(some vertices's w is not equal to '1')
Here, I have curiosity. The rendering pipeline will automatically do division procedure (by w)? after finishing clipping.
Sometimes not all the model can be seen on screen, mostly because some objects of it lie behind the camera (or "point of view"). Those objects are clipped out. If just a part of the object can't be seen, then just that part must be clipped leaving the rest as seen.
OpenGL clips
OpenGL does this clipping in Clipping Coordinate Space (CCS). This is a cube of size 2w x 2w x 2w where 'w' is the fourth coordinate resulting of (4x4) x (4x1) matrix and point multiplication. A mere comparison of coordinates is enough to tell if the point is clipped or not. If the point passes the test then its coordinates are divided by 'w' (so called "perspective division"). Notice that for ortogonal projections 'w' is always 1, and with perspective it's generally not 1.
CPU clips
If the model is too big perhaps you want to save GPU resources or improve the frame rate. So you decide to skip those objects that are going to get clipped anyhow. Then you do the maths on your own (on CPU) and only send to the GPU the vertices that passed the test. Be aware that some objects may have some vertices clipped while other vertices of this same object may not.
Perhaps you do send them to GPU and let it handle these special cases.
You have a volume defined where only objects inside are seen. This volume is defined by six planes. Let's put ourselves in the camera and look at this volume: If your projection is perspective the six planes build a "fustrum", a sort of truncated pyramid. If your projection is orthogonal, the planes form a parallelepiped.
In order to clip or not to clip a vertex you must use the distance form the vertex to each of these six planes. You need a signed distance, this means that the sign tells you what side of the plane is seen form the vertex. If any of the six distance signs is not the right one, the vertex is discarded, clipped.
If a plane is defined by equation Ax+By+Cz+D=0 then the signed distance from p1,p2,p3 is (Ap1+Bp2+Cp3+D)/sqrt(AA+BB+C*C). You only need the sign, so don't bother to calculate the denominator.
Now you have all tools. If you know your planes on "camera view" you can calculate the six distances and clip or not the vertex. But perhaps this is an expensive operation considering that you must transform the vertex coodinates from model to camera (view) spaces, a ViewModel matrix calculation. With the same cost you use your precalculated ProjectionViewModel matrix instead and obtain CCS coordinates, which are much easier to compare to '2w', the size of the CCS cube.
Sometimes you want to skip some vertices not due to they are clipped, but because their depth breaks a criteria you are using. In this case CCS is not the right space to work with, because Z-coordinate is transformed into [-w, w] range, depth is somehow "lost". Instead, you do your clip test in "view space".
I want to create skybox, which is just textured cube around camera. But actually i don't understand how this can work, because the camera viewing volume is frustum and the skybox is cube. According to this source:
http://www.songho.ca/opengl/gl_projectionmatrix.html
Note that the frustum culling (clipping) is performed in the clip
coordinates, just before dividing by wc. The clip coordinates, xc, yc
and zc are tested by comparing with wc. If any clip coordinate is less
than -wc, or greater than wc, then the vertex will be discarded.
vertexes of skybox faces should be clipped, if they are outside of frustum.
So it looks for me that the cube should be actually a frustum and should match exactly the gl frustum faces, so my whole scene is wrapped inside of that skybox, but i am sure this is bad. Is there any way how to fill whole screen with something, that wraps whole gl frustum?
The formulation from your link is rather bad. It is not actually vertices that get clipped, but rather fragments. Drawing a primitive with vertices completely off-screen does not prevent the fragments that would intersect with the screen from getting drawn. (The picture in the link also actually shows this being the case.)
That having been said, however, it may (or may not, depending on the design of your rendering code) be easier to simply draw a full-screen quad, and use the inverse of the projection matrix to calculate the texture coordinates instead.
The function gluPerspective() can be used to set near Z and far Z clipping planes.
I want to draw a scene clipped at a certain far Z plane,
and draw another scene beyond this Z plane.
Is it possible to do this clipping twice per frame?
There's no reason you shouldn't be able to do this.
Simply setup the first perspective, draw the first scene and then setup the second perspective and draw the seconds scene, all within the drawing of the same frame.
This is generally referred to as multi-pass rendering.
You might need to do a draw the farthest scene first and do a glClear(GL_DEPTH_BUFFER_BIT); before you draw the nearest scene.
A possibility is to assign different depth ranges for the scenes. Some pseudo code would be :
glDepthRange(0.5, 1.0)
draw_far_scene
glDepthRange(0.0, 0.5)
draw_near_scene
You have to setup your projection matrices to perform the proper clipping for the near / far scenes.
The depth ranges assignment is needed to prevent the depth buffer to 'merge' both renderings.