原文地址:WebGL学习之纹理盒

我们之前已经学习过二维纹理 gl.TEXTURE_2D,而且还使用它实现了各种效果。但还有一种立方体纹理 gl.TEXTURE_CUBE_MAP,它包含了6个纹理代表立方体的6个面。不像常规的纹理坐标有2个纬度,立方体纹理使用法向量,换句话说三维方向。本节实现的demo请看 天空盒

根据法向量的朝向选取立方体6个面中的一个,这个面的像素用来采样生成颜色。这六个面通过他们相对于立方体中心的方向被引用。它们是分别是

gl.TEXTURE_CUBE_MAP_POSITIVE_X//右
gl.TEXTURE_CUBE_MAP_NEGATIVE_X//左
gl.TEXTURE_CUBE_MAP_POSITIVE_Y//上
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y//下
gl.TEXTURE_CUBE_MAP_POSITIVE_Z//后
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z//前
复制代码

环境贴图

其实我们更应该把cube map叫作纹理盒,通常纹理盒不是给立方体设置纹理用的,设置立方体纹理的标准用法其实是使用二维贴图,那么纹理盒用来做什么的呢?纹理盒最常见的用法是用来做环境贴图。在百度和google地图中的3D街景就是环境贴图应用的一个例子。

纹理

下面是6张红色峡谷图片

将以上尺寸为512x512的图片填充到立方体的每个面,以下就是纹理的创建加载过程

