In GLSL Shader, how to move an image 'smoothly' without 'scrubbing' - java

In particular I am using a Processing Java example that makes use of a GLSL shader (it's called InfiniteTiles). The original sketch is actually just moving a tiled image.
I have a uniform variable called time that I call in java.
tileShader.set("time", millis() / 1000.0);
Now in the fragment shader there is a code section
vec2 pos = gl_FragCoord.xy - vec2(TILES_COUNT_X * time);
vec2 p = (resolution - TILES_COUNT_X * pos) / resolution.x;
vec3 col = texture2D (tileImage, p).xyz;
What I attempted to do in the java code is set the time variable such that I might be able to increase and decrease the speed at which the image scrolls.
I wrote this
float t =millis() / 1000.0;
float pctX = map (mouseX, 0, width, 0, 1);
tileShader.set("time", t*pctX);
What happens is that when I move the mouse, the entire image moves rapidly either left or right depending on where im moving as if its like 'scrubbing' the image. When i stop moving the mouse, then it will move at the desired speed.
I would like to avoid this 'scrubbing' effect and have the image scrolling speed transition smoothly with the mouse movement.
Normally I could accomplish such a thing with just drawing an image in java and scrolling it, but I think I'm not understanding something fundamental about the way glsl works to achieve the same effect on the graphics card.
Any help appreciated.
Full processing code from example:
//-------------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//-------------------------------------------------------------
PImage tileTexture;
PShader tileShader;
void setup() {
size(640, 480, P2D);
textureWrap(REPEAT);
tileTexture = loadImage("penrose.jpg");
loadTileShader();
}
void loadTileShader() {
tileShader = loadShader("scroller.glsl");
tileShader.set("resolution", float(width), float(height));
tileShader.set("tileImage", tileTexture);
}
void draw() {
tileShader.set("time", millis() / 1000.0);
shader(tileShader);
rect(0, 0, width, height);
}
Full Shader code
//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------
uniform float time;
uniform vec2 resolution;
uniform sampler2D tileImage;
#define TILES_COUNT_X 4.0
void main() {
vec2 pos = gl_FragCoord.xy - vec2(4.0 * time);
vec2 p = (resolution - TILES_COUNT_X * pos) / resolution.x;
vec3 col = texture2D (tileImage, p).xyz;
gl_FragColor = vec4 (col, 1.0);
}

Sigh... it was a bit simpler than i thought. answer provided here by JeremyDouglass
https://forum.processing.org/two/discussion/comment/90488
solution:
"This problem isn't specific to shaders -- you would have the same problem if you were doing this with img(). You can't do clock math in this way. Multiplying anything by millis() will always create a scaling effect -- which in this case will always create what you call "scrubbing." For example, if you change the multiplier, 10 seconds suddenly becomes 15.
Instead, in order to change the speed at which the clock changes in the future but not to change how far it has advanced up-to-now, keep your own clock variable separate from millis(), and change the step amount (use addition, not multiplication) each draw frame. Now the speed at which the clock advances will change, but the base offset (the last clock time) won't jump around, because the original value isn't being scaled (multiplied)."

Related

Wrong Display of a simple Cube in Android with OpenGL ES [duplicate]

I'm working on a 2d engine. It already works quite good, but I keep getting pixel-errors.
For example, my window is 960x540 pixels, I draw a line from (0, 0) to (959, 0). I would expect that every pixel on scan-line 0 will be set to a color, but no: the right-most pixel is not drawn. Same problem when I draw vertically to pixel 539. I really need to draw to (960, 0) or (0, 540) to have it drawn.
As I was born in the pixel-era, I am convinced that this is not the correct result. When my screen was 320x200 pixels big, I could draw from 0 to 319 and from 0 to 199, and my screen would be full. Now I end up with a screen with a right/bottom pixel not drawn.
This can be due to different things:
where I expect the opengl line primitive is drawn from a pixel to a pixel inclusive, that last pixel just is actually exclusive? Is that it?
my projection matrix is incorrect?
I am under a false assumption that when I have a backbuffer of 960x540, that is actually has one pixel more?
Something else?
Can someone please help me? I have been looking into this problem for a long time now, and every time when I thought it was ok, I saw after a while that it actually wasn't.
Here is some of my code, I tried to strip it down as much as possible. When I call my line-function, every coordinate is added with 0.375, 0.375 to make it correct on both ATI and nvidia adapters.
int width = resX();
int height = resY();
for (int i = 0; i < height; i += 2)
rm->line(0, i, width - 1, i, vec4f(1, 0, 0, 1));
for (int i = 1; i < height; i += 2)
rm->line(0, i, width - 1, i, vec4f(0, 1, 0, 1));
// when I do this, one pixel to the right remains undrawn
void rendermachine::line(int x1, int y1, int x2, int y2, const vec4f &color)
{
... some code to decide what std::vector the coordinates should be pushed into
// m_z is a z-coordinate, I use z-buffering to preserve correct drawing orders
// vec2f(0, 0) is a texture-coordinate, the line is drawn without texturing
target->push_back(vertex(vec3f((float)x1 + 0.375f, (float)y1 + 0.375f, m_z), color, vec2f(0, 0)));
target->push_back(vertex(vec3f((float)x2 + 0.375f, (float)y2 + 0.375f, m_z), color, vec2f(0, 0)));
}
void rendermachine::update(...)
{
... render target object is queried for width and height, in my test it is just the back buffer so the window client resolution is returned
mat4f mP;
mP.setOrthographic(0, (float)width, (float)height, 0, 0, 8000000);
... all vertices are copied to video memory
... drawing
if (there are lines to draw)
glDrawArrays(GL_LINES, (int)offset, (int)lines.size());
...
}
// And the (very simple) shader to draw these lines
// Vertex shader
#version 120
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;
uniform mat4 mP;
varying vec4 vColor;
void main(void) {
gl_Position = mP * vec4(aVertexPosition, 1.0);
vColor = aVertexColor;
}
// Fragment shader
#version 120
#ifdef GL_ES
precision highp float;
#endif
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor.rgb;
}
In OpenGL, lines are rasterized using the "Diamond Exit" rule. This is almost the same as saying that the end coordinate is exclusive, but not quite...
This is what the OpenGL spec has to say:
http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
Also have a look at the OpenGL FAQ, http://www.opengl.org/archives/resources/faq/technical/rasterization.htm, item "14.090 How do I obtain exact pixelization of lines?". It says "The OpenGL specification allows for a wide range of line rendering hardware, so exact pixelization may not be possible at all."
Many will argue that you should not use lines in OpenGL at all. Their behaviour is based on how ancient SGI hardware worked, not on what makes sense. (And lines with widths >1 are nearly impossible to use in a way that looks good!)
Note that OpenGL coordinate space has no notion of integers, everything is a float and the "centre" of an OpenGL pixel is really at the 0.5,0.5 instead of its top-left corner. Therefore, if you want a 1px wide line from 0,0 to 10,10 inclusive, you really had to draw a line from 0.5,0.5 to 10.5,10.5.
This will be especially apparent if you turn on anti-aliasing, if you have anti-aliasing and you try to draw from 50,0 to 50,100 you may see a blurry 2px wide line because the line fell in-between two pixels.

