qq_43478653 2021-12-16 11:03 采纳率: 25%
浏览 52
已结题

针对《龙书(第三版》第十二章课后习题二LOD实现的问题

要求

实现一个LOD(Level Of Detail)效果,实现物体距视口不同距离来呈现不同的图元细节。

img

思路

因为需要对图元进行一个分割,所以采用在几何着色器中进行。

  1. 根据距离设置细分次数subdivCnt。
  2. 根据三角形图元的三个顶点和细分次数,分割图元并将分割后顶点加入三角形输出流中。

这里主要对第二步进行细述:
首先,如果细分次数为0,则不用细分,输出到三角形流中的三个顶点直接是原始三顶点。

img

第二,如果细分次数为1,则需要在三角形三边寻找中点,并构成4个三角形,在按照三角形带的方式指定输出顺序放入输出流中。

img

第三,如果细分次数为2,则需要在细分1次的基础上,在4个三角形中再次细分,分出6个三角形并同样按照指定顺序放入输出流中。

img

所以,这里采用矩阵记录顶点,每次划分即对每两个顶点之间的边求取中间点,并将中间点放入两顶点之间的矩阵位置的方式来进行划分。具体来看,对于每个顶点,都需要与其上顶点、右顶点、右下方顶点进行上述操作。

img

代码

//***************************************************************************************
// TreeSprite.hlsl by Frank Luna (C) 2015 All Rights Reserved.
//***************************************************************************************

// Defaults for number of lights.
#ifndef NUM_DIR_LIGHTS
#define NUM_DIR_LIGHTS 3
#endif

#ifndef NUM_POINT_LIGHTS
#define NUM_POINT_LIGHTS 0
#endif

#ifndef NUM_SPOT_LIGHTS
#define NUM_SPOT_LIGHTS 0
#endif

// Include structures and functions for lighting.
#include "LightingUtil.hlsl"

Texture2DArray gTreeMapArray : register(t0);


SamplerState gsamPointWrap        : register(s0);
SamplerState gsamPointClamp       : register(s1);
SamplerState gsamLinearWrap       : register(s2);
SamplerState gsamLinearClamp      : register(s3);
SamplerState gsamAnisotropicWrap  : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);

// Constant data that varies per frame.
cbuffer cbPerObject : register(b0)
{
    float4x4 gWorld;
    float4x4 gWorldInvTranspose;
    float4x4 gTexTransform;
};

// Constant data that varies per material.
cbuffer cbPass : register(b1)
{
    float4x4 gView;
    float4x4 gInvView;
    float4x4 gProj;
    float4x4 gInvProj;
    float4x4 gViewProj;
    float4x4 gInvViewProj;
    float3 gEyePosW;
    float cbPerObjectPad1;
    float2 gRenderTargetSize;
    float2 gInvRenderTargetSize;
    float gNearZ;
    float gFarZ;
    float gTotalTime;
    float gDeltaTime;
    float4 gAmbientLight;

    float4 gFogColor;
    float gFogStart;
    float gFogRange;
    float2 cbPerObjectPad2;

    // Indices [0, NUM_DIR_LIGHTS) are directional lights;
    // indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
    // indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
    // are spot lights for a maximum of MaxLights per object.
    Light gLights[MaxLights];
};

cbuffer cbMaterial : register(b2)
{
    float4   gDiffuseAlbedo;
    float3   gFresnelR0;
    float    gRoughness;
    float4x4 gMatTransform;
};

struct VertexIn
{
    float3 PosL    : POSITION;
    float3 NormalL : NORMAL;
    float2 TexC    : TEXCOORD;
};

struct VertexOut
{
    float4 PosH    : SV_POSITION;
    float3 PosL    : POSITION;
    float3 NormalL : NORMAL;
    float2 TexC    : TEXCOORD;
};

struct GeoOut
{
    float4 PosH    : SV_POSITION;
    float3 PosW    : POSITION;
    float3 NormalW : NORMAL;
    float2 TexC    : TEXCOORD;
    uint   PrimID  : SV_PrimitiveID;
};

VertexOut VS(VertexIn vin)
{
    VertexOut vout;

    // Just pass data over to geometry shader.
    float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
    vout.PosH = mul(posW, gViewProj);
    vout.PosL = vin.PosL;
    vout.NormalL = vin.NormalL;
    vout.TexC = vin.TexC;

    return vout;
}

