Directx11教程(54) 简单的基于GS的billboard实现

    本章我们用一个billboard的实现来学习D3D11中的GS。

    在VS shader中,我们输入的是顶点位置及顶点属性,输出的也是顶点位置及顶点属性。在GS shader中,我们输入的是体元(primitive,可以是点,线,三角形等等,凡是D3D11中允许的体元都可以使用), 输出顶点、顶点属性,以及体元信息。

    如下面两个图,左边对一个三角形做vs操作,则会对三个顶点v1,v2,v3分别执行顶点操作,右图对三角形做gs操作,则整个三角形做为输入,输出则为一个四面体(增加了3个三角形)。

 

 

        现在我们开始实现一个GS的billboard,原理很简单,用一个点表示billboard面的中心点,用1个参数表示billboard面的宽和高(宽=高),在gs输入中,我们以点做为primitive输入,宽,高做为顶点属性参数输入,最终生成2个完整的三角形表示billboard,同时也会在gs中生成纹理坐标,以便贴上树的纹理。

 

       我先看看完整的shader 代码,其实只要看懂了shader执行过程,基本上就可以完成编程了,程序中其它代码只是调用一些函数而已:

     gstree.vs, vs代码非常简单,输入顶点,输出顶点,不做任何其他操作,好像一个bypass的过程,但是在D3D11中,vs和ps是两个必须有的shader过程。

GeometryInputType treeVertexShader(VertexInputType input)
{
    GeometryInputType output;

    // 直接把数据传到gs阶段
    output.centerW  = input.centerW;
    output.sizeW    = input.sizeW;

    return output;
}

    gstree.gs中,我们会根据输入的点体元坐标,生成四个点及其纹理坐标,最后生成2个三角形,同时也会求出使得billboard始终面向摄像机的转化矩阵。注意:我们会把点体元的primitive id做为参数传递到ps中去,以便在贴纹理的时候,根据不同的id选择不同的纹理。

    这里,maxvertexcount表示一次gs调用最多产生的顶点数。每次gs调用产生顶点数可以不同,但不能超过这个设置的最大值。

    GS shader至少包括两个参数,一个是体元输入参数,它可以是point, triangle, line,lineadj,triangleadj等体元类型,它的数据就是一个顶点数组,如果数组中1个顶点,体元是点,2个顶点,体元是线,3个顶点,体元是三角形,4个顶点就是邻接线,6个顶点就是邻接三角形,如下面函数中的point GeometryInputType gIn[1]。

     另一个是体元输出参数,它的修饰符是inout,它总是流类型,由一系列的顶点组成,流类型格式可以是TriangleStream,PointStream,LineStream,对于点和线来说,输出的体元总是strip,如果要生成list,则要通过RestartStrip()函数来模拟。

     uint primID : SV_PrimitiveID是可选参数,表示输入体元的id。默认情况下,比如有n个三角形,则体元id就是0,…,n-1。

[maxvertexcount(4)]
void treeGeometryShader(point GeometryInputType gIn[1],    uint primID : SV_PrimitiveID,  inout TriangleStream<PixelInputType> triStream)
{   

    //转化中心点到世界坐标系
    gIn[0].centerW =  mul(gIn[0].centerW, (float3x3)worldMatrix);

    //得到billboard的四个顶点
    float halfWidth  = 0.5f*gIn[0].sizeW.x;
    float halfHeight = 0.5f*gIn[0].sizeW.y;
   
    float4 v[4];
    v[0] = float4(-halfWidth, -halfHeight, 0.0f, 1.0f);
    v[1] = float4(+halfWidth, -halfHeight, 0.0f, 1.0f);
    v[2] = float4(-halfWidth, +halfHeight, 0.0f, 1.0f);
    v[3] = float4(+halfWidth, +halfHeight, 0.0f, 1.0f);
    //
    // 计算四个顶点的纹理坐标.
    //
    float2 texC[4];
    texC[0] = float2(0.0f, 1.0f);
    texC[1] = float2(1.0f, 1.0f);
    texC[2] = float2(0.0f, 0.0f);
    texC[3] = float2(1.0f, 0.0f);
    //
    // 计算使得billboad面向摄像机的世界矩阵
    //
    float3 up = float3(0.0f, 1.0f, 0.0f);
    float3 look = cameraPosition.xyz - gIn[0].centerW;
    look.y = 0.0f;
    look = normalize(look);
    float3 right = cross(up, look);
   
    matrix W;
    matrix gViewProj;
    gViewProj = mul(viewMatrix, projectionMatrix);
   
    W[0] = float4(right,          0.0f);
    W[1] = float4(up,             0.0f);
    W[2] = float4(look,           0.0f);
    W[3] = float4(gIn[0].centerW, 1.0f);
    //float4x4
    matrix WVP = mul(W,gViewProj);
   
    //
    // 转化顶点到世界坐标系,输出三角形带
    //
    PixelInputType gOut;
    [unroll]
    for(int i = 0; i < 4; ++i)
    {
        gOut.posH     = mul(v[i], WVP);
        gOut.posW     = mul(v[i], W);
        gOut.normalW  = look;
        gOut.texC     = texC[i];
        gOut.primID   = primID; //体元id
       
        triStream.Append(gOut);
    }
}

