Frank Luna DirectX12阅读笔记:绘制的不同主题(第十五章-第二十三章)

本文主要是介绍Frank Luna DirectX12阅读笔记:绘制的不同主题(第十五章-第二十三章),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里写目录标题

  • 第十五章 第一人称摄像机和动态索引
    • 摄像机部分略
    • 15.5 动态索引
  • 第十六章 实例化(Instancing)和视锥裁剪(Frustrum Culling)
    • 16.1 硬件Instancing
    • 16.2 包围几何体和视锥
    • 16.3 Frustrum Culling
  • 第十七章 拾取
    • 17.1 屏幕到世界坐标的转换
    • 17.2 拾取射线
    • 17.3 网格射线求交
    • 17.4 Demo
  • 第十八章 立方体纹理
    • 18.1 Cube Mapping
    • 18.2 环境映射
    • 18.3 天空纹理
    • 18.4 反射建模
    • 18.5 动态立方体映射
    • 18.6 使用Geometry Shader进行动态立方体映射
  • 第十九章 法向映射
    • 19.1 动机
    • 19.2 法向映射
    • 19.3 纹理/切向空间 & 19.4 顶点切向空间 & 19.5 纹理空间到物体空间
    • 19.6 法向贴图shader代码
  • 第二十章 阴影映射
    • 20.1 绘制场景深度
    • 20.2 正交投影
    • 20.3 投影纹理坐标
    • 20.4 阴影映射
      • 20.4.1 算法描述
      • 20.4.2 阴影走样
      • 20.4.3 PCF(Percentage Closer Filtering)
      • 20.4.4 建立阴影贴图
      • 20.4.5 阴影系数
      • 20.4.6 阴影映射测试
      • 20.4.7 绘制阴影贴图
    • 20.5 大的PCF kernel
  • 第二十一章 环境光遮蔽(Ambient Occlusion)
    • 21.1 通过Ray Casting进行环境光遮蔽
    • 22.2 屏幕空间的环境光遮蔽(SSAO)
  • 第二十二章 四元数
  • 第二十三章 动画
  • 其他

第十五章 第一人称摄像机和动态索引

摄像机部分略

15.5 动态索引

  • 之前,每个render item都需要传一个材质和一个纹理(保存在每个item的constant buffer中),如果场景中有大量物体,使用了同样的材质和纹理,那么每次渲染重新传,是比较耗时的。动态索引指在一次draw call时就上传所有的材质和纹理,而每个render item的 constant buffer只需要记录一个材质ID和纹理ID,则就能节省很多时间,具体策略如下:
    • 构建一个structured buffer,来存储所有的材质
    • 每个物体的constant buffer中增加一项MaterialIndex
    • 将所有texture SRV一次性地载入场景
    • 在材质中增加一项DiffuseMapIndex,来表示材质使用到的纹理
  • 详见代码

第十六章 实例化(Instancing)和视锥裁剪(Frustrum Culling)

16.1 硬件Instancing

  • 动机如下,显然,复制很多个vertex data和index data是很浪费的,因此,我们只存储一份局部几何信息(vertex list和index list),但多份不同的世界变换矩阵和材质
    • 几个树的模型,多次重复,得到森林
    • 几个小行星的模型,多次重复,得到小行星带
    • 几个人物模型,多次重复,得到人群
  • 这一方法虽然节省了空间,但仍需要每个render item调用一次draw call,造成API调用的损耗(虽然Direct3D 12相对Direct3D 11,已经减少了API调用的损失)。实例化API允许用户使用一次draw call,绘制多个物体。和上一章中的动态索引结合,这一方法更加高效
  • 之所以API调用的损耗是重要的,是因为和GPU相比,CPU才是运算的瓶颈。一个关卡中,往往有非常多的物体,为了达到实时的效果,基本上只能调用几千次的draw call。如果能通过一个draw call绘制多个物体,就能提升效率。硬件实例化就是其中一个方法
  • 之前的代码已经使用硬件Instancing了,即mCommandList->DrawIndexedInstanced(),只不过仅绘制了一次。在
  • 等等

16.2 包围几何体和视锥

  • 利用DirectXCollision.h(DirectX Math的一部分)中的BoundingBox、BoundingOrientedBox、BoundingSphere、BoundingFrustrum结构
  • 其余略

