- 注意!本文是在下几年前入门期间所写(young and naive),其中许多表述可能不正确,为防止误导,请各位读者仔细鉴别。
这篇文章讲的也是两部分内容,一部分是讲如何将一个对一个在场景中重复出现很多次的物体做优化,我们希望尽可能合并一些重复运行的代码,然后第二部分讲的是视锥裁剪,也就是在cpu上做一下检测,将不在视锥内的物体舍弃,不提交给gpu。因为提交给gpu之后gpu还是要运行顶点着色器、几何着色器等,直到裁剪阶段才会将其舍弃掉,这部分的代码就白运行了,不如一开始就不提交给gpu。
Instancing
为什么要用dx的instance功能
一个场景里可能会有很多个重复的物体,比如一片森林有可能是由几种树构成,然后这些树有不同的缩放、位置、旋转等,有的时候可能有不同的材质,这样构成了一片森林,按我们之前的做法,是每棵树都单独传一次cb,但是其实没有必要,我们可以用一个structured buffer一次把所有相同物体的不同的部分传进去,比如材质的index,和世界矩阵等,然后渲染的时候再用index来取到这些值。这样的话一个render item可能包含很多个instance,而每个instance只要传一次值就行,假如一片森林有1000颗树,分为10种,这样做的话我们只要传10次structured buffer,而不用传1000次cb。
如何使用dx的instancing
我们之前使用的Draw call里已经使用到了这个函数了,只不过我们一直都只画了1个instance。
cmdList->DrawIndexedInstanced(ri->IndexCount, 1, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
其中第二个参数就是instance的数量,这个数量是多少,draw call就会运行多少次,对应的在shader里可以获取到一个系统值SV_InstanceID,通过这个系统值可以知道当前是第几个instance(从0开始),然后用这个作为structured buffer的index即可找到这个instance的材质index和世界矩阵等信息。具体的shader代码如下:
// Include structures and functions for lighting.
#include "LightingUtil.hlsl"
struct InstanceData
{
float4x4 World;
float4x4 TexTransform;
uint MaterialIndex;
uint InstPad0;
uint InstPad1;
uint InstPad2;
};
struct MaterialData
{
float4 DiffuseAlbedo;
float3 FresnelR0;
float Roughness;
float4x4 MatTransform;
uint DiffuseMapIndex;
uint MatPad0;
uint MatPad1;
uint MatPad2;
};
// 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[7] : register(t0);
// Put in space1, so the texture array does not overlap with these resources.
// The texture array will occupy registers t0, t1, ..., t6 in space0.
StructuredBuffer<InstanceData> gInstanceData : register(t0, space1);
StructuredBuffer<MaterialData> gMaterialData : register(t1, space1);
SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);
// Constant data that varies per pass.
cbuffer cbPass : register(b0)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
float4 gAmbientLight;
// Indices [0, NUM_DIR_LIGHTS) are directional lights;
// indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
// indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
// are spot lights for a maximum of MaxLights per object.
Light gLights[MaxLights];
};
struct VertexIn
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 TexC : TEXCOORD;
};
struct VertexOut
{
float4 PosH : SV_POSITION;
float3 PosW : POSITION;
float3 NormalW : NORMAL;
float2 TexC : TEXCOORD;
// nointerpolation is used so the index is not interpolated
// across the triangle.
nointerpolation uint MatIndex : MATINDEX;
};
VertexOut VS(VertexIn vin, uint instanceID : SV_InstanceID)
{
VertexOut vout = (VertexOut)0.0f;
// Fetch the instance data.
InstanceData instData = gInstanceData[instanceID];
float4x4 world = instData.World;
float4x4 texTransform = instData.TexTransform;
uint matIndex = instData.MaterialIndex;
vout.MatIndex = matIndex;
// Fetch the material data.
MaterialData matData = gMaterialData[matIndex];
// Transform to world space.
float4 posW = mul(float4(vin.PosL, 1.0f), world);
vout.PosW = posW.xyz;
// Assumes nonuniform scaling; otherwise, need to use inverse-transpose of world matrix.
vout.NormalW = mul(vin.NormalL, (float3x3)world);
// Transform to homogeneous clip space.
vout.PosH = mul(posW, gViewProj);
// Output vertex attributes for interpolation across triangle.
float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), texTransform);
vout.TexC = mul(texC, matData.MatTransform).xy;
return vout;
}
float4 PS(VertexOut pin) : SV_Target
{
// Fetch the material data.
MaterialData matData = gMaterialData[pin.MatIndex];
float4 diffuseAlbedo = matData.DiffuseAlbedo;
float3 fresnelR0 = matData.FresnelR0;
float roughness = matData.Roughness;
uint diffuseTexIndex = matData.DiffuseMapIndex;
// Dynamically look up the texture in the array.
diffuseAlbedo *= gDiffuseMap[diffuseTexIndex].Sample(gsamLinearWrap, pin.TexC);
// Interpolating normal can unnormalize it, so renormalize it.
pin.NormalW = normalize(pin.NormalW);
// Vector from point being lit to eye.
float3 toEyeW = normalize(gEyePosW - pin.PosW);
// Light terms.
float4 ambient = gAmbientLight*diffuseAlbedo;
const float shininess = 1.0f - roughness;
Material mat = {
diffuseAlbedo, fresnelR0, shininess };
float3 shadowFactor = 1.0f;
float4 directLight = ComputeLighting(gLights, mat, pin.PosW,
pin.NormalW, toEyeW, shadowFactor);
float4 litColor = ambient + directLight;
// Common convention to take alpha from diffuse albedo.
litColor.a = diffuseAlbedo.a