热门标签 | 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


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • 在创建新的Android项目时,您可能会遇到aapt错误,提示无法打开libstdc++.so.6共享对象文件。本文将探讨该问题的原因及解决方案。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • XNA 3.0 游戏编程:从 XML 文件加载数据
    本文介绍如何在 XNA 3.0 游戏项目中从 XML 文件加载数据。我们将探讨如何将 XML 数据序列化为二进制文件,并通过内容管道加载到游戏中。此外,还会涉及自定义类型读取器和写入器的实现。 ... [详细]
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • Kubernetes 持久化存储与数据卷详解
    本文深入探讨 Kubernetes 中持久化存储的使用场景、PV/PVC/StorageClass 的基本操作及其实现原理,旨在帮助读者理解如何高效管理容器化应用的数据持久化需求。 ... [详细]
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社区 版权所有