Rendering a laser beam - how to make it face camera?

(I am using a LibGDX framework which is basically just LWJGL(Java) with OpenGL for rendering)
Hi, I'm trying to render a laser beam, so far I've got this effect,
It's just a rectangle and then the whole effect is done in fragment Shader.
However, as it is a laser beam, I want the rectangle to face a camera, so the player always sees this red transparent "line" everytime. And this is driving me crazy. I tried to do some billboarding stuff, however what I want isn't really billboarding. I just want to rotate it on Z axis so that the player always sees the whole line, that's all. No X and Y rotations.
As you can see, that's what I want. And it's not billboarding at all.
If it was billboarding, it would look like this: .
I also tried to draw cylinder and the effect based on gl_FragCoord, which was working fine, but the coords were varying(sometimes the UVs were 0 and 1, sometimes 0 and 0.7) and it was not sampling whole texture, so the effect was broken.
Thus I don't even know what to do now.
I would really appreciate any help. Thanks in advance.
Here's vertexShader code:
attribute vec3 a_position;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans; //model matrix
uniform mat4 u_view; //view matrix
uniform mat4 u_proj; // projection matrix
varying vec2 v_texCoord0;
void main() {
v_texCoord0 = a_texCoord0;
vec4 worldTrans = u_worldTrans * vec4(a_position, 1.0);
gl_Position = u_proj * u_view * worldTrans;
}
and here's fragmentShader codE:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord0;
uniform sampler2D tex; //texture I apply the red color onto. It's how I get the smooth(transparent) edges.
void main() {
vec4 texelColor = texture2D( tex, v_texCoord0 ); //sampling the texture
vec4 color = vec4(10.0,0.0,0.0,1.0); //the red color
float r = 0.15; //here I want to make the whole texture be red, so when there's less transparency, I want it to be more red, and on the edges(more transparency) less red.
if (texelColor.a > 0.5) r = 0.1;
gl_FragColor = vec4(mix(color.rgb,texelColor.rgb,texelColor.a * r),texelColor.a); //and here I just mix the two colors into one, depengind on the alpha value of texColor and the r float.
}
The texture is just a white line opaque in the middle, but transparent at the edges of the texuture. (smooth transition)
If you use DecalBatch to draw your laser, you can do it this way. It's called axial billboarding or cylindrical billboarding, as opposed to the spherical billboarding you described.
The basic idea is that you calculate the direction the sprite would be oriented for spherical billboarding, and then you do a couple of cross products to get the component of that direction that is perpendicular to the axis.
Let's assume your laser sprite is aligned to point up and down. You would do this series of calculations on every frame that the camera or laser moves.
//reusable calculation vectors
final Vector3 axis = new Vector3();
final Vector3 look = new Vector3();
final Vector3 tmp = new Vector3();
void orientLaserDecal (Decal decal, float beamWidth, Vector3 endA, Vector3 endB, Camera camera) {
axis.set(endB).sub(endA); //the axis direction
decal.setDimensions(beamWidth, axis.len());
axis.scl(0.5f);
tmp.set(endA).add(axis); //the center point of the laser
decal.setPosition(tmp);
look.set(camera.position).sub(tmp); //Laser center to camera. This is
//the look vector you'd use if doing spherical billboarding, so it needs
//to be adjusted.
tmp.set(axis).crs(look); //Axis cross look gives you the
//right vector, the direction the right edge of the sprite should be
//pointing. This is the same for spherical or cylindrical billboarding.
look.set(tmp).crs(axis); //Right cross axis gives you an adjusted
//look vector that is perpendicular to the axis, i.e. cylindrical billboarding.
decal.setRotation(look.nor(), axis); //Note that setRotation method requires
//direction vector to be normalized beforehand.
}
I didn't check to make sure the direction doesn't get flipped, because I draw it with back face culling turned off. So if you have culling on and don't see the sprite, that last cross product step might need to have its order reversed so the look vector points in the opposite direction.