16.3 Frustrum Culling

  • 在裁剪阶段,视锥之外的三角形会被丢弃。但是,如果我们的场景非常复杂,数量庞大的三角形仍然会进行vertex shader、(可能)细分的几个shader、(可能)geometry shader的运算,才会在裁剪阶段被丢弃,因此是非常低效的
  • 因此,我们应该尽早进行视锥裁剪的步骤,通过视锥和物体包围盒的比较,早早地将视锥外的物体丢弃。虽然这一过程小小地增加了CPU的负担,但大大减少了GPU的运算
  • 详见代码

第十七章 拾取

17.1 屏幕到世界坐标的转换

17.2 拾取射线

17.3 网格射线求交

17.4 Demo

第十八章 立方体纹理

18.1 Cube Mapping

  • 六个面:0=+X,1=-X,2=+Y,3=-Y,4=+Z,5=-Z
  • 和2D texture不同,我们使用3D texture坐标来获取一个texel,3D坐标为空间的一个方向,该方向从原点出发,和立方体相交处就是这个坐标对应的texel
  • 在HLSL中,它的代码类似如下:
TextureCube gCubeMap : register(t0);
SamplerState gsamLinearWrap : register(s0);
// pixel shader中
gCubeMap.Sample(gsamLinearWrap, p.PosL);

18.2 环境映射

  • 假设要在物体O处建立一个环境映射,那么需要在O的中心,沿着六个方向,以90°视角,拍摄六张图片,作为立方体纹理。如果有很多个点都进行环境映射(环境映射可以提供环境光,在反射中可以提高真实感),则代价很大。对此有两个方法应对:
    • 仅在几个关键点获取环境映射纹理,中间点进行纹理插值。这一方法虽然会产生不正确的反射,但很难注意到
    • 仅对整个环境,做背景的环境映射纹理,不进行任何局部的环境映射
  • 我们可以先用3D世界编辑器搭建一个场景,然后将场景预先渲染到立方体贴图中。Terragen可以用来创建照片级真实感的户外场景
  • 可以使用texassemble命令行来将6张图片合并成一个DDS纹理文件
  • 仍然可以使用DDSTextureLoader.h/.cpp来加载DDS文件,唯一的不同是在建立SRV时,要将维度设置为D3D12_SRV_DIMENSION_TEXTURECUBE,并填写D3D12_SHADER_RESOURCE_VIEW::TextureCube结构

18.3 天空纹理

  • 我们可以创建一个大球体包围住整个场景来作为天空,通过立方体纹理采样获取球体的颜色
  • 天空球以相机为中心,这一过程可以在vertex shader中完成
  • 以前人们可能会用绘制天空来代替清除render target,因此第一个先绘制天空。但现在通常不这么做,因为以下原因。因此,现在往往最后绘制天空
    • 清除render target和depth/stencil buffer比较容易进行硬件层的优化
    • 天空常常大部分被遮挡,不需要全部绘制出来
  • 绘制天空需要不同的shader,因此需要一个新的PSO。此外,由于摄像机在天空球内部,因此需要禁用背面剔除;修改深度测试比较函数为D3D12_COMPARISON_FUNC_LESS_EQUAL(而不是LESS),才能将天空显示出来

18.4 反射建模

  • 第八章光照建模时,高光只来自光源方向,其他间接光只有和环境无关的环境光。可以通过环境映射来增强高光的真实感
  • 从摄像机出发,在顶点上反射,再和纹理求交,如下:
float3 r = reflect(-toEyeW, pin.NormalW);
float4 reflectionColor = gCubeMap.Sample(gsamLinearWrap, r);
float3 fresnelFactor = SchlickFresnel(fresnelR0, pin.NormalW, r);
litColor.rgb = shininess * fresnelFactor * reflectionColor.rgb;
  • 上述方法是正确的,但代码是错误的,它会导致如下的结果:

图片

  • 正确的详见代码

