Vertex shader doesn't work well with cloned objects - opengl

I'm using OpenGL to create a sphere (approximation):
I'm "inflating" the triangle to create an eight of a sphere:
I'm then drawing that octant four times, and each time rotating the model transoformation by 90°, to achieve a hemisphere:
Code related to drawing calls:
for (int i = 0; i < 4; i++) {
model_trans = glm::rotate(model_trans, glm::radians(i * 90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
glUniformMatrix4fv(uniform_model, 1, GL_FALSE, glm::value_ptr(model_trans));
glDrawElementsBaseVertex(GL_TRIANGLES, sizeof(sphere_indices) / sizeof(sphere_indices[0]),
GL_UNSIGNED_INT, 0, (sizeof(grid_vertices)) / (ATTR_COUNT * sizeof(GLfloat)));
}
My goal is to color each vertex based on the angle of its projection in the XY-plane. Since I'm normalizing values, the resulting projection should behave like an trigonometric circle, x value being the cosine value of the angle with the positive end of x-axis. And because the cosine is an continuous function, my sphere should have continual color, too. However, it is not so:
Is this issue caused by cloning the object? That's the only thing I can think of, but it shouldn't matter, since the vertex shader only receives individual vertices. Speaking of which, here is my vertex shader:
#version 150 core
in vec3 position;
/* flat : the color will be sourced from the provoking vertex. */
flat out vec3 Color;
/* transformation matrices */
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(position, 1.0);
vec3 vector_proj = vec3(position.x, position.y, 0.0);
normalize(vector_proj);
/* Addition and division only for mapping range [-1, +1] to [0, 1] */
float cosine = (vector_proj.x + 1) / 2;
Color = vec3(cosine);
}

You want to calculate the color associated to the vertex from the world coordinate of the vertex position.
position is the model coordinate, but not the world coordinate. You have to apply the model matrix to position to transform from model space to world space, before calculating vector_proj:
vec4 world_pos = model * vec4(position, 1.0);
vec3 vector_proj = vec3( world_pos.xy, 0.0 );
The parameter to normalize is not an in-out parameter. It is an input parameter, the normalized result is returned from the function:
vector_proj = normalize(vector_proj);
You can simplify the code as follows:
void main()
{
vec4 world_pos = model * vec4(position, 1.0);
gl_Position = projection * view * world_pos;
vec2 vector_proj = normalize(world_pos.xy);
/* Addition and division only for mapping range [-1, +1] to [0, 1] */
float cosine = (vector_proj.x + 1) / 2;
Color = vec3(cosine);
}

Related

How do I align the raytraced spheres from my fragment shader with GL_POINTS?

I have a very simple shader program that takes in a bunch of position data as GL_POINTS that generate screen-aligned squares of fragments like normal with a size depending on depth, and then in the fragment shader I wanted to draw a very simple ray-traced sphere for each one with just the shadow that is on the sphere opposite to the light. I went to this shadertoy to try to figure it out on my own. I used the sphIntersect function for ray-sphere intersection, and sphNormal to get the normal vectors on the sphere for lighting. The problem is that the spheres do not align with the squares of fragments, causing them to be cut off. This is because I am not sure how to match the projections of the spheres and the vertex positions so that they line up. Can I have an explanation of how to do this?
Here is a picture for reference.
Here are my vertex and fragment shaders for reference:
//vertex shader:
#version 460
layout(location = 0) in vec4 position; // position of each point in space
layout(location = 1) in vec4 color; //color of each point in space
layout(location = 2) uniform mat4 view_matrix; // projection * camera matrix
layout(location = 6) uniform mat4 cam_matrix; //just the camera matrix
out vec4 col; // color of vertex
out vec4 posi; // position of vertex
void main() {
vec4 p = view_matrix * vec4(position.xyz, 1.0);
gl_PointSize = clamp(1024.0 * position.w / p.z, 0.0, 4000.0);
gl_Position = p;
col = color;
posi = cam_matrix * position;
}
//fragment shader:
#version 460
in vec4 col; // color of vertex associated with this fragment
in vec4 posi; // position of the vertex associated with this fragment relative to camera
out vec4 f_color;
layout (depth_less) out float gl_FragDepth;
float sphIntersect( in vec3 ro, in vec3 rd, in vec4 sph )
{
vec3 oc = ro - sph.xyz;
float b = dot( oc, rd );
float c = dot( oc, oc ) - sph.w*sph.w;
float h = b*b - c;
if( h<0.0 ) return -1.0;
return -b - sqrt( h );
}
vec3 sphNormal( in vec3 pos, in vec4 sph )
{
return normalize(pos-sph.xyz);
}
void main() {
vec4 c = clamp(col, 0.0, 1.0);
vec2 p = ((2.0*gl_FragCoord.xy)-vec2(1920.0, 1080.0)) / 2.0;
vec3 ro = vec3(0.0, 0.0, -960.0 );
vec3 rd = normalize(vec3(p.x, p.y,960.0));
vec3 lig = normalize(vec3(0.6,0.3,0.1));
vec4 k = vec4(posi.x, posi.y, -posi.z, 2.0*posi.w);
float t = sphIntersect(ro, rd, k);
vec3 ps = ro + (t * rd);
vec3 nor = sphNormal(ps, k);
if(t < 0.0) c = vec4(1.0);
else c.xyz *= clamp(dot(nor,lig), 0.0, 1.0);
f_color = c;
gl_FragDepth = t * 0.0001;
}
Looks like you have many spheres so I would do this:
Input data
I would have VBO containing x,y,z,r describing your spheres, You will also need your view transform (uniform) that can create ray direction and start position for each fragment. Something like my vertex shader in here:
Reflection and refraction impossible without recursive ray tracing?
Create BBOX in Geometry shader and convert your POINT to QUAD or POLYGON
note that you have to account for perspective. If you are not familiar with geometry shaders see:
rendring cubics in GLSL
Where I emmit sequence of OBB from input lines...
In fragment raytrace sphere
You have to compute intersection between sphere and ray, chose the closer intersection and compute its depth and normal (for lighting). In case of no intersection you have to discard; fragment !!!
From what I can see in your images Your QUADs does not correspond to your spheres hence the clipping and also you do not discard; fragments with no intersections so you overwrite with background color already rendered stuff around last rendered spheres so you have only single sphere left in QUAD regardless of how many spheres are really there ...
To create a ray direction that matches a perspective matrix from screen space, the following ray direction formula can be used:
vec3 rd = normalize(vec3(((2.0 / screenWidth) * gl_FragCoord.xy) - vec2(aspectRatio, 1.0), -proj_matrix[1][1]));
The value of 2.0 / screenWidth can be pre-computed or the opengl built-in uniform structs can be used.
To get a bounding box or other shape for your spheres, it is very important to use camera-facing shapes, and not camera-plane-facing shapes. Use the following process where position is the incoming VBO position data, and the w-component of position is the radius:
vec4 p = vec4((cam_matrix * vec4(position.xyz, 1.0)).xyz, position.w);
o.vpos = p;
float l2 = dot(p.xyz, p.xyz);
float r2 = p.w * p.w;
float k = 1.0 - (r2/l2);
float radius = p.w * sqrt(k);
if(l2 < r2) {
p = vec4(0.0, 0.0, -p.w * 0.49, p.w);
radius = p.w;
k = 0.0;
}
vec3 hx = radius * normalize(vec3(-p.z, 0.0, p.x));
vec3 hy = radius * normalize(vec3(-p.x * p.y, p.z * p.z + p.x * p.x, -p.z * p.y));
p.xyz *= k;
Then use hx and hy as basis vectors for any 2D shape that you want the billboard to be shaped like for the vertices. Don't forget later to multiply each vertex by a perspective matrix to get the final position of each vertex. Here is a visualization of the billboarding on desmos using a hexagon shape: https://www.desmos.com/calculator/yeeew6tqwx

GLSL: Fade 2D grid based on distance from camera

I am currently trying to draw a 2D grid on a single quad using only shaders. I am using SFML as the graphics library and sf::View to control the camera. So far I have been able to draw an anti-aliased multi level grid. The first level (blue) outlines a chunk and the second level (grey) outlines the tiles within a chunk.
I would now like to fade grid levels based on the distance from the camera. For example, the chunk grid should fade in as the camera zooms in. The same should be done for the tile grid after the chunk grid has been completely faded in.
I am not sure how this could be implemented as I am still new to OpenGL and GLSL. If anybody has any pointers on how this functionality can be implemented, please let me know.
Vertex Shader
#version 130
out vec2 texCoords;
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
texCoords = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
}
Fragment Shader
#version 130
uniform vec2 chunkSize = vec2(64.0, 64.0);
uniform vec2 tileSize = vec2(16.0, 16.0);
uniform vec3 chunkBorderColor = vec3(0.0, 0.0, 1.0);
uniform vec3 tileBorderColor = vec3(0.5, 0.5, 0.5);
uniform bool drawGrid = true;
in vec2 texCoords;
void main() {
vec2 uv = texCoords.xy * chunkSize;
vec3 color = vec3(1.0, 1.0, 1.0);
if(drawGrid) {
float aa = length(fwidth(uv));
vec2 halfChunkSize = chunkSize / 2.0;
vec2 halfTileSize = tileSize / 2.0;
vec2 a = abs(mod(uv - halfChunkSize, chunkSize) - halfChunkSize);
vec2 b = abs(mod(uv - halfTileSize, tileSize) - halfTileSize);
color = mix(
color,
tileBorderColor,
smoothstep(aa, .0, min(b.x, b.y))
);
color = mix(
color,
chunkBorderColor,
smoothstep(aa, .0, min(a.x, a.y))
);
}
gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;
}
You need to split your multiplication in the vertex shader to two parts:
// have a variable to be interpolated per fragment
out vec2 vertex_coordinate;
...
{
// this will store the coordinates of the vertex
// before its projected (i.e. its "world" coordinates)
vertex_coordinate = gl_ModelViewMatrix * gl_Vertex;
// get your projected vertex position as before
gl_Position = gl_ProjectionMatrix * vertex_coordinate;
...
}
Then in the fragment shader you change the color based on the world vertex coordinate and the camera position:
in vec2 vertex_coordinate;
// have to update this value, every time your camera changes its position
uniform vec2 camera_world_position = vec2(64.0, 64.0);
...
{
...
// calculate the distance from the fragment in world coordinates to the camera
float fade_factor = length(camera_world_position - vertex_coordinate);
// make it to be 1 near the camera and 0 if its more then 100 units.
fade_factor = clamp(1.0 - fade_factor / 100.0, 0.0, 1.0);
// update your final color with this factor
gl_FragColor.rgb = color * fade_factor;
...
}
The second way to do it is to use the projected coordinate's w. I personally prefer to calculate the distance in units of space. I did not test this code, it might have some trivial syntax errors, but if you understand the idea, you can apply it in any other way.

