十三、D3D12学习笔记——几何着色器

一、概述

有关着色器部分我们基本可以对其进行一下划分:

第一梯队(基础):顶点着色器,像素着色器(片元着色器)

第二梯队(进阶):几何着色器,曲面细分着色器

特殊用途(独立):计算着色器

第一梯队是所有喜欢图形的人都会涉猎和了解的,比如学习Unity Shader,可以说都在写顶点着色器和像素着色器,基本语法非常类似C语言,重点是计算的逻辑,比如顶点着色器里边的空间变换以及像素着色器里边的光照计算。

而对于第二梯队以及特殊的计算着色器,这往往就是进阶的话题,他不太像注重渲染特效的人要关注的话题,更像是重视效率的开发者进行的数学游戏。

二、几何着色器结构解析

1.几何着色器的用途

将点,线,面等完整几何图元作为输入,通过对图元的加工(丰富细节或者删除),形成新的顶点列表(经过了MVP变换的),得到新的输出图元

将一种图元变换为另一种图元。

2.基本结构

[maxvertexcount(N)]//几何着色器输出点列表最大点数量

void GS(

PrimitiveType InputVertexStruct param[vertexNum],

inout StreamOutputObject<OutputVertexType> outputValue

)

{

//几何着色器实现

}

参数1:

        PrimitiveType:点线面(point/line/triangle/lineadj/triangleadj)

        InputVertexStruct:顶点着色器的输出类型

        param[vertexNum]:组成输入图元的点数组

参数2:

        inout:输入输出标记

        StreamOutputObject:流泛型(PointStream/LineStream/TriangleStream)

        OutputVertexType:传入下一阶段(比如PS)的点数据结构体类型

        outputValue:点列表,类似于一个vector数组,使用Append添加新的点,注意加点顺序满足左手系顺时针绕序,如果不能满足要重新设置起点(使用函数RestartStrip())

为了形象,看下示例:

其中VertexOut就是顶点着色器的输出结构体(原来要传入像素着色器的顶点结构体),现在传入几何着色器,之后计算数据还是要继续往下使用GeoOut进行传输:

可以看出GeoOut与VertexOut比较相似,因为原来是指望用VertexOut向PS传输数据,现在是想通过GeoOut来传输数据,并且要求此时必须要包含齐次坐标系下的坐标(经过MVP),所以有一些拓展。

具体的几何着色器的主体基本逻辑:

  1. 针对传入的点数组,计算新图元的点;
  2. .新图元点变换到齐次坐标空间;
  3. 按照左手系顺时针绕序Append(注意我们在第一部分介绍的偶数次前两个索引颠倒)。
  4. 不能一次性Append构建的要使用ReStartStrip()函数。
  5. 根据一些条件,如果不选择输出任何数据,则表示销毁图元。

有关几何着色器的编译与绑定PSO都和VS及PS一致:

 三、实践:公示牌效果

这部分的逻辑很简单:

1.将点作为GS输入,称为公示牌中心点;

2.根据相机坐标与中心点计算视线w,设置上向量v(0,1,0),叉乘得到右向量u;

3.中心点沿上下左右扩散出贴图角点,进行MVP投影,Append作为新图元点列表,传入PS;

关键代码如下:

struct VertexOut
{
    float3 CenterW : POSITION;
    float2 SizeW   : SIZE;
};

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;

    // 构建传入GS的数据

    return vout;
}

[maxvertexcount(4)]
void GS(point VertexOut gin[1], 
        uint primID : SV_PrimitiveID, //旧图元变为新图元但是编号ID不变,系统自动填充
        inout TriangleStream<GeoOut> triStream)
{    
   //计算正对视线的平面

    float3 up = float3(0.0f, 1.0f, 0.0f);
    float3 look = gEyePosW - gin[0].CenterW;
    look.y = 0.0f; // y-axis aligned, so project to xz-plane
    look = normalize(look);
    float3 right = cross(up, look);
    //计算新图元角点
    float halfWidth  = 0.5f*gin[0].SizeW.x;
    float halfHeight = 0.5f*gin[0].SizeW.y;
    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);//MVP变换
        gout.PosW     = v[i].xyz;
        gout.NormalW  = look;
        gout.TexC     = texC[i];
        gout.PrimID   = primID;
        
        triStream.Append(gout);
    }
}

注意primID的使用规则:

1.没有几何着色器,可在PS中使用;

2.如果有几何着色器,必须先在几何着色器中显示传递给PS,PS才能使用。(如果PS不使用可以全程不用)OK,先在GS已经大发神威了,把数据计算完了要往下到PS:

//注意输入参数就是GS的输出

float4 PS(GeoOut pin) : SV_Target
{
    float3 uvw = float3(pin.TexC, pin.PrimID%4);
    float4 diffuseAlbedo = gTreeMapArray.Sample(gsamAnisotropicWrap, uvw) * gDiffuseAlbedo;
 //PS光照计算等

    return litColor;
}

四、题外话

纹理数组:

注意此处使用的纹理数组gTreeMapArray,这是将多个纹理打包为一个数组传入显存供流水线使用的方法,在加载文理的地方可以通过DepthOrArraySize进行设置(我们以前总是设置为1),这里的纹理坐标uvw,uv表示纹理坐标,w表示纹理索引值,使用纹理数组的好处在于减少重新设置纹理及调用绘制的次数,提高效率。有关纹理数组的细节可以好好看看龙书,因为这个部分实际是美工来给我们准备的吧。

alpha-to-coverage

MSAA计算覆盖情况时考虑alpha通道,实现基于alpha通道的多重采样

(通常的MSAA是基于子像素几何覆盖性的)

要点:

1).启用MSAA(默认DX11以上就是启用的);

2).treeSpritePsoDesc.BlendState.AlphaToCoverageEnable = true;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值