news 2026/4/15 8:57:05

OpenGL ES ->图片纹理不变形显示:两层宽高比校正详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenGL ES ->图片纹理不变形显示:两层宽高比校正详解

OpenGL ES图片纹理不变形显示:两层宽高比校正详解

OpenGL ES中把一张图片正确显示到屏幕上,需要解决两个完全不同的宽高比问题。本文用一个完整的数值示例,从顶点定义到最终像素,讲清楚每一步为什么必须这样做。


一、核心矛盾

OpenGL的顶点是[-1, 1]的正方形,但图片和屏幕都不是正方形,如果不做任何处理,正方形的纹理贴到正方形的顶点上再投影到长方形的屏幕,图片会被双重拉伸变形。

顶点(正方形) 图片(竖长方形) 屏幕(更长的竖长方形) ┌──────────┐ ┌───────┐ ┌───────┐ │ │ │ │ │ │ │[-1,1]│ │1000× │ │1080× │ │ │ │1380│ │2400│ │ │ │ │ │ │ └──────────┘ │ │ │ │ └───────┘ │ │ │ │ └───────┘

二、两层宽高比,各解决什么问题

第一层:imageAspect 第二层:viewPortRatio 值 imageWidth/imageHeight screenHeight/screenWidth(竖屏)示例1000/1380=0.722400/1080=2.22

模型矩阵(Model):图片自身的形状,让图片在数学空间中不变形
投影矩阵(Projection): 屏幕对世界的拉伸,让数学空间在屏幕上不变形


三、如果什么都不做会怎样?

原始图片1000×1380屏幕上看到的 ┌───────┐ ┌──────────┐ │ │ │ │ │ │ 两层变形叠加 │ 图片被 │ │ │ ─────────────▶ │ 横向拉宽 │ │ │ │ 纵向拉高 │ │ │ │ │ └───────┘ │ │ 宽高比0.72│ │ └──────────┘ 面目全非 ❌

四、第一层:imageAspect— 模型矩阵校正

问题:

顶点是[-1, 1]正方形,图片是0.72:1的竖长方形。纹理直接贴上去,图片会被横向拉宽:

纹理(0,0)(1,1)顶点(-1,-1)(1,1)┌───────┐ ┌──────────┐ │ │ 贴到正方形上 │ 图片被 │ │0.72│ ────────────▶ │ 横向 │ │:1│ │ 拉伸! │ └───────┘ └──────────┘

解决

在模型矩阵中,把正方形的X方向缩放imageAspect倍,使其变成和图片一样比例的竖长方形,此时模型空间中的形状已经和图片比例一致,纹理贴上去不再变形。如果图片是宽图(imageAspect > 1),则缩放Y方向:Scale(1, 1/imageAspect, 1)

// imageAspect = 0.72 < 1,X 方向缩小Matrix.scaleM(mModelMatrix,0,imageAspect,1f,1f)// Scale(0.72, 1, 1)Scale(0.72,1,1)前后对比: ┌──────────┐ ┌──────┐ │ │ X ×0.72│ │ │[-1,1]│ ─────────▶ │[-0.72│ │ 正方形 │ │,+1]│ │ │ │ │ └──────────┘ └──────┘ X:[-1,+1]X:[-0.72,+0.72]Y:[-1,+1]Y:[-1,+1]比例1:1比例0.72:1=图片比例 ✅

五、第二层:viewPortRatio— 投影矩阵校正

问题

模型空间中图片比例已经对了,但从模型空间到屏幕,还要经过NDC → Viewport映射。屏幕是1080×2400的竖长方形,NDC[-1, 1]被映射到屏幕时两个方向的像素长度不同,如果不处理,模型空间中的正方形在屏幕上会被纵向拉长 2.22 倍。

NDC[-1,1]屏幕 ┌──────────┐ ┌──────────┐ │ │ │ │ │1单位 │ Viewport │1单位X │ │=1单位 │ ──────────▶ │=540px │ │ │ │1单位Y │ └──────────┘ │=1200px │ ← 是X的2.22倍! 数学上等长 │ │ └──────────┘ 物理上不等长 ❌

解决