Shadow map draws

After literally 3 days of finding out how to do shadowmaps without it going mental I finally reached a stage where it's acutally a visible shadow map. But now I have one last problem with some strange appearances of objects on places where it's impossible to be (At least in normal life).
The problem I have is this:
I draw 3 test objects in the scene a sphere, a weird blocky guy, and a smaller version of that blocky guy.
The sphere is basicly the closest to the light, so you won't expect the shadow of the big guy to appear on it.
The small guy is hovering in the air.
The big guy's shadow is on the correct position(He's also at position 0,0,0 in world coordinates), but has the sphere's shadow all over him on places which don't make sense.
This is the first image:
  
And the second one where I moved the big guy to a further location:
  
As you can see on the second image the shadow is also no longer on the sphere.
The order in which I draw them is Sphere -> Big guy -> Small guy
I also send the current ModelView matrix to the shader just before I draw each object
Sphere s = new Sphere();
glPushMatrix();
glTranslatef(100,150,-100);
s.draw(50, 36, 36);
glPopMatrix();
glPushMatrix();
glTranslatef(-50,0,50);
glScalef(0.1f,0.1f,0.1f);
glColor3f(1f,195f/255f,0f);
drawBot();
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,100);
glRotatef(180,0,1,0);
glScalef(0.01f,0.01f,0.01f);
glColor3f(0,114f/255f,1f);
drawBot();
glPopMatrix();
This is my vertex shader:
#version 330 core
uniform mat4 lightP, lightMV, camP, camMV, bias, curMV;
out vec4 shadowCoord;
void main()
{
gl_Position = (camP*curMV) * gl_Vertex;
gl_Position.z-=0.01;
shadowCoord = (bias*(lightP*lightMV)) * (gl_Vertex);
}
This is my fragment shader:
#version 330 core
uniform sampler2D shadowMapTexture;
in vec4 shadowCoord;
out vec4 oColor;
float shadowMapping(){
float visibility = 0.0;
float bias = 0.01;
vec3 shadowPos = shadowCoord.xyz/shadowCoord.w;
if (texture2D( shadowMapTexture, shadowPos.xy ).z < shadowPos.z-bias){
visibility = 0.5;
}
return visibility;
}
void main(){
float shade = shadowMapping();
oColor = vec4(0,0,0,shade);
}
Is there anyone who understands this riddle?

