热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)

这篇文章主要介绍了WebGL利用FBO完成立方体贴图效果的方法,以完整实例形式分析了WebGL实现立方体贴图的具体步骤与相关技巧,并附带了demo源码供读者下载参考,需要的朋友可以参考下

本文实例讲述了WebGL利用FBO完成立方体贴图效果的方法。分享给大家供大家参考,具体如下:

这篇主要记录WebGL的一些基本要点,顺便也学习下如何使用FBO与环境贴图。先看下效果图(需要支持WebGL,Chrome,火狐,IE11)。

主要实现过程如下,先用FBO输出当前环境在立方体纹理中,再画出当前立方体,最后画球,并且把FBO关联的纹理贴在这个球面上。

开始WebGL时,最好有些OpenGL基础,在前面讲Obj完善与MD2时,大家可能已经发现了,因为着色器的添加使用,原来一些Opengl大部分API已经没有使用。WebGL就和这差不多,大部分功能是着色器完成主要功能,记录下主要过程,大家可以比较下前面的,看看是不是很像,为了熟悉WebGL基本功能,本文没有利用比较完善的框架,只是用到一个帮助计算矩阵的框架(gl-matrix.js).

和使用OpenGL一样,我们要初始化使用环境,提取一些全局使用量。相关代码如下:

初始化:

var gl;//WebGLRenderingContext
var cubeVBO;//Cube buffer ID
var sphereVBO;//球体VBO
var sphereEBO;//球体EBO
var cubeTexID;//立方体纹理ID
var fboBuffer;//桢缓存对象
var glCubeProgram;//立方体着色器应用
var glSphereProgram;//球体着色器应用
var fboWidth = 512;//桢缓存绑定纹理宽度
var fboHeight = 512;//桢缓存绑定纹理高度
var targets;//立方体贴图六个方向
var pMatrix = mat4.create();//透视矩阵
var vMatrix = mat4.create();//视图矩阵
var eyePos = vec3.fromValues(0.0, 1.0, 0.0);//眼睛位置
var eyeLookat = vec3.fromValues(0.0, -0.0, 0.0);//眼睛方向
var spherePos = vec3.fromValues(0.0, -0.0, 0.0);//球体位置
var canvanName;
function webGLStart(cName) {
  canvanName = cName;
  InitWebGL();
  InitCubeShader();
  InitSphereShader();
  InitCubeBuffer();
  InitSphereBuffer();
  InitFBOCube();
  //RenderFBO();
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);
  tick();
}
function InitWebGL() {
  //var canvas = document.getElementById(canvanName);
  InitGL(canvanName);
}
function InitGL(canvas) {
  try {
    //WebGLRenderingContext 
    gl = canvas.getContext("experimental-webgl");
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;
    targets = [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];
  } catch (e) { }
  if (!gl) { alert("你的浏览器不支持WebGL"); }
}

在这里,我们初始化在网页中WebGL的上下方环境,并给出一系列初始化过程。下面先给出房间,也就是其中立方体的相关代码。

立方体:

