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
Directx11教程(54) 简单的基于GS的billboard实现相关推荐
- python云盘搭建教程_超简单!基于Python搭建个人“云盘”,目前最好用的个人云盘...
超简单!基于Python搭建个人"云盘",目前最好用的个人云盘 1. 简介 当我们想要从本地向云服务器上传文件时,比较常用的有pscp等工具,但避免不了每次上传都要写若干重复的代码 ...
- 最简单的基于FFmpeg的AVfilter的例子-修正版
代码是参考雷神的博客的代码,不过由于ffmpeg版本不同,记录使用中遇到的问题. 1.调用avfilter_get_by_name("ffbuffersink")时在新版本的ffm ...
- 最简单的基于FFMPEG的Helloworld程序
学习雷神的FFMPEG入门教程,本文基于命令行实现. 文件结构 G:\Coding\FFMpeg\Proj\Console>dir驱动器 G 中的卷没有标签.卷的序列号是 0FD5-0CC8G: ...
- 最简单的基于FFMPEG的推流器附件:收流器
===================================================== 最简单的基于FFmpeg的推流器系列文章列表: <最简单的基于FFmpeg的推流器(以 ...
- 最简单的基于FFmpeg的编码器-纯净版(不包含libavformat)
===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...
- 最简单的基于FFMPEG的视频编码器(YUV编码为H.264)
===================================================== 最简单的基于FFmpeg的视频编码器文章列表: 最简单的基于FFMPEG的视频编码器(YUV ...
- 最简单的基于FFMPEG+SDL的视频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...
- SAP UI5 应用开发教程之六十四 - 基于 OData V4 的 SAP UI5 表格控件如何实现 filter(过滤) 和 sort(排序)功能试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- SAP UI5 应用开发教程之六十四 - 基于 OData V4 的 SAP UI5 表格控件如何实现 filter(过滤) 和 sort(排序)功能
本教程的上一个步骤,我们学习了如何基于 OData V4 开发一个最简单的使用了 Table(表格)控件的 SAP UI5 应用. SAP UI5 应用开发教程之六十二 - 基于 OData V4 的 ...
- SAP UI5 应用开发教程之六十二 - 基于 OData V4 的 SAP UI5 表格控件使用方法介绍试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
最新文章
- 哈希一致性、哈希取余、自定义轮询分片的比较
- 苹果手机怎么投屏 如何操作
- 【MM模块】 External Services 外部服务
- tf.Variable()、tf.get_variable()
- java炸弹游戏_java实现数字炸弹
- 芬兰高性能图表控件-免费试用并提供技术支持
- Python多线程下实现单例模式,以及limit实例模式
- docker常用命令_docker常用命令整理
- Exception in thread http-bio-8081-exec-3 java.lang.OutOfMemoryError: PermGen space
- php数组的奇数_PHP - 查找数组元素是奇数还是偶数
- excel打印预览在哪里_excel预览打印超出范围该怎么办呢?
- js 表格动态增加行通用函数
- JavaWeb基础教程
- UI设计师都用什么工具?
- 股票买卖明细接口是怎样实现查询交易数据的?
- 万兆局域网方案_家庭基础万兆网络——最简单的方案
- DNS与ARP的关系与原理解析
- 数据透视表的发明历史
- linux查看ip等信息的命令
- 业务消息中心系统设计与实现(一)