Consider the following scene.
I transformed pMin and pMax from world space to viewport space. The area bound by pMin and pMax follows the users mouse sliding over the plane (larger rectangle).
Is there a way inside the fragment shader to decide if the fragment lies within the inner area or not? I tried comparing with gl_FragCoord.x and gl_FragCoord.z but it does not yield correct results.
if((gl_FragCoord.x < splitMax.x && gl_FragCoord.x > splitMin.x)
&& (gl_FragCoord.z < splitMax.z && gl_FragCoord.z > splitMin.z)){
//within area following the mouse
} else {
//outside of area following the mouse
}
In cascaded shadow mapping, shadow maps are chosen based on the fragment's z value and whether it lies inside the computed frustum split z values. I'm trying to do the same only that I want my look up to also consider the x coordinate.
Thanks to a guy in ##opengl on freenode, I managed to get this working the following way:
vertex shader: Transform the incoming vertex to world space
out vec4 worldPos;
...
worldPos = modelMatrix * vec4(vertex, 1.0);
fragment shader: Send in pMin and pMax in world space coordinates
in vec3 pMin, pMax;
in vec4 worldPos;
...
if((worldPos.x > pMin.x && worldPos.x < pMax.x) && (worldPos.z > pMin.z && worldPos.z < pMax.z)){
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else {
FragColor is scene lighting
}
Result:
Related
I want to utilize shaders to not only discard fragments if they are on one side of a predefined plane but also render a contour along the intersection.
My fragment shader currently does something along the lines of:
float dot = dot(world_coordinate, normalize(clipping_normal.xyz)) - clipping_normal.w;
if (dot > 0.0f)
discard;
this works but without the desired contour. I tried comparing the dot product against values close to 0.0 but this results in a contour with varying width depending on view etc...
This is what I am trying to achieve. Notice that the white contour/edge of where the plane intersects the sphere is of consistent width:
So below is what the result I currently see:
With the fragment shader:
in vec4 color;
in vec3 world_position;
out vec4 frag_color;
void main()
{
float dist = (dot(clipping_plane.xyz, world_position) - clipping_plane.w) /
dot(clipping_plane.xyz, clipping_plane.xyz);
if(dist >= 0.0f && dist < 0.05f)
frag_color = vec4(0.0f, 0.0f, 0.0f, 1.0f);
else if(dist < 0.0f)
discard;
else
frag_color = ComputePhong(color);
}
The contour of the intersection also belongs to the clipping plane, so the distance to this plane is zero.
Using dot(point, normal) is not enough. You need d= A·x + B·y + C·z + D which is the numerator (without "modulus") of the full distance point-to-plane formula. See plane geometry.
This calculated d gives you not only distance (add the square in the denominator if normal A,B,C is not unitary), but also its sign tells you which side of the plane the point is.
Working inside fragment shader likely you work with NDC coordinates. So transform A,B,C,D into NDC too.
I have been working on projecting decals on to anything that the decals bounding box encapsulates. After reading and trying numerous code snippets (usually in HLSL) I have a some what working method in GLSL for projecting the decals.
Let me start with trying to explain what I'm doing and how this works (so far).
The code below is now fixed and works!
This all is while in the perspective view mode.
I send 2 uniforms to the fragment shader "tr" and "bl". These are the 2 corners of the bounding box. I can and will replace these with hard coded sizes because they are the size of the decals original bounding box. tr = vec3(.5, .5, .5) and br = vec3(-.5, -.5, -.5). I'd prefer to find a way to do the position tests in the decals transformed state. (more about this at the end).
Adding this for clarity. The vertex emitted from the vertex program is the bounding box multiplied by the decals matrix and than by the model view projection matrix.. I use this for the next step:
With that vertex, I get the depth value from the depth texture and with it, calculate the position in world space using the inverse of the projection matrix.
Next, I translate this position using the Inverse of the Decals matrix. (The matrix that scales, rotates and translates the 1,1,1 cube to its world location. I thought that by using the inverse of the decals transform matrix, the correct size and rotation of the screen point would be handled correctly but it is not.
Vertex Program:
//Decals color pass.
#version 330 compatibility
out mat4 matPrjInv;
out vec4 positionSS;
out vec4 positionWS;
out mat4 invd_mat;
uniform mat4 decal_matrix;
void main(void)
{
gl_Position = decal_matrix * gl_Vertex;
gl_Position = gl_ModelViewProjectionMatrix * gl_Position;
positionWS = (decal_matrix * gl_Vertex);;
positionSS = gl_Position;
matPrjInv = inverse(gl_ModelViewProjectionMatrix);
invd_mat = inverse(decal_matrix);
}
Fragment Program:
#version 330 compatibility
layout (location = 0) out vec4 gPosition;
layout (location = 1) out vec4 gNormal;
layout (location = 2) out vec4 gColor;
uniform sampler2D depthMap;
uniform sampler2D colorMap;
uniform sampler2D normalMap;
uniform mat4 matrix;
uniform vec3 tr;
uniform vec3 bl;
in vec2 TexCoords;
in vec4 positionSS; // screen space
in vec4 positionWS; // world space
in mat4 invd_mat; // inverse decal matrix
in mat4 matPrjInv; // inverse projection matrix
void clip(vec3 v){
if (v.x > tr.x || v.x < bl.x ) { discard; }
if (v.y > tr.y || v.y < bl.y ) { discard; }
if (v.z > tr.z || v.z < bl.z ) { discard; }
}
vec2 postProjToScreen(vec4 position)
{
vec2 screenPos = position.xy / position.w;
return 0.5 * (vec2(screenPos.x, screenPos.y) + 1);
}
void main(){
// Calculate UVs
vec2 UV = postProjToScreen(positionSS);
// sample the Depth from the Depthsampler
float Depth = texture2D(depthMap, UV).x * 2.0 - 1.0;
// Calculate Worldposition by recreating it out of the coordinates and depth-sample
vec4 ScreenPosition;
ScreenPosition.xy = UV * 2.0 - 1.0;
ScreenPosition.z = (Depth);
ScreenPosition.w = 1.0f;
// Transform position from screen space to world space
vec4 WorldPosition = matPrjInv * ScreenPosition ;
WorldPosition.xyz /= WorldPosition.w;
WorldPosition.w = 1.0f;
// transform to decal original position and size.
// 1 x 1 x 1
WorldPosition = invd_mat * WorldPosition;
clip (WorldPosition.xyz);
// Get UV for textures;
WorldPosition.xy += 0.5;
WorldPosition.y *= -1.0;
vec4 bump = texture2D(normalMap, WorldPosition.xy);
gColor = texture2D(colorMap, WorldPosition.xy);
//Going to have to do decals in 2 passes..
//Blend doesn't work with GBUFFER.
//Lots more to sort out.
gNormal.xyz = bump;
gPosition = positionWS;
}
And here are a couple of Images showing whats wrong.
What I get for the projection:
And this is the actual size of the decals.. Much larger than what my shader is creating!
I have tried creating a new matrix using the decals and the projection matrix to construct a sort of "lookat" matrix and translate the screen position in to the decals post transformed state.. I have not been able to get this working. Some where I am missing something but where? I thought that translating using the inverse of the decals matrix would deal with the transform and put the screen position in the proper transformed state. Ideas?
Updated the code for the texture UVs.. You may have to fiddle with the y and x depending on if your texture is flipped on x or y. I also fixed the clip sub so it works correctly. As it is, this code now works. I will update this more if needed so others don't have to go through the pain I did to get it working.
Some issues to resolve are decals laying over each other. The one on top over writes the one below. I think I will have to accumulated the colors and normals in to the default FBO and then blend(Add) them to the GBUFFER textures before or during the lighting pass. Adding more screen size textures is not a great idea so I will need to be creative and recycle any textures I can.
I found the solution to decals overlaying each other.
Turn OFF depth masking while drawing the decals and turn int back on afterwards:
glDepthMask(GL_FALSE)
OK.. I'm so excited. I found the issue.
I updated the code above again.
I had a mistake in what I was sending the shader for tr and bl:
Here is the change to clip:
void clip(vec3 v){
if (v.x > tr.x || v.x < bl.x ) { discard; }
if (v.y > tr.y || v.y < bl.y ) { discard; }
if (v.z > tr.z || v.z < bl.z ) { discard; }
}
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.
I'm trying to render arbitrary wide lines (in screen space) using a geometry shader. At first it seems all good, but on certain view position the lines are rendered incorrectly:
The image on the left present the correct rendering (three lines on positive X, Y and Z axes, 2 pixel wide).
When the camera moves near the origin (and indeed near the lines), the lines are rendered like the right image. The shader seems straightforward, and I don't understand what's going on my GPU:
--- Vertex Shader
#version 410 core
// Modelview-projection matrix
uniform mat4 ds_ModelViewProjection;
// Vertex position
in vec4 ds_Position;
// Vertex color
in vec4 ds_Color;
// Processed vertex color
out vec4 ds_VertexColor;
void main()
{
gl_Position = ds_ModelViewProjection * ds_Position;
ds_VertexColor = ds_Color;
}
--- Geometry Shader
#version 410 core
// Viewport size, in pixels
uniform vec2 ds_Viewport;
// Line width, in pixels
uniform float ds_LineWidth = 2.0;
// Processed vertex color (from VS, in clip space)
in vec4 ds_VertexColor[2];
// Processed primitive vertex color
out vec4 ds_GeoColor;
layout (lines) in;
layout (triangle_strip, max_vertices = 4) out;
void main()
{
vec3 ndc0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
vec3 ndc1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;
vec2 lineScreenForward = normalize(ndc1.xy - ndc0.xy);
vec2 lineScreenRight = vec2(-lineScreenForward.y, lineScreenForward.x);
vec2 lineScreenOffset = (vec2(ds_LineWidth) / ds_ViewportSize) * lineScreenRight;
gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0);
ds_GeoColor = ds_VertexColor[0];
EmitVertex();
gl_Position = vec4(ndc0.xy - lineScreenOffset, ndc0.z, 1.0);
ds_GeoColor = ds_VertexColor[0];
EmitVertex();
gl_Position = vec4(ndc1.xy + lineScreenOffset, ndc1.z, 1.0);
ds_GeoColor = ds_VertexColor[1];
EmitVertex();
gl_Position = vec4(ndc1.xy - lineScreenOffset, ndc1.z, 1.0);
ds_GeoColor = ds_VertexColor[1];
EmitVertex();
EndPrimitive();
}
--- Fragment Shader
// Processed primitive vertex color
in vec4 ds_GeoColor;
// The fragment color.
out vec4 ds_FragColor;
void main()
{
ds_FragColor = ds_GeoColor;
}
Your mistake is in this:
gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0 /* WRONG */);
To fix it:
vec4 cpos = gl_in[0].gl_Position;
gl_Position = vec4(cpos.xy + lineScreenOffset*cpos.w, cpos.z, cpos.w);
What you did was: lose information about W, and thus detune the HW clipper, downgrading it from a 3D clipper into a 2D one.
Today I've found the answer by myself. I don't understand it entirely, but this solved the question.
The problem arise when the line vertices goes beyond the near plane of the projection matrix defined for the scene (in my case, all ending vertices of the three lines). The solution is to manually clip the line vertices within the view frustum (in this way the vertices cannot go beyond the near plane!).
What happens to ndc0 and ndc1 when they are out of the view frustum? Looking at the images, it seems that the XY components have the sign changed (after they are transformed in clip space!): that would mean that the W coordinate is opposite of the normal one, isn't?
Without the geometry shader, the rasterizer would have be taken the responsability to clip those primitives outside the view frustum, but since I've introduced the geometry shader, I need to compute those result by myself. Do anyone can suggest me some link about this matter?
I met a similar problem, where I was trying to draw normals of vertices as colored lines. The way I was drawing the normal was that I draw all the vertices as points, and then use the GS to expand each vertex to a line. The GS was straightforward, and I found that there were random incorrect lines running through the entire screen. Then I added this line into the GS (marked by the comment below), the problem is fixed. Seems like that the problem was because one end of the line is within frustum but the other is outside, so I end up with lines running across the entire screen.
// Draw normal of a vertex by expanding a vertex into a line
[maxvertexcount(2)]
void GSExpand2( point PointVertex points[ 1 ], inout LineStream< PointInterpolants > stream )
{
PointInterpolants v;
float4 pos0 = mul(float4(points[0].pos, 1), g_viewproj);
pos0 /= pos0.w;
float4 pos1 = mul(float4(points[0].pos + points[0].normal * 0.1f, 1), g_viewproj);
pos1 /= pos1.w;
// seems like I need to manually clip the lines, otherwise I end up with incorrect lines running across the entire screen
if ( pos0.z < 0 || pos1.z < 0 || pos0.z > 1 || pos1.z > 1 )
return;
v.color = float3( 0, 0, 1 );
v.pos = pos0;
stream.Append( v );
v.color = float3( 1, 0, 0 );
v.pos = pos1;
stream.Append( v );
stream.RestartStrip();
}
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]);