function InitCubeShader() {
  //WebGLShader
  var shader_vertex = GetShader("cubeshader-vs");
  var shader_fragment = GetShader("cubeshader-fs");
  //WebglCubeProgram
  glCubeProgram = gl.createProgram();
  gl.attachShader(glCubeProgram, shader_vertex);
  gl.attachShader(glCubeProgram, shader_fragment);
  gl.linkProgram(glCubeProgram);
  if (!gl.getProgramParameter(glCubeProgram, gl.LINK_STATUS)) {
    alert("Shader hava error.");
  }
  gl.useProgram(glCubeProgram);
  glCubeProgram.positiOnAttribute= gl.getAttribLocation(glCubeProgram, "a_position");
  glCubeProgram.normalAttribute = gl.getAttribLocation(glCubeProgram, "a_normal");
  glCubeProgram.texCoordAttribute = gl.getAttribLocation(glCubeProgram, "a_texCoord");
  glCubeProgram.view = gl.getUniformLocation(glCubeProgram, "view");
  glCubeProgram.perspective = gl.getUniformLocation(glCubeProgram, "perspective");
}
function InitCubeBuffer() {
  var cubeData = [
      -10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 0.0,
      -10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 1.0,
      10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, 0.0, 0.0, -10.0, 1.0, 0.0,
      -10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 0.0,
      10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 0.0,
      10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 1.0,
      -10.0, -10.0, 10.0, 0.0, 0.0, 10.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 0.0, 0.0,
      10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 1.0, 0.0,
      10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 1.0, 1.0,
      10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 1.0, 1.0,
      -10.0, -10.0, 10.0, 0.0, -10.0, 0.0, 0.0, 1.0,
      -10.0, -10.0, -10.0, 0.0, -10.0, 0.0, 0.0, 0.0,
      10.0, -10.0, -10.0, 10.0, 0.0, 0.0, 0.0, 0.0,
      10.0, 10.0, -10.0, 10.0, 0.0, 0.0, 1.0, 0.0,
      10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 1.0, 1.0,
      10.0, -10.0, 10.0, 10.0, 0.0, 0.0, 0.0, 1.0,
      10.0, -10.0, -10.0, 10.0, 0.0, 0.0, 0.0, 0.0,
      10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 0.0, 0.0,
      -10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 1.0, 0.0,
      -10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 1.0, 1.0,
      10.0, 10.0, 10.0, 0.0, 10.0, 0.0, 0.0, 1.0,
      10.0, 10.0, -10.0, 0.0, 10.0, 0.0, 0.0, 0.0,
      -10.0, 10.0, -10.0, -10.0, 0.0, 0.0, 0.0, 0.0,
      -10.0, -10.0, -10.0, -10.0, 0.0, 0.0, 1.0, 0.0,
      -10.0, -10.0, 10.0, -10.0, 0.0, 0.0, 1.0, 1.0,
      -10.0, -10.0, 10.0, -10.0, 0.0, 0.0, 1.0, 1.0,
      -10.0, 10.0, 10.0, -10.0, 0.0, 0.0, 0.0, 1.0,
      -10.0, 10.0, -10.0, -10.0, 0.0, 0.0, 0.0, 0.0,
  ];
  cubeVBO = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVBO);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeData), gl.STATIC_DRAW);
}
function RenderCube() {
  gl.useProgram(glCubeProgram);
  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVBO);
  gl.vertexAttribPointer(glCubeProgram.positionAttribute, 3, gl.FLOAT, false, 32, 0);
  gl.enableVertexAttribArray(glCubeProgram.positionAttribute);
  gl.vertexAttribPointer(glCubeProgram.normalAttribute, 3, gl.FLOAT, false, 32, 12);
  gl.enableVertexAttribArray(glCubeProgram.normalAttribute);
  gl.vertexAttribPointer(glCubeProgram.texCoordAttribute, 2, gl.FLOAT, false, 32, 24);
  gl.enableVertexAttribArray(glCubeProgram.texCoordAttribute);
  gl.uniformMatrix4fv(glCubeProgram.view, false, vMatrix);
  gl.uniformMatrix4fv(glCubeProgram.perspective, false, pMatrix);
  gl.drawArrays(gl.TRIANGLES, 0, 36);
}

上面的代码主要分为初始化立方体的着色器对象,初始化相关缓存,然后绘制立方体,可以说在Opengl中,如果用着色器来画,过程也是差不多的,在Opengl里,已经没有固定管线的一些功能如InterleavedArrays来指定是顶点还是法线或是纹理了,统一用vertexAttribPointer来传递应用程序与着色器之间的数据。在前面 MD2桢动画实现里面后面的参数传递改进版也有相关应用。

相应着立方体着色器主要代码如下:

立方体着色器实现:




着色器中,已经没有ftransform()功能可供调用,要自己传递如模型,视图,透视矩阵,在这里,模型是以原点为中心来绘画,意思模型视图矩阵也就是视图矩阵,所以屏幕位置的计算只需要视图和透视矩阵。在片断着色器中,x,y是从顶点着色器中的纹理坐标传递过来,相应过程6.28*8.0,相当于8个360度,用于控制立方体上的方块显示,而tex2是着色器中的顶点映射[0,1]的值,分别给立方体的六面分别设置不同的意思,然后用二个矢量的乘积来混合这二种颜色显示,gl_FragColor = vec4(tex2,1.0) * vec4(sign(cos(x)+cos(y)))。

