Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

cephalo

Member Since 04 Mar 2011
Offline Last Active Yesterday, 06:30 PM
*****

Topics I've Started

Mip Mapping in the Domain Shader

Yesterday, 01:25 PM

Ok, I want to do some displacement mapping in my domain shader, and some corresponding normal mapping in my pixel shader. I've never done displacement mapping before, but I would guess that it is very important to use the same mip mapping technique for both shader stages. In the pixel shader, mip-mapping happens sort of automatically so I don't really know exactly what's happening enough to emulate it in the domain shader.

 

I figure that for something this basic, there's probably a well established way to use the same mips for sampling in the domain and pixel shaders. Can anyone point me to an article somewhere with some code examples? I'm finding articles that say you should use mip-mapping in the domain shader, but nothing as to how to do that exactly.


How much to tessellate?

23 April 2013 - 05:06 PM

Ok, I have the current distance based LOD scheme set up in my hull shader. It works pretty well, but has some non-optimal aspects. cp[0], cp[1] and cp[2] are the corners of my triangle patch.

PatchConstants PatchHS(InputPatch<VertexOutput,10> cp, uint patchID : SV_PrimitiveID)
{
	PatchConstants pt;

	float3 mid0 = lerp(cp[1].PositionWorld,cp[2].PositionWorld,0.5f);
	float3 mid1 = lerp(cp[2].PositionWorld,cp[0].PositionWorld,0.5f);
	float3 mid2 = lerp(cp[0].PositionWorld,cp[1].PositionWorld,0.5f);

	const float dMax = 250.0f;

	float d = distance(mid0,vecEye.xyz);
	pt.EdgeTess[0] = 12.0f * pow(saturate((dMax - d)/dMax),3);

	d = distance(mid1,vecEye.xyz);
	pt.EdgeTess[1] = 12.0f * pow(saturate((dMax - d)/dMax),3);

	d = distance(mid2,vecEye.xyz);
	pt.EdgeTess[2] = 12.0f * pow(saturate((dMax - d)/dMax),3);

	d = distance(cp[9].PositionWorld,vecEye.xyz);
	pt.InsideTess = 16.0f * pow(saturate((dMax - d)/dMax),2);

	return pt;
}

I'm using the midpoint of my triangle patch edges to calculate distance to the camera. Visually, the need for more or less triangles is nowhere near linear in it's relationship to distance, so I am also applying an exponential curve that sort-of, kind-of helps. This scheme is an improvement over simply using fixed tessellation factors, but it seems that it should be possible to come up with something better. The following screenshot shows one of the weaknesses of a distance based scheme.

Attached File  TessFactors.jpg   73.06K   29 downloads

You can see a mountain in the foreground and a valley between this mountain and other mountains in the background. The tiles in the valley are both relatively flat and also oblique to the camera so that they need very little tessellation, yet they are being rendered with more triangles than the mountains in the background that could actually use some extra triangles. Wasteful in the former case and sort of ugly in the latter.

 

Before I tried the distance scheme, I tried to tessellate based on the screen length of the respective patch edges, but I couldn't figure out how to get it working. Such a scheme would be much more efficient and would also preserve water tight stitching since neighbor edges would have the exact same length. Does anybody know how I can calculate that in the hull shader? I tried multiplying my corner vertices by my ProjectionView matrix and then measuring length, but I couldn't figure out what to do with the results of that calculation. How long is a pixel in my world? I have no idea. If an edge goes from one corner of the screen to the other, how long is that? 100? 0.01? I couldnt find a useful context for the distance between my transformed corner control points.


Which edge is which when tessellating?

22 April 2013 - 03:15 PM

Ok, so I have 10 control points for a 3 order bezier triangle. It is arbitrary which points are which, I know who they are and how to use them, but there is no obvious relation to these points from the edges to tessellate. In my hull shader, I decide how much to tessellate each of the three edges, but how do I know which edge is edge zero, one, two etc?

 

For example, if cp[0], cp[1] and cp[2] are the control points to my bezier triangle corners, how do I decide how much to tessellate each edge based on the screen size of the edge?


Calculating Surface Normal on a Bezier Triangle

18 April 2013 - 09:42 AM

Ok, everything I know about this matter comes from this Gamasutra article. To interpolate positions over the triangle, you use this formula:

 

