aboutsummaryrefslogtreecommitdiff
path: root/samples/d3d11/shaders/tess_hs.hlsl
blob: 630ea603049912fba9d7705208a69a8de51c36fc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//----------------------------------------------------------------------------------
// File:        FaceWorks/samples/sample_d3d11/shaders/tess_hs.hlsl
// SDK Version: v1.0
// Email:       [email protected]
// Site:        http://developer.nvidia.com/
//
// Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//----------------------------------------------------------------------------------

#include "common.hlsli"
#include "tess.hlsli"

void calcHSConstants(
	in InputPatch<Vertex, 3> i_cps,
	out PatchConstData o_pcd)
{
	// Backface culling: check if the camera is behind all three tangent planes
	float3 vecNdotV =
	{
		dot(g_posCamera - i_cps[0].m_pos, i_cps[0].m_normal),
		dot(g_posCamera - i_cps[1].m_pos, i_cps[1].m_normal),
		dot(g_posCamera - i_cps[2].m_pos, i_cps[2].m_normal),
	};
	if (all(vecNdotV < 0.0))
	{
		o_pcd.m_tessFactor[0] = 0.0;
		o_pcd.m_tessFactor[1] = 0.0;
		o_pcd.m_tessFactor[2] = 0.0;
		o_pcd.m_insideTessFactor = 0.0;
		return;
	}

	// Frustum culling: check if all three verts are out on the same side of the frustum
	// This isn't quite correct because the displacement could make a patch visible even if
	// it fails this test; but in practice this is nearly impossible to notice
	float4 posClip0 = mul(float4(i_cps[0].m_pos, 1.0), g_matWorldToClip);
	float4 posClip1 = mul(float4(i_cps[1].m_pos, 1.0), g_matWorldToClip);
	float4 posClip2 = mul(float4(i_cps[2].m_pos, 1.0), g_matWorldToClip);
	float3 xs = { posClip0.x, posClip1.x, posClip2.x };
	float3 ys = { posClip0.y, posClip1.y, posClip2.y };
	float3 ws = { posClip0.w, posClip1.w, posClip2.w };
	if (all(xs < -ws) || all(xs > ws) || all(ys < -ws) || all(ys > ws))
	{
		o_pcd.m_tessFactor[0] = 0.0;
		o_pcd.m_tessFactor[1] = 0.0;
		o_pcd.m_tessFactor[2] = 0.0;
		o_pcd.m_insideTessFactor = 0.0;
		return;
	}

	// Adaptive tessellation based on a screen-space error estimate using curvature

	// Calculate approximate screen-space edge length, but including z length as well,
	// so we don't undertessellate edges that are foreshortened
	float edge0 = length(i_cps[2].m_pos - i_cps[1].m_pos) / (0.5 * (posClip2.w + posClip1.w));
	float edge1 = length(i_cps[0].m_pos - i_cps[2].m_pos) / (0.5 * (posClip0.w + posClip2.w));
	float edge2 = length(i_cps[1].m_pos - i_cps[0].m_pos) / (0.5 * (posClip1.w + posClip0.w));

	// Calculate dots of the two normals on each edge - used to give more tessellation
	// in areas with higher curvature
	float normalDot0 = dot(i_cps[2].m_normal, i_cps[1].m_normal);
	float normalDot1 = dot(i_cps[0].m_normal, i_cps[2].m_normal);
	float normalDot2 = dot(i_cps[1].m_normal, i_cps[0].m_normal);

	// Calculate target screen-space error
	static const float errPxTarget = 0.5;
	static const float tanHalfFov = tan(0.5 * 0.5);
	static const float errTarget = errPxTarget * 2.0 * tanHalfFov / 1080.0;

	// Calculate tess factors using curve fitting approximation to screen-space error
	// derived from curvature and edge length
	static const float tessScale = 0.41 / sqrt(errTarget);
	o_pcd.m_tessFactor[0] = g_tessScale * sqrt(edge0) * pow(1.0 - saturate(normalDot0), 0.27);
	o_pcd.m_tessFactor[1] = g_tessScale * sqrt(edge1) * pow(1.0 - saturate(normalDot1), 0.27);
	o_pcd.m_tessFactor[2] = g_tessScale * sqrt(edge2) * pow(1.0 - saturate(normalDot2), 0.27);

	// Clamp to supported range
	o_pcd.m_tessFactor[0] = clamp(o_pcd.m_tessFactor[0], 1.0, s_tessFactorMax);
	o_pcd.m_tessFactor[1] = clamp(o_pcd.m_tessFactor[1], 1.0, s_tessFactorMax);
	o_pcd.m_tessFactor[2] = clamp(o_pcd.m_tessFactor[2], 1.0, s_tessFactorMax);

	// Set interior tess factor to maximum of edge factors
	o_pcd.m_insideTessFactor = max(max(o_pcd.m_tessFactor[0],
									   o_pcd.m_tessFactor[1]),
									   o_pcd.m_tessFactor[2]);
}

[domain("tri")]
[maxtessfactor(s_tessFactorMax)]
[outputcontrolpoints(3)]
[outputtopology("triangle_cw")]
[partitioning("fractional_odd")]
[patchconstantfunc("calcHSConstants")]
Vertex main(
	in InputPatch<Vertex, 3> i_cps,
	in uint iCp : SV_OutputControlPointID)
{
	return i_cps[iCp];
}