VertexOut MidPoint(VertexOut lhs, VertexOut rhs)
{
    lhs.PosL = normalize(lhs.PosL);
    rhs.PosL = normalize(rhs.PosL);
    
    VertexOut m;

    m.PosL = 0.5f * (lhs.PosL + rhs.PosL);
    m.PosL = normalize(m.PosL);
    m.NormalL = m.PosL;
    m.TexC = 0.5f * (lhs.TexC + rhs.TexC);

    return m;
}

GeoOut ToGeoOut(VertexOut inVert, uint primID)
{
    GeoOut gout;

    // 将顶点变换到世界空间
    gout.PosW = mul(float4(inVert.PosL, 1.0f), gWorld).xyz;
    gout.NormalW = mul(inVert.NormalL, (float3x3)gWorldInvTranspose);

    // 把顶点变换到齐次裁剪空间
    gout.PosH = mul(float4(gout.PosW, 1.0f), gViewProj);
    gout.TexC = inVert.TexC;

    gout.PrimID = primID;
    return gout;
}

void SubdivideAndOutput(VertexOut inVerts[3], int subCnt, inout TriangleStream<GeoOut> triStream, uint primID)
{
    VertexOut m[10][10], t[10][10];
    int row = 2, col = 2, i = 0, j = 0;
    m[0][0] = inVerts[0];
    m[1][0] = inVerts[1];
    m[0][1] = inVerts[2];

    while (subCnt--)
    {
        for (i = 0; i < row; ++i)
        {
            int cur_col = col - i;  // 当前行的列数
            for (j = 0; j < cur_col; ++j)
            {
                int targetRow = 2 * i, targetCol = 2 * j;
                t[targetRow][targetCol] = m[i][j];

                if (j < cur_col - 1)
                {
                    // 向上寻找中点
                    t[targetRow + 1][targetCol] = MidPoint(m[i][j], m[i + 1][j]);
                    // 向右寻找中点
                    t[targetRow][targetCol + 1] = MidPoint(m[i][j], m[i][j + 1]);
                }
                if (i > 0)
                {
                    // 向斜下方寻找中点
                    t[targetRow - 1][targetCol + 1] = MidPoint(m[i][j], m[i - 1][j + 1]);
                }
            }
        }
        row = 2 * row - 1;
        col = 2 * col - 1;
        // 拷贝t->m
        for (i = 0; i < row; ++i)
            for (j = 0; j < col - i; ++j)
                m[i][j] = t[i][j];
    }

    // 将顶点按三角形带加入输出流
    for (i = 0; i < row; ++i)
    {
        int cur_col = col - i;
        for (j = 0; j < cur_col - 1; ++j)
        {
            triStream.Append(ToGeoOut(m[i][j], primID));
            triStream.Append(ToGeoOut(m[i + 1][j], primID));
        }
        if (i + 1 != row)
            triStream.Append(ToGeoOut(m[i][cur_col - 1], primID));
        triStream.RestartStrip();   // 记录完一行三角形,重新开始记录下一行
    }
}

[maxvertexcount(8)]
void GS(triangle VertexOut gin[3], uint primID : SV_PrimitiveID, inout TriangleStream<GeoOut> triStream)
{
    float3 posW = float3(gWorld._m30, gWorld._m31, gWorld._m32);
    float dis = distance(posW, gEyePosW);
    int subCnt = 0;

    if (dis < 15)
        subCnt = 2;
    else if (dis < 30)
        subCnt = 1;
    else
        subCnt = 0;

    SubdivideAndOutput(gin, subCnt, triStream, primID);
}

float4 PS(VertexOut pin) : SV_Target
{
    return float4(1.0f, 1.0f, 1.0f, 1.0f);
}

问题

在细分2次时,结果不正确,产生了如下情况,推测第二、三层的节点没加入或三角形带绘制出错。

img

  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 12月24日
    • 创建了问题 12月16日

    悬赏问题

    • ¥15 远程桌面文档内容复制粘贴,格式会变化
    • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
    • ¥15 这种微信登录授权 谁可以做啊
    • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
    • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”
    • ¥15 网络设备配置与管理这个该怎么弄
    • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
    • ¥20 西门子S7-Graph,S7-300,梯形图
    • ¥50 用易语言http 访问不了网页
    • ¥50 safari浏览器fetch提交数据后数据丢失问题