beq2.gif

 

To calculate two tangent vectors in order to calculate the surface normal. You need to use two partial derivatives of the above formula given as so:

beq4.gif

 

How this looks in code for a 3rd order bezier triangle, or how I think this looks in code, can be seen in my domain shader.

#define p300 0
#define p030 1
#define p003 2
#define p210 3
#define p201 4
#define p120 5
#define p021 6
#define p012 7
#define p102 8
#define p111 9

#define U bary.x
#define V bary.y
#define W bary.z

cbuffer PerFrameBuffer : register(b0)
{
	//float4x4 World; This is just the identity matrix, so not needed
	float4x4 ViewProjection;
	float4 vecEye; 
	float4 LightDirection;
	float4 LightColor; 
};

struct PatchTess
{
	float EdgeTess[3]	: SV_TessFactor;
	float InsideTess	: SV_InsideTessFactor;
};

struct HullOut
{
	float3 PositionWorld	: POSITION;
	float2 MainTexCoord		: TEXCOORD;
};

struct DomainOut
{
	float4 PositionH		: SV_Position;
	float3 Normal			: NORMAL0;
	float3 Tangent			: TANGENT0;
	float3 Bitangent		: BITANGENT0;
	float3 View				: NORMAL1;
	float2 MainTexCoord		: TEXCOORD0;
};

[domain("tri")]
DomainOut DS(PatchTess patchTess, float3 bary : SV_DomainLocation, const OutputPatch<HullOut,10> cp)
{
	DomainOut dout;

	float3 position =
		cp[p300].PositionWorld * pow(U,3) +
		cp[p030].PositionWorld * pow(V,3) +
		cp[p003].PositionWorld * pow(W,3) +
		cp[p210].PositionWorld * 3 * pow(U,2) * V +
		cp[p201].PositionWorld * 3 * pow(U,2) * W +
		cp[p120].PositionWorld * 3 * U * pow(V,2) +
		cp[p021].PositionWorld * 3 * pow(V,2) * W +
		cp[p012].PositionWorld * 3 * V * pow(W,2) +
		cp[p102].PositionWorld * 3 * U * pow(W,2) +
		cp[p111].PositionWorld * 6 * U * V * W;

	float3 tangent =
		cp[p300].PositionWorld * pow(U,2) +
		cp[p120].PositionWorld * pow(V,2) +
		cp[p102].PositionWorld * pow(W,2) +
		cp[p201].PositionWorld * 2 * U * W +
		cp[p210].PositionWorld * 2 * U * V +
		cp[p111].PositionWorld * 2 * V * W;

	float3 bitangent = 
		cp[p030].PositionWorld * pow(V,2) +
		cp[p012].PositionWorld * pow(W,2) +
		cp[p210].PositionWorld * pow(U,2) +
		cp[p120].PositionWorld * 2 * U * V +
		cp[p021].PositionWorld * 2 * V * W +
		cp[p111].PositionWorld * 2 * U * W;

	tangent = normalize(tangent);
	bitangent = normalize(bitangent);
	float3 normal = normalize(cross(tangent,bitangent));

	dout.View = vecEye.xyz - position.xyz;
	dout.PositionH = mul(float4(position,1.0f),ViewProjection);
	dout.Normal = normal;
	dout.Tangent = tangent;
	dout.Bitangent = bitangent;

	dout.MainTexCoord = cp[p300].MainTexCoord * U + cp[p030].MainTexCoord * V + cp[p003].MainTexCoord * W;

	return dout;
}

 

However, the results I am getting are not quite right, and I wonder if I have some misunderstanding of how this is supposed to work. The positions calculated across each bezier triangle are exactly correct, but I am having trouble calculating a usable and continuous normal from the tangents. The following screenshot uses the above domain shader and is basically a series of instanced bezier triangles defining a height map drawn in two draw calls. One with triangles pointing down and another with triangles pointing up.

Attached File  Bad Normals.jpg   224.66K   18 downloads

You can see here some strange effects, the directional light source comes from the top, and there is some sense that some triangles have proper lighting while others do not. I originally thought that the angle between the two tangents was unpredictable, so some of the normals were pointing the opposite direction, but if I brute force the normals to point up with the following code the situation is improved but still not right.

	float3 normal = normalize(cross(tangent,bitangent));
	if(normal.y < 0.0)
		normal = normalize(cross(bitangent,tangent));

 

