DirectX 读书笔记(14) Cube mapping之SkyBox

作者:i_dovelemon

来源:CSDN

日期:2014 / 10 / 26

主题:Cube mapping, SkyBox

引言

在3D游戏中,特别是在室外游戏场景中,往往需要模拟出天空的效果。如下图所示:

在这张图片中,读者可以发现,场景中存在这天空,而且不管游戏角色如何在场景中进行移动,都无法靠近天空。通过添加这样的效果,使得游戏世界更加的丰富多彩,更加的接近现实的世界。本节,就想大家讲述,如何在DirectX中实现这样的天空效果。

Cube Mapping

Cube Mapping是Enviroment Mapping中的一种。我们知道,传统的纹理映射方式,是用一张2D的图片,映射到2D的表面上去。这样的映射方法在我的博客中关于纹理映射一章中讲述了如何一一的进行了映射。从中我们可以发现,这是一种平面上的一对一的映射方法。而Cube
mapping是一种3D空间的映射方法。对于一个3D空间的物体,我们有时候会发现使用2D的纹理来对3D物体的每一个表面进行映射,在物体的整体感觉上会差很多。那么,有没有一种方法,能够将纹理进行整体的考量,然后映射到3D物体上去,这样物体的纹理效果看上去就十分的和谐,整体上十分的舒适。使用Cube mapping就能够做到这一点。

Cube mapping是将一张由6个面组成的纹理,构成一个在逻辑上是Cube立方体的3D纹理图。也就是如下图所示的样子:

这样的一张图,它有6个面组成,分别对应了一个立方体的6个面。一般来说,我们将以如下的顺序来构成这个纹理图:

从图中可以看出,这6个面就构成了一个立方体。在有了这个逻辑上,可以作为立方体来看待的纹理图之后,我们需要的就是如何定义这个纹理上的坐标。很显然,对于3D的纹理图,自然应该使用3D空间坐标来表示。那么,这个3D空间坐标是如何映射到这个3D空间纹理上来的了。

我们来看看如下的一个例子,为了解释的方便,我在2D空间上作图,看下图:

上图中外围的立方体就是我们的3D空间的纹理,而里面的球体,就是我们希望将3D纹理映射到的物体。进行Cube mapping的时候,我们从球体的几何中心发射一条射线到我们希望映射的那个球体的点上去,这个射线同时也会和立方体相交,那么我们就可以将这个与立方体相交的点作为球体上的点的纹理。通过这样的方法,对于任何一个球体上的点,我们都可以在立方体上找到与之相对应的纹理。那么剩下的问题是,纹理坐标从何而来?

从上面的描述中,读者会发现,这条射线应该就是球体上的坐标点的坐标值(如果球心就是模型坐标的中心点的话)V(x,y,z)。那么,如何根据这个纹理坐标来获取我们那个纹理图上的纹理了?要知道,虽然逻辑上它是一个3D纹理图,但是实际在存储的时候,还是使用2D的方法进行存储的。所以,我们需要从向量V中获取与射线相交的那一个点到底在哪一个面上?

对于任何一个向量V(x,y,z),我们选取它的任何一个绝对值最大的分量,那么这个分量就标示了这条射线与哪一个面相交。比如说,对于向量V(-3,1,0)来说,这个向量所发出的射线是与立方体的左面,即-X面相交的。对于向量V(1,4,-1)来说,这个向量所发出的射线是与立方体的上面,即+Y面相交的。这是很显然的事实,读者可以自行在图上画画,了解一下。

在获取到向量与哪一个面相交之后,剩下的问题就是如何从这个面上获取纹理。这个问题就变化成为了一个2D平面上获取纹理的问题了。从纹理映射一节中,我们知道,纹理映射的方法,是纹理的左上角坐标为(0,0),右下角坐标为(1,1),即纹理坐标的u和v坐标范围是(0,1)上。那么,我们如何获取这个坐标值了?