// 创建纹理。
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);const faceInfos = [{target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, url: '/img/sorbin_rt.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, url: '/img/sorbin_lf.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, url: '/img/sorbin_up.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, url: '/img/sorbin_dn.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, url: '/img/sorbin_bk.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, url: '/img/sorbin_ft.jpg',},
];
faceInfos.forEach((faceInfo) => {const {target, url} = faceInfo;// 上传画布到立方体贴图的每个面const level = 0;const format = gl.RGBA;const width = 512;const height = 512;const type = gl.UNSIGNED_BYTE;// 设置每个面,使其立即可渲染gl.texImage2D(target, level, format, width, height, 0, format, type, null);// 异步加载图片const image = new Image();image.src = url;image.onload = function() {// 图片加载完成将其拷贝到纹理gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);gl.texImage2D(target, level, internalFormat, format, type, image);gl.generateMipmap(gl.TEXTURE_CUBE_MAP);};
});
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
复制代码

法向量

标准立方体法向量 和 纹理盒法向量的区别

3D立方体使用纹理盒有一个巨大的好处就是不需要额外指定纹理坐标。只要盒子是被放置在世界坐标系的原点,盒子本身的坐标就可以作为纹理坐标使用,因为在3D世界中位置本身就是一个向量,表示一个方向,我们要的就是这个方向。

所以顶点着色器非常简单

attribute vec4 a_position;
uniform mat4 u_vpMatrix;
varying vec3 v_normal;void main() {gl_Position = u_vpMatrix * a_position;//因为位置是以几何中心为原点的,可以用顶点坐标作为法向量v_normal = normalize(a_position.xyz);
}
复制代码

片段着色器中我们需要用samplerCube 代替 sampler2DtextureCube代替texture2DtextureCube 需要vec3类型的向量。 法向量从顶点着色器传递过来经过了插值处理,需要重新单位化。

precision mediump float; // 从顶点着色器传入。
varying vec3 v_normal; // 纹理。
uniform samplerCube u_texture; void main() {   gl_FragColor = textureCube(u_texture, normalize(v_normal));
}
复制代码

实现

运行后得到如下的效果,很明显就能看出是个立方体,并不是我们想要的360度环绕的3D场景。

其实我们只需要将相机位置置于原点(0,0,0),同时lookAt向其中的一个面就可以了。但是在原点有个问题,如果要旋转查看场景怎么办?我们可以通过旋转相机的位置,这其实就相当于立方体旋转,同时我们不需要矩阵位移相关的信息,只需要方向相关的信息就好了。同时还可以禁止写入深度缓存,造成背景在很远的假象,让效果更加真实。

const viewPosition = new Vector3([0,0,1]);//相机位置
const lookAt = [0, 0, 0];//原点//相机绕y轴旋转
cameraMatrix.rotate(0.2,0,1,0);
viewPoint = cameraMatrix.multiplyVector3(viewPosition);
vpMatrix.setPerspective( 30, canvas.width / canvas.height, 0.1, 5 );
vpMatrix.lookAt(...viewPoint.elements, ...lookAt, 0, 1, 0);//重置位移
vpMatrix.elements[12] = 0;
vpMatrix.elements[13] = 0;
vpMatrix.elements[14] = 0;// 禁止写入深度缓存,造成背景在很远的假象
gl.depthMask(false);
复制代码

环境纹理映射

环境贴图还有个更通俗的叫法-天空盒。接着我们还要实现一个非常帅气的效果,在天空盒三维场景中,让其中的物体反射场景周围的着色。这个操作就叫做环境纹理映射(environment mapping)。

反射

如果物体的表面像光滑的镜子,那么我们就能看到物体反射出天空和周围的景色。反射的原理非常简单,那就是使用反射公式映射纹理盒对应的纹素:

相机位置(观察点)和 物体顶点的位置,顶点位置又包含着法线信息,通过GLSL的reflect函数就可以非常容易的计算反射向量R,进而确定看到的是哪一块表面的着色。

实现

我们就在天空盒下面增加一个镜面立方体,那就需要增加一对着色器,首先顶点着色器需要增加法线,mvp矩阵

attribute vec4 a_position;
attribute vec4 a_normal;
uniform mat4 u_vpMatrix;
uniform mat4 u_modelMatrix;
varying vec3 v_position;
varying vec3 v_normal;void main() {v_position = (u_modelMatrix * a_position).xyz;v_normal = vec3(u_modelMatrix * a_normal);gl_Position = u_vpMatrix * u_modelMatrix * a_position;
}
复制代码

片元着色器则需要添加相机位置,纹理以及顶点着色器传递过来的法线和顶点位置

precision highp float;
varying vec3 v_position;
varying vec3 v_normal;
uniform samplerCube u_texture;
uniform vec3 u_viewPosition;void main() {vec3 normal = normalize(v_normal);vec3 eyeToSurfaceDir = normalize(v_position - u_viewPosition);vec3 direction = reflect(eyeToSurfaceDir,normal);gl_FragColor = textureCube(u_texture, direction);
}
复制代码

这样我们绘制的时候就要轮流切换着色器program

function draw(){gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//天空盒gl.useProgram(program.program);//绘制天空盒//...//立方体gl.useProgram(cProgram.program);//绘制立方体//...requestAnimationFrame(draw);
}
复制代码

最后实现如下效果,demo情况 天空盒

后记

其实纹理盒除了可以做环境贴图,还可以结合光照,阴影贴图作出很多酷炫的效果。

转载于:https://juejin.im/post/5ccfbad6e51d453b560f2d4c

WebGL学习之纹理盒相关推荐

  1. webgl学习六 纹理贴图

    <html><head><title>纹理贴图</title><script type="text/javascript" s ...

  2. 【实战】(字节跳动、日本东京大学)学习使用白盒表示+GAN来创作卡通图片:Learning to Cartoonize Using White-Box Cartoon Representations

    要说亚洲动漫制作的王者,当然是我们一衣带水的邻邦--日本. 2020年字节跳动和日本东京大学联合发表了一篇<Learning to Cartoonize Using White-Box Cart ...

  3. WebGL学习(1) - 三角形

    原文地址:WebGL学习(1) - 三角形 还记得第一次看到canvas的粒子特效的时候,真的把我给惊艳到了,原来在浏览器也能做出这么棒的效果.结合<HTML5 Canvas核心技术>和网 ...

  4. 我的webgl学习之路(一)

    我的webgl学习之路 (一) 做项目中偶然需要移动端3d展示,所以学习了threejs;但是有着各种限制,还有就是项目要达到好的效果肯定是需要后期处理等等,所以展开了我学习webgl之路,我刚开始毫 ...

  5. 原生webgl学习(四) WebGL绘制矩形(一)

    本专栏所有文章示例代码均可在我的gitee码云上获取,读者可自行下载:https://gitee.com/babyogl/learnWebGL:本节代码在文件夹chapter-02里面的color-t ...

  6. webgl学习路线_WebGL:WebAssembly和功能路线图

    webgl学习路线 Yesterday, engineers from Google, Microsoft and Mozilla (makers of Chrome, Edge and Firefo ...

  7. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

  8. WebGL学习笔记七点一

    第六章讲的是一些GL的一些语法,前面已经涉及,学习时直接跳过,来看第七章,第七章是真正意义的三维立体的出现,其实图形绘制方法是差不多的,就是Z坐标此时不再为0,所以很容易能构造出一些立体图形,但是立体 ...

  9. WEBGL学习【八】模型视图投影矩阵

    版权声明:本文为博主原创文章,未经博主允许不得转载.更多学习资料请访问我爱科技论坛:www.52tech.tech https://blog.csdn.net/m0_37981569/article/ ...

最新文章

  1. 最完美的ASCII 表
  2. 算法:柱状图中最大矩形
  3. 如何让百度第一时间收录你的网站
  4. C++学习——const
  5. 微信小程序时代,哪些人能赚到第一桶金
  6. Holer实现外网访问本地MySQL数据库
  7. ANSI颜色字体一篇通
  8. 物质之学 —— 晶体与非晶体
  9. Android 自定义组件学习 3
  10. vasp软件全名是什么_qvasp一款简单易用的VASP辅助计算软件
  11. pwnable.kr第五题:passcode
  12. Unity 雨水滴到屏幕效果
  13. CWS(美国国土安全部下属的软件保证项目)与SANS(权威安全培训组织)联合编制的最危险的25个编程错误
  14. 算法与算力在智能语音系统中的应用
  15. 软件测试 | 测试开发 | 年薪超过40W,一位测试媛宝妈的 BAT 大厂逆袭之旅
  16. Python爬取热门城市火车票信息
  17. linux未安装MySQL但有进程_Linux_RedHat下安装MySQL出现的问题及解决方法
  18. html 地球页面代码,纯CSS3实现地球自转实现代码(图文教程附送源码)
  19. 【C#】C#客户端自动升级技术简析
  20. Zebec获BNB Chain生态大力支持,ZBC或继续登录一线平台

热门文章

  1. 初识未来趋势:Java与Kotlin;EclipsE与IntelliJ
  2. 为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区...
  3. mysql集群的使用与简单测试
  4. Android零基础入门第11节:简单几步带你飞,运行Android Studio工程
  5. tomcat及负载均衡
  6. Tomcat启动 java.lang.OutOfMemoryError
  7. 神经网络:卷积神经网络CNN
  8. ngModel 值不更新/显示
  9. 用Windows系统实现RAID功能
  10. 【译】保护 Consul 在特定设置中免受 RCE 风险的影响