标签:oid spl assembler str flag _for 位长 范围 sync
本章演示程序基于上一章的“陆地与波浪演示程序”的基础上构建而成的,其中利用了一个方向光来表示太阳,用户可以使用方向键来控制太阳的方向。
光照的计算需要依赖于表面法线,所以我们会在顶点层级定义法线,方便在光栅化过程中进行插值计算,由此展开逐像素光照。同时我们也不需要指定顶点的颜色,而是以每一个像素应用光照方程之后所生成的像素颜色代替指定顶点颜色。下面是顶点结构体:
//c++顶点结构体
struct Vertex
{
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT3 Noraml;
}
//对应的HLSL顶点结构体
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
}
//新的输入布局描述
mInputLayout =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
GeometryGenerator类中可以用于生成各种几何形状的函数,已经可以通过顶点法线去创建对应的图形数据,不过,为了使地形(terrain)拥有更加真实的表面,我们将会为地形生成法向量。
因为地形曲面的函数
\[
y = (x, z);
\]
已经给出来了,所以对于曲面上的任何一个点,我们都可以通过偏导数在+x和+z上建立两个切向量(tangent vector),因为这两个向量都位于曲面的切平面上,所以我们可以通过求这两个向量的叉乘来求取该点的法向量
\[
用于生成陆地网格的函数:f(x, z) = 0.3z·sin(0.1x) + 0.3x·cos(0.1z)
\]
则偏导数为:
\[
对x的偏导数:0.03z·cos(0.1x) + 0.3·cos(0.1z)
\]
\[ 对y的偏导数:0.3·sin(0.1x) - 0.03x·sin(0.1z) \]
此处跳过求两个切向量的叉乘步骤。
则位于曲面上一点(x, f(x, z), z)的曲面法线为:
\[
n(x, z) = (-w,1,-s);
\]
w为对x的偏导数,s为对y的偏导数。可以很清楚的看出,上述法线并不具有单位长度,所以在进行光照计算之前,我们需要对上述法线进行规格化处理。
我们要对地形的每一个顶点进行上述的法线计算,以获取他们对应的顶点法线:
XMFLOAT3 LitWavesApp::GetHillsNormal(float x, float z)const
{
XMFLOAT3 n(
-0.03f*z*cosf(0.1f*x) - 0.3f*cosf(0.1f*z),
1.0f,
-0.3f*sinf(0.1f*x) + 0.03f*x*sinf(0.1f*z));
XMVECTOR unitNormal = XMVector3Normalize(XMLoadFloat3(&n));
XMStoreFloat3(&n, unitNormal);
return n;
}
在8.13.7节中,我们将Light数组妨碍渲染过程常量缓冲区中。在演示程序中,我们使用一个方向光来表示太阳,并允许用户使用方向键来控制光源的方位。这也就是说,我们需要在每一帧更新阳光照射的方向,并且将结果设置到渲染过程常量中。
我们这里使用球坐标来追踪太阳的位置,但是由于太阳的距离使无限远的,所以对于径向距离的取值是无关紧要的。在实例程序中,我们将径向距离设置为1,使太阳始终在单位球体这一轨道上运动。下列代码用于更新方向光源方位:
float mSunTheta = 1.25f*XM_PI;
float mSunPhi = XM_PIDIV4;
void LitWavesApp::OnKeyboardInput(const GameTimer& gt)
{
const float dt = gt.DeltaTime();
if(GetAsyncKeyState(VK_LEFT) & 0x8000)
mSunTheta -= 1.0f*dt;
if(GetAsyncKeyState(VK_RIGHT) & 0x8000)
mSunTheta += 1.0f*dt;
if(GetAsyncKeyState(VK_UP) & 0x8000)
mSunPhi -= 1.0f*dt;
if(GetAsyncKeyState(VK_DOWN) & 0x8000)
mSunPhi += 1.0f*dt;
mSunPhi = MathHelper::Clamp(mSunPhi, 0.1f, XM_PIDIV2);
}
void LitWavesApp::UpdateMainPassCB(const GameTimer& gt)
{
……
XMVECTOR lightDir = -MathHelper::SphericalToCartesian(1.0f, mSunTheta, mSunPhi);
XMStoreFloat3(&mMainPassCB.Lights[0].Direction, lightDir);
mMainPassCB.Lights[0].Strength = { 1.0f, 1.0f, 0.9f };
auto currPassCB = mCurrFrameResource->PassCB.get();
currPassCB->CopyData(0, mMainPassCB);
}
为了实现光照,我们为着色器引入了一个材质常量缓冲区,为了支持这个新引入的常量缓冲区,我们需要改写之前的根签名。
void LitWavesApp::BuildRootSignature()
{
// 创建根参数(根参数可以是描述符表,根描述符,根常量)
CD3DX12_ROOT_PARAMETER slotRootParameter[3];
// 创建根CBV
slotRootParameter[0].InitAsConstantBufferView(0);
slotRootParameter[1].InitAsConstantBufferView(1);
slotRootParameter[2].InitAsConstantBufferView(2);
// 根签名是一系列根参数组成的
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(3, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
……
}
标签:oid spl assembler str flag _for 位长 范围 sync
原文地址:https://www.cnblogs.com/yaya12138/p/11909365.html