在上面,我们使用绝对值最大的分量来确定了与哪一个面相交,那么剩下的两个分量与纹理坐标是否有联系了?答案是肯定的,读者可以想象一下,在3D空间中,当我们正对着3D纹理的一个面的时候,是不是它的坐标就对应着剩下的两个分量了?也就是说,我们通过一种线性的变换,将剩下的两个值,转化成为[0,1]这个空间来,就能够根据这个像素值来获取上面的纹理值了。

这样的线性变换十分的简单,我们知道,一个绝对值最大的分量,那么我们就可以使用这个分量的绝对值来作为参考,因为其他值的绝对值都会比这个值来的小,即,其他值除以这个值之后的值所在的范围是[-1,1]。我们对这个结果进行平移操作使得范围变成了[0,2],再在这个基础上乘以0.5就变成了[0,1]。由于前面进行变换都是线性的,所以一一对应的这种属性并没有随着变换而发生改变,即原来的值对应的纹理,在进过变换之后,依然还是这个纹理,只是从3D空间的坐标变成了2D空间的纹理坐标,便于我们在纹理图上进行访问。

上面就是进行Cube mapping的过程。这个过程在DirectX中,已经支持了。我们可以不需要自己来实现。我们的工作只是需要创建一个这样的3D纹理图。然后将这个图附加给我们想要映射的3D物体即可。在上面的讨论中,我们已经明白了进行这种映射时的纹理坐标就是3D物体本身的模型坐标。

Sky Box

好了,在讲解了Cube mapping之后,现在就来讲解下,如何实现天空。实现天空的方法,有很多。一般来讲,我们可以创建一个天空几何体,这个几何体可以是球体,也可以是立方体。这里我们使用球体来代表天空盒子。创建好了天空盒子之后,在绘制的时候,我们需要注意一点,也就是上面我们说的,不管相机如何的移动,总是没有办法接近SkyBox的边缘。而实现这样的效果,要么将天空盒子做的无限大,很显然在计算机中没有无限大的概念,那么就只有将天空盒子随着相机一起进行移动。也就是说,使的相机总是治愈天空盒子的中心处,并且将天空盒子的z值设置成为最远的z值。这样就能够保证,天空总是处于最外围的包围状态。下面是实现这个Skybox的代码。

<span style="font-family:Microsoft YaHei;">//Load the texture
HR(D3DXCreateCubeTextureFromFile(m_pDevice, L"grassenvmap1024.dds", &m_pCubeTex));</span>

调用D3DXCreateCubeTextureFromFile来读取3D纹理图。这个纹理图可以通过一些软件制作得到。这里不介绍如何制作这些图片。

<span style="font-family:Microsoft YaHei;">void CubeDemo::drawCube()
{
	UINT pass = 0 ;
	m_pEffect->Begin(&pass, 0);

	for(UINT i = 0 ; i< pass ; i ++ )
	{
		m_pEffect->BeginPass(i);
		D3DXMATRIX m ;
		D3DXMatrixIdentity(&m);
		D3DXMatrixTranslation(&m, m_Camera.pos().x, m_Camera.pos().y,m_Camera.pos().z );
		m_pEffect->SetMatrix(m_gWVP, &(m * m_Camera.viewproj()));
		m_pEffect->SetTexture(m_gTex, m_pCubeTex);
		m_SphereMesh->DrawSubset(0);

		m_SphereMesh->DrawSubset(0);
		m_pEffect->EndPass();
	}

	m_pEffect->End();
}</span>

这个函数用来将天空盒子进行平移,使得相机总是至于天空盒子的中心处。

<span style="font-family:Microsoft YaHei;">//---------------------------------------------------------------------
// declaration	: Copyright (c), by XJ , 2014 . All right reserved.
// brief	: This shader file will define the skybox using the cube mapping
// file		: SkyBox.fx
// author	: XJ
// date		: 2014 / 10 / 26
// version	: 1.0
//------------------------------------------------------------------------

