news 2026/9/3 0:45:41

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/9/2 22:56:19

‍AI客服软件:应对高复杂售前咨询 图书行业客服能力的进化路径

图书行业一直被认为是“低客单、重咨询”的典型代表。看似标准化的商品背后&#xff0c;实际承载的是高度复杂的决策过程&#xff1a;版本差异、出版社不同、教材地区适配、内容体系差异、是否配套音视频资源……任何一个环节解释不清&#xff0c;都会直接影响用户是否下单。在…

作者头像 李华
网站建设 2026/8/30 6:24:06

Kafka 与大数据存储系统的完美融合方案

Kafka 与大数据存储系统的完美融合方案 关键词:Kafka、大数据存储、实时数据流、数据融合、分布式系统 摘要:在数字化时代,企业每天产生海量实时数据(如用户行为、IoT传感器、交易记录),这些数据需要“既快又稳”地被处理和存储。Kafka作为全球最流行的实时数据流平台,擅…

作者头像 李华
网站建设 2026/9/2 14:09:18

cv_resnet50_face-reconstruction VSCode Python环境配置详解

cv_resnet50_face-reconstruction VSCode Python环境配置详解 想在自己的电脑上跑通那个能从一张照片生成3D人脸的神奇模型吗&#xff1f;很多朋友在第一步——配置开发环境上就卡住了。网上的教程要么太零散&#xff0c;要么默认你已经是个Python老手&#xff0c;对新手一点都…

作者头像 李华
网站建设 2026/8/26 7:08:47

零基础教程:用亚洲美女-造相Z-Turbo一键生成惊艳人像

零基础教程&#xff1a;用亚洲美女-造相Z-Turbo一键生成惊艳人像 最近想用AI生成一些符合亚洲审美的精致人像&#xff0c;但发现很多模型要么对中文理解不好&#xff0c;要么生成的亚洲面孔总带着点“异域风情”&#xff0c;要么就是部署起来太复杂&#xff0c;对电脑配置要求…

作者头像 李华
网站建设 2026/9/2 18:44:25

小白必看!Cosmos-Reason1-7B推理工具保姆级使用教程

小白必看&#xff01;Cosmos-Reason1-7B推理工具保姆级使用教程想用AI解决数学题、逻辑推理或编程问题&#xff0c;但担心隐私泄露或使用限制&#xff1f;这个纯本地运行的推理工具可能是你的最佳选择你是否遇到过这样的情况&#xff1a;遇到复杂的数学题需要一步步推理&#x…

作者头像 李华