在投影矩阵中,让frustumY方向可视范围扩大viewPortRatio倍,这一步的本质:先压缩,再被拉伸,两次抵消,Y先被压缩1/2.22倍 → 再被Viewport拉伸2.22倍 → 抵消 ✅
原理:frustum中写入的范围越大 → 映射到固定的NDC [-1, 1]时压缩越多 → 正好抵消Viewport阶段对该方向的物理拉伸。

// 竖屏Matrix.frustumM(mProjectionMatrix,0,-1f*distance,// left: X 范围 [-d, +d]1f*distance,// right-viewPortRatio*distance,// bottom: Y 范围 [-2.22d, +2.22d] ← 比 X 大viewPortRatio*distance,// topnear,far)near 平面可视窗口 NDC 压缩 Viewport 拉伸 ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ │ │ │ Y范围大 │ │ 屏幕Y长 │ │ │ Y │ 映射到[-1,1]│ Y被压缩 │ 又把Y拉长 │ Y恢复 │ │ 范围大 │ ──────────▶ │2.22倍 │ ─────────▶│ 正常 │ │2.22x │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ 投影前 NDC中 屏幕上

六、完整变换链路图

以图片1000×1380、屏幕1080×2400为例,scale=1translate=(0,0)

┌─────────────────────────────────────────────────────────────────────────────────┐ │ 完 整 变 换 链 路 │ ├──────────┬───────────────┬────────────────┬────────────────┬───────────────────┤ │ 原始顶点 │ imageAspect │ Projection │ Y 翻转 │ 屏幕像素 │ │ │ 模型矩阵校正 │ 投影矩阵校正 │ 纹理方向 │ 最终结果 │ ├──────────┼───────────────┼────────────────┼────────────────┼───────────────────┤ │(-1,+1)(-0.72,+1)(-0.72,+0.45)(-0.72,-0.45)│ screen:│ │(-1,-1)(-0.72,-1)(-0.72,-0.45)(-0.72,+0.45)(151,660)│ │(+1,+1)(+0.72,+1)(+0.72,+0.45)(+0.72,-0.45)(929,1740)│ │(+1,-1)(+0.72,-1)(+0.72,-0.45)(+0.72,+0.45)│ 图片778×1080│ │ │ │ │ │ 比例0.72:1✅ │ │ 正方形 │ 竖长方形 │ Y被压缩1/2.22│ Y翻转 │ 屏幕上不变形 │ │1:10.72:1│ 比例变为1.6:1│ 图片正向 │ │ │ │ │(预补偿屏幕拉伸)│ │ │ └──────────┴───────────────┴────────────────┴────────────────┴───────────────────┘ 对应代码位置: ┌──────────────────────────┐ ┌────────────────────────┐ ┌───────────┐ │ Matrix.scaleM(│ │ Matrix.frustumM(│ │ Matrix.│ │ model,imageAspect,1,1)│ │ proj,...,│ │scaleM(│ │ │ │-vr*d,vr*d,...)│ │ mvp,1,-1,1│ │ 解决:图片不是正方形 │ │ 解决:屏幕不是正方形 │ │)│ └──────────────────────────┘ └────────────────────────┘ └───────────┘ 对应代码位置: ┌──────────────────────────┐ ┌────────────────────────┐ ┌───────────┐ │ Matrix.scaleM(│ │ Matrix.frustumM(│ │ Matrix.│ │ model,imageAspect,1,1)│ │ proj,...,│ │scaleM(│ │ │ │-vr*d,vr*d,...)│ │ mvp,1,-1,1│ │ 解决:图片不是正方形 │ │ 解决:屏幕不是正方形 │ │)│ └──────────────────────────┘ └────────────────────────┘ └───────────┘

七、为什么一个在Model,一个在Projection

Model 矩阵回答:「这个物体本身长什么样?」 → 图片是0.72:1的竖长方形 → 每张图片不同,所以跟着图片走 Projection 矩阵回答:「世界通过什么形状的窗口被观察?」 → 屏幕是1:2.22的竖长方形 → 对所有物体都一样,所以跟着屏幕走
图片比例 ──▶ Model ──▶ 模型空间(数学上不变形) │ ▼ 屏幕比例 ──▶ Projection ──▶ NDC(预补偿屏幕拉伸) │ ▼ Viewport │ ▼ 屏幕(物理上不变形)✅

