d3d12龙书学习之MiniEngine的最小化实现(八) 龙书第12章 几何着色器 公告板

文章目录

  • 几何着色器简介
  • 公告板实现流程
  • 公告板几何着色器解释
  • 看下效果
    • 开启MSAA以及AlphaToCoverage之后的效果
  • 修改debug渲染效率过低的问题
  • 总结

几何着色器简介

可以看d3d12龙书第146页。

对于本章内容来说,只有3个着色器。顶点着色器(VS)、几何着色器(GS)、像素着色器(PS)
VS ==> GS ==> PS

VS:拿到渲染目标的顶点,可以修改传入的顶点参数
GS:拿到完整的图元(如果是三角形渲染就是3个顶点,线段就是2个顶点,点就是一个顶点)。通过增减顶点来增减图元
PS:计算顶点的颜色值

公告板实现流程

跟渲染其他目标一样。这里呢采用几何着色器扩展图元来实现

渲染一个个离散的点,在GS中把每一个点扩展为一个矩形并设置该矩形4个顶点所需的其他参数

  1. 编写渲染点的VS,GS,PS
  2. 创建渲染点的PSO
  3. 创建点集合的渲染目标

2-3步前边做过很多了,只是改一个参数,由画三角形改为画点,这里就不再细说了。

公告板几何着色器解释

几何着色器是本章重点,详看注释

struct VertexOut
{
    float3 CenterW : POSITION;		// VS传过来的,这里也就是外边设置的点的世界坐标
    float2 SizeW   : SIZE;			// 公告板的宽高数据
};

struct GeoOut
{
    float4 PosH    : SV_POSITION;       // 顶点的齐次坐标
    float3 PosW    : POSITION;          // 顶点的世界坐标
    float3 NormalW : NORMAL;            // 顶点的世界法向量
    float2 TexC    : TEXCOORD;          // 顶点的纹理坐标
    uint   PrimID  : SV_PrimitiveID;    // 顶点ID
};

[maxvertexcount(4)]
void main(
	point VertexOut gin[1],						// 每次调用GS传入一个完整图元(一个点)
    uint primID : SV_PrimitiveID,				// 系统自动编号(本次是16个点,编号也就是0-15)
	inout TriangleStream< GeoOut > triStream	// 输出一个三角形带。三角形带就是每3个顶点作为一个三角形。例如4个顶点就是2个三角形。
)
{
    // 计算up向量
    float3 up = float3(0.0f, 1.0f, 0.0f);
    // 计算目标点到观察点的向量
    float3 look = gEyePosW - gin[0].CenterW;
    // 保证目标点和观察点在通一个xz平面
    look.y = 0.0f;
    // 标准化
    look = normalize(look);
    // 计算右向量,这里的右以观察角度来看是左方向。龙书409页
    float3 right = cross(up, look);

    // 计算公告板树的宽和高
    float halfWidth = 0.5f * gin[0].SizeW.x;
    float halfHeight = 0.5f * gin[0].SizeW.y;

    // 计算树的4个顶点
    float4 v[4];
    v[0] = float4(gin[0].CenterW + halfWidth * right - halfHeight * up, 1.0f);		// 以观察角度来看,左下角
    v[1] = float4(gin[0].CenterW + halfWidth * right + halfHeight * up, 1.0f);		// 以观察角度来看,左上角
    v[2] = float4(gin[0].CenterW - halfWidth * right - halfHeight * up, 1.0f);		// 以观察角度来看,右下角
    v[3] = float4(gin[0].CenterW - halfWidth * right + halfHeight * up, 1.0f);		// 以观察角度来看,右上角

    // 四个点对应的纹理坐标
    float2 texC[4] =
    {
        float2(0.0f, 1.0f),		// 左下
        float2(0.0f, 0.0f),		// 左上
        float2(1.0f, 1.0f),		// 右下
        float2(1.0f, 0.0f)		// 右上
    };

    // 输出图源
    GeoOut gout;
    [unroll]
    for (int i = 0; i < 4; ++i)
    {
        gout.PosH = mul(v[i], gViewProj);   // 顶点由世界坐标系转到投影坐标系
        gout.PosW = v[i].xyz;               // 顶点的世界坐标
        gout.NormalW = look;                // 顶点的法向量
        gout.TexC = texC[i];                // 顶点的纹理
        gout.PrimID = primID;               // 该顶点所组成的面的ID

        triStream.Append(gout);
    }
}

看下效果

开启MSAA以及AlphaToCoverage之后的效果

	// 公告板PSO
    GraphicsPSO billboardPSO = defaultPSO;
    billboardPSO.SetRasterizerState(Graphics::RasterizerDefaultCwMsaa);
    blend = Graphics::BlendDisable;
    blend.AlphaToCoverageEnable = true;
    billboardPSO.SetBlendState(blend);

修改debug渲染效率过低的问题

visual studio =》 调试 =》 性能探查器(勾选CPU),运行几秒后关闭,会自动展示代码的热度。
发现水体的顶点update有很严重的效率问题。
这里稍微修改下。详见代码对比。

总结

本章主要是学习GS的概念和写法。

GS功能:通过对顶点的增减来增减图元。一个点传进来,我甚至可以在任意位置生成任意多个图元。

对于文中提到的纹理数组,MiniEngine很好的做了支持,毕竟这是DDS加载自带的支持,我本身也没有细看。

本章github:
https://github.com/mversace/DirectX12-MiniEngine-Dragon/tree/70df8e2a923b3da694a9894bf8fed9aeffbbe0b3

你可能感兴趣的:(DirectX12,龙书学习)