Sprite rotation over sphere - opengl

I have a 2D mode which displays moving sprites over the world. each sprite has rotation.
When i'm trying to implement the same in 3D world, over a sphere, i met a problem calculating the sprite rotation so it will look like it is moving toward the direction. I'm aware that the sprite is billboard only and the rotation will be 2D only, and will not be 100% rotated toward the direction but at least to make it look reasonable for the eye.
I've tried to consider the vector to the north (of the world) in my rotation but still, there are allot of cases when we move the camera around the sphere that the sprite arrow is not in the direction of the movement.
Can anyone direct me for a solution ?
-------- ADDITION -----------
More explanation: I have 2D world (x,y). In this world I have a point that moves toward a direction (an angle is saved in the object). The rotations are calculated in the fragment shader of course.
In the 3D world, i'm converting this (x, y) to a (x,y,z) by simple sphere formula.
My sphere (world) origin is (0,0,0) with radius 1.
The angle (saved in the point for the direction of movement) is used in 2D for rotating the texture as well (As shown above in the first image). The problem is the rotation of the texture in 3D. The rotating should consider the point direction angle, and the camera.
-------- ADDITION -----------
My fragment shader for 2D - If it is helping. And few more pictures and my wish
varying vec2 TextureCoord;
varying vec2 TextureSize;
uniform sampler2D sampler;
varying float angle;
uniform vec4 uColor;
void main()
{
vec2 calcedCoord = gl_PointCoord;
float c = cos(angle);
float s = sin(angle);
vec2 trans = vec2(-0.5, -0.5);
mat2 rot = mat2(c, s, -s, c);
calcedCoord = calcedCoord + trans;
calcedCoord = rot * calcedCoord;
calcedCoord = calcedCoord - trans;
vec2 realTexCoord = TextureCoord + (calcedCoord * TextureSize);
vec4 fragColor = texture2D(sampler, realTexCoord);
gl_FragColor = fragColor * uColor;
}

After struggling allot with this issue I came into this solution.
Instead of attaching as attribute the direction angle to each sprite, I sent the next sprite location instead. And calculating the 2D angle in the vertex shader as follow:
varying float angle;
attribute vec3 nextPointAtt;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec4 nextPnt = gl_ModelViewProjectionMatrix * vec4(nextPointAtt, gl_Vertex.w);
vec2 ver = gl_Position.xy / gl_Position.w;
vec2 nextVer = nextPnt.xy / nextPnt.w;
vec2 d = nextVer - ver;
angle = atan(d.y, d.x);
}
The angle will be used in the fragment shader (Look at my question for the fragment shader code).

Related

OpenGL flickering issue in rendering

I have an OpenGL rendering issue and I think that is due to a problem with the Z-Buffer.
I have a code to render a set of points where their size depends on the distance from the camera. Thus bigger points means that are closer to the camera. Moreover in the following snapshots the color reflect the z-buffer of the fragment.
How you can see there is a big point near the camera.
However some frames later the same point is rendered behind more distant points.
These are the functions that I call before render the points:
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
this is the vertex shader:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec4 color;
// uniform variable
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform float pointSize;
out vec4 fragColor;
void main() {
gl_Position = projection * view * model * vec4(position, 1.0f);
vec3 posEye = vec3(view * model * vec4(position, 1.0f));
float Z = length(posEye);
gl_PointSize = pointSize / Z;
fragColor = color;
}
and this is the fragment shader
#version 330 core
in vec4 fragColor;
out vec4 outColor;
void main() {
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
float r = dot(cxy, cxy);
if(r > 1.0) discard;
// calculate lighting
vec3 pos = vec3(cxy.x,cxy.y,sqrt(1.0-r));
vec3 lightDir = vec3(0.577, 0.577, 0.577);
float diffuse = max(0.0, dot(lightDir, pos));
float alpha = 1.0;
float delta = fwidth(r);
alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
outColor = fragColor * alpha * diffuse;
}
UPDATE
looks like that the problem was due to the definition of the near and far planes.
There is something that I do not understand about which are the best values that I should use.
this is the function that I use to create the projective matrix
glm::perspective(glm::radians(fov), width/(float)height, zNear, zFar);
where winth=1600 height=1200 fov=45
when things didn't work zNear was set to zero and zFar was set to double the distance of the farthest point from the center of gravity of the point cloud, i.e. in my case 1.844
If I move the near clipping plane from zero to 0.1 the flicker seems resolved. However, the distant objects, which I saw before, disappear. So I also changed the far plane to 10 and everything seems to work. Unfortunately, I don't understand why the values I used before were not good.
As already updated the issue is called Z-fighting when choosing the wrong near and far panes in the projection matrix. If they are to far away from your objects, there is only a very discrete number of values for z left. Then the drawing is detemined by the call order and processing order in the shaders, since there is no difference in z. If one has a hint of what needs to come first, draw it that way. Once the rendering-call was set, there is no proper way to determine which processor gets the objects first and that you'll see as flickering.
So please update the way you establish your projection Matrix. Best practise: look at the objects need to be rendered and determine a roughly bounding box, make it a bounding ball and center-radius is a point in the near pane, center+radius is a point in the far pane. Done!
Update
looking into
glm::perspective(glm::radians(fov), width/(float)height, zNear, zFar);
gives a specific for the only influence of z: (before normalization):
Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
meaning other than zFar!=zNear, it should be also avoided to set zNear to zero. This means there is no difference in z, so it has to Flicker. I would then assume that you don't applied some transform on your projection matrix, better don't. If all your objects live in a space around the coordinates center meaning also in the center of your projection, move them in front of the projection as a last step. So apply some translation not onto the projection/view matrix, but on your object matrix, to avoid having such an ill formed projection space. E.g. set near to 1, and move all objects with that amount through your scene.

