A Basic Tessellation Demo

A Basic Tessellation Demo

把前面几节所讲的triangle和quad tessellation shaders集成到应用程序中并不需要编写特别复杂的C++代码。总的来说,首先创建对应的materials与这两个shaders进行交互,然后创建用于表示单个triangle和quad的vertex buffers,并添加一些成员变量用于保存tessellation factors值。在该示例中,可以使用键盘操作,在quad和triangle tessellation之间进行切换,并修所有的tessellation factors值,可以使这些factors值保持一致或各不相同。绘制一个细分的object与之前示例程序中建立的设计方法基本相同,但是需要指定primitive topology为control point patch list topologies中的某一种。其中,对于triangles使用D3D11_PRIMITIVE_TOPOTOGY_3_CONTROL_POINT_PATCHLIST,
而quads则使用D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST。
列表21.9中该示例程序中渲染部分的核心代码。
列表21.9 Draw Code for the Basic Tessellation Demo

ID3D11DeviceContext* direct3DDeviceContext = mGame->Direct3DDeviceContext();
direct3DDeviceContext->RSSetState(RasterizerStates::Wireframe);

if (mShowQuadTopology)
{
direct3DDeviceContext->IASetInputLayout(mQuadInputLayout);
direct3DDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);

UINT stride = mQuadMaterial->VertexSize();
UINT offset = 0;
direct3DDeviceContext->IASetVertexBuffers(0, 1, &mQuadVertexBuffer, &stride, &offset);

mQuadMaterial->WorldViewProjection() << mCamera->ViewMatrix() * mCamera->ProjectionMatrix();
mQuadMaterial->TessellationEdgeFactors() << mTessellationEdgeFactors;
mQuadMaterial->TessellationInsideFactors() << mTessellationInsideFactors;
mQuadPass->Apply(0, direct3DDeviceContext);

direct3DDeviceContext->Draw(4, 0);
}
else
{
direct3DDeviceContext->IASetInputLayout(mTriInputLayout);
direct3DDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);

UINT stride = mTriMaterial->VertexSize();
UINT offset = 0;
direct3DDeviceContext->IASetVertexBuffers(0, 1, &mTriVertexBuffer, &stride, &offset);

std::vector<float> tessellationEdgeFactors(mTessellationEdgeFactors.begin(), mTessellationEdgeFactors.end() - 1);

mTriMaterial->WorldViewProjection() << mCamera->ViewMatrix() * mCamera->ProjectionMatrix();
mTriMaterial->TessellationEdgeFactors() << tessellationEdgeFactors;
mTriMaterial->TessellationInsideFactor() << mTessellationInsideFactors[0];
mTriPass->Apply(0, direct3DDeviceContext);

direct3DDeviceContext->Draw(3, 0);
}

Displacement Tessellated Vertices

使用表面细分功能主要是为了从low-fidelity(低精度)模型中产生high-fidelity(高精度)的渲染。但是在同一个平面上进行三角形细分或四边形细分并不能提供这种细节的渲染;一个经过细分的平面还是一个平面。要使用该过程达到真正的细分效果需要对新创建的vertices进行适当的移位。这种思想与第9章“Normal Mapping和Displacement Mapping”所讲的,使用一个纹理存储displacement值以及一个缩放因子的方法类似。列表21.10中列出了四边形细分的shader代码,该四边形与xz平面对齐,并根据一个displacement纹理图对vertices的y坐标值进行移位。
列表21.10 The QuadHeightmapTessellation.fx Effect

/************* Resources *************/
static const float4 ColorWheat = { 0.961f, 0.871f, 0.702f, 1.0f };

cbuffer CBufferPerFrame
{
    float TessellationEdgeFactors[4];
    float TessellationInsideFactors[2];
}

cbuffer CBufferPerObject
{
    float4x4 WorldViewProjection;
    float4x4 TextureMatrix;

    float DisplacementScale;
}

Texture2D Heightmap;

SamplerState TrilinearSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};

/************* Data Structures *************/

struct VS_INPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct VS_OUTPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct HS_CONSTANT_OUTPUT
{
    float EdgeFactors[4] : SV_TessFactor;
    float InsideFactors[2] : SV_InsideTessFactor;
};

struct HS_OUTPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct DS_OUTPUT
{
    float4 Position : SV_Position;
};

/************* Vertex Shader *************/

