实例化实际上就是通过一套顶点和索引,绘制多个物体。
可以为物体设置不同的转换矩阵,纹理等所需属性。
简单流程如下:
那么通过实例化只需要一次DrawCall就可以把这些目标全部渲染。
在上一篇博客动态索引中,实际上已经把纹理属性和纹理都写入了缓冲区。
渲染流程如下:
在这里,采用实例化的方式,通过shader中的SV_InstanceID参数,我们可以确定当前绘制的是第几个实例化目标。
把每个渲染目标的数据也提前一次性写入缓冲区。
整体非常的简单,也没有什么坑,这里就不再细说了。
github:
https://github.com/mversace/DirectX12-MiniEngine-Dragon/tree/63d3490e0cccedd2a01fc5402c53ce9a208252b0
这个的意思就是,利用CPU根据当前的摄像机计算出可以看到的视锥体,然后把与视锥体不相交的渲染目标剔除出渲染队列。
这可以有效的减少shader运算的负担。否则的话,渲染流水线依旧会提交这些顶点,直到经过几何着色阶段之后,开始裁剪时才会去除目标,而这会带来大量的计算开销。
CPU提前进行视锥体裁剪就是CPU多计算一点,来提高整体的运行效率。
MiniEngine中在Camera类中本身有剔除类Frustum(被删掉了,这次加回来)
我们需要修改Frustum这个类中构造透视视锥体的代码,来匹配左手坐标系
void Frustum::ConstructPerspectiveFrustum( float HTan, float VTan, float NearClip, float FarClip )
{
// 已改为左手坐标系
const float NearX = HTan * NearClip;
const float NearY = VTan * NearClip;
const float FarX = HTan * FarClip;
const float FarY = VTan * FarClip;
// 视锥体,计算近远两个面的4个顶点
m_FrustumCorners[ kNearLowerLeft ] = Vector3(-NearX, -NearY, NearClip); // Near lower left
m_FrustumCorners[ kNearUpperLeft ] = Vector3(-NearX, NearY, NearClip); // Near upper left
m_FrustumCorners[ kNearLowerRight ] = Vector3( NearX, -NearY, NearClip); // Near lower right
m_FrustumCorners[ kNearUpperRight ] = Vector3( NearX, NearY, NearClip); // Near upper right
m_FrustumCorners[ kFarLowerLeft ] = Vector3( -FarX, -FarY, FarClip); // Far lower left
m_FrustumCorners[ kFarUpperLeft ] = Vector3( -FarX, FarY, FarClip); // Far upper left
m_FrustumCorners[ kFarLowerRight ] = Vector3( FarX, -FarY, FarClip); // Far lower right
m_FrustumCorners[ kFarUpperRight ] = Vector3( FarX, FarY, FarClip); // Far upper right
const float NHx = RecipSqrt( 1.0f + HTan * HTan );
const float NHz = NHx * HTan;
const float NVy = RecipSqrt( 1.0f + VTan * VTan );
const float NVz = NVy * VTan;
// 计算视锥体的6个面,存储的是法向量以及面上的一个点
m_FrustumPlanes[kNearPlane] = BoundingPlane( 0.0f, 0.0f, 1.0f, NearClip );
m_FrustumPlanes[kFarPlane] = BoundingPlane( 0.0f, 0.0f, -1.0f, FarClip );
m_FrustumPlanes[kLeftPlane] = BoundingPlane( NHx, 0.0f, NHz, 0.0f );
m_FrustumPlanes[kRightPlane] = BoundingPlane( -NHx, 0.0f, NHz, 0.0f );
m_FrustumPlanes[kTopPlane] = BoundingPlane( 0.0f, -NVy, NVz, 0.0f );
m_FrustumPlanes[kBottomPlane] = BoundingPlane( 0.0f, NVy, NVz, 0.0f );
}
还有点修改,详见log记录。
MiniEngine中设计的还是比较巧妙的,原版本是Camera中的矩阵存储了摄像机深度比例。我们后来修改后采用了dx的api,这个比例就没有了,所以给Frustum的构造函数额外传一个参数提供这个比例,以生成正确的视锥体。
龙书中是设置了一个上传缓冲区,动态的将渲染目标的一些参数拷贝进去。拷贝的量有点大,我这里采用另外一种方式来实现。
这种方式,对于每次拷贝的数据量稍微小点。
这个稍微改下就可以了。
有个点需要注意,shader中的数组,数组中的每个值都会至少分配float4的空间(vs2019图像调试经常崩溃无法定位,研究了很久才找到问题)
所以我们传入的数据要适配这种格式。
cbuffer cbPass1 : register(b1)
{
int gDrawObjs[128]; // 数组中的每个元素都会被封装为float4,d3d12龙书727页
};
相应的修改C++中渲染目标之类的结构体,稍微调整下。