/**
* Define the variant
*/
uniform extern float4x4 gWVP ;				// the world-view-projection matrix
uniform extern texture gTex   ;				// the cube mapping texture

sampler EnvMaps = sampler_state
{
	Texture = <gTex> ;
	MinFilter = LINEAR ;
	MagFilter = LINEAR ;
	MipFilter = LINEAR ;
	AddressU = WRAP ;
	AddressV = WRAP ;
};

void SkyVS(float3 posL: POSITION0,
	   out float4 posH: POSITION0,
	   out float3 oEnvTex: TEXCOORD0)
{
	//set the z = w, to make the z/w =1, which means the vertex is always in the far plane
	posH = mul((float4(posL, 1.0f)), gWVP).xyww;

	//save the vertex position as the texture coordinate
	oEnvTex = posL ;
}

float4 SkyPS(float3 oEnvTex: TEXCOORD0):COLOR0
{
	return texCUBE(EnvMaps, oEnvTex);
}

technique SkyBox
{
	pass p0
	{
		vertexShader = compile vs_2_0 SkyVS();
		pixelShader = compile ps_2_0 SkyPS();

		CullMode = None ;
		ZFunc	= Always ;

		StencilEnable = true ;
		StencilFunc = Always ;
		StencilPass = Replace ;
		StencilRef = 0 ;
	}
}</span>

这个是天空盒子的Shader文件。可以发现,我们在VS中,将齐次化之后的坐标值的z值设置为w,这样在进行最后的齐次化操作的时候,z/w = 1。而我们知道,在DirectX中,进行3次基本变换之后的空间是一个X[-1,1] - Y[-1,1] - Z[0,1]的空间。也就是说,如果z值为1的时候,就是距离最远的地方,在大的话,就会被硬件上的流水操作裁剪掉了。所以,通过这样的方法,我们就能够保证,天空盒子用于处在最远处。

同时还需要注意的是,我们这时候使用的映射方法不是以前的tex2D映射方式了,而是texCUBE映射方法。

下面是最终的截图:

好了,今天的笔记到此结束!!!

时间: 2024-11-05 11:15:34

DirectX 读书笔记(14) Cube mapping之SkyBox的相关文章

MapReduce与批处理------《Designing Data-Intensive Applications》读书笔记14

之前的文章大量的内容在和大家探讨分布式存储,接下来的章节进入了分布式计算领域.坦白说,个人之前专业的重心侧重于存储,对许多计算的内容理解可能不是和确切,如果文章中的理解有所不妥,愿虚心赐教.本篇将和大家聊一聊分布式计算的一个子集:批处理. 批处理系统通常也叫脱机系统,需要大量的输入数据,运行一个作业来处理它,并产生一些输出数据.工作通常需要一段较长的时间(从几分钟到几天).批处理作业通常是周期性地运行的(例如,一天一次).批处理作业的主要性能度量通常是吞吐量. 1.MapReduce 批处理是我

(转)【D3D11游戏编程】学习笔记二十一:Cube Mapping及其应用之一:天空盒的实现

(注:[D3D11游戏编程]学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~) 这一节讨论有关纹理映射的进阶内容:Cube Mapping. 1. 简介 单从名字上,就大概可以看出点端倪了,翻译成中文为立方体映射,因此肯定跟立方体有关系.确实,Cube Mapping就是使用六张正方形的图片来进行纹理映射的.这六张图片分别对应了一个立方体中的六个面.由于这个立方体是轴对齐的,因此每个面可以用坐标系中的六个轴方向来惟

《TCP/IP详解卷1:协议》第14章 DNS:域名系统---读书笔记