18.5 动态立方体映射

  • 预先做好立方体纹理当然是比较简单高效的,但如果场景中有一些移动的物体,也需要作为立方体贴图一部分,就需要每帧先渲染6个纹理,再作为立方体纹理。比如,角色靠近玻璃球,玻璃球需要能反射出角色自身,由于角色本身是移动的,因此对于玻璃球每帧需更新纹理
  • 动态立方体纹理是非常昂贵的,因此我们需要尽量减少动态立方体映射。比如
    • 仅在关键物体上进行渲染动态立方体纹理
    • 减小立方体纹理的分辨率
  • 代码中给了一个立方体映射的辅助类CubeRenderTarget,可供参考

18.6 使用Geometry Shader进行动态立方体映射

  • 之前的方法是非常昂贵的,使用geometry shader有助于提高效率
  • 首先,之前我们对每个面都创建一个render target view和对应的texture,现在我们仅创造一个render target view,它的维度类型为D3D10_DSV_DIMENSION_TEXTURE2DARRAY,在一个RTV中包含了6个texture,类似地,也只创造一个depth stencil view,它的维度类型也是D3D10_DSV_DIMENSION_TEXTURE2DARRAY,在一个DSV中包含6个texture
  • 然后在constant buffer中计算好6个视角的变换矩阵,在geometry shader中,将每个三角形变换6次,分别映射到不同的render target上。映射到不同render target由SV_RenderTargetArrayIndex控制,这一参数仅能由geometry shader输出
  • 具体geometry shader代码如下:
struct PS_CUBEMAP_IN {float4 Pos : SV_POSITION;float2 Tex : TEXCOORD0;uint RTIndex : SV_RenderTargetArrayIndex;
};
[maxvertexcount(18)]
void GS_CubeMap(triangle GS_CUBEMAP_IN input[3],inout TriangleStream<PS_CUBEMAP_IN> CubeMapStream) {// 每个三角形for (int f=0; f<6; ++f) {PS_CUBEMAP_IN output;output.RTIndex = f;for (int v=0; v<3; v++) {output.Pos = mul(input[v].Pos, g_mViewCM[f]);output.Pos = mul(output.Pos, mProj);output.Tex = input[v].Tex;CubeMapStream.Append(output);}CubeMapStream.RestartStrip();} 
}
  • 通过这样的方法,我们可以将6次draw call变成1次draw call。然而它在另一方面产生了较大的代价:
    • geometry shader输出了过多的数据(18个顶点),这会导致效率下降
    • 一个三角形通常只会渲染到一个纹理上,因此复制的6份中,有5份最终都会被裁剪。因此在实际应用中,还是应该先进行视锥裁剪,再绘制。而这也导致了geometry shader无法使用
  • 但另一方面,这一策略适合于绘制场景的包围网格,如会随时间变化的天空球(云朵飘动、天空颜色改变等)。因为天空在发生变化,因此无法使用预先bake的纹理。此外,如果GPU不是性能瓶颈,则geometry shader效率低一些也无妨

第十九章 法向映射

19.1 动机

  • 如果没有法向贴图,则不平整的纹理和平整的法向、光滑的高光形成矛盾
  • 细分也无法解决法向问题
  • 提前将光照bake到纹理中则无法解决动态光源的问题

19.2 法向映射

  • 法向映射是一个纹理,但它的rgb通道分别表示了法向的三个坐标轴xyz
  • 由于法向往往垂直于平面,因此法向纹理往往z轴的值较大,因此在视觉上呈现为蓝色
  • 采样和之前完全一致:
float3 normalT = gNormalMap.Sample(gTriLinearSam, pin.Tex);

19.3 纹理/切向空间 & 19.4 顶点切向空间 & 19.5 纹理空间到物体空间

  • 这一部分考虑如何将纹理空间上的法向转换为物体空间中的法向
  • 对于三角形面上一点,那么纹理空间上的三角形到物体空间的三角形仅仅是一个刚性变换,法向也相似地变换即可。在纹理空间上的xyz三轴,转换到世界坐标中,我们记为T(Tangent)轴、B(binormal)轴和N(normal)轴。这里的N轴实际上就是模型三角形的面法向,法向贴图上的值基于TBN坐标系,对面上顶点的法向进行了修正
  • 对于三角形和三角形之间的顶点,则N轴就是相邻三角形的法向平均,T轴也是相邻三角形的T轴平均(通过施密特正交化法和N轴正交),B轴由N轴、T轴叉乘得到
  • 有了坐标系的变换,那么法向贴图的法向也相应变换即可

