cocos creator | 为什么 shader 效果在编辑器中显示正常,运行后却显示异常? 合图纹理的uv计算
获取源码,请关注公众号:
感谢QQ群(521643513)内 honmono 大佬的指导
01
uv 坐标
之后的文章中,我们再详细介绍顶点着色器,片段着色器,纹理等知识
这里简单了解下基本概念
渲染流程:
我们传递给顶点着色器每个顶点坐标及其对应的纹理坐标和纹理颜色后,顶点着色器经过顶点变换,图元装配(将顶点根据原始的连接关系还原成网格结构),光栅化(通过对顶点数据进行插值,获得三角形所覆盖的像素区域)等操作后,得到片元序列(二维图像上每个点都包含了颜色、深度和纹理数据,将该点和相关信息叫做一个片元),然后将片元传递给片元着色器,它会为每个片元进行纹理采样,颜色填充等操作
其中纹理坐标即 uv 坐标
uv 坐标是纹理尺寸的的百分比坐标
在 cocos 中,uv 坐标的原点在左上角,u 轴向右,v 轴向下,范围是 0-1
02
效果对比
demo 中用了两个 shader,分别是水波和圆角
在编辑器中的表现正常:
而运行后,在浏览器中的表现却不是我们预期的效果
小羊的水波效果边缘出现了其他纹理
小天使的圆角效果只有左上角实现了,但被放大了好多倍
03
原因分析
为了降低 DrawCall,cocos 提供了两种合图机制
· 自动合图(Auto Atlas)
在项目构建时的静态合图方法
· 动态合图(Dynamic Atlas)
在项目运行时动态的将贴图合并到一张大贴图中
当渲染一张贴图的时候,动态合图系统会自动检测这张贴图是否已经被合并到了图集(图片集合)中,如果没有,并且此贴图又符合动态合图的条件,就会将此贴图合并到图集中
动态合图是按照 渲染顺序 来选取要将哪些贴图合并到一张大图中的,这样就能确保相邻的 DrawCall 能合并为一个 DrawCall(又称“合批”)
也就是说,合图后,该纹理会被放到一个较大的纹理中:
运行时,cocos 会将合图后的纹理传递给 shader,即 shader 中 texture:
uniform sampler2D texture;
同时 cocos 也会帮我们重新计算该纹理在合图纹理中的 uv 坐标,然后将此 uv 坐标传递给 shader,即 shader 中的 v_uv0:
in vec2 v_uv0;
shader 中使用 v_uv0 参与计算的公式,因为 v_uv0 的改变,计算结果也会随之改变,所以,在运行后,我们看到的效果就和在编辑器中不一致了
举个例子
对椰子头来说,假如合图前传入的 uv 坐标为(0.0, 0.0),即纹理的左上角,而经过合图后,椰子头左上角的 uv 坐标就会变成(0.3, 0.0),即为合图中的纹理的 uv 坐标
计算 uv 坐标的相关源码在 CCSpriteAtlas.js 中:
源码路径:
resources\engine\cocos2d\core\assets\CCSpriteFrame.js
_calculateUV:
_calculateUV () {
// SpriteFrame 的纹理矩形区域
let rect = this._rect,
// 获取使用的纹理实例,如果是合图的话,即合图的纹理
texture = this._texture,
// SpriteFrame 的 uv 数组
uv = this.uv,
// 使用纹理的宽度
texw = texture.width,
// 使用纹理的高度
texh = texture.height;
// SpriteFrame 是否旋转
if (this._rotated) {
let l = texw === 0 ? 0 : rect.x / texw;
let r = texw === 0 ? 0 : (rect.x + rect.height) / texw;
let b = texh === 0 ? 0 : (rect.y + rect.width) / texh;
let t = texh === 0 ? 0 : rect.y / texh;
uv[0] = l;
uv[1] = t;
uv[2] = l;
uv[3] = b;
uv[4] = r;
uv[5] = t;
uv[6] = r;
uv[7] = b;
}
else {
// SpriteFrame 的纹理矩形区域的左上角像素坐标(即在使用纹理中的像素坐标)为(rect.x, rect.y)
// uv 坐标的原点在左上角
// 通过与使用纹理的宽高计算得到四个边界坐标
// 四个边界坐标均为相对于合图纹理中的百分比坐标
// xMin
let l = texw === 0 ? 0 : rect.x / texw;
// xMax
let r = texw === 0 ? 0 : (rect.x + rect.width) / texw;
// yMax
let b = texh === 0 ? 0 : (rect.y + rect.height) / texh;
// yMin
let t = texh === 0 ? 0 : rect.y / texh;
// 左下角
uv[0] = l;
uv[1] = b;
// 右下角
uv[2] = r;
uv[3] = b;
// 左上角
uv[4] = l;
uv[5] = t;
// 右上角
uv[6] = r;
uv[7] = t;
}
// SpriteFrame 是否翻转 x 轴
if (this._flipX) {
let tempVal = uv[0];
uv[0] = uv[2];
uv[2] = tempVal;
tempVal = uv[1];
uv[1] = uv[3];
uv[3] = tempVal;
tempVal = uv[4];
uv[4] = uv[6];
uv[6] = tempVal;
tempVal = uv[5];
uv[5] = uv[7];
uv[7] = tempVal;
}
// SpriteFrame 是否翻转 y 轴
if (this._flipY) {
let tempVal = uv[0];
uv[0] = uv[4];
uv[4] = tempVal;
tempVal = uv[1];
uv[1] = uv[5];
uv[5] = tempVal;
tempVal = uv[2];
uv[2] = uv[6];
uv[6] = tempVal;
tempVal = uv[3];
uv[3] = uv[7];
uv[7] = tempVal;
}
let vertices = this.vertices;
if (vertices) {
vertices.nu.length = 0;
vertices.nv.length = 0;
for (let i = 0; i < vertices.u.length; i++) {
vertices.nu[i] = vertices.u[i]/texw;
vertices.nv[i] = vertices.v[i]/texh;
}
}
this._calculateSlicedUV();
}
经过 _calculateUV() 计算后,SpriteFrame 的 uv 存放的便是该纹理在合图纹理中的 uv 坐标
04
解决方案
1关闭动态合图
通过代码关闭动态合图功能,相应的,可能会增加 DrawCall
onLoad() {
cc.dynamicAtlasManager.enabled = false;
}
2禁止贴图参与合图
在纹理的属性便面中关闭 Packable(是否允许贴图参与合图),依然可能会增加 DrawCall
3动态计算
通过之前的源码,我们可以得知纹理在合图中的四个边界 uv 坐标,通过四个边界坐标,可以计算出归一化的 uv 坐标
在脚本组件中获取纹理的边界 uv 坐标及旋转状态,传递给 shader
let frame = this.sprite.spriteFrame;
// xMin
let l = frame.uv[0];
// xMax
let r = frame.uv[6];
// yMax
let b = frame.uv[3];
// yMin
let t = frame.uv[5];
// 纹理在合图中的四个边界 uv 坐标
let u_uvOffset = new cc.Vec4(l, t, r, b);
// 纹理是否旋转
let u_uvRotated = frame.isRotated() ? 1.0 : 0.0;
// 设置材质的属性
this.material.setProperty("u_uvOffset", u_uvOffset);
this.material.setProperty("u_uvRotated", u_uvRotated);
在 shader 的片元着色器中,通过当前的 uv 坐标(v_uv0)与纹理在合图中的偏移坐标,计算出归一化后的 uv 坐标
vec2 uvNormalize;
uvNormalize.x = (v_uv0.x - u_uvOffset.x) / (u_uvOffset.z - u_uvOffset.x);
uvNormalize.y = (v_uv0.y - u_uvOffset.y) / (u_uvOffset.w - u_uvOffset.y);
if(u_uvRotated > 0.5)
{
float temp = uvNormalize.x;
uvNormalize.x = uvNormalize.y;
uvNormalize.y = 1.0 - temp;
}
uvNormalize 即为归一化的 uv 坐标,也就是纹理在合图前的 uv 坐标
使用该坐标完成 shader 中对纹理坐标的 计算
但当获取纹理的色值时,依然需要使用 v_uv0,因为 shader 中的纹理(texture)为合图纹理,v_uv0 才是该纹理在合图纹理中正确的 uv 坐标
源码获取,请在公众号回复:
合图 uv 坐标
cocos creator | 为什么 shader 效果在编辑器中显示正常,运行后却显示异常? 合图纹理的uv计算相关推荐
- h5页面保存img_如何设计H5编辑器中的模版库并实现自动生成封面图
往期精选 H5编辑器的图片上传和图片库设计方案 如何实现H5编辑器的实时预览和真机扫码预览功能 在线IDE开发入门之从零实现一个在线代码编辑器 基于React+Koa实现一个h5页面可视化编辑器-Do ...
- 如何设计H5编辑器中的模版库并实现自动生成封面图
往期精选 H5编辑器的图片上传和图片库设计方案 如何实现H5编辑器的实时预览和真机扫码预览功能 在线IDE开发入门之从零实现一个在线代码编辑器 基于React+Koa实现一个h5页面可视化编辑器-Do ...
- WPS PPT 公式编辑 希腊字母不显示及公式编辑器中运算符号、希腊字母不显示
问题1:在别处复制的正常的公式在word.PPT中不能正常显示希腊字母和运算符号. 解决办法:在网上下载(下载地址:http://www.pc6.com/softview/SoftView_67283 ...
- C# winform单元格的formatted值的类型错误 DataGridView中CheckBox列运行时候System.FormatException异常
C# winform单元格的formatted值的类型错误 DataGridView中CheckBox列运行时候System.FormatException异常 参考文章: (1)C# winform ...
- C#中datagridview选中行后textbox显示选中的内容
我想让datagridview中某一行被选中时,textbox中显示选中的值,datagridview的选中模式是整行: this.dataGridView1.SelectionMode = Data ...
- Cocos Creator 3D后期效果解决方案源码剖析--从入门到融汇贯通
注:本文既有经验上的总结,又有实现方式上的讲解.既有流程上的描述,又有代码细节上的剖析. 全文字数5000+,看的时候最好带上笔和纸. 零.你的序 感谢大家的厚爱,KylinsPostEffects上 ...
- 快速入门 Cocos Creator 3D Shader 上篇
前言 Shader,又名着色器,是控制 GPU 绘制的指令集.从某种意义来说,着色器是一个把输入转换到输出的程序,为图形渲染管线的某个特定部分而运行.图形渲染管线是渲染的核心组件.通过给定虚拟相机.3 ...
- 《Cocos Creator游戏实战》棋类游戏中的棋子摆放逻辑
棋类游戏中的棋子摆放逻辑 创建节点 代码编写 其实要点就一句话:我们看到的不应是棋盘,而是坐标. 现在通过下面的五子棋(或围棋)实例来看下如何理解这句话. 运行效果如下: Cocos Creator版 ...
- cocos creator 使用shader 不要勾选项目设置的动态图集和图片的自动合批
学习链接 https://forum.cocos.org/t/creator-shader-2019-10-22-2-2-0/82548 因为主要目的是为了避开cocos creator Mask的 ...
最新文章
- linux 挂载网络文件系统,linux 挂载网络文件系统
- viewDidLoad, viewWillDisappear, viewWillAppear等区别及各自的加载顺序
- OC之OBJC2_UNAVAILABLE
- 您可能不知道可以使用Chrome开发者控制台执行的操作
- 【nginx笔记】系统参数设置-使Nginx支持更多并发请求的TCP网络参数
- 普林斯顿大学计算机系,普林斯顿大学计算机科学系
- linux 系统分区及格式化
- android 息屏锁频录音、录视频、电话自动录音
- Nginx从入门到入坟(九)- Nginx静态资源如何防盗链
- ​关于技术变现的几点思考
- HBuilder开发app,扫描枪中,使用input输入框,然后点击扫描,获取不到条码!
- 海龟作图python等边三角形_python 海龟作图
- 最值得爸爸妈妈学习的儿童教育书籍推荐
- 文件管理学习:从百度网盘搬家onedrive测评
- 锂离子电池是由什么组成的
- Linux服务器开发,Posix API与网络协议栈
- 【Java集合类】- Java集合类
- 十大算法展辉煌历史,十大问题引锦绣前程
- 搜索引擎关键词快速排名软件_SEO技巧排名软件怎么做搜索引擎关键词排名?
- 【数学基础】最简分数