[crytek]Spherical Skinning withDual-Quaternions and QTangents

本文探讨了使用Quaternion和DualQuaternion来优化Skinning过程的方法,包括替代Matrix4x3和Vertexstream中的Tangent项,从而减少了Shaderconstant的使用,提高了性能。同时,介绍了TangentFrameCompress技术,通过压缩Vertex中的Tangent/binormal/normal项,将数据大小从36byte减少到8byte,进一步提升了性能。此外,文章还讨论了动画制作过程中需要对线性到球面的转换进行调整。


http://crytek.com/cryengine/presentations/spherical-skinning-with-dual-quaternions-and-qtangents


使用quaternion对skinning进行优化:

  • 使用dual quaternion来取代matrix4x3
  • 使用quaternion取代vertex stream中的tangnet,binormal项
得到的结果是:
  • vertex shader constants削减1/3
  • 把vertex中的tangent/binormal/normal项压缩至8byte
DualQuaternion
总体上说会带来性能的提升,具体在:
  • 积累transformation会变快
  • apply transform会变慢
  • shader constant的下降会大幅度提升性能(这里在某些显卡上有一个constant waterfall的情况,所以constant的降低非常有意义)
美术制作这里:
制作动画会从linear变到spherical,所以需要做一些改变。

TangentFrameCompress
这里所谓的tangent frame就是vertex中的tangent/binormal/normal项。
如果完全不做压缩,那就是sizeof(float)*3*3 = 36byte,所有相关压缩之后会降到8byte。
这里是把这个矩阵压缩到一个quaternion,解出t,b,n的方法是:

由于quaternion是normalized,所以quaternion又可以只保留xyz项,w项推算出来,符号不是问题。

这里一个注意就是gpu 浮点数处理0的时候和cpu的ieee不一样,所以如果quaternion的w是0的时候要加一个很小的bias。

在 Unity URP 中实现类似 Crytek 风格的 SSAO(屏幕空间环境光遮蔽)效果,通常涉及对深度缓冲和法线缓冲进行采样,并通过特定算法计算每个像素周围的遮蔽因子。Crytek 的 SSAO 实现以其高效性和视觉质量著称,主要依赖于随机噪声纹理、深度重建以及模糊处理等技术[^1]。 ### 实现步骤概述 #### 1. 准备渲染纹理 首先需要获取以下信息: - **深度缓冲**:用于重建视图空间位置。 - **法线缓冲**:用于计算视线方向与表面法线之间的角度。 - **噪声纹理**:用于扰动采样方向以减少重复模式,提升视觉效果。 ```csharp // 在 C# 脚本中配置渲染纹理 public class SSAOEffect : MonoBehaviour { public Material ssaoMaterial; private RenderTexture depthRT; private RenderTexture normalRT; void Start() { depthRT = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.Depth); normalRT = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBHalf); } void OnRenderImage(RenderTexture source, RenderTexture destination) { // 将深度和法线数据传递给着色器 Graphics.Blit(source, destination, ssaoMaterial); } } ``` #### 2. 深度重建 在屏幕空间中,从深度值重建视图空间坐标是关键步骤之一。可以通过逆投影矩阵来实现这一过程: ```glsl float3 ReconstructViewPos(float depth, float2 uv) { float4 clipPos = float4(uv * 2.0 - 1.0, depth, 1.0); float4 viewPos = mul(_InverseProjectionMatrix, clipPos); return viewPos.xyz / viewPos.w; } ``` #### 3. SSAO 核心计算 接下来,在片段着色器中对周围点进行采样并计算遮蔽因子: ```glsl float ComputeSSAO(float2 uv, sampler2D depthTex, sampler2D noiseTex) { float3 fragPos = ReconstructViewPos(SampleDepth(uv), uv); float3 normal = DecodeNormal(tex2D(normalTex, uv)); float3 randomVec = normalize(tex2D(noiseTex, uv * _NoiseScale).xyz * 2.0 - 1.0); float occlusion = 0.0; for (int i = 0; i < KERNEL_SIZE; ++i) { float3 samplePos = fragPos + _Kernel[i].xyz * _SampleRadius; float3 dir = samplePos - fragPos; float dist = length(dir); float3 offset = normalize(dir) * (dist * _DepthBias + _DistanceFalloff * dist * dist); float3 sampleUV = fragPos + offset; float3 sampleDepthPos = ReconstructViewPos(SampleDepth(sampleUV.xy), sampleUV.xy); float rangeCheck = smoothstep(0.0, 1.0, _MaxDistance / abs(fragPos.z - sampleDepthPos.z)); occlusion += (sampleDepthPos.z <= samplePos.z ? 1.0 : 0.0) * rangeCheck; } return 1.0 - (occlusion / KERNEL_SIZE); } ``` #### 4. 模糊处理 为了去除噪点并提高性能,通常会对生成的 SSAO 纹理进行横向和纵向的高斯模糊: ```glsl float4 BlurSSAO(float2 uv, sampler2D ssaoTex) { float result = 0.0; for (int i = -2; i <= 2; ++i) { float2 offset = vec2(i * _BlurRadius, 0.0); result += tex2D(ssaoTex, uv + offset).r * _Weights[i + 2]; } return float4(result.xxx, 1.0); } ``` ### 优化建议 - 使用降分辨率的 SSAO 渲染目标以提升性能。 - 利用 TAA(时间抗锯齿)或抖动采样分布以减少闪烁效应。 - 对比不同光照条件下调整遮蔽强度和模糊参数。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值