X-Y-Distance from camera to object in vertex shader

I want to show some fog / aerial view in my application. But I only want to use the x,y world distance from camera to the model to determine the appearance.
I already managed to get the signed z-distance from camera to the models with this calculation.
The red objects have positive z distance to camera, the blue ones are negative in contrast to this implementation, where all values seem positive.
Vertex shader:
uniform mat4 u_mvp; // Model-View-Projection-Matrix
uniform mat4 u_mv; // Model-View-Matrix
uniform vec4 u_color; // Object color
attribute vec4 a_pos; // Vertex position
varying vec4 color; // Out color
// Fog
const float density = 0.007;
const float gradient = 1.5;
void main() {
gl_Position = u_mvp * a_pos;
// Fog
float distance = -(u_mv * a_pos).z; // Direct distance from camera
// 4000 is some invented constant to bring distance to ~[-1,1].
float visibility = clamp((distance / 4000.0), 0.0, 1.0);
color = mix(vec4(1.0, 0.0, 0.0, 1.0), u_color, visibility);
if(distance < 0){
color = vec4(0.0, 0.0, 1.0, 1.0);
}
}
Fragment shader:
varying vec4 color;
void main() {
gl_FragColor = color;
}
Why there can be a negative z-value? Or is it common?
How can I calculate the x,y world distance to camera?
If you want to get the distance to the camera, in the range [-1, 1], then you can use the clips pace coordinated. The clipspace coordinate can be transformed to a normalized device coordinate by Perspective divide. The normalized device coordinates (x, y and z) are in range [-1, 1] and can be transformed to the range [0, 1] with ease:
gl_Position = u_mvp * a_pos; // clip space
vec3 ndc = gl_Position.xyz / gl_Position.w; // NDC in [-1, 1] (by perspective divide)
float depth = ndc.z * 0.5 + 0.5; // depth in [0, 1]