19.6 法向贴图shader代码

  • TBN需要在CPU中先算好,再传入到GPU中(这一步似乎比较难在GPU中完成,因为vertex buffer仅在点上运算 ,hull shader仅对patch进行细分,geometry shader仅考虑面元,都无法取得邻近三角形数据)。由于TBN是由顶点位置和顶点纹理坐标决定的,因此CPU计算完成后,可以保存在模型文件中,下次直接读取即可
float3 NormalSampleToWorldSpace(float3 normalMapSample,float3 unitNormalW, float3 tangentW) {float3 normalT = 2.0f * normalMapSample - 1.0f;float3 N = unitNormalW;float3 T = normalize(tangentW - dot(tangentW, N) * N);float3 B = cross(N, T);float3 TBN = float3x3(T, B, N);float3 bumpedNormalW = mul(normalT, TBN);return bumpedNormalW;
}

第二十章 阴影映射

20.1 绘制场景深度

  • 首先以光源为视角,绘制场景深度;然后以摄像机为视角,以之前的场景深度作为一个贴图,比较像素和光源的距离是否小于等于贴图

20.2 正交投影

20.3 投影纹理坐标

  • 投影纹理坐标,指将纹理像投影仪那样,将光线投射到任意的表面上
  • 我们可以将投影仪视为一个摄像机,纹理就是一个视平面。对于三维空间中的一个顶点,首先将顶点投影到视平面上,然后转换到NDC(标准设备坐标)空间,再转换到纹理坐标,就可以得到它的颜色,如下图所示:

图片

  • 对于投影到纹理外的点,通常赋值0即可。另一个策略是使用聚光灯,因为聚光灯内部光强较大,向外逐渐减弱,因此有一个自然的过渡
  • 投影时,除了使用透视投影外,也可使用平行投影

20.4 阴影映射

20.4.1 算法描述

  • 透视投影表现聚光源,平行投影表现平行光源。平行投影限制了平行光在一个矩形范围内,因此若要包含一个很大的空间,则要将扩大投影的矩形
  • 顶点p和光源的距离为d§,若d§>s§,则在阴影中;若d§<=s§,则不在阴影中

20.4.2 阴影走样

  • 在非阴影的区域,会出现条纹状artifact,如下:

图片

  • 这是因为阴影贴图每个texel只是对应了texel中心距离光源的深度,因此在texel边缘的深度会高于或低于中心,呈现锯齿状(shadow acne):

图片

  • 因此,我们需要进行一定的偏移(增大s§到s’§),使得所有锯齿状的深度都能保证d§<=s’§,如图:

图片

  • 但偏移不能太大,否则一些应为阴影的点会被判定为非阴影,从而产生物体悬浮在场景上方的现象(peter-panning):

图片

  • 然而,一个固定的bias不能对所有的物体都有效。在光线和物体平面几乎垂直时,需要的bias非常小;而光线和物体几乎平行时,则需要的bias很大,如下图:

图片

  • 因此,我们希望偏移量和光线、物体平面之间角度相关,越平行则bias越大。硬件对此有内置的支持,即slope-scled-bias,即:
