热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

DirectX11教程:利用几何着色器实现简易Billboard

本文通过实现一个基于几何着色器(GeometryShader)的Billboard案例,深入探讨DirectX11中几何着色器的功能与应用。文章详细介绍了如何在顶点着色器中处理顶点数据,并在几何着色器中生成面向摄像机的四边形。

本文将通过一个具体的案例——基于几何着色器(GS)的Billboard实现,来深入了解DirectX 11中几何着色器的应用。Billboard是一种常用的图形技术,用于创建始终面向摄像机的对象,如树木或标志牌等。


在顶点着色器(VS)中,我们处理的是单个顶点的数据,包括位置和属性,输出同样为顶点的位置和属性。而几何着色器(GS)则接收整个基本图元(如点、线、三角形等),并可以生成新的顶点、顶点属性和图元信息。


例如,对于一个三角形,顶点着色器会分别处理每个顶点,而在几何着色器中,整个三角形作为输入,输出可以是一个包含多个三角形的复杂形状。通过这种方式,几何着色器能够实现更为复杂的图形变换和生成。


在实现Billboard时,我们使用一个点来表示Billboard的中心,通过一个参数控制其宽度和高度。几何着色器接收这个点作为输入,生成四个顶点及其纹理坐标,形成两个三角形来表示Billboard。此外,还会计算一个转换矩阵,确保Billboard始终面向摄像机。


以下是顶点着色器(VS)的代码示例,它主要负责将输入的顶点数据直接传递给几何着色器,不做额外处理:


GeometryInputType treeVertexShader(VertexInputType input)
{
GeometryInputType output;
output.centerW = input.centerW;
output.sizeW = input.sizeW;
return output;
}

接下来是几何着色器(GS)的关键部分,它负责生成面向摄像机的四边形,并计算纹理坐标:


[maxvertexcount(4)]
void treeGeometryShader(point GeometryInputType gIn[1], uint primID : SV_PrimitiveID, inout TriangleStream 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);

// 计算使Billboard面向摄像机的世界矩阵
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);

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);
}
}

像素着色器(PS)负责采样纹理并输出最终的颜色值。如果纹理的Alpha值低于0.25,则丢弃该像素:


float4 treePixelShader(PixelInputType pIn) : SV_Target
{
float4 diffuse = shaderTexture.Sample(SampleType, pIn.texC);
clip(diffuse.a - 0.25f);
return diffuse;
}

为了支持多个树的渲染,我们在像素着色器中引入了一个纹理数组,根据基本图元ID选择不同的纹理。这样可以在同一场景中使用多种不同的树纹理:


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
{
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);

clip(diffuse.a - 0.25f);
return diffuse;
}

为了确保其他着色器不会误用几何着色器,需要在渲染时显式地禁用几何着色器:


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

完整的项目文件和源代码可以在这里下载:http://files.cnblogs.com/mikewolf2002/d3d1139-49.ziphttp://files.cnblogs.com/mikewolf2002/pictures.zip


推荐阅读
author-avatar
哲纸
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有