在显示球体之前,应该先生成当前环境的立方体绘图,在这里使用FBO,先生成桢缓存和立方体绘理,并关联,然后以原点为中心,分别向上下左右前右绘图,然后利用桢缓冲分别输出到立方体上的六个面,主要代码如下:

FBO与立方体纹理:

function InitFBOCube() {
  // WebGLFramebuffer
  fboBuffer = gl.createFramebuffer();
  gl.bindFramebuffer(gl.FRAMEBUFFER, fboBuffer);
  fboBuffer.width = 512;
  fboBuffer.height = 512;
  cubeTexID = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexID);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  for (var i = 0; i 

在上面不知是gl-matrix提供的矩阵算法有问题,还是本来应该这样,在上下面的时候生成的纹理图不对,需要偏转摄像机的向上矢量。因为这是摄像机位置与目标的生成的Z轴和设定的UP轴平行了,这样导致不能正确计算X轴,然后对应的UP轴也计算不出来,相应视图矩阵出现错误。

最后是球体的绘画,代码主要和立方体的差不多,注意球体的顶点算法。

球体:

function InitSphereShader() {
  //WebGLShader
  var shader_vertex = GetShader("sphereshader-vs");
  var shader_fragment = GetShader("sphereshader-fs");
  //WebglCubeProgram
  glSphereProgram = gl.createProgram();
  gl.attachShader(glSphereProgram, shader_vertex);
  gl.attachShader(glSphereProgram, shader_fragment);
  gl.linkProgram(glSphereProgram);
  if (!gl.getProgramParameter(glSphereProgram, gl.LINK_STATUS)) {
    alert("Shader hava error.");
  }
  glSphereProgram.positiOnAttribute= gl.getAttribLocation(glSphereProgram, "a_position");
  glSphereProgram.normalAttribute = gl.getAttribLocation(glSphereProgram, "a_normal");
  glSphereProgram.eye = gl.getUniformLocation(glSphereProgram, "eye");
  glSphereProgram.mapCube = gl.getUniformLocation(glSphereProgram, "mapCube");
  glSphereProgram.model = gl.getUniformLocation(glSphereProgram, "model");
  glSphereProgram.view = gl.getUniformLocation(glSphereProgram, "view");
  glSphereProgram.perspective = gl.getUniformLocation(glSphereProgram, "perspective");
}
function InitSphereBuffer() {
  var radius = 1;
  var segments = 16;
  var rings = 16;
  var length = segments * rings * 6;
  var sphereData = new Array();
  var sphereIndex = new Array();
  for (var y = 0; y 

可以看到,也是和立方体一样的三步,初始化着色器,初始化顶点与法线,绘画。下面给出着色器代码:

球体着色器:




和前面立方体有点不同的是,球体有自己的模型矩阵,这也是一般正常的用法,然后传递眼睛对应球体顶点矢量与法线传递在片断着色器中,在片断着色器中,就有用到前面所生成的立方体纹理,我们根据眼睛经过顶点通过对应法向量反射到立体体纹理上的点来获取当前球体所对应的环境颜色,在这里,我们可以直接调用textureCube来完成上面所说的过程,不需要我们手动来计算。

其中GetShader函数的使用,参照了http://msdn.microsoft.com/zh-TW/library/ie/dn302360(v=vs.85) 这里的讲解。

可以说,上面主要的绘制函数已经完成,但是我们这个是能动的,所以需要模拟如客户端环境每隔多久绘制一次,主要代码如下:

动画:

function tick() {
  Update();
  OnDraw();
  setTimeout(function () { tick() }, 15);
}
function OnDraw() {
  //fbo rander CUBE_MAP
  RenderFBO();
  //element rander
  gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 200.0);
  mat4.lookAt(vMatrix, eyePos, eyeLookat, vec3.fromValues(0.0, 1.0, 0.0));
  RenderCube();
  RenderSphere();
}
var lastTime = new Date().getTime();
function Update() {
  var timeNow = new Date().getTime();
  if (lastTime != 0) {
    var elapsed = timeNow - lastTime;
    //3000控制人眼的旋转速度。8控制人眼的远近
    eyePos[0] = Math.cos(elapsed / 3000) * 8;
    eyePos[2] = Math.sin(elapsed / 2000) * 8;
    spherePos[0] = Math.cos(elapsed / 4000) * 3;
    spherePos[2] = Math.cos(elapsed / 4000) * 3;
  }
}

在上面,每隔15毫秒调用一次Update与Draw函数,其中Update用于更新眼睛与球体位置,Draw绘画。

完整实例代码点击此处本站下载。

更多关于JS特效相关内容感兴趣的读者可查看本站专题:《jQuery动画与特效用法总结》及《jQuery常见经典特效汇总》

希望本文所述对大家Javascript程序设计有所帮助。


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • macOS Big Sur全新设计大版本更新,10+个值得关注的新功能
    本文介绍了Apple发布的新一代操作系统macOS Big Sur,该系统采用全新的界面设计,包括图标、应用界面、程序坞和菜单栏等方面的变化。新系统还增加了通知中心、桌面小组件、强化的Safari浏览器以及隐私保护等多项功能。文章指出,macOS Big Sur的设计与iPadOS越来越接近,结合了去年iPadOS对鼠标的完善等功能。 ... [详细]
  • 随着前端技术的发展,越来越多的开发者开始使用react、vue等web框架,但很少有人深入理解这些框架的源码。然而,这些框架底层都是由原生的javascript构建而成。对于初学前端的人来说,可能会认为javascript很容易上手,但实际上只是因为它被高度封装了。与能够使用封装类的人相比,能够理解框架原理的人则处于另一个层面。本文将深入剖析jquery源码,探寻框架底层的原理,帮助读者更好地理解web框架的运行机制。 ... [详细]
  • 本文介绍了在满足特定条件时如何在输入字段中使用默认值的方法和相应的代码。当输入字段填充100或更多的金额时,使用50作为默认值;当输入字段填充有-20或更多(负数)时,使用-10作为默认值。文章还提供了相关的JavaScript和Jquery代码,用于动态地根据条件使用默认值。 ... [详细]
  • 如何压缩网站页面以减少页面加载时间
    本文介绍了影响网站打开时间的两个因素,即网页加载速度和网站页面大小。重点讲解了如何通过压缩网站页面来减少页面加载时间。具体包括图片压缩、Javascript压缩、CSS压缩和HTML压缩等方法,并推荐了相应的压缩工具。此外,还提到了一款Google Chrome插件——网页加载速度分析工具Speed Tracer。 ... [详细]
  • 本文整理了常用的CSS属性及用法,包括背景属性、边框属性、尺寸属性、可伸缩框属性、字体属性和文本属性等,方便开发者查阅和使用。 ... [详细]
  • CS231n简介详见CS231n课程笔记1:Introduction。本文都是作者自己的思考,正确性未经过验证,欢迎指教。作业笔记本部分实现的是Momentum,RMSProb, ... [详细]
  • Opencv提供了几种分类器,例程里通过字符识别来进行说明的1、支持向量机(SVM):给定训练样本,支持向量机建立一个超平面作为决策平面,使得正例和反例之间的隔离边缘被最大化。函数原型:训练原型cv ... [详细]
  • VBA操作Excel之设置单元格属性
    VBA操作Excel简介一、VBA读写Excel文件二、VBA设置单元格属性三、VBA弹出输入和输出窗口参考文档一、VBA读写Excel文件VBA简介及打开Excel文件方法见VB ... [详细]
  • 遗传算法matlab_三分钟学会遗传算法
    遗传算法此节介绍最著名的遗传算法(GA)。遗传算法属于进化算法,基本思想是取自“物竞天泽、适者生存”的进化法则。简单来说,遗传算法就是将问题编码成为染色 ... [详细]
author-avatar
blue一个人的秋天_854
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有