How to render a circular vignette with GLSL

I’m trying to achieve a circular vignette with GLSL, but the result is elliptical when the texture is rectangular. What is the correct way to make it square regardless of the texture size? The input texture size (resolution) can be both rectangular or square.
I tried a solution using the discard method, but this doesn't suit what I require, as I need to use smoothstep to get a gradient edge.
Current result:
GLSL shader:
varying vec2 v_texcoord;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
vec4 applyVignette(vec4 color)
{
vec2 position = (gl_FragCoord.xy / u_resolution) - vec2(0.5);
float dist = length(position);
float radius = 0.5;
float softness = 0.02;
float vignette = smoothstep(radius, radius - softness, dist);
color.rgb = color.rgb - (1.0 - vignette);
return color;
}
void main()
{
vec4 color = texture2D(u_texture, v_texcoord);
color = applyVignette(color);
gl_FragColor = color;
}
You have to respect the aspect ration when you calculate the distance to the center point of the circular view:
float dist = length(position * vec2(u_resolution.x/u_resolution.y, 1.0));
Note, if you have a rectangular viewport, where the width is greater than the height, then a perfect circle is squeezed at it left and right to an ellipse, when the coordinates are transformed from view space the normalized devices space.
You must counteract this squeezing by scaling up the x axis of the distance vector.

How to handle lightning (ambient, diffuse, specular) for point spheres in openGL

