2D semi-diagonal pixel offsetting - opengl

I'm writing a fully 2d game (top-down using a 2/1 isometric viewpoint using OpenGL with an orthographic projection) in which entities often move semi-diagonally (e.g. northeast: 2 pixels to the right and 1 pixel up).
The problem with is that if you're not using fixed increments of exactly 2 and 1 (for the northeast example) then you end up with judder. For example, if you're increment is 1.0 and 0.5 then you're really incrementing x each frame but y only every other frame - and there are even worse scenarios than this.
Is this simply an inherent limitation of 2d graphics or am I missing an obvious workaround (without moving away from orthographic)?

Related

Why is the OpenGL projection matrix needlessly complicated?

The following image shows the main values used in calculating the perspective projection matrix in OpenGL. They are labelled "HALFFOV", "RIGHT", "LEFT", "NEAR" AND "NEAR x 2":
Now, as you'll see in the following picture, to figure out the x value after projection supposedly it does 2 x NEAR divided by RIGHT - LEFT. The fact is that 2 x NEAR divided by RIGHT - LEFT is the same as simply doing NEAR / RIGHT. In both cases you're simply doubling, doubling the NEAR, and doubling the RIGHT, so the fraction is the same.
Also, in the 3rd column there are operations where there should be zeroes, for example: RIGHT + LEFT divided by RIGHT - LEFT always ends up being 0 / RIGHT - LEFT, which is always zero.
When the GLM math library makes a perspective projection matrix for me those two that always end up zero are always zero.
Why is it that the matrix is written like this? Are there certain cases for which my assumptions are wrong?
Why is it that the matrix is written like this?
Because a symmetrical, view centered projection is just one of many possibilities. Sometimes you want to skew and/or shift the planes for certain effects or rendering techniques.
Are there certain cases for which my assumptions are wrong?
For example plane parallel shifting the view frustum is required for tiled rendering (not to be confused with a tiled rasterizer) where the image to be rendered is split up into a grid of tiles, each one rendered individually and then merged later. This is needed if the desired output images resolution exceeds the maximum viewport/renderbuffer size limits of the used OpenGL implementation.
Other cases are if you want to simulate tilt-shift photography.
And last but not least a shifted projection matrix is required for stereoscopic rendering targeting a fixed position screen display device, that's viewed using 3D glasses.
(Rendering for headmounted displays requires a slightly different projection setup).

Method to fix the video-projector deformation with GLSL/HLSL full-screen shader

I am working in VR field where good calibration of a projected screen is very important, and because of difficult-to-adjust ceiling mounts and other hardware specificities, I am looking for a fullscreen shader method to “correct” the shape of the screen.
Most of 2D or 3D engines allows to apply a full-screen effect or deformation by redrawing the rendering result on a quad that you can deform or render in a custom way.
The first idea was to use a vertex shader to offset the corners of this screen quad, so the image is deformed as a quadrilateral (like the hardware keystone on a projector), but it won’t be enough for the requirements
(this approach is described on math.stackexchange with a live fiddle demo).
In my target case:
The image deformation must be non-linear most of the time, so 9 or 16 control points are needed to get a finer adjust.
The borders of the image are not straight (barrel or pillow effect), so even with few control points, the image must be distorted in a curved way in between. Otherwise the deformation would make visible linear seams between at each control points’ limits.
Ideally, knowing the corrected position of each control points of 3x3 or 4x4 grid, the way would be to define a continuous transform for the texture coordinates of the image being drawn on the full screen
quad:
u,v => corrected_u, corrected_v
You can find an illustration here.
I’ve saw some FFD algorithm that works in 2D or 3D that would allow to deform “softly” an image or mesh as if it was made of rubber, but the implementation seems heavy for a real-time shader.
I thought also of a weight-based deformation as we have in squeletal/soft-bodies animation, but seems uncertain to weight properly the control points.
Do you know a method, algorithm or general approach that could help me solve the problem ?
I saw some mesh-based deformation like the new Oculus Rift DK2 requires for its own deformations, but most of the 2D/3D engine use a single quad made of 4 vertices only in standard.
If you need non linear deformation Bezier Surfaces are pretty handy and easy to implement.
You can either pre build them in CPU, or use hardware tessellation (example provided here)
Continuing my research, I found a way.
I created a 1D RGB texture corresponding to a "ramp" or cosine values. This will be the 3 influence coefficients of offset parameters on a 0..1 axis, with 3 coefficients at 0, 0.5 and 1 :
Red starts at 1 at x=0 and goes down to 0 at x=.5
Green start at 0 at x=0, goes to 1 at x=0.5 and goes back to 0 at x=1
Blue starts at 0 at x=0.1 and goes up to 1 at x=1
With these, from 9 float2 uniforms I can interpolate very softly my parameters over the image (with 3 lookups on horizontal, and a final one for vertical).
Then, one interpolated, I offsets the texture coord with these and it works :-D
This is more or less a weighted interpolation of the coordinates using texture lookups for speedup.