GLSL shaders, gl_ModelViewMatrix not correct?

So I was trying to make a shader that changed the color of my crystal a little bit over time, and it all went fine until i noticed that it didn't get darker the further away it went from the light source ( default opengl lights for now! ). So I tried to tone down the color values by the distance away from the light it was but that didn't work. Later on i discovered ( by setting the color to red if the x position of the vertex in world coordinates was greater than a certain value ), that the vertex.x value was around 0. Even though it should be about 87.0.
void main()
{
vec3 vertexPosition = vec3(gl_ModelViewMatrix * vertexPos);
vec3 surfaceNormal = (gl_NormalMatrix * normals).xyz;
vec3 lightDirection = normalize(gl_LightSource[0].position.xyz - vertexPosition);
float diffuseLI = max(0.0, dot(surfaceNormal, lightDirection));
vec4 texture = texture2D(textureSample, gl_TexCoord[0]);
if(vertexPosition.x > 0)gl_FragColor.rgba = vec4(1, 0, 0, 1);
/*And so on....*/
}
As far as I know gl_ModelViewMatrix * gl_Vertex should give the world coordinates of the vertex. Am I just stupid or what?
( I also tried to do the same if statement with the light position which was correct! )

OpenGL Shader - Rotating a model around its origin (2D World)