VS_OUTPUT vertex_shader(VS_INPUT IN)
{
    VS_OUTPUT OUT = (VS_OUTPUT)0;

    OUT.ObjectPosition = IN.ObjectPosition;
    OUT.TextureCoordinate = mul(float4(IN.TextureCoordinate, 0, 1), TextureMatrix).xy;

    return OUT;
}

/************* Hull Shaders *************/

HS_CONSTANT_OUTPUT constant_hull_shader(InputPatch<VS_OUTPUT, 4> patch, uint patchID : SV_PrimitiveID)
{
    HS_CONSTANT_OUTPUT OUT = (HS_CONSTANT_OUTPUT)0;

    [unroll]
    for (int i = 0; i < 4; i++)
    {
        OUT.EdgeFactors[i] = TessellationEdgeFactors[i];
    }

    OUT.InsideFactors[0] = TessellationInsideFactors[0];
    OUT.InsideFactors[1] = TessellationInsideFactors[1];

    return OUT;
}

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("constant_hull_shader")]
HS_OUTPUT hull_shader(InputPatch<VS_OUTPUT, 4> patch, uint controlPointID : SV_OutputControlPointID)
{
    HS_OUTPUT OUT = (HS_OUTPUT)0;

    OUT.ObjectPosition = patch[controlPointID].ObjectPosition;
    OUT.TextureCoordinate = patch[controlPointID].TextureCoordinate;

    return OUT;
}

/************* Domain Shader *************/

[domain("quad")]
DS_OUTPUT domain_shader(HS_CONSTANT_OUTPUT IN, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUTPUT, 4> patch)
{
    DS_OUTPUT OUT;

    float4 v0 = lerp(patch[0].ObjectPosition, patch[1].ObjectPosition, uv.x);
    float4 v1 = lerp(patch[2].ObjectPosition, patch[3].ObjectPosition, uv.x);
    float4 objectPosition = lerp(v0, v1, uv.y);

    float2 texCoord0 = lerp(patch[0].TextureCoordinate, patch[1].TextureCoordinate, uv.x);
    float2 texCoord1 = lerp(patch[2].TextureCoordinate, patch[3].TextureCoordinate, uv.x);
    float2 textureCoordinate = lerp(texCoord0, texCoord1, uv.y);

    objectPosition.y = (2 * Heightmap.SampleLevel(TrilinearSampler, textureCoordinate, 0).x - 1) * DisplacementScale;

    OUT.Position = mul(float4(objectPosition.xyz, 1.0f), WorldViewProjection);

    return OUT;
}

/************* Pixel Shader *************/

float4 pixel_shader(DS_OUTPUT IN) : SV_Target
{
    return ColorWheat;
}

/************* Techniques *************/

technique11 main11
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_5_0, vertex_shader()));
        SetHullShader(CompileShader(hs_5_0, hull_shader()));
        SetDomainShader(CompileShader(ds_5_0, domain_shader()));
        SetPixelShader(CompileShader(ps_5_0, pixel_shader()));
    }
}

在该shader中有一些非常有趣的部分。首先可以观察到在domain shader中纹理坐标值的计算方法与vertex坐标值的推导方法完全一样。然后需要注意的是,如何采样displacement值,并把该值缩放调整到介于范围[-1, 1]之间,在该范围内灰度值为0.5表示不移位。这样就可以使用displacement纹理图中的暗色值(小于0.5)表示表面的凹陷,而亮色值(大于0.5)表示表面的凸起。接下来还要使用shader变量DisplacementScale对displacement值作进一步调整。
最后仔细分析在vertex shader中是如何对纹理坐标执行变换的。通过这种变换可使texture形成动画效果。例如,在应用程序中使用一个 随着时间变化不断更新的平移矩阵(包括水平和垂直方向的平移)赋值给TextureMatrix变量,可以使细分后的四边形看起来像是波浪起伏的效果。图21.6中显示了quad heightmap tessellation示例的输出结果,其中在屏幕的左下角渲染了displacement纹理图。在本书的配套网站上提供了该示例程序的完整代码。

这里写图片描述
图21.6 Output of the quad heightmap tessellation demo.

Dynamic Levels of Detail

在实际应用程序中,我们并不需要拖动设置tessellation factors,而是根据表面到camera的距离动态计算tessellation数量。这就是一种动态的LOD系统。
列表21.11列出了该LOD系统中的hull和domain shaders代码。
列表21.11 The Hull and Domain Shaders for a Dynamic Tessellation Effect

/************* Resources *************/
static const float4 ColorWheat = { 0.961f, 0.871f, 0.702f, 1.0f };