gstree.ps代码:

float4 treePixelShader(PixelInputType pIn) : SV_Target
{
    float4 diffuse = shaderTexture.Sample(SampleType, pIn.texC);

    // alpha值小于0.25,放弃该像素
    clip(diffuse.a - 0.25f);
   
    // 输出纹理颜色
   
    return diffuse;
}

    其它的代码就是增加一个TreeMeshClass,表示树Mesh类,然后就是GSShaderClass,在这个类中,我们会装入gstree.vs,gstree.gs, gstree.ps,并传输const buffer以及相关设置。最后就是在GraphicsClass中,调用GSShaderClass渲染树。

需要注意的是,在其它ShaderClass中,我们要在RenderShader函数中加入下面的代码:

deviceContext->GSSetShader(NULL, NULL, 0);

否则的话,用这些shader类渲染的物体,会使用GSShaderClass指定的gs,从而使得渲染结果出错。

 

程序执行的结果如下:

完整的代码请参考:

工程文件myTutorialD3D11_48

代码下载:

http://files.cnblogs.com/mikewolf2002/d3d1139-49.zip

 

http://files.cnblogs.com/mikewolf2002/pictures.zip

 

 

     在myTutorialD3D11_49中,我们增加很多树,并使用纹理数组做为ps的输入,从而根据体元id号调用不同纹理贴图。

gtree.ps的代码稍微有点不同:

 

Texture2D shaderTexture[4];
SamplerState SampleType;

struct PixelInputType
{
    float4 posH    : SV_POSITION;
    float3 posW    : POSITION;
    float3 normalW : NORMAL;
    float2 texC    : TEXCOORD;
    uint primID    : SV_PrimitiveID;
};

float4 treePixelShader(PixelInputType pIn) : SV_Target
{
    // 从纹理行列式中得到
    //float3 uvw = float3(pIn.texC, pIn.primID%4);
    //float4 diffuse = gDiffuseMapArray.Sample(SampleType, uvw );
    int i = pIn.primID%4;
    float4 diffuse;
    if(i==0)
       diffuse = shaderTexture[0].Sample(SampleType, pIn.texC);
    else if(i==1)
       diffuse = shaderTexture[1].Sample(SampleType, pIn.texC);
    else if(i==2)
       diffuse = shaderTexture[2].Sample(SampleType, pIn.texC);
    else
       diffuse = shaderTexture[3].Sample(SampleType, pIn.texC);

    // alpha值小于0.25,放弃该像素
    clip(diffuse.a - 0.25f);
   
    // 输出纹理颜色
   
    return diffuse;
}

程序执行见面如下:

完整的代码请参考:

工程文件myTutorialD3D11_49

代码下载:

http://files.cnblogs.com/mikewolf2002/d3d1139-49.zip

 

http://files.cnblogs.com/mikewolf2002/pictures.zip

 

 

在myTutorialD3D11_47中,我们修改之前的基于雾,流水、山的代码,再在山上加上树,程序执行后的最终界面如下:

 

完整的代码请参考:

工程文件myTutorialD3D11_47

代码下载:

http://files.cnblogs.com/mikewolf2002/d3d1139-49.zip

 

http://files.cnblogs.com/mikewolf2002/pictures.zip

 

 

 