八、图片纹理上叠加自定义View

自定义View不参与OpenGL渲染,但需要知道图片纹理在屏幕上占多少像素才能定位裁剪框。这个方法就是反算上面整条链路的最终结果:

如果不取短边的最小值/2,这个时候x,y虽然落到了[-1,1]区间,但是并不等长,也不是完整的归一化,x轴的1个单位不等于y轴的1个单位 x/(viewW/2),x 的1=viewW/2像素 y/(viewH/2),y 的1=viewH/2像素, Projection 让[-1,1]范围=min(screenW,screenH)像素 NDC 中长度=2↓ 屏幕像素长度=min(screenW,screenH)1个 NDC 单位=min(W,H)/2像素 Model 让某个方向缩放了 imageAspect 合在一起: 竖图(aspect ≤1):=min(W,H)× imageAspect ← X 被缩小 高=min(W,H)← Y 铺满 宽图(aspect>1):=min(W,H)← X 铺满 高=min(W,H)/imageAspect ← Y 被缩小 以1000×1380图片、1080×2400屏幕为例: customViewWidth=1080×0.72=778px customViewHeight=1080=1080px
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/11 0:12:04

小红的二叉树【牛客tracker 每日一题】

小红的二叉树 时间限制&#xff1a;1秒 空间限制&#xff1a;1024M 知识点&#xff1a;数论 网页链接 牛客tracker 牛客tracker & 每日一题&#xff0c;完成每日打卡&#xff0c;即可获得牛币。获得相应数量的牛币&#xff0c;能在【牛币兑换中心】&#xff0c;换取相…

作者头像 李华
网站建设 2026/4/10 23:42:24

苹果应用隐私政策配置指南

引言 在开发iOS应用的过程中,隐私政策的配置是一个不可忽视的重要环节。苹果公司对应用的隐私保护有着严格的要求,如果不正确配置隐私信息,可能会导致应用无法通过审核。本文将详细介绍如何配置苹果应用的隐私政策,并通过一个实际案例来展示解决常见问题的步骤。 理解隐私…

作者头像 李华
网站建设 2026/4/11 20:32:57

多线程Web爬虫:如何避免超时错误

在解决LeetCode的多线程Web爬虫问题时,我发现一个有趣的现象:使用ThreadPoolExecutor时,代码可能会超时,即使是在非常简单的测试用例中。今天,我们来探讨一下为什么会发生这种情况,并提供一个优化方案。 问题分析 首先,让我们回顾一下原始的代码实现: class Solutio…

作者头像 李华
网站建设 2026/4/12 7:56:50

大数据环境下 Kafka 的集群搭建指南

大数据环境下 Kafka 的集群搭建指南 关键词&#xff1a;Kafka 集群、大数据、分布式系统、消息队列、高吞吐量 摘要&#xff1a;在大数据时代&#xff0c;如何高效处理海量实时数据流是企业的核心需求之一。Kafka 作为一款分布式消息队列&#xff0c;凭借高吞吐量、低延迟和强容…

作者头像 李华
网站建设 2026/4/12 12:48:03

智能配电监控模块:50A磁保持,负载5500W电机设备,工业配电安全新方案

智能配电监控模块是一款集大功率远程控制、每路独立电流监控和多功能自动化逻辑于一体的先进电气管理终端设备。一、核心特性 50A磁保持&#xff1a;指其核心执行单元。 能力&#xff1a;每路通道能安全承载和控制高达50安培的大电流&#xff0c;可直接驱动电机、电热器等11KW级…

作者头像 李华
网站建设 2026/4/9 19:18:54

mPLUG视觉问答工具提示词技巧:让分析更精准

mPLUG视觉问答工具提示词技巧&#xff1a;让分析更精准 1. 引言 你是否曾经遇到过这样的情况&#xff1a;上传一张图片到AI视觉问答工具&#xff0c;却得到了一个完全偏离主题的回答&#xff1f;或者明明图片中有明显的物体&#xff0c;但AI就是识别不出来&#xff1f;这往往…

作者头像 李华