Calculating Surface Normal on a Bezier Triangle

Started by
11 comments, last by cephalo 11 years ago

Ok, I think I have something I can be happy with. I'm actually doing everything in the domain shader, and I am calculating the three edge normals and interpolating them exponentially according to the distance from the edge, such that they have the most weight near their edge and close to zero past the middle or near the other edges. I don't think this is any kind of general purpose solution for stitching together bezier triangles, but it should work if you are using a 3 order bezier triangle who's control points on an edge are co-planar with all the others on that edge.

It's a bit slow, but it should be ok if I can wisely limit the amount of tessellation. I'm posting the code after the screenshot, so if anyone knows how I can make it faster I would like to hear about it. I have some branching in here to avoid some divide by zero errors and backward cross products. Is branching a really bad thing to do in a domain shader?

[attachment=15172:Bad Normals6.jpg]


#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 PatchConstants
{
	float EdgeTess[3]	: SV_TessFactor;
	float InsideTess	: SV_InsideTessFactor;

	float3 NormalCP[6]	: NORMAL;
};

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(PatchConstants pc, float3 bary : SV_DomainLocation, const OutputPatch<HullOut,10> cp)
{
	DomainOut dout;

	float3 p1_200 = U * cp[p300].PositionWorld + V * cp[p210].PositionWorld + W * cp[p201].PositionWorld;
	float3 p1_110 = U * cp[p210].PositionWorld + V * cp[p120].PositionWorld + W * cp[p111].PositionWorld;
	float3 p1_101 = U * cp[p201].PositionWorld + V * cp[p111].PositionWorld + W * cp[p102].PositionWorld;
	float3 p1_020 = U * cp[p120].PositionWorld + V * cp[p030].PositionWorld + W * cp[p021].PositionWorld;
	float3 p1_011 = U * cp[p111].PositionWorld + V * cp[p021].PositionWorld + W * cp[p012].PositionWorld;
	float3 p1_002 = U * cp[p102].PositionWorld + V * cp[p012].PositionWorld + W * cp[p003].PositionWorld;

	float3 p2_100 = U * p1_200 + V * p1_110 + W * p1_101;
	float3 p2_010 = U * p1_110 + V * p1_020 + W * p1_011;
	float3 p2_001 = U * p1_101 + V * p1_011 + W * p1_002;

	float3 position = U * p2_100 + V * p2_010 + W * p2_001;

	float A;
	if(U + V == 0.0) //if U and V are both zero, they are the same meaning that A is at 0.5, but we can't have divide by zero
		A = 0.5;
	else
		A = V/(U + V);
	float3 bitangent;

	float3 p1_0 = lerp(cp[p300].PositionWorld,cp[p210].PositionWorld,A);
	float3 p1_1 = lerp(cp[p210].PositionWorld,cp[p120].PositionWorld,A);
	float3 p1_2 = lerp(cp[p120].PositionWorld,cp[p030].PositionWorld,A);

	float3 p2_0 = lerp(p1_0,p1_1,A);
	float3 p2_1 = lerp(p1_1,p1_2,A);
	
	float3 tangent = normalize(p2_1 - p2_0);
	float3 planeVec1 = normalize(cp[p210].PositionWorld - cp[p300].PositionWorld);
	float3 planeVec2 = normalize(cp[p120].PositionWorld - cp[p300].PositionWorld);
	bitangent = normalize(cross(planeVec1,planeVec2));
	if(planeVec1.y > planeVec2.y)
		bitangent = -bitangent;
	float3 edgeANormal = normalize(cross(bitangent, tangent));

	float3 B;
	if(V + W == 0.0)
		B = 0.5;
	else
		B = W/(V + W);

	p1_0 = lerp(cp[p030].PositionWorld,cp[p021].PositionWorld,B);
	p1_1 = lerp(cp[p021].PositionWorld,cp[p012].PositionWorld,B);
	p1_2 = lerp(cp[p012].PositionWorld,cp[p003].PositionWorld,B);

	p2_0 = lerp(p1_0,p1_1,B);
	p2_1 = lerp(p1_1,p1_2,B);
	
	tangent = normalize(p2_1 - p2_0);
	planeVec1 = normalize(cp[p021].PositionWorld - cp[p030].PositionWorld);
	planeVec2 = normalize(cp[p012].PositionWorld - cp[p030].PositionWorld);
	bitangent = normalize(cross(planeVec1,planeVec2));
	if(planeVec1.y > planeVec2.y)
		bitangent = -bitangent;
	float3 edgeBNormal = normalize(cross(bitangent, tangent));

	float3 C;
	if(U + W == 0.0)
		C = 0.5;
	else
		C = U/(U + W);

	p1_0 = lerp(cp[p003].PositionWorld,cp[p102].PositionWorld,C);
	p1_1 = lerp(cp[p102].PositionWorld,cp[p201].PositionWorld,C);
	p1_2 = lerp(cp[p201].PositionWorld,cp[p300].PositionWorld,C);

	p2_0 = lerp(p1_0,p1_1,C);
	p2_1 = lerp(p1_1,p1_2,C);
	
	tangent = normalize(p2_1 - p2_0);
	planeVec1 = normalize(cp[p102].PositionWorld - cp[p003].PositionWorld);
	planeVec2 = normalize(cp[p201].PositionWorld - cp[p003].PositionWorld);
	bitangent = normalize(cross(planeVec1,planeVec2));
	if(planeVec1.y > planeVec2.y)
		bitangent = -bitangent;
	float3 edgeCNormal = normalize(cross(bitangent, tangent));

	float3 normal = (pow(1.0f - W,5) * edgeANormal + pow(1.0f - U,5) * edgeBNormal + pow(1.0f - V,5) * edgeCNormal)/(pow(1.0f - W,5) + pow(1.0f - U,5) + pow(1.0f - V,5));

	dout.View = vecEye.xyz - position.xyz;
	dout.PositionH = mul(float4(position,1.0f),ViewProjection);
	dout.Normal = normalize(normal);
	dout.Tangent = float3(0,1,0);
	dout.Bitangent = float3(0,1,0);

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

	return dout;
}

Advertisement

What can happen if you add some small value to either of U/V/W instead of branch?

//if(U + W == 0.0)
  //      C = 0.5;
    //else
U += 0.00001f;
        C = U/(U + W);

What can happen if you add some small value to either of U/V/W instead of branch?


//if(U + W == 0.0)
  //      C = 0.5;
    //else
U += 0.00001f;
        C = U/(U + W);

That works perfectly belfegor. Thanks! I just did C = U/(U + W + 0.00001f)

This topic is closed to new replies.

Advertisement