Initial situation
I want to visualize simulation data in openGL.
My data consists of particle positions (x, y, z) where each particle has some properties (like density, temperature, ...) which will be used for coloring. Those (SPH) particles (100k to several millions), grouped together, actually represent planets, in case you wonder. I want to render those particles as small 3D spheres and add ambient, diffuse and specular lighting.
Status quo and questions
In MY case: In which coordinate frame do I do the lightning calculations? Which way is the "best" to pass the various components through the pipeline?
I saw that it is common to do it in view space which is also very intuitive. However: The normals at the different fragment positions are calculated in the fragment shader in clip space coordinates (see appended fragment shader). Can I actually convert them "back" into view space to do the lightning calculations in view space for all the fragments? Would there be any advantage compared to doing it in clip space?
It would be easier to get the normals in view space if I would use meshes for each sphere but I think with several million particles this would decrease performance drastically, so better do it with sphere intersection, would you agree?
PS: I don't need a model matrix since all the particles are already in place.
//VERTEX SHADER
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 2) in float density;
uniform float radius;
uniform vec3 lightPos;
uniform vec3 viewPos;
out vec4 lightDir;
out vec4 viewDir;
out vec4 viewPosition;
out vec4 posClip;
out float vertexColor;
// transformation matrices
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
lightDir = projection * view * vec4(lightPos - position, 1.0f);
viewDir = projection * view * vec4(viewPos - position, 1.0f);
viewPosition = projection * view * vec4(lightPos, 1.0f);
posClip = projection * view * vec4(position, 1.0f);
gl_Position = posClip;
gl_PointSize = radius;
vertexColor = density;
}
I know that projective divion happens for the gl_Position variable, does that actually happen to ALL vec4's which are passed from the vertex to the fragment shader? If not, maybe the calculations in the fragment shader would be wrong?
And the fragment shader where the normals and diffuse/specular lightning calculations in clip space:
//FRAGMENT SHADER
#version 330 core
in float vertexColor;
in vec4 lightDir;
in vec4 viewDir;
in vec4 posClip;
in vec4 viewPosition;
uniform vec3 lightColor;
vec4 colormap(float x); // returns vec4(r, g, b, a)
out vec4 vFragColor;
void main(void)
{
// AMBIENT LIGHT
float ambientStrength = 0.0;
vec3 ambient = ambientStrength * lightColor;
// Normal calculation done in clip space (first from texture (gl_PointCoord 0 to 1) coord to NDC( -1 to 1))
vec3 normal;
normal.xy = gl_PointCoord * 2.0 - vec2(1.0); // transform from 0->1 point primitive coords to NDC -1->1
float mag = dot(normal.xy, normal.xy); // sqrt(x=1) = sqrt(x)
if (mag > 1.0) // discard fragments outside sphere
discard;
normal.z = sqrt(1.0 - mag); // because x^2 + y^2 + z^2 = 1
// DIFFUSE LIGHT
float diff = max(0.0, dot(vec3(lightDir), normal));
vec3 diffuse = diff * lightColor;
// SPECULAR LIGHT
float specularStrength = 0.1;
vec3 viewDir = normalize(vec3(viewPosition) - vec3(posClip));
vec3 reflectDir = reflect(-vec3(lightDir), normal);
float shininess = 64;
float spec = pow(max(dot(vec3(viewDir), vec3(reflectDir)), 0.0), shininess);
vec3 specular = specularStrength * spec * lightColor;
vFragColor = colormap(vertexColor / 8) * vec4(ambient + diffuse + specular, 1);
}
Now this actually "kind of" works but i have the feeling that also the sides of the sphere which do NOT face the light source are being illuminated, which shouldn't happen. How can I fix this?
Some weird effect: In this moment the light source is actually BEHIND the left planet (it just peaks out a little bit at the top left), bit still there are diffuse and specular effects going on. This side should be actually pretty dark! =(
Also at this moment I get some glError: 1282 error in the fragment shader and I don't know where it comes from since the shader program actually compiles and runs, any suggestions? :)
The things that you are drawing aren't actually spheres. They just look like them from afar. This is absolutely ok if you are fine with that. If you need geometrically correct spheres (with correct sizes and with a correct projection), you need to do proper raycasting. This seems to be a comprehensive guide on this topic.
1. What coordinate system?
In the end, it is up to you. The coordinate system just needs to fulfill some requirements. It must be angle-preserving (because lighting is all about angles). And if you need distance-based attenuation, it should also be distance-preserving. The world and the view coordinate systems usually fulfill these requirements. Clip space is not suited for lighting calculations as neither angles nor distances are preserved. Furthermore, gl_PointCoord is in none of the usual coordinate systems. It is its own coordinate system and you should only use it together with other coordinate systems if you know their relation.
2. Meshes or what?
Meshes are absolutely not suited to render spheres. As mentioned above, raycasting or some screen-space approximation are better choices. Here is an example shader that I used in my projects:
#version 330
out vec4 result;
in fData
{
vec4 toPixel; //fragment coordinate in particle coordinates
vec4 cam; //camera position in particle coordinates
vec4 color; //sphere color
float radius; //sphere radius
} frag;
uniform mat4 p; //projection matrix
void main(void)
{
vec3 v = frag.toPixel.xyz - frag.cam.xyz;
vec3 e = frag.cam.xyz;
float ev = dot(e, v);
float vv = dot(v, v);
float ee = dot(e, e);
float rr = frag.radius * frag.radius;
float radicand = ev * ev - vv * (ee - rr);
if(radicand < 0)
discard;
float rt = sqrt(radicand);
float lambda = max(0, (-ev - rt) / vv); //first intersection on the ray
float lambda2 = (-ev + rt) / vv; //second intersection on the ray
if(lambda2 < lambda) //if the first intersection is behind the camera
discard;
vec3 hit = lambda * v; //intersection point
vec3 normal = (frag.cam.xyz + hit) / frag.radius;
vec4 proj = p * vec4(hit, 1); //intersection point in clip space
gl_FragDepth = ((gl_DepthRange.diff * proj.z / proj.w) + gl_DepthRange.near + gl_DepthRange.far) / 2.0;
vec3 vNormalized = -normalize(v);
float nDotL = dot(vNormalized, normal);
vec3 c = frag.color.rgb * nDotL + vec3(0.5, 0.5, 0.5) * pow(nDotL, 120);
result = vec4(c, frag.color.a);
}
3. Perspective division
Perspective division is not applied to your attributes. The GPU does perspective division on the data that you pass via gl_Position on the way to transforming them to screen space. But you will never actually see this perspective-divided position unless you do it yourself.
4. Light in the dark
This might be the result of you mixing different coordinate systems or doing lighting calculations in clip space. Btw, the specular part is usually not multiplied by the material color. This is light that gets reflected directly at the surface. It does not penetrate the surface (which would absorb some colors depending on the material). That's why those highlights are usually white (or whatever light color you have), even on black objects.

Simple GLSL Spotlight Shader

I need help with a simple spotlight shader.
All vertices inside the cone should be colored yellow, all vertices outside the cone should be colored black.
I just can't get it work. I asume it has something to do with the transformation from world into eye coordinates.
Vertex Shader:
uniform vec4 lightPositionOC; // in object coordinates
uniform vec3 spotDirectionOC; // in object coordinates
uniform float spotCutoff; // in degrees
void main(void)
{
vec3 lightPosition;
vec3 spotDirection;
vec3 lightDirection;
float angle;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
// Transforms light position and direction into eye coordinates
lightPosition = (lightPositionOC * gl_ModelViewMatrix).xyz;
spotDirection = normalize(spotDirectionOC * gl_NormalMatrix);
// Calculates the light vector (vector from light position to vertex)
vec4 vertex = gl_ModelViewMatrix * gl_Vertex;
lightDirection = normalize(vertex.xyz - lightPosition.xyz);
// Calculates the angle between the spot light direction vector and the light vector
angle = dot( normalize(spotDirection),
-normalize(lightDirection));
angle = max(angle,0);
// Test whether vertex is located in the cone
if(angle > radians(spotCutoff))
gl_FrontColor = vec4(1,1,0,1); // lit (yellow)
else
gl_FrontColor = vec4(0,0,0,1); // unlit(black)
}
Fragment Shader:
void main(void)
{
gl_FragColor = gl_Color;
}
Edit:
Tim is right. This
if(angle > radians(spotCutoff))
should be:
if(acos(angle) < radians(spotCutoff))
New question:
The light seems not to stay at a fixed position in the scene, instead it seems to move relative to my camera as the cone gets smaller or bigger when I move forward or backward.
(Let spotDirection be vector A, and lightDirection be vector B)
You are assigning;
angle = dot(A,B)
Shouldn't the formula be:
cos(angle) = dot(A,B)
or
angle = arccos(dot(A,B))
http://en.wikipedia.org/wiki/Dot_product#Geometric_interpretation
in my old shader I used that code:
float spotEffect = dot(normalize(gl_LightSource[0].spotDirection.xyz),
normalize(-light));
if (spotEffect < gl_LightSource[0].spotCosCutoff)
{
spotEffect = smoothstep(gl_LightSource[0].spotCosCutoff-0.002,
gl_LightSource[0].spotCosCutoff, spotEffect);
}
else spotEffect = 1.0;
instead of sending Angles to the shader it is better to send Cos of those Angles
To answer your new question, here is a link to a similar question: GLSL point light shader moving with camera.
The solution is to remove gl_NormalMatrix and gl_ModelViewMatrix.
spotDirection = normalize(spotDirectionOC * gl_NormalMatrix);
Would become:
spotDirection = normalize(spotDirectionOC);
https://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/graphics/shader.vert?r=1639
if (spotEffect >= cos(radians(uLightOuterCone[index])))
and
//vec3 NSpotDir = (uViewMatrix * vec4(uLightDirection[index],0)).xyz; //must do outside or become flashlight follow
vec3 NSpotDir = normalize(uLightDirection[index]);

OpenGL point sprites rotation in fragment shader

I'm following this tutorial to learn something more about OpenGL and in particular point sprites. But I'm stuck on one of the exercises at the end of the page:
Try to rotate the point sprites 45 degrees by changing the fragment shader.
There are no hints about this sort of thing in the chapter, nor in the previous ones. And I didn't find any documentation on how to do it. These are my vertex and fragment shaders:
Vertex Shader
#version 140
attribute vec2 coord2d;
varying vec4 f_color;
uniform float offset_x;
uniform float scale_x;
uniform float point_size;
void main(void) {
gl_Position = vec4((coord2d.x + offset_x) * scale_x, coord2d.y, 0.0, 1.0);
f_color = vec4(coord2d.xy / 2.0 + 0.5, 1.0, 1.0);
gl_PointSize = point_size;
}
Fragment Shader
#version 140
varying vec4 f_color;
uniform sampler2D texture;
void main(void) {
gl_FragColor = texture2D(texture, gl_PointCoord) * f_color;
}
I thought about using a 2x2 matrix in the FS to rotate the gl_PointCoord, but I have no idea how to fill the matrix to accomplish it. Should I pass it directly to the FS as a uniform?
The traditional method is to pass a matrix to the shader, whether vertex or fragment. If you don't know how to fill in a rotation matrix, Google and Wikipedia can help.
The main thing is that you're going to run into is the simple fact that a 2D rotation is not enough. gl_PointCoord goes from [0, 1]. A pure rotation matrix rotates around the origin, which is the bottom-left in point-coord space. So you need more than a pure rotation matrix.
You need a 3x3 matrix, which has part rotation and part translation. This matrix should be generated as follows (using GLM for math stuff):
glm::mat4 currMat(1.0f);
currMat = glm::translate(currMat, glm::vec3(0.5f, 0.5f, 0.0f));
currMat = glm::rotate(currMat, angle, glm::vec3(0.0f, 0.0f, 1.0f));
currMat = glm::translate(currMat, glm::vec3(-0.5f, -0.5f, 0.0f));
You then pass currMat to the shader as a 4x4 matrix. Your shader does this:
vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy
gl_FragColor = texture2D(texture, texCoord) * f_color;
I'll leave it as an exercise for you as to how to move the translation from the fourth column into the third, and how to pass it as a 3x3 matrix. Of course, in that case, you'll do vec3(gl_PointCoord, 1) for the matrix multiply.
I was stuck in the same problem too, but I found a tutorial that explain how to perform a 2d texture rotation in the same fragment shader with only with passing the rotate value (vRotation).
#version 130
uniform sampler2D tex;
varying float vRotation;
void main(void)
{
float mid = 0.5;
vec2 rotated = vec2(cos(vRotation) * (gl_PointCoord.x - mid) + sin(vRotation) * (gl_PointCoord.y - mid) + mid,
cos(vRotation) * (gl_PointCoord.y - mid) - sin(vRotation) * (gl_PointCoord.x - mid) + mid);
vec4 rotatedTexture=texture2D(tex, rotated);
gl_FragColor = gl_Color * rotatedTexture;
}
Maybe this method is slow but is only to prove/show that you have an alternative to perform a texture 2D rotation inside fragment shader instead of passing a Matrix.
Note: vRotation should be in Radians.
Cheers,
You're right - a 2x2 rotation matrix will do what you want.
This page: http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche31.html shows how to compute the elements. Note that you will be rotating the texture coordinates, not the vertex positions - the result will probably not be what you're expecting - it will rotate around the 0,0 texture coordinate, for example.
You may alse need to multiply the point_size by 2 and shrink the gl_PointCoord by 2 to ensure the whole texture fits into the point sprite when it's rotated. But do that as a second change. Note that a straight scale of texture coordinates move them towards the texture coordinate origin, not the middle of the sprite.
If you use a higher dimension matrix (3x3) then you will be able to combine the offset, scale and rotation into one operation.