# Subdividing triangles using a GS

This topic is 898 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi,

I've been trying to subdivide triangles using the geometry shader.

After lots of attempts I'm afraid I'm blindstaring and overseeing something.

The object I'm using is a sphere.

Here is the code I'm using. I've also created a 'dummy' geometry shader to see if the rest of the application is OK. The dummy GS gives correct output. Also tried different meshes/ models as input (cube etc)., but in alle cases the output is a 'star-like' mesh, see the screenshot below.

Any input would be really appreciated.

// the geometry shader
[maxvertexcount(8)]
void GS(triangle VertexOut gin[3], inout TriangleStream<GeoOut> triStream)
{
// calculate distance to camera
/*	float4 worldVtxIn = mul(testOut[0], gMatWorld);
float3 toEye = gEyePosW - worldVtxIn.xyz;
float dist = length(toEye);

if(dist > 50.0f) return;*/

VertexOut tempVertexOut[6];
SubDivide(gin, tempVertexOut);

GeoOut gout[6];
[unroll]
for(int i=0;i<6;++i)
{
gout[i].PosW     = mul(float4(tempVertexOut[i].PosL, 1.0f), gMatWorld).xyz;
gout[i].NormalW  = mul(tempVertexOut[i].NormalL, (float3x3)gWorldInvTranspose);

gout[i].PosH     = mul(float4(tempVertexOut[i].PosL, 1.0f), gWorldViewProj);

gout[i].Tex      = tempVertexOut[i].Tex;
}

[unroll]
for(int j=0;j<5;++j)
{
triStream.Append(gout[j]);
}
triStream.RestartStrip();

triStream.Append(gout[1]);
triStream.Append(gout[5]);
triStream.Append(gout[3]);
}

// SPECIFIC FUNCTIONS FOR SUBDIVISION OF TRIANGLES - CHAPTER 11, EXERCISE 2: ICOSAHEDRON
void SubDivide(VertexOut inVerts[3], out VertexOut outVerts[6])
{
VertexOut m[3];

// calculate edge midpoints
m[0].PosL = 0.5f * (inVerts[0].PosL + inVerts[1].PosL);
m[1].PosL = 0.5f * (inVerts[1].PosL + inVerts[2].PosL);
m[2].PosL = 0.5f * (inVerts[2].PosL + inVerts[0].PosL);

// project onto unit sphere
m[0].PosL = normalize(m[0].PosL);
m[1].PosL = normalize(m[1].PosL);
m[2].PosL = normalize(m[2].PosL);

// derive normals
m[0].NormalL = m[0].PosL;
m[1].NormalL = m[1].PosL;
m[2].NormalL = m[2].PosL;

// interpolate tex coords
m[0].Tex = 0.5f * (inVerts[0].Tex + inVerts[1].Tex);
m[1].Tex = 0.5f * (inVerts[1].Tex + inVerts[2].Tex);
m[2].Tex = 0.5f * (inVerts[2].Tex + inVerts[0].Tex);

//  output
outVerts[0] = inVerts[0];
outVerts[1] = m[0];
outVerts[2] = m[2];
outVerts[3] = m[1];
outVerts[4] = inVerts[2];
outVerts[5] = inVerts[1];
};



And the backup / working GS:

// backup geometry shader
[maxvertexcount(3)]
void GSBackup(triangle VertexOut gin[3], inout TriangleStream<GeoOut> triStream)
{
float4 testOut[3];
testOut[0] = float4(gin[0].PosL, 1.0f);
testOut[1] = float4(gin[1].PosL, 1.0f);
testOut[2] = float4(gin[2].PosL, 1.0f);

// calculate distance to camera
float4 worldVtxIn = mul(testOut[0], gMatWorld);
float3 toEye = gEyePosW - worldVtxIn.xyz;
float dist = length(toEye);

//	if(dist > 50.0f) return;

GeoOut gout;
[unroll]
for(int i=0;i<3;++i)
{
gout.PosH     = mul(testOut[i], gViewProj);
gout.PosW     = mul(testOut[i], gMatWorld).xyz;
gout.NormalW  = mul(float4(gin[i].NormalL, 1.0f), gMatWorld).xyz;
gout.Tex      = gin[i].Tex;

triStream.Append(gout);
}
}



Result:

##### Share on other sites