Attached File  Bad Normals2.jpg   136.09K   23 downloads

 

Notice that each tri instance has one continuous edge with its neighbor, but the other two edges are wrong. Also I have no idea what causes the diagonal artifact in both images other than my calculations being just wrong. Ultimately I would like a heighmap made from bezier triangles with continuous normals across the whole field. Does anyone here have experience working with bezier triangles who can tell me where my assumptions are wrong?


[solved]My tessellation pipeline that draws nothing.

12 April 2013 - 08:25 AM

Ok, I believe that I am using the right blend state, and I believe that my ProjectionView matrix is ok, and I've eliminated relevant DX11 warnings and errors, but I still can't get anything to appear on screen. I'm greatly hampered by the fact that there is no way to debug my tessellation shaders with SharpDX and VS2012 express. This is my first stab at tessellation so I probably am doing something obviously wrong, so I would like to post my shaders to see if anything jumps out at anyone. What I am trying to do is draw instanced 3rd order bezier triangles. Besides the above, what other things might I check when nothing is drawn?

 

Here is the VS:

cbuffer PerFrameBuffer : register(b0)
{
	//float4x4 World; This is just the identity matrix, so not needed
	float4x4 ViewProjection;
	float4 vecEye; 
	float4 LightDirection;
	float4 LightColor; 
};

struct VertexInput
{
	//imediate data
	float3 PositionLocal		: POSITION0;
	int index					: BLENDINDICES;
	//instance data
	float3 Translation			: POSITION1;
	float Altitudes[4]			: PSIZE;
};

struct VertexOutput
{
	float3 PositionWorld	: POSITION;
	float2 MainTexCoord		: TEXCOORD;
};

VertexOutput VS(VertexInput vin)
{
	VertexOutput vout;

	//Add altitudes
	float3 transPos = vin.PositionLocal + vin.Translation;
	transPos.y += vin.Altitudes[vin.index];

	vout.MainTexCoord = transPos.xz;
	vout.PositionWorld = transPos;

	return vout;
}

 

 

Here is the pass thru Hull shader:


struct VertexOutput
{
	float3 PositionWorld	: POSITION;
	float2 MainTexCoord		: TEXCOORD;
};

struct PatchTess
{
	float EdgeTess[3]	: SV_TessFactor;
	float InsideTess	: SV_InsideTessFactor;
};

PatchTess PatchHS(InputPatch<VertexOutput,10> patch, uint patchID : SV_PrimitiveID)
{
	PatchTess pt;

	pt.EdgeTess[0] = 6;
	pt.EdgeTess[1] = 6;
	pt.EdgeTess[2] = 6;
	pt.InsideTess = 6;

	return pt;
}

struct HullOut
{
	float3 PositionWorld	: POSITION;
	float2 MainTexCoord		: TEXCOORD;
};

[domain("tri")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(10)]
[patchconstantfunc("PatchHS")]
HullOut HS(InputPatch<VertexOutput,10> p, uint i : SV_OutputControlPointID, uint patchId : SV_PrimitiveID)
{
	HullOut hout;

	hout = p[i];

	return hout;
}

 

 

Here is the domain shader, where the meat of the intended operation is. The math here might be wrong, but should still produce visible geometry:

#define p300 0
#define p030 1
#define p003 2
#define p210 3
#define p201 4
#define p120 5
#define p021 6
#define p012 7
#define p102 8
#define p111 9

#define U bary.x
#define V bary.y
#define W bary.z

cbuffer PerFrameBuffer : register(b0)
{
	//float4x4 World; This is just the identity matrix, so not needed
	float4x4 ViewProjection;
	float4 vecEye; 
	float4 LightDirection;
	float4 LightColor; 
};

struct PatchTess
{
	float EdgeTess[3]	: SV_TessFactor;
	float InsideTess	: SV_InsideTessFactor;
};

struct HullOut
{
	float3 PositionWorld	: POSITION;
	float2 MainTexCoord		: TEXCOORD;
};

