DirectX 11, using Tessellation & Geometry shader in a single pass

Started by
16 comments, last by Jason Z 10 years, 9 months ago

Hello ;)

Before all, sorry for my poor english !

With DirectX 11, i'm trying to create a random map full with GPU.

Using Hull shader stage, I'm managing LOD with tessellation.

Using Domain shader stage, I'm generating the map (based on perlin noise).

Now my goal, is to compute normals in the geometry shader (normal on vertex). For that, I must use vertex adjency, like geometry is capable of.

But here is the problem... For tessellation, my primitives must be D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST.

But for geometry shader with 6 vertices(triangle primitive and adjency), I must use : D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ.

Think I'm missing something... It must be possible to tessellate and use the results in the geometry shader...

However, it's working with 3 points, but I cannot use the 3 others (they are 0.0, 0.0, 0.0)....

Thank you in advance for any help ;)

Advertisement

The domain shader can only (as of D3D 11.0) output points, lines and triangles - without adjacency information, so I don't think this is possible. Even if, I think it would get tricky at patch edges.

So: you need to do your normals in the domain shader.

Thank you !

Do you think, what is the best (if possible) :

1- Computing normals in Domain Shader, which is slow because I must call the noise function multiple times (adjency vertex based on x offset/delta)

2- Using the stream output from Geometry Shader, and in the second pass, passing the buffer data with "TRIANGLE_ADJ" and computing normals in the Geometry shader

Or any other suggestion ?

There is no best wink.png . Only profiling will tell.

Some random thoughts:

1- Computing normals in Domain Shader, which is slow because I must call the noise function multiple times (adjency vertex based on x offset/delta)

One general advice to improve shader performance is to pull up calculations where possible (to hull shader or even vertex shader). In this case unlikely. How about precalculating the noise to a texture and use SampleLevel in the domain shader ? You could even prepare the normals this way, i.e. generate a normal map with a Sobel filter.

2- Using the stream output from Geometry Shader, and in the second pass, passing the buffer data with "TRIANGLE_ADJ" and computing normals in the Geometry shader

I expect pressure on the bandwidth at high tesselation. Also, I have no idea how to deal with the patch edges. Actually, I wonder if this is even so straightforward in the interior. Doesn't the domain shader just give you triangles ? Do we know in which order they come ? Maybe you need to output points anyway to get something sensible to work with.
Thank you again for your help ;)
My goal here, is to have a fully GPU rendered map (spherical). Without "limit", LOD is managed by the tessellator, details by the noise loop (octaves and parameters).
So "in theory", you can go very close to the map without loosing quality & speed.
Because trying solutions is a lot of work, I'm searching the "probably" best way before testing.
You talked about texture... You mean, first pass, generating a texture, and after reading it in the second pass ?
It could be interesting..
What about :
First Pass
- Passing a precomputed spherical model to the GPU
- Using tessellator with camera position & direction for adding vertices (LOD)
- Streaming the buffer output from the geometry shader
OPTION A
Second Pass
- Using only the Compute Shader for computing noise and normals
(In this case, what about parallelism ? How to design the loop ?)
Third Pass
- Pixel shader for lightning
OPTION B
Second Pass
- Using Triangle_Adj as primitives
- Computing noise in vertex shader
- Computing normals in geometry shader
- Pixel shader for lightning
Or maybe, a totaly another solution ?
I don't care for the speed for medium-low hardware.. Only the quality and a good speed on high power GPU (GTX780).

Why not do the noise lookup in your vertex shader? That would allow you to grab the sample early in the pipeline, and then the interpolation between points after you tessellate could be based off of that early lookup. This would effectively reduce your computational load, and if you choose your vertex density correctly then there will be very little difference between what you are proposing and doing it this way.

In fact, since Perlin noise is more or less an interpolation technique, you should be able to get away very easily with this type of approximation...

Thank you Jason for your answer ;)

Actualy, I load a fixed icosphere to the vertex shader. Which hasn't lot of vertices...

If we think about it as a planet, there wil be only 1 vertex for hundreds of kilometers (or miles :-)) !

So, if the noise is applied in this stage, the interpolation after the tesselator will be too large, no details...

I can load a much more detailed sphere, but, if the camera is very close, there will be the same problem, leak of details (or if far away, much more work for the GPU)..

My goal is to have the same amount of vertices (and noise details), whether the camera is very very close or far away.

Sorry for my poor english, don't know if you understand what I mean...

Don't worry - your English is more than sufficient :) I understand your current approach, but this is actually what I am talking about changing. When you use only a single vertex for hundreds of kilometers of area, you are effectively not taking advantage of the vertex shader for any significant computation. Instead of having a fixed resolution icosphere, why not start with a screen space set of vertices which would be generated based on the current view location? That would let you put the resolution where it needs to be, and let your tessellation algorithms be more effective. Remember, there is a limit to the amount of tessellation that can be achieved in one pass, so you can't count on it being too much of an amplification.

Again, thank you ;)

Ok, I understand part of what you explain...

Juste one thing I don't understand totaly :


Instead of having a fixed resolution icosphere, why not start with a screen space set of vertices which would be generated based on the current view location?

Can you tell me more about what you mean ?

Don't know if it's the same process.. But in an earlier project, I was using a projective grid.. Some good results but lot artefacts and problems when camera was too close...

[EDITED]

My proposal, have first pass only to tessellate, and the second, using Vertex Shader for computing noise, would not be too much slower I think.. ? And could resolve totaly the problem ? (with a much more detailed sphere as base model to avoid tessellator limits)

That could work - using one pass to tessellate, and then a second pass to add in the noise. But I think you could just do the noise lookup in the domain shader in that case and produce the whole thing in one pass. This is something you would have to try out and see which way works faster - either with stream output or directly working in the domain shader. Just architect your code to be able to be done in discrete chunks so that you can swap them in and out for profiling.

The projective grid is one possibility, but you could just as easily have a flat, uniform grid of vertices based on the portion of the planet that you are near. Just think of it as a set of patches that make up the planet. You could have multiple resolution patches too, so that when you are close by then you switch to a smaller vertex to vertex distance.

This topic is closed to new replies.

Advertisement