Besides how texels are fetched, what differences are there between 2D and 3D textures?

I'm in the process of porting an OpenGL app to the web, and discovered that WebGL 1.0 does not support 3D textures (will support in 2.0).
I use 16 x 16 x 16 textures for the colour information of some simple models (for a blocky kind of look).
Now without support for the 3D models, I realized I could instead spread the 16 layers on to a 4 x 4 plane, like so:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 = layer
The result being a 64 x 64 "pseudo-3D" texture.
Which leads me to my question: what's the difference between 3D and 2D textures on GPUs? And in a situation where a 2D texture can be used instead of a 3D texture (as in my example), would there be a performance difference between the two?
I'm guessing 3D textures have hardware support for interpolation along the z-axis, whereas with a 2D texture two texel fetches would be required, and a manual interpolation between them, for the same result.
You are correct, the biggest difference that comes to my mind immediately has to do with texture filtering 3D vs. 2D. Linear filtering has to sample 4 texels (assuming a 2D image) if the texture coordinate is not precisely at the center of a texel and then do weighted averaging based on the distance the sample location is from each of the texels fetched. This is such a fundamental operation that the hardware is built specially for this (newer hardware even lets you take advantage of this in the form of instructions like gather4).
You can implement linear filtering yourself (and in fact, you sometimes have to for things like multisampled textures) if you use a nearest-neighbor filter and compute the weighting factor, etc. yourself but it will always come out slower than the hardware implementation.
In your case, if you wanted to implement linear filtering on a 3D texture you would actually have to sample 8 texels (23 to handle interpolation across all 3 dimensions). Obviously hardware is biased toward working with 2D textures, as there is no gather8 instruction... but you can bet that native linear interpolation will be quicker than your manual hack.
WebGL does not even expose gather4/textureGather (as that is a DX10.1 / GL4 feature), but hardware worked this way long before that was an instruction you could use in a shader.
You might be able to come up with a compromise if you are clever, where you use the hardware's linear filtering capabilities for filtering in the S and T directions of each 2D slice and then perform your own linear filtering between the slices (R). You will have to be careful when dealing with texels at the edge of your image, however. Make sure there is at least 1 texel worth of border between the slice images in your virtual 3D texture so the hardware does not interpolate across slices.

OpenGL 2D transformations without keeping aspect

I need to have a 2D layer in my OpenGL application.I have implemented it first using a typical ortho projection like this:
Mat4 ortho =Glm.ortho(0,viewWidth , 0 ,viewHeight);
The 2d worked fine except the fact that when running in different screen sizes the 2d shapes are scaled relatively to a new aspect.That is not what I want (opposite to what usually people need). I need the 2d shapes to get stretched or squeezed according to the new screen size.
I tried not to use the ortho matrix but just an identity.This one works but in such a case I have to use numbers in range 0 -1 to manipulate the objects in the visible frustum area.And I need to use numbers in regular (not normalized ) ranges.So it is sort of forcing me to get back to ortho projection which is problematic because of what already said.
So the question is how do I transform 2d object without perspective staying in the world coordinates system.
UPDATE:
The best example is 2D layers in Adobe AfterEffects. If one changes composition dimension ,2d layers don't get scaled according to new dimensions.That is what I am after.
It's tricky to know how to answer this, because to some degree your requirements are mutually exclusive. You don't want normalised coordinates, you want to use screen coordinates. But by definition, screen coordinates are defined in pixels, and pixels are usually square... So I think you need some form of normalised coordinates, albeit maybe uniformly scaled.
Perhaps what you want is to fix the ratio for width and height in your ortho. That would allow you to address the screen in some kind of pseudo-pixel unit, where one axis is "real" pixels, but the other can be stretched. So instead of height, pass 3/4 of the width for a 4:3 display, or 9/16ths on a 16:9, etc. This will be in units of pixels if the display is the "right" dimension, but will stretch in one dimension only if it's not.
You may need to switch which dimension is "real" pixels depending on the ratio being less or greater than your "optimal" ratio, but it's tricky to know what you're really shooting for here.