cbuffer CBufferPerFrame
{
    float TessellationEdgeFactors[3];
    float TessellationInsideFactor;
    float3 CameraPosition : CAMERAPOSITION;
    int MaxTessellationFactor = 64;
    float MinTessellationDistance = 2.0f;
    float MaxTessellationDistance = 20.0f;
}

cbuffer CBufferPerObject
{
    float4x4 WorldViewProjection : WORLDVIEWPROJECTION;
    float4x4 World : WORLD;
}

Texture2D ColorTexture;

SamplerState TrilinearSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};

/************* Data Structures *************/

struct VS_INPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct VS_OUTPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct HS_CONSTANT_OUTPUT
{
    float EdgeFactors[3] : SV_TessFactor;
    float InsideFactor : SV_InsideTessFactor;
};

struct HS_OUTPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate: TEXCOORD;
};

struct DS_OUTPUT
{
    float4 Position : SV_Position;
    float2 TextureCoordinate: TEXCOORD;
};

/************* Vertex Shader *************/

VS_OUTPUT vertex_shader(VS_INPUT IN)
{
    VS_OUTPUT OUT = (VS_OUTPUT)0;

    OUT.ObjectPosition = IN.ObjectPosition;
    OUT.TextureCoordinate = IN.TextureCoordinate;

    return OUT;
}

/************* Hull Shaders *************/

HS_CONSTANT_OUTPUT constant_hull_shader(InputPatch<VS_OUTPUT, 3> patch, uint patchID : SV_PrimitiveID)
{
    HS_CONSTANT_OUTPUT OUT = (HS_CONSTANT_OUTPUT)0;

    [unroll]
    for (int i = 0; i < 3; i++)
    {
        OUT.EdgeFactors[i] = TessellationEdgeFactors[i];
    }

    OUT.InsideFactor = TessellationInsideFactor;

    return OUT;
}

[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("constant_hull_shader")]
HS_OUTPUT hull_shader(InputPatch<VS_OUTPUT, 3> patch, uint controlPointID : SV_OutputControlPointID)
{
    HS_OUTPUT OUT = (HS_OUTPUT)0;

    OUT.ObjectPosition = patch[controlPointID].ObjectPosition;
    OUT.TextureCoordinate = patch[controlPointID].TextureCoordinate;

    return OUT;
}

HS_CONSTANT_OUTPUT distance_constant_hull_shader(InputPatch<VS_OUTPUT, 3> patch, uint patchID : SV_PrimitiveID)
{
    HS_CONSTANT_OUTPUT OUT = (HS_CONSTANT_OUTPUT)0;

    // Caclulate the center of the patch
    float3 objectCenter = (patch[0].ObjectPosition.xyz + patch[1].ObjectPosition.xyz + patch[2].ObjectPosition.xyz) / 3.0f;
    float3 worldCenter = mul(float4(objectCenter, 1.0f), World).xyz;

    // Calculate uniform tessellation factor based on distance from the camera
    float tessellationFactor = max(min(MaxTessellationFactor, (MaxTessellationDistance - distance(worldCenter, CameraPosition)) / (MaxTessellationDistance - MinTessellationDistance) * MaxTessellationFactor), 1);

    [unroll]
    for (int i = 0; i < 3; i++)
    {
        OUT.EdgeFactors[i] = tessellationFactor;
    }

    OUT.InsideFactor = tessellationFactor;

    return OUT;
}

[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("distance_constant_hull_shader")]
HS_OUTPUT distance_hull_shader(InputPatch<VS_OUTPUT, 3> patch, uint controlPointID : SV_OutputControlPointID)
{
    HS_OUTPUT OUT = (HS_OUTPUT)0;

    OUT.ObjectPosition = patch[controlPointID].ObjectPosition;
    OUT.TextureCoordinate = patch[controlPointID].TextureCoordinate;

    return OUT;
}

/************* Domain Shader *************/

[domain("tri")]
DS_OUTPUT domain_shader(HS_CONSTANT_OUTPUT IN, float3 uvw : SV_DomainLocation, const OutputPatch<HS_OUTPUT, 3> patch)
{
    DS_OUTPUT OUT = (DS_OUTPUT)0;

    float3 objectPosition = uvw.x * patch[0].ObjectPosition.xyz + uvw.y * patch[1].ObjectPosition.xyz + uvw.z * patch[2].ObjectPosition.xyz;
    float2 textureCoordinate = uvw.x * patch[0].TextureCoordinate + uvw.y * patch[1].TextureCoordinate + uvw.z * patch[2].TextureCoordinate;

    OUT.Position = mul(float4(objectPosition, 1.0f), WorldViewProjection);
    OUT.TextureCoordinate = textureCoordinate;

    return OUT;
}