struct DomainOut
{
	float4 PositionH		: SV_Position;
	float3 Normal			: NORMAL0;
	float3 Tangent			: TANGENT0;
	float3 Bitangent		: BITANGENT0;
	float3 View				: NORMAL1;
	float2 MainTexCoord		: TEXCOORD0;
};

[domain("tri")]
DomainOut DS(PatchTess patchTess, float3 bary : SV_DomainLocation, const OutputPatch<HullOut,10> cp)
{
	DomainOut dout;

	float3 position =
		cp[p300].PositionWorld * pow(U,3) +
		cp[p030].PositionWorld * pow(V,3) +
		cp[p003].PositionWorld * pow(W,3) +
		cp[p210].PositionWorld * 3 * pow(U,2) * V +
		cp[p201].PositionWorld * 3 * pow(U,2) * W +
		cp[p120].PositionWorld * 3 * U * pow(V,2) +
		cp[p021].PositionWorld * 3 * pow(V,2) * W +
		cp[p012].PositionWorld * 3 * V * pow(W,2) +
		cp[p102].PositionWorld * 3 * U * pow(W,2) +
		cp[p111].PositionWorld * 6 * U * V * W;

	float3 tangent =
		cp[p300].PositionWorld * pow(U,2) +
		cp[p120].PositionWorld * pow(V,2) +
		cp[p102].PositionWorld * pow(W,2) +
		cp[p201].PositionWorld * 2 * U * W +
		cp[p210].PositionWorld * 2 * U * V +
		cp[p111].PositionWorld * 2 * V * W;

	float3 bitangent = 
		cp[p030].PositionWorld * pow(V,2) +
		cp[p012].PositionWorld * pow(W,2) +
		cp[p210].PositionWorld * pow(U,2) +
		cp[p120].PositionWorld * 2 * U * V +
		cp[p021].PositionWorld * 2 * V * W +
		cp[p111].PositionWorld * 2 * U * W;

	tangent = normalize(tangent);
	bitangent = normalize(bitangent);
	float3 normal = normalize(cross(tangent,bitangent));

	dout.View = vecEye.xyz - position.xyz;
	dout.PositionH = mul(float4(position,1.0f),ViewProjection);
	dout.Normal = normal;
	dout.Tangent = tangent;
	dout.Bitangent = bitangent;

	dout.MainTexCoord = cp[p300].MainTexCoord * U + cp[p030].MainTexCoord * V + cp[p003].MainTexCoord * W;

	return dout;
}

 

Last is the pixel shader, which has a simple shading algorithm that has been disabled to merely produce one color. I'm really hoping to see that color 'Reddish' but it's not happening:

cbuffer PerFrameBuffer : register(b0)
{
	//float4x4 World; This is just the identity matrix, so not needed
	float4x4 ViewProjection;
	float4 vecEye; 
	float4 LightDirection;
	float4 LightColor; 
};

struct DomainOut
{
	float4 PositionH		: SV_Position;
	float3 Normal			: NORMAL0;
	float3 Tangent			: TANGENT0;
	float3 Bitangent		: BITANGENT0;
	float3 View				: NORMAL1;
	float2 MainTexCoord		: TEXCOORD0;
};

float4 PS(DomainOut input) : SV_Target
{
    float4 Color = float4 (0, 0, 0, 1); 
	float4 Reddish = float4 (0.8,0.5,0.8,1.0);
	float4 specularColor = float4(1,1,1,1);

    float4 diffuse = {LightColor.rgb, 1.0f};   
    float4 ambient = { 0.03f, 0.03f, 0.03f, 1.0f};   
  
    float3 LightDir = normalize(LightDirection.xyz);   
    float3 ViewDir = normalize(input.View);    
       
    float4 diff = saturate(dot(input.Normal, LightDir)); // diffuse component   
  
    // R = 2 * (N.L) * N - L   
    float3 Reflect = normalize(2 * diff * input.Normal - LightDir);    
    float specular = pow(saturate(dot(Reflect, ViewDir)), 15); // R.V^n   
  
    // I = Acolor + Dcolor * N.L + (R.V)n   
    Color += ambient + diffuse  * diff + specular * (specularColor * LightColor);  

	float4 Final = Color * Reddish;
	Final.a = 1.0;

	//return Final;
	return Reddish;
       
}

 

 


PARTNERS