OpenGL - gluPerspective / glFrustum - zNear & zFar problems

I'm writing a space exploration application. I've decided on light years being the units and have accurately modeled the distances between stars. After tinkering and a lot of arduous work (mostly learning the ropes) I have got the camera working correctly from the point of view of a starship traversing through the cosmos.
Initially I paid no attention to the zNear parameter of gluPerspective () until I worked on planetary objects. Since my scale is in light year units I soon realized that due to zNear being 1.0f I would not be able to see such objects. After experimentation I arrived at these figures:
#define POV 45
#define zNear 0.0000001f
#define zFar 100000000.0f
gluPerspective (POV, WinWidth/WinHeight, zNear ,zFar);
This works exceptionally well in that I was able to cruise my solar system (position 0,0,0) and move up close to the planets which look great lit and texture mapped. However other systems (not at position 0,0,0) were much harder to cruise through because the objects moved away from the camera in unusual ways.
I had noticed however that strange visual glitches started to take place when cruising through the universe. Objects behind me would 'wrap around' and show ahead, if I swing 180 degrees in the Y direction they'll also appear in their original place. So when warping through space, most the stars are correctly parallaxing but some appear and travel in the opposite direction (which is disturbing to say the least).
By changing the zNear to 0.1f immediately corrects ALL of these glitches (but also won't resolve solar system objects). So I'm stuck. I've also tried working with glFrustum and it produces exactly the same results.
I use the following to view the world:
glTranslatef(pos_x, pos_y, pos_z);
With relevant camera code to orientate as required. Even disabling camera functionality does not change anything. I've even tried gluLookAt() and again it produces the same results.
Does gluPerspective() have limits when extreme zNear / zFar values are used? I tried to reduce the range but to no avail. I even changed my world units from light years to kilometers by scaling everything up and using a bigger zNear value - nothing. HELP!
The problem is that you want to resolve too much at the same time. You want to view things on the scale of the solar system, while also having semi-galactic scale. That is simply not possible. Not with a real-time renderer.
There is only so much floating-point precision to go around. And with your zNear being incredibly close, you've basically destroyed your depth buffer for anything that is more than about 0.0001 away from your camera.
What you need to do is to draw things based on distance. Near objects (within a solar system's scale) are drawn with one perspective matrix, using one depth range (say, 0 to 0.8). Then more distant objects are drawn with a different perspective matrix and a different depth range (0.8 to 1). That's really the only ways you're going to make this work.
Also, you may need to compute the matrices for objects on the CPU in double-precision math, then translate them back to single-precision for OpenGL to use.
OpenGL should not be drawing anything farther from the camera than zFar, or closer to the camera than zNear.
But for things in between, OpenGL computes a depth value that is stored in the depth buffer which it uses to tell whether one object is blocking another. Unfortunately, the depth buffer has limited precision (generally 16 or 24 bits) and according to this, roughly log2(zFar/zNear) bits of precision are lost. Thus, a zFar/zNear ratio of 10^15 (~50 bits lost) is bound to cause problems. One option would be to slightly increase zNear (if you can). Otherwise, you will need to look into Split Depth Buffers or Logarithmic Depth Buffers
Nicol Bolas already told you one piece of the story. The other is, that you should start thinking about a structured way to store the coordinates: Store the position of each object in relation to the object that dominates it gravitatively and use apropriate units for those.
So you have stars. Distances between stars are measured in lightyears. Stars are orbited by planets. Distances within a starsystem are measured in lightminutes to lighthours. Planets are orbited by moons. Distances in a planetary system are measured in lightseconds.
To display such scales you need to render in multiple passes. The objects with their scales form a tree. First you sort the branches distant to close, then you traverse the tree depth first. For each branching level you use apropriate projection parameters so that the near→far clip planes snuggily fit the to be rendered objects. After rendering each level clear the depth buffer.