时间: 2024-10-09 07:51:17

Directx11教程(54) 简单的基于GS的billboard实现的相关文章

Directx11教程(53) D3D11管线(8) GS的调度执行

       在前面的教程中,我们分析了VS-PS的shader管线组合执行过程,本章我们分析一下VS-GS-PS的管线执行过程,主要是GS阶段hardware何如调度.      参考资料:http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-8/      从D3D10开始,D3D 管线中增加了GS 阶段,如下图所示:        从前面的教程中,我们知道在VS-PS的管

Directx11教程(51) 简单的billboard

       billboard称作公告板,通常用一个quad(四边形)表示[有的billboard用两个正交的quad表示],它的特点就是始终面向摄像机的方向.在大规模场景渲染中,可以公告板上贴一个纹理,比如树,这样在比较远的场景中,可以用它表示模型数据,从而减少场景中的顶点数量.            假定xz平面是大地,y轴是向上的方向,则billboard和y轴的方向一致,随着摄像机位置的改变,billboard必须改变自己的世界坐标,始终朝向摄像机.       在billboard类

Directx11教程(29) 简单的光照模型(8)

     现在我们新建一个工程myTutorialD3D_23,在这个工程中,对前面一章的代码进行一些整理: 1.我们在顶点属性中增加材质的的漫反射系数和高光系数,前面我们放在一个光照材质结构中,这样我们能够比较灵活的定义不同顶点的材质属性,当然这也增加了顶点缓冲的大小. struct VertexType     {     D3DXVECTOR3 position;     D3DXVECTOR3 normal; //法向     D3DXVECTOR4 Kd;  //材质漫反射系数    

Windows nginx安装教程及简单实践_nginx

概述 安装与使用     安装         从源代码编译 Nginx         Windows 安装         使用     nginx 配置实战     http反向代理配置     负载均衡配置     网站有多个webapp的配置     https反向代理配置 参考 概述 什么是nginx? Nginx (engine x) 是一款轻量级的Web 服务器 .反向代理服务器及电子邮件(IMAP/POP3)代理服务器. 什么是反向代理? 反向代理(Reverse Proxy)

100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】

转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 简介 流程图 simplest_ffmpeg_player标准版代码 simplest_ffmpeg_player_suSU版代码 结果 FFMPEG相关学习资料 补充问题     ===================================================== 最简单的

CSS3实例教程代码:简单制作遨游浏览器LOGO

文章简介:CSS3实例教程代码:简单制作遨游浏览器LOGO. <!DOCTYPE HTML> <html> <head> <title>LOGO</title> <style type="text/css"> * {margin:0; padding:0} .main {width:800px; margin:50px auto 0}  .main_wrapper {width:240px; height:240p

Javascript实例教程(8) 利用Javascript基于浏览器类型的重定向

javascript|教程|浏览器 利用javascript基于浏览器类型的重定向 基于浏览器类型的重定向的实现可以通过使用javascript函数来检查navigator.userAgent的字符串"MSIE",它将告诉你用户是否使用Microsoft Internet Explorer(微软的IE浏览器).通过修改windows.location函数可以重定向到正确的URL(同意资源定位器).下面是详细的代码: <HTML> <HEAD> <SCRIP

简单谈基于SQL SERVER 分页存储过程的演进

server|存储过程|分页 简单谈基于SQL SERVER 分页存储过程的演进 作者:郑佐日期:2006-9-30 针对数据库数据在UI界面上的分页是老生常谈的问题了,网上很容易找到各种"通用存储过程"代码,而且有些还定制查询条件,看上去使用很方便.笔者打算通过本文也来简单谈一下基于SQL SERVER 2000的分页存储过程,同时谈谈SQL SERVER 2005下分页存储过程的演进. 在进行基于UI显示的数据分页时,常见的数据提取方式主要有两种.第一种是从数据库提取所有数据然后在

PS教程:简单的婚片外景调色教程

PS教程:简单的婚片外景调色教程,图文教程 原图 效果图 打开原图,调整灰度 用色相饱和度调草颜色.如图: 用黑色画笔擦除不需要的树叶是它成黑色天空 黑色天空效果如图 用白色画笔点亮点 添加滤镜镜头光晕 打上自己喜欢的字 图层截图 最终效果!