OpenGL ES2.0 Lighting in the vertex shader or fragment shader - android

I have seen many different tutorials on lighting in OpenGL ES2.0.
Some use the vertex shader to do all the lighting and transforms and then just pass the final colour through the fragment shader.
Others pass the position and other variables from the vertex shader and then do all the lighting in the fragment shader.
From my experience i always thought lighting should be done in the fragment shader. Can anyone tell my why do one over the other?

Traditional, fixed-pipeline OpenGL did lighting at the vertices and merely interpolated per fragment. So it tended to show visible seaming along edges:
That was considered an acceptable compromise however, because lighting was too expensive to do per-pixel. Hardware is better now but lighting is still more expensive to do per pixel. So I guess there's a potential argument there. Also I guess if you were trying to emulate the old fixed pipeline you might deliberately do lighting inaccurately.
However I'm struggling to think of any particularly sophisticated algorithm that would be amenable. Is it possible that the examples you've seen are just doing things like figuring out the tangent and cotangent vectors per vertex, or some other similar expensive step, then interpolating those per pixel and doing the absolute final calculations in there?

Lighting calculations can be fairly expensive. Since there are a lot more fragments than vertices while rendering a typical model, it's generally more efficient to do the lighting calculations in the vertex shader, and interpolate the results across the fragments. Beyond the pure number of shader executions, performing typical lighting calculations in the fragment shader can also need more operations because interpolated normal need to be re-normalized, which requires relatively expensive sqrt operations.
The downside of per-vertex lighting is that it works poorly if the lighting values change quickly across a surface. This makes perfect sense, because the values are interpolated linearly across triangles. If the desired value does not change approximately linearly across the triangle, this will introduce artifacts.
The prototypical example are specular highlights. If you define a shiny material with relatively sharp/small specular highlights, you can easily see the brightness of the highlight changing while the object is animated. It also looks like the highlight seems to "wander" around on the object. For example, if you rotate a sphere with a specular highlight around its center, the highlight should stay exactly the same. But with per-vertex lighting, the brightness of the highlight will increase and decrease, and it will wobble slightly.
There's two main ways to avoid these effects, or at least reduce them to a level where they don't look disturbing anymore:
Use per-fragment lighting.
Use a finer tessellation for the geometry.
Which solution is better needs to be decided case by case. Of course using a finer tessellation adds overhead on the geometry processing side, while using per-fragment lighting adds overhead in the fragment shader.
Per-vertex lighting becomes even more problematic when you want to apply effects like bump mapping, where the lighting values change very quickly across the surface. In those cases, there's almost no way around using per-fragment lighting.
I have seen advice suggesting that GPUs were so fast now that per-vertex lighting should never be used anymore. I think that's a gross simplification. Even if you can get the desired performance with per-fragment lighting, most computers/devices these days are battery powered. To be power efficient, making your rendering as efficient as possible is as important as it ever was. And I believe that there are still use cases where per-vertex lighting is the most efficient approach.

Related

OpenGLES 3.0, Android: How to draw intersection of two objects

I am currently working on a project using OpenGL-ES 3.0 for Android. In my project, I have drawn a 3d human head, whose centroid lies at the origin. I also have a cylinder, with the center of one of its faces lying on the origin. The cylinder extends out longer than the length of the head. In other words, I have a cylinder running through the head.
Right now, I am just using the default depth test (GL_LESS) to NOT draw the section of the cylinder that lies inside of the head. What would be ideal for my project is if I could somehow only draw the circle where the cylinder intersects with the head. I tried changing my depth test to (GL_EQUAL) but it did not do the trick.
How can I do this? Keep in mind that the head object is very complex, with a large amount of points and triangles.
The most practical solution that comes to mind is to discard the fragments outside the cylinder in the fragment shader.
One approach for this is that you pass the original coordinates from the vertex shader into the fragment shader. Say you currently have a typical vertex shader that applies a MVP transformation:
uniform mat4 MvpMat;
in vec4 InPos;
...
gl_Position = MvpMat * InPos;
You can extend this to:
uniform mat4 MvpMat;
in vec4 InPos;
out vec4 OrigPos;
...
gl_Position = MvpMat * InPos;
OrigPos = InPos;
Then in the fragment shader (for a cylinder with the given radius along the z-axis):
uniform float CylRad;
in vec4 OrigPos;
...
if (dot(OrigPos.xy, OrigPos.xy) > CylRad * CylRad) {
discard;
}
There are countless variations of how exactly you can handle this. For example, instead of passing the original coordinates into the fragments shader, you could transform the cylinder geometry, and then perform the test using the transformed vertex/fragment coordinates. You'll have to figure out what looks the cleanest based on your exact use, but this should illustrate the basic idea.
Discarding fragments can have a negative performance impact. But unless you're already pushing the performance envelope, this approach might work just fine. And I doubt that there's a solution that will not have a performance cost.
It would be nice to operate on the vertex level. Full OpenGL has clip planes, which could potentially be used creatively for this case, but the clip plane feature is not in any version of OpenGL ES. Without this, there is really no way of discarding vertices, at least that I can think of. So discarding fragments might be the most reasonable option.