typedef struct D3D12_RASTERIZER_DESC {// ...// Bias = (float)DepthBias*r + SlopeScaledDepthBias * MaxDepthSlope// 其中r为最小浮点数,如24位float,则r=2^{-24}// MaxDepthSlope则是像素在水平方向和竖直方向上的深度斜率的最大值INT DepthBias; // 固定biasFLOAT DepthBiasClamp; // 最大允许的biasFLOAT SlopeScaledDepthBias; // 基于物体平面角度的bias乘数// ...
} D3D12_RASTERIZER_DESC;
  • depth bias发生在光栅化的阶段,因此不影响裁剪
  • 不同的场景需要的参数可能非常不同,因此需要尝试不同的参数来适应场景
  • Kilgard在2001年的Shadow Mapping with Today’s OpenGL Hardware(https://www.slideshare.net/Mark_Kilgard/shadow-mappingwith-todays-opengl-hardware—)中建议:
    • 通常这一参数是有效的:scale=1.1,bias=4.0(虽然不知道和这里的比例关系是否一致)
    • 两权相害取其轻,太大的bias比太小的bias好
    • shadow map精度越高,需要的bias越小
    • 如果shadow map被放大了(应该指点光源/聚光源的情况),需要的scale越大

20.4.3 PCF(Percentage Closer Filtering)

  • 直接使用阴影贴图,会产生硬边。如果空间上一点投影到纹理上,它不在texel中心,按照之前纹理的方法,需要进行双线性插值。但在阴影中,双线性插值无法避免硬边的问题。类似地,使用mipmap技术,也无法解决硬边的问题。因此,我们需要对最邻近的4个texel中心,先按每个中心计算,该点是否在阴影中,再将这一结果进行平均,这就是PCF。它的HLSL代码如下:
static const float SMAP_SIZE = 2048.0f; // 阴影贴图分辨率
static const float SMAP_DX = 1.0f / SMAP_SIZE;
// ...
// 找到最邻近的4个texel中心点,并进行采样,比较深度
float s0 = gShadowMap.Sample(gShadowSam, projTexC.xy).r;
float s1 = gShadowMap.Sample(gShadowSam, projTexC.xy + float2(SMAP_DX, 0)).r;
float s2 = gShadowMap.Sample(gShadowSam, projTexC.xy + float2(0, SMAP_DX)).r;
float s3 = gShadowMap.Sample(gShadowSam, projTexC.xy + float2(SMAP_DX, SMAP_DX)).r;
float result0 = depth <= s0;
float result1 = depth <= s1;
float result2 = depth <= s2;
float result3 = depth <= s3;
// 转换到纹理空间
float2 texelPos = SMAP_SIZE * projTexC.xy;
float2 t = frac(texelPos); // frac()取小数部分
// 对结果进行双线性插值
return lerp(lerp(result0, result1, t.x), lerp(result2, result3, t.y))
  • 效果如下图所示:

图片

  • PCF最大的缺点是需要进行4次采样,毕竟采样是GPU中最耗时的操作之一。不过Direct3D 11+的图形硬件内置SampleCmpLevelZero()支持PCF:
Texture2D gShadowMap : register(t1);
SamplerComparisonState gsamShadow : register(s6);
// 计算深度
shadowPosH.xyz /= shadowPosH.w;
float depth = shadowPosH.z;
// 自动PCF
gShadowMap.SampleCmpLevelZero(gsamShadow, shadowPosH.xy, depth).r;
  • 注意这里使用的采样器是SamplerComparisonState,而不是通常的SamplerState,因此我们在设置静态采样器时,需如下设置:
const CD3DX12_STATIC_SAMPLER_DESC shadow(6, // shader register,D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, // filterD3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressUD3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressVD3D12_TEXTURE_ADDRESS_MODE_BORDER, // addressW0.0f, // mipLODBias16, // maxAnisotropyD3D12_COMPARISON_FUNC_LESS_EQUAL,D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK  
)
  • 上述的例子中我们采样了最近的4个texel,更大的采样范围可以获得更柔和的边缘
  • 另外,注意到PCF只需要在阴影的边缘计算即可,Isidoro在2006年的方法可以解决这一问题
  • PCF也不一定取最近的四个texel,也有一些方法通过随机采点的方式组成PCF kernel

20.4.4 建立阴影贴图

  • 详见代码。注意可以关闭颜色绘制,因为我们仅关心深度

20.4.5 阴影系数

  • 判断出哪里是阴影,哪里不是阴影后,剩下来的就是光照上要乘上一个阴影系数,详见代码

20.4.6 阴影映射测试

  • 比较d§和s§,详见代码

20.4.7 绘制阴影贴图

  • 详见代码

20.5 大的PCF kernel

  • 使用PCF可能会带来一些问题,比如在下图中,p点没有受到任何遮挡,但由于PCF的原因,它被认定为有1/3在阴影中

图片

  • 这一问题可以使用bias来解决,但随着PCF的扩大,bias就要越来越大,带来bias过大的问题。因此,仅仅依靠bias是不够的
  • 首先学习一下HLSL中的ddx()和ddy()函数,它们分别衡量了∂p/∂x和∂p/∂y,写成数学公式的形式就是 q x + 1 , y − q x , y q_{x+1,y} - q_{x,y} qx+1,yqx,y。它们可以用来表示
    • 相邻像素间颜色如何变化
    • 相邻像素间深度如何变化
    • 相邻像素间法向如何变化
  • 首先介绍Tuft在2010提出的方法。这一方法基于p相邻像素和p落在同一平面上的假设。如果在平面上,那么根据x轴/y轴距离的远近,和x轴/y轴上的梯度,推算出PCF采样点处的bias。数学推导详见书本
  • Isidoro在2006年的方法思想和上述一致,但实现上不同。详见书本

第二十一章 环境光遮蔽(Ambient Occlusion)

  • 第八章中,物体的环境光表现为环境光系数乘以散射系数,如果没有贴图,那么就是一个常量。它们唯一的作用就是让阴影中的物体不要变成全黑,和真实感物理没有什么关系。本章要对此进行改进

21.1 通过Ray Casting进行环境光遮蔽

  • 在顶点p向外半球发射射线,若总共发射的N条射线中,有h条击中了邻近的网格(击中阈值之外的网格仍能产生环境光),则这h条不产生环境光,因此遮蔽系数为h/N,如下:

图片

  • 通过这种方法,环境光遮蔽的对比效果如下:

图片图片

  • 对于静态模型,可以预计算环境光遮蔽,它可以在初始化时计算,然后存储成顶点上的一个属性。一些工具(http://www.xnormal.net)甚至还可以形成环境光遮蔽的贴图。但是对于会运动的物体,这些方法都失效了。Ray Casting的方法计算环境光需消耗大量时间,因此在实时计算中是不现实的

22.2 屏幕空间的环境光遮蔽(SSAO)

  • 在模型的每个顶点上计算AO非常耗时,如果仅在屏幕所显示的像素上计算,就能高效许多。我们将顶点法向渲染到render target上,将深度渲染到depth/stencil buffer上,那么,我们就可以根据这些构造一个屏幕可以看见区域的三维场景,并在这个场景内计算AO,这一技术就是屏幕空间环境光遮蔽(SSAO)
  • 如图,p是待计算AO的顶点,q是p外向半球上随机选取的一点,r点是视线看往q点时和眼睛最近的一个点,如果r和q的距离小于阈值,且向量r-p和法向n夹角小于90°,则认为r点遮挡了p的环境光。这样的q点随机多采几个,取平均,就构成了p点的环境光遮蔽系数。q的选取,一方面是随机的,另一方面又需要尽量均匀分布。代码中采取了这样的方法:首先生成固定的均匀分布的向量(比如立方体8个顶点+6个面中心),然后生成一个随机向量的贴图,从而每个顶点都又一个随机的向量,最后将那些固定的均匀分布向量按照随机向量反射,反射得到了既随机又均匀分布的向量

图片

  • 首先将法向渲染到DXGI_FORMAT_R16G16B16A16_FLOAT的纹理上,将深度渲染都depth/stencil buffer上
  • 然后在各个像素上计算SSAO系数,这些系数组成了SSAO贴图。一般法向渲染的分辨率和屏幕一致,但SSAO贴图可以是屏幕的一半,这样可以提高效率,且对结果影响不大,因为SSAO本来就是一个低频的光照
  • 由于采样点数量有限,因此得到的SSAO贴图有较多噪声。可以对SSAO贴图做blur,这样可以降低噪声。这里blur没有使用compute shader,因为通过率已经被保存在贴图中,如果给定像素之间的距离,那么就可以计算得到邻近点的坐标,从而通过Sample的方法得到邻近点的值。如何邻近点法向和中心点法向相差太大,或者邻近点深度和中心点深度相差太大,则认为不应该blur时不应考虑邻近点。相比于compute shader,个人觉得优势在于减少graphics shader和compute shader之间的切换,缺点在于重复sample次数较多,没法通过共享内存减少sample次数

第二十二章 四元数

第二十三章 动画

其他

这本书写得非常详细,配套的代码也非常好,不过它主要讲解Direct3D 12的使用,涉及渲染方面的知识比较基础,如延迟渲染、光线追踪等内容都没有包含进来。下面会列举一些找到的其他资料:

  • https://developer.nvidia.com/rtx/raytracing/dxr/dx12-raytracing-tutorial-part-1

这篇关于Frank Luna DirectX12阅读笔记:绘制的不同主题(第十五章-第二十三章)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/202288

相关文章

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2