这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
本文标题:WebGL第二十八课:多贴图实战| 8月更文挑战
友情提示
这篇文章是WebGL课程专栏的第28篇,强烈建议从前面开始看起。因为花了大量的工夫来讲解向量的概念和矩阵运算。这些基础知识会影响你的思维。
本课代码直接跳转获取:二十八课代码
引子
在前面两次课,讲到了有关贴图的东西。
问题就是,如果我们有多个图,都想传到gl里,这时候怎么写呢?
我们在fragment_shader里正确获取到我们想要的图呢?
本次课就来解决这些问题。
代码分离
由于在html里直接写js代码,看起来十分的混乱,所以从这里开始,html 里只包含页面的东西和两个shader。
其余的代码放到一个单独的文件里:logic.js。
大概类似这样:
index.html // 页面
logic.js // 主逻辑
models_tri.js // 模型代码
images/ // 放图片
准备两个图
由于我们要画多个图,所以你至少准备两张图,我准备了如下两张图:
images/eye.png
images/funny-cat.jpg
| |
|
先拉取图片
因为图片的成功加载,是所有后续逻辑的依赖,所以,图片加载这里,我们要考虑到:
所有图片全部加载完成之后,再调用后面的函数。
所以,我们一开始就要记录一个图片加载的进度,然后每一个图片加载完成之后,进度就加1,当进度和图片的数量相等时,调用后面的函数。代码这样:
var imageUrlList = ["images/eye.png", "images/funny-cat.jpg",]; // 全局的变量,存储了想要加载的图片
var images_progress = 0; // 当前进度:0张
var textureList = []; // 创建的texture数组
function CreateTextureAndLoadImage() {
// 在 WebGL 里创建一个 texture
if (images_progress == imageUrlList.length) { // 当前进度已经完成
gl_draw(); // 绘制的函数
return;
}
let texture = gl.createTexture();
textureList.push(texture);
gl.bindTexture(gl.TEXTURE_2D, texture);
let image = new Image();
image.src = imageUrlList[images_progress];
console.log(image.src);
image.addEventListener('load', function () {
gl.activeTexture(gl.TEXTURE0 + images_progress); // 激活当前的gl图片下标
gl.bindTexture(gl.TEXTURE_2D, textureList[images_progress]);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
gl.generateMipmap(gl.TEXTURE_2D);
} else {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
images_progress++;
CreateTextureAndLoadImage();
});
}
在 fragment_shader 里体现两张图片
我们在前面的课程中知道,fragment_shader 中可以用 一个uniform变量来选取想要的图片,变量类型是 sampler2D:
uniform sampler2D u_funny_cat; // 有趣的猫的图片
我们如法炮制:
uniform sampler2D u_funny_cat; // 有趣的猫的图片
uniform sampler2D u_eye; // 眼睛图片
在 js 里获取 u_eye的引用然后设置
var u_EyeLocation = gl.getUniformLocation(program, "u_eye");
gl.activeTexture(gl.TEXTURE0); // 我们要操作gl中的下标0
gl.bindTexture(gl.TEXTURE_2D, textureList[0]); // 我们将图片0绑定到下标0的位置
gl.uniform1i(u_EyeLocation, 0); // 我们将 u_eye,设置成下标0的图片
两张图片融合:相乘
由于在fragment_shader中,我们有了两张图片,我们可以对着两张图片,根据我们的UV信息,进行两次采样:
vec4 sample_color_cat = texture2D(u_funny_cat, uv);
vec4 sample_color_eye = texture2D(u_eye, uv);
问题就来了,然后最终的颜色,到底怎么搞呢?
这就非常灵活了,我们可以直接将两个图片的颜色相乘:
vec4 sample_color_cat = texture2D(u_funny_cat, uv);
vec4 sample_color_eye = texture2D(u_eye, uv);
gl_FragColor = vec4(sample_color_cat.xyz * sample_color_eye.xyz, 1.0);
这种效果如下:
这个效果看上去没什么奇特的,不过有时候,相乘还是有特殊的用处的,这个暂时不说。
两张图片融合:线性组合
我们知道,
对cat图片采用的结果是一个vec3(第四维我们先不管)
对eye图片采样的结果也是一个vec3
既然都是向量,那我们用线性组合来搞一搞:
这是一个经典的插值,t的不同,会影响最终的颜色:
- 如果t趋近于1,那么就是cat的颜色
- 如果t趋近于0,那么就是eye的颜色
代码如下:
uniform float u_t;
//
gl_FragColor = vec4(sample_color_cat.xyz * u_t + (1.0 - u_t) * sample_color_eye.xyz, 1.0);
效果如下:
我们用一个滑竿来调解这个u_t的值,可以清晰的看见两张图片的融合关系。
两张图片融合:混乱
两张图片的信息,在这里了,你想怎么玩就怎么玩,甚至于可以,用一张图片的采样到的颜色信息,当做另一张图片的UV信息:
vec4 sample_color_cat = texture2D(u_funny_cat, uv);
vec4 sample_color_eye = texture2D(u_eye, sample_color_cat.xy);
gl_FragColor = vec4(sample_color_cat.xyz * u_t + (1.0 - u_t) * sample_color_eye.xyz, 1.0);
我们看到,采样eye图片的时候,我们竟然用cat图片的颜色信息的xy,来当做UV。。。
这样是什么效果呢:
怎么搞的像描边一样,,这完全是一个意外的效果,你选的两张图,并不一定能出现这个效果,就是玩玩而已。 如果你的效果,也和描边一样,那说明我这个东西,还能应用到描边里去。。。。。。
正文结束,下面是答疑
小能能说:我去看看有没有别的融合办法,玩一玩。
- 答:肯定行啊。