Shared vertex indices with normals in opengl

In opengl or opengl-es you can use indices to share a vertices. This works fine if you are only using vertex coords and texture coords that don't change, but when using normals, the normal on a vertex may change depending on the face. Does this mean that you are essentially forced to scrap vertex sharing in opengl? This article http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/
seems to imply that this is the case, but I wanted a second opinion. I'm using .obj models so should I just forget about trying to share verts? This seems like it would increase the size of my model though as I iterate and recreate the array since i am repeating tons of verts and their tex/normal attributes.
The link you posted explains the situation well. I had the same question in my mind couple months ago.I remember I read that tutorial.
If you need exactly 2 different normal, so you should add that vertex twice in your index list. For example, if your mesh is a cube you should add your vertices twice.
Otherwise indexing one vertex and calculating an average normal is kind of smoothing your normal transitions on your mesh. For example if your mesh is a terrain or a detailed player model etc. you can use this technique which you save free space and get better looking result.
If you ask how to calculate average normal, I used average normal calculating algorithm from this question and result is fast and good.
If the normals are flat faces then you can annotate the varying use in the fragment shader with the "flat" qualifier. This means only the value from the provoking vertex is used. With a good model exporter you can get relatively good vertex sharing with this.
Not sure on availability on GLES2, but is part of GLES3.
Example: imagine two triangles, expressed as a tri-strip:
V0 - Norm0
V1 - Norm1
V2 - Norm2
V2 - Norm3
Your two triangles will be V0/1/2 and V1/2/3. If you mark the varying variable for the normal as "flat" then the first triangle will use Norm0 and the second triangle will use Norm1 (i.e. only the first vertex in the triangle - known as the provoking vertex - needs to have the correct normal). This means that you can safely reuse vertices in other triangles, even if the normal is "wrong" provides that you make sure that it isn't the provoking vertex for that triangle.

Does opengl render a large mesh broken into smaller triangle faster?

If you render a large quad that extends beyond the borders of the screen would opengl render faster if you break that quad into smaller triangle/segments? To a point of course, say instead of two triangles in the quad you managed to break it up into 18 triangles or so. This way the triangles that are off the screen would not be sent to the fragment shader? Too many triangles obviously defeats the purpose and slows it down. Am I way off on that one? Based on my understanding of opengl when triangles are off screen they aren't rendered (sent to fragment shader) even if a draw call is called. The fragment shader is the slowest part and if less fo the quad is going to it, it would be more efficient?
I wouldn't expect splitting large triangles into smaller triangles to be beneficial for performance.
Clipping happens in a fixed function block between vertex shader and fragment shader. The fragment shader will never see fragments that are outside the window. So you don't have to worry about processing fragments outside the window. They will be eliminated no matter how big or small the triangles are.
The spec suggests that clipping happens on the vertices in clip coordinate space, before rasterization. But as always, OpenGL implementations are free to handle this differently, as long as the end result is the same. So some hardware architectures may clip the vertices, while others may rasterize first and then perform clipping on the resulting fragments. Or they may use a hybrid between the two approaches.
Now, if you're running on a hardware architecture that (partly) clips in fragment space, it's theoretically possible that you have more rasterization overhead with large triangles that are mostly off screen. But it seems very unlikely that this would ever turn into a real bottleneck.
If a large part of your geometry is getting clipped, it may be beneficial to apply some form of culling before rendering, so that objects completely outside the view volume are never rendered.

Specular over texture in OpenGL ES?