Directional shadow mapping in deferred shading

I'm implementing directional shadow mapping in deferred shading.
First, I render a depth map from light view (orthogonal projection).
Result:
I intend to do VSM so above buffer is R32G32 storing depth and depth * depth.
Then for a full-screen shading pass for shadow (after a lighting pass), I write the following pixel shader:
#version 330
in vec2 texCoord; // screen coordinate
out vec3 fragColor; // output color on the screen
uniform mat4 lightViewProjMat; // lightView * lightProjection (ortho)
uniform sampler2D sceneTexture; // lit scene with one directional light
uniform sampler2D shadowMapTexture;
uniform sampler2D scenePosTexture; // store fragment's 3D position
void main() {
vec3 fragPos = texture(scenePosTexture, texCoord).xyz; // get 3D position of pixel
vec4 fragPosLightSpace = lightViewProjMat * vec4(fragPos, 1.0); // project it to light-space view: lightView * lightProjection
// projective texture mapping
vec3 coord = fragPosLightSpace.xyz / fragPosLightSpace.w;
coord = coord * 0.5 + 0.5;
float lightViewDepth; // depth value in the depth buffer - the maximum depth that light can see
float currentDepth; // depth of screen pixel, maybe not visible to the light, that's how shadow mapping works
vec2 moments; // depth and depth * depth for later variance shadow mapping
moments = texture(shadowMapTexture, coord.xy).xy;
lightViewDepth = moments.x;
currentDepth = fragPosLightSpace.z;
float lit_factor = 0;
if (currentDepth <= lightViewDepth)
lit_factor = 1; // pixel is visible to the light
else
lit_factor = 0; // the light doesn't see this pixel
// I don't do VSM yet, just want to see black or full-color pixels
fragColor = texture(sceneTexture, texCoord).rgb * lit_factor;
}
The rendered result is a black screen, but if I hard coded the lit_factor to be 1, result is:
Basically that's how the sceneTexture looks like.
So I think either my depth value is wrong, which is unlikely, or my projection (light space projection in above shader / projective texture mapping) is wrong. Could you validate it for me?
My shadow map generation code is:
// vertex shader
#version 330 compatibility
uniform mat4 lightViewMat; // lightView
uniform mat4 lightViewProjMat; // lightView * lightProj
in vec3 in_vertex;
out float depth;
void main() {
vec4 vert = vec4(in_vertex, 1.0);
depth = (lightViewMat * vert).z / (500 * 0.2); // 500 is far value, this line tunes the depth precision
gl_Position = lightViewProjMat * vert;
}
// pixel shader
#version 330
in float depth;
out vec2 out_depth;
void main() {
out_depth = vec2(depth, depth * depth);
}
The z component of the fragment shader built in variable gl_FragCoord contains the depth value in range [0.0, 1.0]. This is the value which you shoud store to the depth map:
out_depth = vec2(gl_FragCoord.z, depth * depth);
After the calculation
vec3 fragPos = texture(scenePosTexture, texCoord).xyz; // get 3D position of pixel
vec4 fragPosLightSpace = lightViewProjMat * vec4(fragPos, 1.0); // project it to light-space view: lightView * lightProjection
vec3 ndc_coord = fragPosLightSpace.xyz / fragPosLightSpace.w;
the variable ndc_coord contains a normalized device coordinate, where all components are in range [-1.0, 1.0].
The z component of the normalized device coordiante can be conveted to the depth value (if the depth range is [0.0, 1.0]), by
float currentDepth = ndc_coord.z * 0.5 + 0.5;
This value can be compared to the value from the depth map, because currentDepth and lightViewDepth are calcualted by the same view matrix and projection matrix:
moments = texture(shadowMapTexture, coord.xy).xy;
lightViewDepth = moments.x;
if (currentDepth <= lightViewDepth)
lit_factor = 1; // pixel is visible to the light
else
lit_factor = 0; // the light doesn't see this pixel
This is the depth you store in the shadow map:
depth = (lightViewMat * vert).z / (500 * 0.2);
This is the depth you compare the read back value to:
vec4 fragPosLightSpace = lightViewProjMat * vec4(fragPos, 1.0);
currentDepth = fragPosLightSpace.z;
If fragPos is in world space then I assume lightViewMat * vert == fragPos. You are compressing depth by dividing by 500 * 0.2, but that does not equal to fragPosLightSpace.z.
Hint: Write out the value of currentDepth in one channel and the value from the shadow map in another channel, you can then compare them visually or in RenderDoc or similar.

