你有没有玩过那种大场景的 3D 游戏角色跑起来很流畅但一到复杂的场景帧率就往下掉其实很多时候画面里有很大一部分区域是玩家根本不会仔细看的——比如远处的草地、快速移动时的背景。这些区域用全分辨率去渲染说白了就是浪费算力。VRSVariable Rate Shading可变着色率就是为了解决这个问题的。它允许你对画面的不同区域使用不同的着色精度画面中心玩家关注的地方用高精度边缘和远处用低精度。这样既保证了视觉效果又省了 GPU 算力。而自适应VRS 更进一步——它不需要你手动指定哪些区域用低精度而是根据上一帧的颜色和深度信息自动分析出哪些区域可以降低着色率。省心又高效。下面是自适应 VRS 的整体工作流程不支持支持检查设备是否支持自适应 VRS使用普通渲染设置输入参数INPUT_SIZE: 输入图像尺寸INPUT_REGION: 渲染区域TEXEL_SIZE: 着色率粒度ERROR_SENSITIVITY: 灵敏度每帧调用 DispatchAdaptiveVRS输入: 上一帧颜色 当前帧深度算法自动分析各区域复杂度输出: 着色率图ApplyAdaptiveVRS 应用着色率后续绘制按着色率图着色这个功能的使用前提在 OpenGL ES 里使用自适应 VRS 之前你得先确认设备支持这个特性。怎么确认呢就是用上一篇文章讲的扩展查询接口constGLubyte*extensionsHMS_XEG_GetString(XEG_EXTENSIONS);bool supportsAdaptiveVRSstrstr((constchar*)extensions,XEG_adaptive_vrs)!NULL;只有当supportsAdaptiveVRS为 true 的时候你才能继续往下走。整体流程自适应 VRS 的使用分三步设置参数告诉算法输入图像的尺寸、裁剪区域等信息计算着色率图算法根据上一帧的颜色和深度生成一张着色率图像应用着色率把生成的着色率图像应用到当前帧的渲染目标上下面一步一步来看。第一步设置参数你需要通过HMS_XEG_AdaptiveVRSParameter函数来设置各种输入参数。这个函数接受两个参数pname是参数名用宏定义param是参数值的指针。设置输入图像尺寸必填这是最重要的参数必须设置。它告诉算法上一帧渲染结果的宽高GLsizei inputSize[2]{1920,1080};// 上一帧渲染结果的宽和高HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_SIZE,inputSize);XEG_ADAPTIVE_VRS_INPUT_SIZE的值是0x1U。传入的必须是长度为 2 的GLsizei数组第一个元素是宽度第二个是高度。这个值必须和后面HMS_XEG_DispatchAdaptiveVRS函数传入的inputColorImage纹理的宽高保持一致否则会产生未定义行为。设置渲染区域可选如果你只渲染了画面的一部分区域比如有黑边或者裁剪可以用XEG_ADAPTIVE_VRS_INPUT_REGION来指定GLuint inputRegion[4]{0,0,1920,1080};// 左下角x, y, 宽, 高HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_REGION,inputRegion);XEG_ADAPTIVE_VRS_INPUT_REGION的值是0x2U。传入的是长度为 4 的GLuint数组依次表示渲染区域左下角的 x、y 坐标和区域的宽高。如果不设置默认值是整个输入图像的范围。设置纹素大小可选XEG_ADAPTIVE_VRS_TEXEL_SIZE控制着色率图的最小单元大小GLsizei texelSize[2]{8,8};// 支持 [8, 8] 和 [16, 16]HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_TEXEL_SIZE,texelSize);XEG_ADAPTIVE_VRS_TEXEL_SIZE的值是0x3U。默认值是[8, 8]意味着每个 8x8 的像素块共享一个着色率。你也可以设成[16, 16]精度更低但性能更好。设置误差灵敏度可选XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY控制算法判定可以降低着色率的阈值GLfloat errorSensitivity0.5f;// 取值范围 [0, 1]HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY,errorSensitivity);XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY的值是0x4U。这个值越大算法越激进地降低着色率——性能会更好但画质会下降。默认值是 0.5。如果你的游戏对画质要求高可以调低一点如果帧率优先可以调高一点。设置是否翻转图像可选有些渲染管线的坐标系是上下翻转的你可以通过XEG_ADAPTIVE_VRS_FLIP来控制GLboolean flipGL_FALSE;// true 表示翻转false 表示不翻转HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_FLIP,flip);XEG_ADAPTIVE_VRS_FLIP的值是0x5U。默认是false不翻转。如果你发现着色率图的效果不对可以试试翻转一下。第二步计算着色率图参数设好之后调用HMS_XEG_DispatchAdaptiveVRS来让算法生成着色率图像HMS_XEG_DispatchAdaptiveVRS(reprojectionMatrix,// 重投影矩阵inputColorImage,// 上一帧的颜色附件纹理IDinputDepthImage,// 当前帧的深度附件纹理IDshadingRateImage// 输出的着色率纹理ID需要你提前创建);这里面有四个参数每个都很重要reprojectionMatrix这是一个 4x4 的列主序矩阵用于描述上一帧和当前帧之间的空间变换关系。计算公式是上一帧投影矩阵 * 上一帧观察矩阵* 逆当前帧投影矩阵 * 当前帧观察矩阵。这个矩阵是可选的但如果你能提供的话画质会更好。如果你传 NULL算法也能工作只是效果可能差一点。inputColorImage上一帧渲染管线最终输出的颜色纹理 ID。纹理类型必须是GL_TEXTURE_2DmipLevels 必须是 1。inputDepthImage当前帧渲染管线最终输出的深度纹理 ID。同样要求是GL_TEXTURE_2DmipLevels 为 1。shadingRateImage这是你要提前创建好的一张纹理用来接收算法输出的着色率信息。它的尺寸应该和着色率的粒度匹配。第三步应用着色率图着色率图生成之后你需要调用HMS_XEG_ApplyAdaptiveVRS把它应用到当前的渲染目标上HMS_XEG_ApplyAdaptiveVRS(shadingRateImage);调用之后后续的绘制操作就会按照着色率图里的信息来决定每个区域的着色精度。如果你想关闭自适应 VRS传入 0 就行HMS_XEG_ApplyAdaptiveVRS(0);// 关闭自适应VRS完整的使用流程把上面的步骤串起来每帧渲染时的自适应 VRS 调用时序如下每帧渲染开始HMS_XEG_DispatchAdaptiveVRS传入重投影矩阵传入上一帧颜色纹理传入当前帧深度纹理输出着色率纹理HMS_XEG_ApplyAdaptiveVRS正常绘制场景GPU 按着色率图决定每区域精度帧渲染完成把上面的步骤串起来一个典型的使用流程是这样的// 1. 检查设备支持constGLubyte*extensionsHMS_XEG_GetString(XEG_EXTENSIONS);if(strstr((constchar*)extensions,XEG_adaptive_vrs)NULL){return;// 不支持走普通渲染}// 2. 设置参数GLsizei inputSize[2]{1920,1080};HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_SIZE,inputSize);GLfloat errorSensitivity0.5f;HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY,errorSensitivity);// 3. 计算着色率图每一帧都要调用HMS_XEG_DispatchAdaptiveVRS(reprojectionMatrix,lastFrameColorTexture,currentFrameDepthTexture,shadingRateTexture);// 4. 应用着色率图HMS_XEG_ApplyAdaptiveVRS(shadingRateTexture);// 5. 继续正常的渲染流程...// 此时绘制的内容会按照着色率图来着色几个要注意的点每一帧都要重新计算着色率图是根据上一帧和当前帧的信息生成的所以每帧都要调用HMS_XEG_DispatchAdaptiveVRS。纹理格式要求所有传入的纹理都必须是GL_TEXTURE_2D类型mipLevels 为 1。不满足这个条件的话结果是未定义的。重投影矩阵的计算如果你的相机在移动最好提供重投影矩阵。这个矩阵的计算公式是(prevProjection * prevView) * inverse(currProjection * currView)。如果你的相机是固定的传 NULL 也可以。性能和画质的平衡ERROR_SENSITIVITY参数是你调节性能和画质平衡的主要手段。建议从 0.5 开始根据实际效果微调。自适应 VRS 是一个很实用的性能优化手段特别适合那些场景复杂、GPU 负担重的游戏。用好了帧率提升个 10%-20% 是很常见的。
鸿蒙开发-想让GPU自动省着画?GLES自适应可变速率着色
你有没有玩过那种大场景的 3D 游戏角色跑起来很流畅但一到复杂的场景帧率就往下掉其实很多时候画面里有很大一部分区域是玩家根本不会仔细看的——比如远处的草地、快速移动时的背景。这些区域用全分辨率去渲染说白了就是浪费算力。VRSVariable Rate Shading可变着色率就是为了解决这个问题的。它允许你对画面的不同区域使用不同的着色精度画面中心玩家关注的地方用高精度边缘和远处用低精度。这样既保证了视觉效果又省了 GPU 算力。而自适应VRS 更进一步——它不需要你手动指定哪些区域用低精度而是根据上一帧的颜色和深度信息自动分析出哪些区域可以降低着色率。省心又高效。下面是自适应 VRS 的整体工作流程不支持支持检查设备是否支持自适应 VRS使用普通渲染设置输入参数INPUT_SIZE: 输入图像尺寸INPUT_REGION: 渲染区域TEXEL_SIZE: 着色率粒度ERROR_SENSITIVITY: 灵敏度每帧调用 DispatchAdaptiveVRS输入: 上一帧颜色 当前帧深度算法自动分析各区域复杂度输出: 着色率图ApplyAdaptiveVRS 应用着色率后续绘制按着色率图着色这个功能的使用前提在 OpenGL ES 里使用自适应 VRS 之前你得先确认设备支持这个特性。怎么确认呢就是用上一篇文章讲的扩展查询接口constGLubyte*extensionsHMS_XEG_GetString(XEG_EXTENSIONS);bool supportsAdaptiveVRSstrstr((constchar*)extensions,XEG_adaptive_vrs)!NULL;只有当supportsAdaptiveVRS为 true 的时候你才能继续往下走。整体流程自适应 VRS 的使用分三步设置参数告诉算法输入图像的尺寸、裁剪区域等信息计算着色率图算法根据上一帧的颜色和深度生成一张着色率图像应用着色率把生成的着色率图像应用到当前帧的渲染目标上下面一步一步来看。第一步设置参数你需要通过HMS_XEG_AdaptiveVRSParameter函数来设置各种输入参数。这个函数接受两个参数pname是参数名用宏定义param是参数值的指针。设置输入图像尺寸必填这是最重要的参数必须设置。它告诉算法上一帧渲染结果的宽高GLsizei inputSize[2]{1920,1080};// 上一帧渲染结果的宽和高HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_SIZE,inputSize);XEG_ADAPTIVE_VRS_INPUT_SIZE的值是0x1U。传入的必须是长度为 2 的GLsizei数组第一个元素是宽度第二个是高度。这个值必须和后面HMS_XEG_DispatchAdaptiveVRS函数传入的inputColorImage纹理的宽高保持一致否则会产生未定义行为。设置渲染区域可选如果你只渲染了画面的一部分区域比如有黑边或者裁剪可以用XEG_ADAPTIVE_VRS_INPUT_REGION来指定GLuint inputRegion[4]{0,0,1920,1080};// 左下角x, y, 宽, 高HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_REGION,inputRegion);XEG_ADAPTIVE_VRS_INPUT_REGION的值是0x2U。传入的是长度为 4 的GLuint数组依次表示渲染区域左下角的 x、y 坐标和区域的宽高。如果不设置默认值是整个输入图像的范围。设置纹素大小可选XEG_ADAPTIVE_VRS_TEXEL_SIZE控制着色率图的最小单元大小GLsizei texelSize[2]{8,8};// 支持 [8, 8] 和 [16, 16]HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_TEXEL_SIZE,texelSize);XEG_ADAPTIVE_VRS_TEXEL_SIZE的值是0x3U。默认值是[8, 8]意味着每个 8x8 的像素块共享一个着色率。你也可以设成[16, 16]精度更低但性能更好。设置误差灵敏度可选XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY控制算法判定可以降低着色率的阈值GLfloat errorSensitivity0.5f;// 取值范围 [0, 1]HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY,errorSensitivity);XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY的值是0x4U。这个值越大算法越激进地降低着色率——性能会更好但画质会下降。默认值是 0.5。如果你的游戏对画质要求高可以调低一点如果帧率优先可以调高一点。设置是否翻转图像可选有些渲染管线的坐标系是上下翻转的你可以通过XEG_ADAPTIVE_VRS_FLIP来控制GLboolean flipGL_FALSE;// true 表示翻转false 表示不翻转HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_FLIP,flip);XEG_ADAPTIVE_VRS_FLIP的值是0x5U。默认是false不翻转。如果你发现着色率图的效果不对可以试试翻转一下。第二步计算着色率图参数设好之后调用HMS_XEG_DispatchAdaptiveVRS来让算法生成着色率图像HMS_XEG_DispatchAdaptiveVRS(reprojectionMatrix,// 重投影矩阵inputColorImage,// 上一帧的颜色附件纹理IDinputDepthImage,// 当前帧的深度附件纹理IDshadingRateImage// 输出的着色率纹理ID需要你提前创建);这里面有四个参数每个都很重要reprojectionMatrix这是一个 4x4 的列主序矩阵用于描述上一帧和当前帧之间的空间变换关系。计算公式是上一帧投影矩阵 * 上一帧观察矩阵* 逆当前帧投影矩阵 * 当前帧观察矩阵。这个矩阵是可选的但如果你能提供的话画质会更好。如果你传 NULL算法也能工作只是效果可能差一点。inputColorImage上一帧渲染管线最终输出的颜色纹理 ID。纹理类型必须是GL_TEXTURE_2DmipLevels 必须是 1。inputDepthImage当前帧渲染管线最终输出的深度纹理 ID。同样要求是GL_TEXTURE_2DmipLevels 为 1。shadingRateImage这是你要提前创建好的一张纹理用来接收算法输出的着色率信息。它的尺寸应该和着色率的粒度匹配。第三步应用着色率图着色率图生成之后你需要调用HMS_XEG_ApplyAdaptiveVRS把它应用到当前的渲染目标上HMS_XEG_ApplyAdaptiveVRS(shadingRateImage);调用之后后续的绘制操作就会按照着色率图里的信息来决定每个区域的着色精度。如果你想关闭自适应 VRS传入 0 就行HMS_XEG_ApplyAdaptiveVRS(0);// 关闭自适应VRS完整的使用流程把上面的步骤串起来每帧渲染时的自适应 VRS 调用时序如下每帧渲染开始HMS_XEG_DispatchAdaptiveVRS传入重投影矩阵传入上一帧颜色纹理传入当前帧深度纹理输出着色率纹理HMS_XEG_ApplyAdaptiveVRS正常绘制场景GPU 按着色率图决定每区域精度帧渲染完成把上面的步骤串起来一个典型的使用流程是这样的// 1. 检查设备支持constGLubyte*extensionsHMS_XEG_GetString(XEG_EXTENSIONS);if(strstr((constchar*)extensions,XEG_adaptive_vrs)NULL){return;// 不支持走普通渲染}// 2. 设置参数GLsizei inputSize[2]{1920,1080};HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_SIZE,inputSize);GLfloat errorSensitivity0.5f;HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY,errorSensitivity);// 3. 计算着色率图每一帧都要调用HMS_XEG_DispatchAdaptiveVRS(reprojectionMatrix,lastFrameColorTexture,currentFrameDepthTexture,shadingRateTexture);// 4. 应用着色率图HMS_XEG_ApplyAdaptiveVRS(shadingRateTexture);// 5. 继续正常的渲染流程...// 此时绘制的内容会按照着色率图来着色几个要注意的点每一帧都要重新计算着色率图是根据上一帧和当前帧的信息生成的所以每帧都要调用HMS_XEG_DispatchAdaptiveVRS。纹理格式要求所有传入的纹理都必须是GL_TEXTURE_2D类型mipLevels 为 1。不满足这个条件的话结果是未定义的。重投影矩阵的计算如果你的相机在移动最好提供重投影矩阵。这个矩阵的计算公式是(prevProjection * prevView) * inverse(currProjection * currView)。如果你的相机是固定的传 NULL 也可以。性能和画质的平衡ERROR_SENSITIVITY参数是你调节性能和画质平衡的主要手段。建议从 0.5 开始根据实际效果微调。自适应 VRS 是一个很实用的性能优化手段特别适合那些场景复杂、GPU 负担重的游戏。用好了帧率提升个 10%-20% 是很常见的。