I'm trying to get specular highlight over objetcs that are texture mapped. As far as I know, the only direct way for OpenGL to not compute the final color over the texture color (this is, as for example plain white) is with the call glLightModelf(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR), but that is not supported in OpenGL ES.
So, how can I do this? Do I have to use another texture for the specluar highlight, of is there another easier way?
Than you!
P.S. I'm using OpenGL ES 1.x
One workaround would be to run in two passes: first pass renders the texture with ambient & diffuse lighting, the second pass renders the specular highlights on top of that (without texturing enabled).
Thanks for the great suggestions, they saved my day. The only problem was a flicker. I first thought there was a problem with a depth buffer and limited depth resolution but it was not the case. I had to use gl.glDepthFunc(GL10.GL_ALWAYS); for perfect blending. However for complex primitives you will be able to see through the object.
After playing for another week I figured that one simply has to disable depth test when doing blending and simply take care of the order in which you render objects on each pass. Typically you want to finish all rendering passes for far object before drawing a closer one.
This takes care for flicker problem that I had completely.

Android OpenGL ES glDrawArrays or glDrawElements?

What is the best way: if I use glDrawArrays, or if I use glDrawElements? Any difference?
For both, you pass OpenGL some buffers containing vertex data.
glDrawArrays is basically "draw this contiguous range of vertices, using the data I gave you earlier".
Good:
You don't need to build an index buffer
Bad:
If you organise your data into GL_TRIANGLES, you will have duplicate vertex data for adjacent triangles. This is obviously wasteful.
If you use GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN to try and avoid duplicating data: it isn't terribly effective and you'd have to make a rendering call for each strip and fan. OpenGL calls are expensive and should be avoided where possible
With glDrawElements, you pass in buffer containing the indices of the vertices you want to draw.
Good
No duplicate vertex data - you just index the same data for different triangles
You can just use GL_TRIANGLES and rely on the vertex cache to avoid processing the same data twice - no need to re-organise your geometry data or split rendering over multiple calls
Bad
Memory overhead of index buffer
My recommendation is to use glDrawElements
The performance implications are probably similar on the iphone, the OpenGL ES Programming Guide for iOS recommends using triangle strips and joining multiple strips through degenerate triangles.
The link has a nice illustration of the concept. This way you could reuse some vertices and still do all the drawing in one step.
For best performance, your models should be submitted as a single unindexed triangle strip using glDrawArrays with as few duplicated vertices as possible. If your models require many vertices to be duplicated (because many vertices are shared by triangles that do not appear sequentially in the triangle strip or because your application merged many smaller triangle strips), you may obtain better performance using a separate index buffer and calling glDrawElements instead. There is a trade off: an unindexed triangle strip must periodically duplicate entire vertices, while an indexed triangle list requires additional memory for the indices and adds overhead to look up vertices. For best results, test your models using both indexed and unindexed triangle strips, and use the one that performs the fastest.
Where possible, sort vertex and index data so that that triangles that share common vertices are drawn reasonably close to each other in the triangle strip. Graphics hardware often caches recent vertex calculations, so locality of reference may allow the hardware to avoid calculating a vertex multiple times.
The downside is that you probably need a preprocessing step that sorts your mesh in order to obtain long enough strips.
I could not come up with a nice algorithm for this yet, so I can not give any performance or space numbers compared to GL_TRIANGLES. Of course this is also highly dependent on the meshes you want to draw.
Actually you can degenerate the triangle strip to create continuous strips so that you don't have to split it while using glDrawArray.
I have been using glDrawElements and GL_TRIANGLES but thinking about using glDrawArray instead with GL_TRIANGLE_STRIP. This way there is no need for creating inidices vector.
Anyone that knows more about the vertex cache thing that was mentioned above in one of the posts? Thinking about the performance between glDrawElements/GL_TRIANGLE vs glDrawArray/GL_TRIANGLE_STRIP.
The accepted answer is slightly outdated. Following the doc link in Jorn Horstmann's answer, OpenGL ES Programming Guide for iOS, Apple describes how to use "degenerate-triangles" trick with DrawElements, thereby gaining the best of both worlds.
The minor savings of a few indices by using DrawArrays isn't worth the savings you get by combining all your data into a single GL call, DrawElements. (You could combine all using DrawArrays, but then any "wasted elements" would be wasting vertices, which are much larger than indices, and require more render time too.)
This also means you don't need to carefully consider all your models, as to whether most of them can be rendered as a minimal number of strips, or they are too complex. One uniform solution, that handles everything. (But do try to organize into strips where possible, to minimize data sent, and maximize GPU likeliness of re-using recently cached vertex calculations.)
BEST: A single DrawElements call with GL_TRIANGLE_STRIP, containing all your data (that is changing in each frame).

Categories

Resources