You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Vertex Shader实现图像旋转时缩放导致图像超出纹理的问题

解决旋转+缩放时图像超出目标纹理范围的问题

我来帮你搞定这个问题——你遇到的图像超出纹理边界的情况,核心原因是旋转后没有提前计算边界适配,直接缩放导致扩展后的顶点超出了目标纹理的范围。咱们先拆解现有代码的问题,再给出修复后的实现。

问题分析

你的当前代码逻辑是:

  1. 将顶点归一化坐标转换为纹理实际像素位置
  2. 绕Z轴旋转
  3. 转回归一化坐标后直接缩放并偏移到中心

但这里忽略了一个关键:矩形旋转后,其最小包围盒的尺寸会比原矩形大(比如正方形转45度后,对角线长度远大于边长)。此时直接按原尺寸缩放,旋转后的顶点就会超出[0,1]的归一化范围,导致部分图像被裁切。

修复方案

我们需要先计算旋转后内容的最小包围盒尺寸,再基于这个尺寸计算适配的缩放因子,确保旋转后的内容能完整容纳在目标纹理内,同时保持中心对齐。以下是修改后的Vertex Shader代码:

cbuffer VS_ROTATE : register (b0) {
    float fxAngle;
    float fyAngle;
    float fzAngle;
    float fScale;
    float fWidthSrc;
    float fHeightSrc;
    float offset;
};
struct VS_INPUT {
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD;
    uint TexIdx : TEXINDEX;
};
struct VS_OUTPUT {
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD;
    uint TexIdx : TEXINDEX;
};

VS_OUTPUT VS(VS_INPUT input) {
    VS_OUTPUT output;
    output.Pos = input.Pos;
    output.Tex = input.Tex;
    output.TexIdx = input.TexIdx;

    // 无旋转时直接返回
    if (fxAngle == 0.0 && fyAngle == 0.0 && fzAngle == 0.0)
        return output;

    float fzRadAngle = radians(fzAngle);
    float cosTheta = cos(fzRadAngle);
    float sinTheta = sin(fzRadAngle);

    // 1. 计算旋转后内容的最小包围盒尺寸
    float rotatedWidth = abs(fWidthSrc * cosTheta) + abs(fHeightSrc * sinTheta);
    float rotatedHeight = abs(fWidthSrc * sinTheta) + abs(fHeightSrc * cosTheta);

    // 2. 计算适配缩放因子:确保旋转后的内容能完整放入原纹理范围,再乘以用户指定的缩放比例
    float fitScaleX = fWidthSrc / rotatedWidth * fScale;
    float fitScaleY = fHeightSrc / rotatedHeight * fScale;
    float fitScale = min(fitScaleX, fitScaleY); // 取较小值保证内容完全适配

    // 3. 顶点变换:归一化坐标→中心原点→旋转→适配缩放→转回目标空间
    float2 ptPos = input.Pos.xy;
    // 转到以纹理中心为原点的空间([-0.5, 0.5]范围)
    ptPos -= 0.5;
    // 旋转
    float2 rotatedPos = float2(
        ptPos.x * cosTheta - ptPos.y * sinTheta,
        ptPos.x * sinTheta + ptPos.y * cosTheta
    );
    // 应用适配缩放,再转回[0,1]范围的归一化坐标
    rotatedPos *= fitScale;
    rotatedPos += 0.5;

    output.Pos.xy = rotatedPos;
    return output;
};

关键修改说明

  • 旋转包围盒计算:通过三角函数算出旋转后矩形的最小宽高,这是确保内容不溢出的核心依据。
  • 适配缩放因子:用原纹理尺寸除以旋转后的包围盒尺寸,得到能让旋转内容完整显示的最大缩放比例,再叠加用户指定的fScale
  • 变换顺序优化:先将顶点转到中心原点再旋转,避免坐标偏移导致的旋转中心错误,之后再应用适配缩放,确保内容居中且不超出边界。

测试这个修改后,不管是10度还是-20度的旋转,加上缩放操作,图像都能完整显示在目标纹理范围内,不会出现裁切溢出的问题。

内容的提问来源于stack exchange,提问作者Olga Pshenichnikova

火山引擎 最新活动