Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十八章:立方体贴图

代码工程地址:

https://github.com/jiabaodan/Direct12BookReadingNotes



学习目标

  1. 学习什么是立方体贴图,并且如何在HLSL中对它们采样;
  2. 如何使用DX的纹理工具创建立方体贴图;
  3. 学习如何用立方体贴图来模仿反射;
  4. 学习如何使用立方体贴图对球体采样来模拟一个天空和远处的山。


1 立方体纹理映射

在Direct3D中,立方体纹理是使用一组具有6个元素的纹理数组:
1、索引0代表指向+X面;
2、索引1代表指向-X面;
3、索引2代表指向+Y面;
4、索引3代表指向-Y面;
5、索引4代表指向+Z面;
6、索引5代表指向-Z面;

相对于2D纹理,我们不能再用2D纹理坐标来采样,需要使用3D纹理坐标(代表看向的方向)来采样。在第九章中介绍的纹理滤波器对于立方体纹理依然适用。
在这里插入图片描述
对于立方体纹理采样,查找向量的长度是不重要的,只有方向重要;如果两个具有相同方向但是不同长度的向量,采样出来的结果是一致的。

在HLSL中立方体纹理是TextureCube类型,下面的代码段展示了如何采样:

TextureCube gCubeMap;
SamplerState gsamLinearWrap : register(s2);
…
// in pixel shader
float3 v = float3(x,y,z); // some lookup vector

查找向量应该和立方体纹理关联的坐标系是一致的,否则采样结果会不正确。



2 环境贴图

对于立方体贴图最主要的应用就是环境纹理映射(environment mapping)。它的思路就是将相机固定在一个位置,然后朝6个方向分别拍摄图片,然后组成立方体纹理来模拟环境:
在这里插入图片描述
根据上面的描述,我们需要对场景中的每个物体创建用以环境纹理采样的环境贴图,这样会更准确,但是也需要更多的纹理内存。有一种折中的办法是,在场景中几个重要的点上创建环境纹理,然后物体从最近的环境纹理上进行采样;这个方法在应用中效果不错,因为对于曲面物体,不正确的采样很难让玩家察觉。还有一种简化的方法是省略场景中一些特定的物品:比如只拍摄远处的山和天空,近处的物体直接省略掉。如果要拍摄近处的物体,我们就需要使用Direct3D来渲染6个图片,这个在本章第五节中讲解。
在这里插入图片描述
如果相机向下拍摄的图片是是在世界坐标的轴,那么这个环境贴图我们就说是与世界坐标系相关。

因为立方体贴图只是保存了纹理数据,所以它可以让艺术家提前制作,而不需要在D3D中实时渲染。对于户外环境,可以使用软件Terragen(http://www.planetside.co.uk/)来生成。

如果你尝试使用Terragen,你需要到摄像机设置中,设置缩放因子为1.0来达到一个90度的视野。同时设置输出图片的维度要相等,这样水平和竖直方向的视野就都是相等的90度。
(https://developer.valvesoftware.com/wiki/Skybox_(2D)_with_Terragen)中有一个很好用的Terragen脚本,会使用当前摄像机位置,渲染6个立方体贴图使用的纹理。

DDS纹理贴图格式可以支持立方体贴图,并且我们可以使用texassemble工具通过6个图像来创建立方体贴图。下面是使用texassemble创建的一个例子:

texassemble -cube -w 256 -h 256 -o cubemap.dds
lobbyxposjpg lobbyxneg.jpg lobbyypos.jpg
lobbyyneg.jpg lobbyzpos.jpg lobbyzneg.jpg

NVIDIA提供了一个PS的保存.DDS格式立方体贴图的插件:https://developer.nvidia.com/nvidia-texture-tools-adobe-photoshop


2.1 在D3D中加载和使用立方体贴图

我们的DDS纹理加载代码(DDSTextureLoader.h/.cpp)已经支持的对立方体贴图的加载。加载代码会检测出包含的立方体贴图,然后创建纹理数组并加载它们:

auto skyTex = std::make_unique<Texture>();
skyTex->Name = "skyTex";
skyTex->Filename = L"Textures/grasscube1024.dds";
ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.mCommandList.Get(), skyTex->Filename.c_str(),
skyTex->Resource, skyTex->UploadHeap));

在SRV中使用D3D12_SRV_DIMENSION_TEXTURECUBE维度和TextureCube属性:

D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Shader4ComponentMapping =
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
srvDesc.TextureCube.MostDetailedMip = 0;
srvDesc.TextureCube.MipLevels = skyTex->GetDesc().MipLevels;
srvDesc.TextureCube.ResourceMinLODClamp = 0.0f;
srvDesc.Format = skyTex->GetDesc().Format;
md3dDevice->CreateShaderResourceView(skyTex.Get(), &srvDesc, hDescriptor);


3 纹理映射一个天空

创建一个大的球体然后映射一个环境贴图:
在这里插入图片描述
我们假设天空球是无限远的,并且把它在世界坐标系下的位置设置为何摄像机一致。
着色器文件代码如下:

//*********************************************************************
// Sky.hlsl by Frank Luna (C) 2015 All Rights Reserved.
//*********************************************************************

// Include common HLSL code.
#include "Common.hlsl"

struct VertexIn
{
	float3 PosL : POSITION;
	float3 NormalL : NORMAL;
	float2 TexC : TEXCOORD;
};

struct VertexOut
{
	float4 PosH : SV_POSITION;
	float3 PosL : POSITION;
};

VertexOut VS(VertexIn vin)
{
	VertexOut vout;
	
	// Use local vertex position as cubemap lookup vector.
	vout.PosL = vin.PosL;
	
	// Transform to world space.
	float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
	
	// Always center sky about camera.
	posW.xyz += gEyePosW;
	
	// Set z = w so that z/w = 1 (i.e., skydome always on far plane).
	vout.PosH = mul(posW, gViewProj).xyww;
	
	return vout;
}

float4 PS(VertexOut pin) : SV_Target
{
	return gCubeMap.Sample(gsamLinearWrap, pin.PosL);
}

绘制天空的着色器程序明显和我们绘制物体的着色器程序不同,但是他们分享了相同的根签名,所以我们不需要切换根签名。下面的代码在Default.hlsl和Sky.hlsl是一样的,所以直接移到了Common.hlsl里面,来防止代码重复:

//****************************************************************************
// Common.hlsl by Frank Luna (C) 2015 All Rights Reserved.
//****************************************************************************

// Defaults for number of lights.
#ifndef NUM_DIR_LIGHTS
	#define NUM_DIR_LIGHTS 3
#endif
#ifndef NUM_POINT_LIGHTS
	#define NUM_POINT_LIGHTS 0
#endif
#ifndef NUM_SPOT_LIGHTS
	#define NUM_SPOT_LIGHTS 0
#endif

// Include structures and functions for lighting.
#include "LightingUtil.hlsl"

struct MaterialData
{
	float4 DiffuseAlbedo;
	float3 FresnelR0;
	float Roughness;
	float4x4 MatTransform;
	uint DiffuseMapIndex;
	uint MatPad0;
	uint MatPad1;
	uint MatPad2;
};

TextureCube gCubeMap : register(t0);

// An array of textures, which is only supported in shader model 5.1+. Unlike
// Texture2DArray, the textures in this array can be different sizes and
// formats, making it more flexible than texture arrays.
Texture2D gDiffuseMap[4] : register(t1);

// Put in space1, so the texture array does not overlap wit
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值