Implementing clip planes with geometry shaders? - c++

What am I using: Qt 5.11.1, MinGW 5.3, Windows 10, C++11, GPU: NVidia 820M (supports OpenGL 4.5)
My task: I have non-solid (just surface) object, rendering by glDrawArrays, and i need to get cross-section of this object by plane. I have found ancient openGL function glClipPlane, but its not compability with VAOs and VBOs. Also Ive found out that its possible to rewrite glClipPlane via geometry shader.
My questions/problems:
Do you know other ways to realize this task?
I really dont understand, how to add geometry shader in QtCreator, there is no "icon" of geometry shader, I tried to add vertex shader and rename it to .gsh or just .glsl, tried to use QOpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::Geometry, QString &source) and write shader code in program, but every time I get "QOpenGLShader: could not create shader" on string with adding geometry shader.
look of adding shader into program
Vertex shader:
layout (triangles) in;
layout (triangles) out;
layout (max_vertices = 3) out;
void main()
{
int i;
for (i = 0; i < gl_in.length(); i++)
{
gl_Position = gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}
Geometry shader:
layout (triangles) in;
layout (triangles) out;
layout (max_vertices = 3) out;
void main()
{
int i;
for (i = 0; i < gl_in.length(); i++)
{
gl_Position = gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}
Fragment shader:
precision mediump float;
uniform highp float u_lightPower;
uniform sampler2D u_texture;
uniform highp mat4 u_viewMatrix;
varying highp vec4 v_position;
varying highp vec2 v_texCoord;
varying highp vec3 v_normal;
void main(void)
{
vec4 resultColor = vec4(0.25, 0.25, 0.25, 0.0);
vec4 diffMatColor = texture2D(u_texture, v_texCoord);
vec3 eyePosition = vec3(u_viewMatrix);
vec3 eyeVect = normalize(v_position.xyz - eyePosition);
float dist = length(v_position.xyz - eyePosition);
vec3 reflectLight = normalize(reflect(eyeVect, v_normal));
float specularFactor = 1.0;
float ambientFactor = 0.05;
vec4 diffColor = diffMatColor * u_lightPower * dot(v_normal, -eyeVect);// * (1.0 + 0.25 * dist * dist);
resultColor += diffColor;
gl_FragColor = resultColor;
}

Let's sort out a few misconceptions first:
have found ancient openGL function glClipPlane, but its not compability with VAOs and VBOs.
That is not correct. The user defined clip planes via glClipPlane are indeed deprecated in modern GL, and removed from core profiles. But if you use a context where they still exist, you can combine them with VAOs and VBOs without any issue.
Also Ive found out that its possible to rewrite glClipPlane via geometry shader.
You don't need a geometry shader for custom clip planes.
The modern way of user-defined clip planes is calculating gl_ClipDistance for each vertex. While you can modify this value in a geometry shader, you can also directly generate it in the vertex shader. If you don't otherwise need a geometry shader, there is absolutely no reason to add it just for the clip planes.
I really dont understand, how to add geometry shader in QtCreator, there is no "icon" of geometry shader, I tried to add vertex shader and rename it to .gsh or just .glsl, tried to use OpenGLShaderProgram::addShaderFromSourceCode(QOpenGLShader::Geometry, QString &source) and write shader code in program, but every time I get "QOpenGLShader: could not create shader" on string with adding geometry shader.
You first need to find out which OpenGL version you're actually using. With Qt, you can easily end up with an OpenGLES 2.0 context (depending on how you create the context, and also how your Qt was compiled). Your shader code is either desktop GL 2.x (GLSL 1.10/1.20) or GLES 2.0 (GLSL 1.00ES), but not valid in modern core profiles of OpenGL.
GLES2 does not support geometry shaders at all. It also does not support gl_ClipDistance, so if you _really) have to use GLES2, you can try to emulate the clipping in the fragment shader. But the better option would be switching to a modern core profile GL context.

While glClipPlane is deprecated in modern OpenGL, the concept of clipping planes is not.
In your CPU code before you start drawing the geometry to be clipped you must enable one of the clipping planes.
glEnable(GL_CLIP_DISTANCE0);
Once you have finished drawing you would disable this in a similar way.
glDisable(GL_CLIP_DISTANCE0);
You are guaranteed to be able to enable minimum of 8 clipping planes.
In your vertex or geometry shader you must then tell OpenGL the signed distance of your vertex from the plane so that it knows what to clip. To be clear you don't need a geometry shader for clipping but it can be done there if you wish. The shader code would look something like the following:
// vertex in world space
vec4 vert_pos_world = world_matrix * vec4(vert_pos_model, 1.0);
// a horizontal plane at a specified height with normal pointing up
// could be a uniform or hardcoded
vec4 plane = vec4(0, 1, 0, clip_height_world);
// 0 index since that's the clipping plane we enabled
gl_ClipDistance[0] = dot(vert_pos_world, plane);

Related

OpenGL procedural texture antialiasing

I`ve made a grid using a simple GLSL shader, passing texture coordinates to fragment shader. It was applied onto a large scaled plane.
Fragment shader:
#version 330 core
out vec4 fragColor;
smooth in vec2 f_TexCoord;
vec4 gridColor;
void main()
{
if(fract(f_TexCoord.x / 0.0005f) < 0.025f || fract(f_TexCoord.y / 0.0005f) < 0.025f)
gridColor = vec4(0.75, 0.75, 0.75, 1.0);
else
gridColor = vec4(0);
// Check for alpha transparency
if(gridColor.a != 1)
discard;
fragColor = gridColor;
}
As you can see the lines are not smooth and they start to "flickering" at the horizon.
Is it possible to apply some sort of filtering/antialiasing on it? I've tried to increase number of samples (up to 4, because higher values gives me a qt error), but it has no affect on shader.
Switch to GLSL version 4.20 (at least), activate multisampling and use the Auxiliary Storage Qualifier sample for the vertex shader output (and fragment shader input):
#version 420 core
sample smooth in vec2 f_TexCoord;
The qualifier causes per-sample interpolation.

DirectX11 / OpenGL only renders half of the texture

This is how it should look like. It uses the same vertices/uv coordinates which are used for DX11 and OpenGL. This scene was rendered in DirectX10.
This is how it looks like in DirectX11 and OpenGL.
I don't know how this can happen. I am using for both DX10 and DX11 the same code on top and also they both handle things really similiar. Do you have an Idea what the problem may be and how to fix it?
I can send code if needed.
also using another texture.
changed the transparent part of the texture to red.
Fragment Shader GLSL
#version 330 core
in vec2 UV;
in vec3 Color;
uniform sampler2D Diffuse;
void main()
{
//color = texture2D( Diffuse, UV ).rgb;
gl_FragColor = texture2D( Diffuse, UV );
//gl_FragColor = vec4(Color,1);
}
Vertex Shader GLSL
#version 330 core
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexColor;
layout(location = 3) in vec3 vertexNormal;
uniform mat4 Projection;
uniform mat4 View;
uniform mat4 World;
out vec2 UV;
out vec3 Color;
void main()
{
mat4 MVP = Projection * View * World;
gl_Position = MVP * vec4(vertexPosition,1);
UV = vertexUV;
Color = vertexColor;
}
Quickly said, it looks like you are using back face culling (which is good), and the other side of your model is wrongly winded. You can ensure that this is the problem by turning back face culling off (OpenGL: glDisable(GL_CULL_FACE​)).
The real correction is (if this was the problem) to have correct winding of faces, usually it is counter-clockwise. This depends where you get this model. If you generate it on your own, correct winding in your model generation routine. Usually, model files created by 3D modeling software have correct face winding.
This is just a guess, but are you telling the system the correct number of polygons to draw? Calls like glBufferData() take the size in bytes of the data, not the number of vertices or polygons. (Maybe they should have named the parameter numBytes instead of size?) Also, the size has to contain the size of all the data. If you have color, normals, texture coordinates and vertices all interleaved, it needs to include the size of all of that.
This is made more confusing by the fact that glDrawElements() and other stuff takes the number of vertices as their size argument. The argument is named count, but it's not obvious that it's vertex count, not polygon count.
I found the error.
The reason is that I forgot to set the Texture SamplerState to Wrap/Repeat.
It was set to clamp so the uv coordinates were maxed to 1.
A few things that you could try :
Is depth test enabled ? It seems that your inner faces of the polygons from the 'other' side are being rendered over the polygons that are closer to the view point. This could happen if depth test is disabled. Enable it just in case.
Is lighting enabled ? If so turn it off. Some flashes of white seem to be coming in the rotating image. Could be because of incorrect normals ...
HTH

Calculate Normals Geometry Shader

Im currently writing my own GLSL shaders, and wanted to have smooth shading. The shading worked when i calculated the normals bevore sending them to a VBO, but the problem here is when I implement animations with bone matricies, the normals are not corect.
I am using a geometry shader to calculate the normals, but i cant find out how to smooth them.
Here is my geometry shader:
#version 150
layout(triangles) in;
layout (triangle_strip, max_vertices=3) out;
in vec2 texCoord0[3];
in vec3 worldPos0[3];
out vec2 texCoord1;
out vec3 normal1;
out vec3 worldPos1;
void main()
{
vec3 n = cross(worldPos0[1].xyz-worldPos0[0].xyz, worldPos0[2].xyz-worldPos0[0].xyz);
for(int i = 0; i < gl_in.length(); i++)
{
gl_Position = gl_in[i].gl_Position;
texCoord1 = texCoord0;
normal1 = n;
worldPos1 = worldPos0;
EmitVertex();
}
}
I need the faces next to the face that I calculate the normals for, but i dont know how to get them.
The geometry shader in OpenGL only has access to single triangles and not the whole mesh, so the normal must be calculated from a single triangle.
The usual solution to this problem is to calculate the normals once for each vertex and store them in vertex arrays for easy access. This turns out to be faster and simpler, as you don't need to recalculate anything in shaders.

Point Sprites for particle system

Are point sprites the best choice to build a particle system?
Are point sprites present in the newer versions of OpenGL and drivers of the latest graphics cards? Or should I do it using vbo and glsl?
Point sprites are indeed well suited for particle systems. But they don't have anything to do with VBOs and GLSL, meaning they are a completely orthogonal feature. No matter if you use point sprites or not, you always have to use VBOs for uploading the geometry, be they just points, pre-made sprites or whatever, and you always have to put this geometry through a set of shaders (in modern OpenGL of course).
That being said point sprites are very well supported in modern OpenGL, just not that automatically as with the old fixed-function approach. What is not supported are the point attenuation features that let you scale a point's size based on it's distance to the camera, you have to do this manually inside the vertex shader. In the same way you have to do the texturing of the point manually in an appropriate fragment shader, using the special input variable gl_PointCoord (that says where in the [0,1]-square of the whole point the current fragment is). For example a basic point sprite pipeline could look this way:
...
glPointSize(whatever); //specify size of points in pixels
glDrawArrays(GL_POINTS, 0, count); //draw the points
vertex shader:
uniform mat4 mvp;
layout(location = 0) in vec4 position;
void main()
{
gl_Position = mvp * position;
}
fragment shader:
uniform sampler2D tex;
layout(location = 0) out vec4 color;
void main()
{
color = texture(tex, gl_PointCoord);
}
And that's all. Of course those shaders just do the most basic drawing of textured sprites, but are a starting point for further features. For example to compute the sprite's size based on its distance to the camera (maybe in order to give it a fixed world-space size), you have to glEnable(GL_PROGRAM_POINT_SIZE) and write to the special output variable gl_PointSize in the vertex shader:
uniform mat4 modelview;
uniform mat4 projection;
uniform vec2 screenSize;
uniform float spriteSize;
layout(location = 0) in vec4 position;
void main()
{
vec4 eyePos = modelview * position;
vec4 projVoxel = projection * vec4(spriteSize,spriteSize,eyePos.z,eyePos.w);
vec2 projSize = screenSize * projVoxel.xy / projVoxel.w;
gl_PointSize = 0.25 * (projSize.x+projSize.y);
gl_Position = projection * eyePos;
}
This would make all point sprites have the same world-space size (and thus a different screen-space size in pixels).
But point sprites while still being perfectly supported in modern OpenGL have their disadvantages. One of the biggest disadvantages is their clipping behaviour. Points are clipped at their center coordinate (because clipping is done before rasterization and thus before the point gets "enlarged"). So if the center of the point is outside of the screen, the rest of it that might still reach into the viewing area is not shown, so at the worst once the point is half-way out of the screen, it will suddenly disappear. This is however only noticable (or annyoing) if the point sprites are too large. If they are very small particles that don't cover much more than a few pixels each anyway, then this won't be much of a problem and I would still regard particle systems the canonical use-case for point sprites, just don't use them for large billboards.
But if this is a problem, then modern OpenGL offers many other ways to implement point sprites, apart from the naive way of pre-building all the sprites as individual quads on the CPU. You can still render them just as a buffer full of points (and thus in the way they are likely to come out of your GPU-based particle engine). To actually generate the quad geometry then, you can use the geometry shader, which lets you generate a quad from a single point. First you do only the modelview transformation inside the vertex shader:
uniform mat4 modelview;
layout(location = 0) in vec4 position;
void main()
{
gl_Position = modelview * position;
}
Then the geometry shader does the rest of the work. It combines the point position with the 4 corners of a generic [0,1]-quad and completes the transformation into clip-space:
const vec2 corners[4] = {
vec2(0.0, 1.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(1.0, 0.0) };
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
uniform mat4 projection;
uniform float spriteSize;
out vec2 texCoord;
void main()
{
for(int i=0; i<4; ++i)
{
vec4 eyePos = gl_in[0].gl_Position; //start with point position
eyePos.xy += spriteSize * (corners[i] - vec2(0.5)); //add corner position
gl_Position = projection * eyePos; //complete transformation
texCoord = corners[i]; //use corner as texCoord
EmitVertex();
}
}
In the fragment shader you would then of course use the custom texCoord varying instead of gl_PointCoord for texturing, since we're no longer drawing actual points.
Or another possibility (and maybe faster, since I remember geometry shaders having a reputation for being slow) would be to use instanced rendering. This way you have an additional VBO containing the vertices of just a single generic 2D quad (i.e. the [0,1]-square) and your good old VBO containing just the point positions. What you then do is draw this single quad multiple times (instanced), while sourcing the individual instances' positions from the point VBO:
glVertexAttribPointer(0, ...points...);
glVertexAttribPointer(1, ...quad...);
glVertexAttribDivisor(0, 1); //advance only once per instance
...
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count); //draw #count quads
And in the vertex shader you then assemble the per-point position with the actual corner/quad-position (which is also the texture coordinate of that vertex):
uniform mat4 modelview;
uniform mat4 projection;
uniform float spriteSize;
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 corner;
out vec2 texCoord;
void main()
{
vec4 eyePos = modelview * position; //transform to eye-space
eyePos.xy += spriteSize * (corner - vec2(0.5)); //add corner position
gl_Position = projection * eyePos; //complete transformation
texCoord = corner;
}
This achieves the same as the geometry shader based approach, properly-clipped point sprites with a consistent world-space size. If you actually want to mimick the screen-space pixel size of actual point sprites, you need to put some more computational effort into it. But this is left as an exercise and would be quite the oppisite of the world-to-screen transformation from the the point sprite shader.

How to get flat normals on a cube

I am using OpenGL without the deprecated features and my light calculation is done on fragment shader. So, I am doing smooth shading.
My problem, is that when I am drawing a cube, I need flat normals. By flat normals I mean that every fragment generated in a face has the same normal.
My solution to this so far is to generate different vertices for each face. So, instead of having 8 vertices, now I have 24(6*4) vertices.
But this seems wrong to me, replicating the vertexes. Is there a better way to get flat normals?
Update: I am using OpenGL version 3.3.0, I do not have support for OpenGL 4 yet.
If you do the lighting in camera-space, you can use dFdx/dFdy to calculate the normal of the face from the camera-space position of the vertex.
So the fragment shader would look a little like this.
varying vec3 v_PositionCS; // Position of the vertex in camera/eye-space (passed in from the vertex shader)
void main()
{
// Calculate the face normal in camera space
vec3 normalCs = normalize(cross(dFdx(v_PositionCS), dFdy(v_PositionCS)));
// Perform lighting
...
...
}
Since a geometry shader can "see" all three vertices of a triangle at once, you can use a geometry shader to calculate the normals and send them to your fragment shader. This way, you don't have to duplicate vertices.
// Geometry Shader
#version 330
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out vec3 gNormal;
// You will need to pass your untransformed positions in from the vertex shader
in vec3 vPosition[];
uniform mat3 normalMatrix;
void main()
{
vec3 side2 = vPosition[2] - vPosition[0];
vec3 side0 = vPosition[1] - vPosition[0];
vec3 facetNormal = normalize(normalMatrix * cross(side0, side2));
gNormal = facetNormal;
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gNormal = facetNormal;
gl_Position = gl_in[1].gl_Position;
EmitVertex();
gNormal = facetNormal;
gl_Position = gl_in[2].gl_Position;
EmitVertex();
EndPrimitive();
}
Another option would be to pass MV-matrix and the unrotated AxisAligned coordinate to the fragment shader:
attribute aCoord;
varying vCoord;
void main() {
vCoord = aCoord;
glPosition = aCoord * MVP;
}
At Fragment shader one can then identify the normal by calculating the dominating axis of vCoord, setting that to 1.0 (or -1.0) and the other coordinates to zero -- that is the normal, which has to be rotated by the MV -matrix.