<TCP/IP详解卷1:协议>第14章 DNS:域名系统---读书笔记 1.引言 5.指针查询 DNS中一直难于理解的部分就是指针查询方式,即给定一个IP地址,返回与该地址对应的域名. 当一个组织加入Internet,并获得DNS域名空间的授权,如noao.edu,则它们也获得了对应IP地址的in-addr.arpa域名空间的授权.在noao.edu这个例子中,它的网络号是140.252的B类网络.在DNS树中结点in-addr.arpa的下一级必须是该IP地址的第一字节(例中为140),再下

《DirectX 9.0 3D游戏开发编程基础》 第一章 初始化Direct3D 读书笔记

REF设备 参考光栅设备,他能以软件计算方式完全支持Direct3D Api.借助Ref设备,可以在代码中使用那些不为当前硬件所支持的特性,并对这此特性进行测试. D3DDEVTYPE 在程序代码中,HAL设备用值D3DDEVTYPE_HAL来表示.该值是一个枚举变量.REF设备用D3DDEVTYPE_REF来表示.这种类型非常重要,你需要铭记,因为在创建设备的时候,我们必须指定使用哪种设备类型. COM(组件对象模型) 创建COM接口时不可以使用c++关键字new.此外使用完接口,应调用Rel

算法导论读书笔记(14) - 二叉查找树的具体实现

算法导论读书笔记(14) - 二叉查找树的具体实现 目录 二叉查找树的简单Java实现 二叉查找树的简单Java实现 /** * 二叉查找树 * 部分代码参考自TreeMap的源码 */ public class BinarySearchTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; pub

(转)【D3D11游戏编程】学习笔记二十三:Cube Mapping进阶之动态环境图

(注:[D3D11游戏编程]学习笔记系列由CSDN作者BonChoix所写,转载请注明出处:http://blog.csdn.net/BonChoix,谢谢~) 在前面两篇介绍Cube Mapping的文章中,我们所使用到的Cube Map都是事先制作好的,这样的一个好处就是运行时效率很高,适合于大多数情形.但如果对于即时动态变化的场景来说,依靠静态图来实现反射效果就不再适用了.因为在不同时刻,一个物体周围的场景是不断变化的,想要把这些变化在物表的反射中体现出来,就需要一张动态的环境图. 1.C

读书笔记: 博弈论导论 - 14 - 不完整信息的静态博弈 机制设计

读书笔记: 博弈论导论 - 14 - 不完整信息的静态博弈 机制设计 机制设计(Mechanism Design) 本文是Game Theory An Introduction (by Steven Tadelis) 的学习笔记. 机制设计的概念 机制设计的目标是设计一个可以达到期望收益的博弈. 由于这是根据博弈结果来推导博弈的形式,也被称为反向博弈论(reverse game theory). 这个理论明显在经济和政治方面有很多用途. 我们假象这样一个例子: 某个政府需要设计一个关于化工厂的环

SQL Server2012 T-SQL基础教程--读书笔记(5-7章)

SQL Server2012 T-SQL基础教程--读书笔记(5-7章) SqlServer T-SQL 示例数据库:点我 Chapter 05 表表达式 5.1 派生表 5.1.1 分配列别名 5.1.2 使用参数 5.1.3 嵌套 5.1.4 多个引用 5.2 公用表表达式 5.2.1 分别列别名 5.2.2 使用参数 5.2.3 定义多个CTE 5.2.4 CTE的多次引用 5.2.5 递归CTE 5.3 视图 5.3.1 视图和ORDER BY 子句 5.3.2 视图选项 5.4 内嵌表

《Effective C++》读书笔记汇总

我之前边读<Effective C++>边写下每个条款的读书笔记,这一版是C++11之前的版本.这里我将每个条款令我印象深刻的点小结一下. 1.C++包括:Plain C(面向过程).OOP(面向对象).模板(泛型和模板元编程).STL(C++标准库). 2.用inline.enum.const代替#define.#define定义的宏,一旦复杂起来,高手都很难掌控.不要带入C的习惯. 3.灵活使用const前缀.不需要进行改变的数据加上const前缀.指针的const前缀有两种形式,cons