SSAO not displaying correct results, mostly no visible occlusion

I'm following the tutorial by John Chapman (http://john-chapman-graphics.blogspot.nl/2013/01/ssao-tutorial.html) to implement SSAO in a deferred renderer. The input buffers to the SSAO shaders are:
World-space positions with linearized depth as w-component.
World-space normal vectors
Noise 4x4 texture
I'll first list the complete shader and then briefly walk through the steps:
#version 330 core
in VS_OUT {
vec2 TexCoords;
} fs_in;
uniform sampler2D texPosDepth;
uniform sampler2D texNormalSpec;
uniform sampler2D texNoise;
uniform vec3 samples[64];
uniform mat4 projection;
uniform mat4 view;
uniform mat3 viewNormal; // transpose(inverse(mat3(view)))
const vec2 noiseScale = vec2(800.0f/4.0f, 600.0f/4.0f);
const float radius = 5.0;
void main( void )
{
float linearDepth = texture(texPosDepth, fs_in.TexCoords).w;
// Fragment's view space position and normal
vec3 fragPos_World = texture(texPosDepth, fs_in.TexCoords).xyz;
vec3 origin = vec3(view * vec4(fragPos_World, 1.0));
vec3 normal = texture(texNormalSpec, fs_in.TexCoords).xyz;
normal = normalize(normal * 2.0 - 1.0);
normal = normalize(viewNormal * normal); // Normal from world to view-space
// Use change-of-basis matrix to reorient sample kernel around origin's normal
vec3 rvec = texture(texNoise, fs_in.TexCoords * noiseScale).xyz;
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
// Loop through the sample kernel
float occlusion = 0.0;
for(int i = 0; i < 64; ++i)
{
// get sample position
vec3 sample = tbn * samples[i]; // From tangent to view-space
sample = sample * radius + origin;
// project sample position (to sample texture) (to get position on screen/texture)
vec4 offset = vec4(sample, 1.0);
offset = projection * offset;
offset.xy /= offset.w;
offset.xy = offset.xy * 0.5 + 0.5;
// get sample depth
float sampleDepth = texture(texPosDepth, offset.xy).w;
// range check & accumulate
// float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0;
occlusion += (sampleDepth <= sample.z ? 1.0 : 0.0);
}
occlusion = 1.0 - (occlusion / 64.0f);
gl_FragColor = vec4(vec3(occlusion), 1.0);
}
The result is however not pleasing. The occlusion buffer is mostly all white and doesn't show any occlusion. However, if I move really close to an object I can see some weird noise-like results as you can see below:
This is obviously not correct. I've done a fair share of debugging and believe all the relevant variables are correctly passed around (they all visualize as colors). I do the calculations in view-space.
I'll briefly walk through the steps (and choices) I've taken in case any of you figure something goes wrong in one of the steps.
view-space positions/normals
John Chapman retrieves the view-space position using a view ray and a linearized depth value. Since I use a deferred renderer that already has the world-space positions per fragment I simply take those and multiply them with the view matrix to get them to view-space.
I take a similar approach for the normal vectors. I take the world-space normal vectors from a buffer texture, transform them to [-1,1] range and multiply them with transpose(inverse(mat3(..))) of view matrix.
The view-space position and normals are visualized as below:
This looks correct to me.
Orient hemisphere around normal
The steps to create the tbn matrix are the same as described in John Chapman's tutorial. I create the noise texture as follows:
std::vector<glm::vec3> ssaoNoise;
for (GLuint i = 0; i < noise_size; i++)
{
glm::vec3 noise(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f);
noise = glm::normalize(noise);
ssaoNoise.push_back(noise);
}
...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
I can visualize the noise in the fragment shader so that seems to work.
sample depths
I transform all samples from tangent to view-space (samples are random between [-1,1] on xy axis and [0,1] on z-axis and translate them to fragment's current view-space position (origin).
I then sample from linearized depth buffer (which I visualize below when looking close to an object):
and finally compare sampled depth values to current fragment's depth value and add occlusion values. Note that I do not perform a range-check since I don't believe that is the cause of this behavior and I'd rather keep it as minimal as possible for now.
I don't know what is causing this behavior. I believe it is somewhere in sampling the depth values. As far as I can tell I am working in the right coordinate system, linearized depth values are in view-space as well and all variables are set somewhat properly.