Hi,

This order:

    outVerts[0] = inVerts[0];
outVerts[1] = m[0];
outVerts[2] = m[2];
outVerts[3] = m[1];
outVerts[4] = inVerts[2];
outVerts[5] = inVerts[1];


Doesn't look right. m[1] is between inVert[1] and inVert[2] so a triangle made up from them doesn't seem correct. You should state, are you using triangle list or strip, because you have a restart strip command in there. Also a pic of what you expect would help.

PS: Why is max vertex count 8?

Edited by TeaTreeTim

##### Share on other sites

Hi.
I'm inputting 1 triangle (lists) with 3 verts. The output of each GS execution should be a tri strip with 3 triangles, 2 as one strip and the last triangle as a new strip (subdivision). I believe that's why I need max 8 verts out. But now I think about it, 6 should be enough.

This is the Original mesh/ object, without subdivision (drawn with the dummy GS):

This is the aimed result, all triangles subdivided once through the GS:

(generated by making the Original mesh have 1 subdivision)

The unlogical order of the vertices of the 1st two triangles, might be because they're drawn as a strip.

Edited by cozzie

##### Share on other sites
I think I figured the theory out.
An example:

v0
.. /\
. / . \
./ . \
/------\
v1. v2

........ /.. \
v3.../.......\. v4
..../---------- \
../...\…..../.....\
/------\-*-/-------\
.........v5

This leads to 6 unique vertices.
To be able to output the subdivided triangle, I need two strips:

Strip 1: v1, v3, v5, v4, v2
Strip 2: v3, v0, v4

Voila, 5 + 3 = max vertexcount of the GS: 8.

To calculate the new vertices (v3, 4 and 5), I take the midpoints of the 3 edges of the original triangle. Just figured this out by drawing it on paper and making the calculations by hand. Now I just have to verify this (correct) theory with the code.

##### Share on other sites

Hi, A long hand version:

PSIn MidPoint(PSIn a, PSIn b)
{
PSIn result;
result.pos = (a.pos + b.pos) / 2.0f;
result.tex = (a.tex + b.tex) / 2.0f;
// project pos to the same radius as the other points here
return result;
}

[maxvertexcount(8)]
void GS(triangle PSIn input[3], inout TriangleStream<PSIn> triStream)
{
PSIn pointA = input[0];
PSIn pointB = input[1];
PSIn pointC = input[2];

PSIn pointAB = MidPoint(pointA, pointB);
PSIn pointAC = MidPoint(pointA, pointC);
PSIn pointBC = MidPoint(pointB, pointC);

pointA.pos = mul(pointA.pos, viewMatrix);
pointA.pos = mul(pointA.pos, projectionMatrix);
pointB.pos = mul(pointB.pos, viewMatrix);
pointB.pos = mul(pointB.pos, projectionMatrix);
pointC.pos = mul(pointC.pos, viewMatrix);
pointC.pos = mul(pointC.pos, projectionMatrix);
pointAB.pos = mul(pointAB.pos, viewMatrix);
pointAB.pos = mul(pointAB.pos, projectionMatrix);
pointAC.pos = mul(pointAC.pos, viewMatrix);
pointAC.pos = mul(pointAC.pos, projectionMatrix);
pointBC.pos = mul(pointBC.pos, viewMatrix);
pointBC.pos = mul(pointBC.pos, projectionMatrix);

triStream.Append(pointA);
triStream.Append(pointAB);
triStream.Append(pointAC);
triStream.Append(pointBC);
triStream.Append(pointC);

triStream.RestartStrip();

triStream.Append(pointAB);
triStream.Append(pointB);
triStream.Append(pointBC);

triStream.RestartStrip();
}



Using triangle strip just seems silly to me, I'd use list and it would make more logical sense but the above will work, you'd need to project the mid points to the sphere as well though. I stand by my original point that I think your order is incorrect, and add the second restart strip like I did.

##### Share on other sites
Thanks, I agree on using the lists. Using a strip is for practice and understanding (an actual excercise in my d3d11 book).

I'm gonna play around and see if I can get it working.

1. 1
2. 2
Rutin
16
3. 3
4. 4
5. 5

• 26
• 11
• 9
• 9
• 11
• ### Forum Statistics

• Total Topics
633703
• Total Posts
3013455
×