/************* Pixel Shader *************/

float4 pixel_shader(DS_OUTPUT IN) : SV_Target
{
    return ColorTexture.Sample(TrilinearSampler, IN.TextureCoordinate);
}

float4 solid_color_pixel_shader(DS_OUTPUT IN) : SV_Target
{
    return ColorWheat;
}

/************* Techniques *************/

technique11 textured_manual_factors
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_5_0, vertex_shader()));
        SetHullShader(CompileShader(hs_5_0, hull_shader()));
        SetDomainShader(CompileShader(ds_5_0, domain_shader()));
        SetPixelShader(CompileShader(ps_5_0, pixel_shader()));
    }
}

technique11 solid_color_manual_factors
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_5_0, vertex_shader()));
        SetHullShader(CompileShader(hs_5_0, hull_shader()));
        SetDomainShader(CompileShader(ds_5_0, domain_shader()));
        SetPixelShader(CompileShader(ps_5_0, solid_color_pixel_shader()));
    }
}

technique11 solid_color_distance_factors
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_5_0, vertex_shader()));
        SetHullShader(CompileShader(hs_5_0, distance_hull_shader()));
        SetDomainShader(CompileShader(ds_5_0, domain_shader()));
        SetPixelShader(CompileShader(ps_5_0, solid_color_pixel_shader()));
    }
}

另外,在constant hull shader中首先计算三角形patch的中心坐标点,然后把该坐标点变换到world space中。接下来,根据表面细分中定义的最大和最小距离(允许执行细分的距离范围)计算一个统一的tessellation factor。当object到camera的距离超过了最大距离,tessellation factor就会被clamp为1(表示不执行细分)。当object到camera的距离小于最小距离,tessellation factor就会被clmap为由应用程序输入的MaxTessellationFactor变量值。当object到camera的距离介于最大和最小距离之间,tesselllation factor值随着距离的增加而减小。图21.7中显示对一个sphere模型执行自动细分的输出结果,从图中可以看出,距离camera越近的表面比距离越远的表面执行更多的细分。
这里写图片描述
图21.7 Output of the dynamic tessellation demo.

注意

从性能上来讲,当object到camera的距离足够远以至于不会发生细分的情况下,最好是禁用tessellation阶段。要实现这种功能,只需要在CPU应用程序中根据object的bounding volume(用于表示模型物理碰撞范围的包围体)与camera之间的距离,动态切换启用和禁用tessellation的两个shader techniques。

总结

在本章我们主要学习了geometry和tessellation shaders。并实现了一个point sprite系统用于演示geometry shaders,以及多个使用硬件tessellation的示例程序。

Exercises

  1. Experiment with geometry shaders. Modify the point sprite demo to output different vertex topologies.
  2. Explore the three tessellation demos on the book’s companion website. For the basic tessellation shader demo (whose output is shown in Figures 21.4 and 21.5), vary the edge and inside tessellation factors uniformly and nonuniformly, and observe the results. For the dynamic level-of-detail tessellation demo, experiment with the max tessellation factor and minimum and maximum tessellation distances, and observe the results. For the heightmap tessellation demo, alter the heightmap and the transformation for animating the UVs, and observe the results.
  3. Modify the dynamic level-of-detail tessellation shader to use an icosaheron model (included on the companion website). Run the demo and observe how the shape is not made more spherical as tessellation increases. Then modify the associated domain shader to displace the vertices along a unit sphere (to normalize the computed object position), and observe the results.

1、测试geometry shader的使用方法,并修改point sprite示例程序,使其输出不同的vertex topologies。
2、测试本书配套网站上提供的3个tessellation示例程序。在basic tessellation shader示例中(该示例的输出如图21.4和21.5),统一改变patch的边缘和内部细分因子以及分别改变各个因子值,并观察输出结果。对于dynamic level-of-detail tessellation示例,测试使用不同max tessellation factor值,以及tessellation的最大和最小距离值,并观察输出结果。对于heigthmap tessellation示例中,改变heightmap以及用于动态修改UVs的变换矩阵,并观察显示结果。

本章配套学习代码:
Render Engine及全部示例代码
示例所需要的全部Assets资源文件,需要把Assets目录及文件复制到Render Engine的根目录下。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值