So I created a vertex shader that takes in an angle and calculates the rotation. There is a problem though that the model rotates around the world center and not its own axis/origin.
Side note: This is 2D rotation.
How do I make the model rotate through its own axis?
Here is my current vertex shader:
#version 150 core
in vec4 in_Position;
in vec4 in_Color;
in vec2 in_TextureCoord;
out vec4 pass_Color;
out vec2 pass_TextureCoord;
void main(void) {
gl_Position = in_Position;
pass_Color = in_Color;
pass_TextureCoord = in_TextureCoord;
}
Rotating CPU side:
Vector3f center = new Vector3f(phyxBody.getPosition().x,phyxBody.getPosition().y,0);
Matrix4f pos = new Matrix4f();
pos.m00 = (phyxBody.getPosition().x)-(getWidth()/30f/2f);
pos.m01 = (phyxBody.getPosition().y)+(getHeight()/30f/2f);
pos.m10 = (phyxBody.getPosition().x)-(getWidth()/30f/2f);
pos.m11 = (phyxBody.getPosition().y)-(getHeight()/30f/2f);
pos.m20 = (phyxBody.getPosition().x)+(getWidth()/30f/2f);
pos.m21 = (phyxBody.getPosition().y)-(getHeight()/30f/2f);
pos.m30 = (phyxBody.getPosition().x)+(getWidth()/30f/2f);
pos.m31 = (phyxBody.getPosition().y)+(getHeight()/30f/2f);
pos.rotate(phyxBody.getAngle(),center);
Result is a weird rotated stretch of the object.. Do you know why? Don't worry about the /30f part.
phyxBody is an instance of the class Body from the JBox2D library.
phyxBody.getAngle() is in raidians.
Matrix4f is a class from the LWJGL library.
EDIT:
Vector3f center = new Vector3f(0,0,0);
Matrix4f pos = new Matrix4f();
pos.m00 = -(getWidth()/30f/2f);
pos.m01 = +(getHeight()/30f/2f);
pos.m10 = -(getWidth()/30f/2f);
pos.m11 = -(getHeight()/30f/2f);
pos.m20 = +(getWidth()/30f/2f);
pos.m21 = -(getHeight()/30f/2f);
pos.m30 = +(getWidth()/30f/2f);
pos.m31 = +(getHeight()/30f/2f);
pos.rotate(phyxBody.getAngle(),center);
pos.m00 += phyxBody.getPosition().x;
pos.m01 += phyxBody.getPosition().y;
pos.m10 += phyxBody.getPosition().x;
pos.m11 += phyxBody.getPosition().y;
pos.m20 += phyxBody.getPosition().x;
pos.m21 += phyxBody.getPosition().y;
pos.m30 += phyxBody.getPosition().x;
pos.m31 += phyxBody.getPosition().y;
This is currently the transformation code, yet the rotation still doesn't work correctly.
My try at the rotate method: (What am I doing wrong?)
if (phyxBody.getAngle() != 0.0) {
pos.m00 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
pos.m01 *= Math.sin(Math.toDegrees(phyxBody.getAngle()));
pos.m10 *= -Math.sin(Math.toDegrees(phyxBody.getAngle()));
pos.m11 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
pos.m20 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
pos.m21 *= Math.sin(Math.toDegrees(phyxBody.getAngle()));
pos.m30 *= -Math.sin(Math.toDegrees(phyxBody.getAngle()));
pos.m31 *= Math.cos(Math.toDegrees(phyxBody.getAngle()));
}
The order is scaling * rotation * translation - see this question. I'm guessing you've already translated your coordinates outside of your shader. You'll have to rotate first, then translate. It's good to know the linear algebra behind what you're doing so you know why things work or don't work.
The typical way to do this is to pass a pre-computed ModelView matrix that has already taken care of scaling/rotation/translation. If you've already translated your vertices, you can't fix the problem in your shader without needlessly undoing it and then redoing it after. Send in your vertices untranslated and accompany them with data, like your angle, to translate them. Or you can translate and rotate both beforehand. It depends on what you want to do.
Bottom line: You must rotate before you translate.
Here is the typical way you do vertex transformations:
OpenGL side:
Calculate ModelView matrix: Scale * Rotation * Translation
Pass to shader as a uniform matrix
GLSL side:
Multiply vertices by ModelView matrix in vertex shader
Send to gl_Position
Response to Edit:
I'm inclined to think your implementation needs to be completely redone. You have points that belong to a model. These points are all oriented around the origin. For example, if you had a car, the points would form a mesh of triangles.
If you simply do not translate these points and then rotate them, the car will rotate around its center. If you translate afterwards, the car will translate in its rotated fashion to the place you've specified. The key here is that the origin of your model lines up with the origin of rotation so you end up rotating the model "around itself."
If you instead translate to the new position and then rotate, your model will rotate as if it were orbiting the origin. This is probably not what you want.
If you're modifying the actual vertex positions directly instead of using transformation matrices, you're doing it wrong. Even if you just have a square, leave the coordinates at (-1,-1) (-1,1) (1,1) (1,-1) (notice how the center is at (0,0)) and translate them to where you want to be.
You don't have to re-implement math functionality and probably shouldn't (unless your goal is explicitly to do so). GLM is a popular math library that does everything you want and it's tailored specifically for OpenGL.
Final Edit
Here is a beautiful work of art I drew for you demonstrating what you need to do.
Notice how in the bottom right the model has been swept out around the world origin about 45 degrees. If we went another 45, it would have its bottom edge parallel to the X-axis and intersecting the positive Y-axis with the blue vertex in the bottom left and purple vertex in the bottom right.
You should probably review how to work with vertices, matrices, and shaders. Vertices should be specified once, matrices should be updated every time you chance the scale, rotation, or position of the object, and shaders should multiply the each vertex in the model by a uniform (constant).
Your sprite lacks sufficient information to be able to do what you're trying to do. In order to compute a rotation about a point, you need to know what that point is. And you don't.
So if you want to rotate about an arbitrary location, you will need to pass that location to your shader. Once there, you subtract it from your positions, rotate the position, and add it back in. However, that would require a lot of work, which is why you should just compute a matrix on the CPU to do all of that. Your shader would be given this matrix and perform the transform itself.
Of course, that itself requires something else, because you keep updating the position of these objects by offsetting the vertices on the CPU. This is not good; you should be keeping these objects relative to their origin in the buffer. You should then transform them to their world-position as part of their matrix.
So your shader should be taking object-relative coordinates, and it should be passed a matrix that does a rotation followed by a translation to their world-space position. Actually, scratch that; the matrix should transform to their final camera-space position (world-space is always a bad idea).

Categories