国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

WebGL利用FBO完成立方體貼圖效果完整實例(附demo源碼下載)

2019-11-20 10:41:51
字體:
來源:轉載
供稿:網友

本文實例講述了WebGL利用FBO完成立方體貼圖效果的方法。分享給大家供大家參考,具體如下:

這篇主要記錄WebGL的一些基本要點,順便也學習下如何使用FBO與環境貼圖。先看下效果圖(需要支持WebGL,Chrome,火狐,IE11)。

主要實現過程如下,先用FBO輸出當前環境在立方體紋理中,再畫出當前立方體,最后畫球,并且把FBO關聯的紋理貼在這個球面上。

開始WebGL時,最好有些OpenGL基礎,在前面講Obj完善與MD2時,大家可能已經發現了,因為著色器的添加使用,原來一些Opengl大部分API已經沒有使用。WebGL就和這差不多,大部分功能是著色器完成主要功能,記錄下主要過程,大家可以比較下前面的,看看是不是很像,為了熟悉WebGL基本功能,本文沒有利用比較完善的框架,只是用到一個幫助計算矩陣的框架(gl-matrix.js).

和使用OpenGL一樣,我們要初始化使用環境,提取一些全局使用量。相關代碼如下:

初始化:

var gl;//WebGLRenderingContextvar cubeVBO;//Cube buffer IDvar sphereVBO;//球體VBOvar sphereEBO;//球體EBOvar cubeTexID;//立方體紋理IDvar 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楨動畫實現里面后面的參數傳遞改進版也有相關應用。

相應著立方體著色器主要代碼如下:

立方體著色器實現:

<script id="cubeshader-fs" type="x-shader/x-fragment">    precision mediump float;    varying vec3 normal;    varying vec3 tex1;    varying vec3 tex2;    void main( void )    {    float x = tex1.x * 6.28 * 8.0; //2兀 * 8    float y = tex1.y * 6.28 * 8.0; //2兀 * 8    //cos(x)= 8個 (1 -1 1)    gl_FragColor = vec4(tex2,1.0) * vec4(sign(cos(x)+cos(y))); //    //gl_FragColor = vec4(normal*vec3(0.5)+vec3(0.5), 1);    }</script><script id="cubeshader-vs" type="x-shader/x-vertex">    attribute vec3 a_position;    attribute vec3 a_normal;    attribute vec2 a_texCoord;    uniform mat4 view;    uniform mat4 perspective;    varying vec3 normal;    varying vec3 tex1;    varying vec3 tex2;    void main( void )    {    gl_Position = perspective * view * vec4(a_position,1.0);    normal = a_normal;    tex1 = vec3(a_texCoord,0.0);    tex2 = normalize(a_position)*0.5+0.5;    }</script>

著色器中,已經沒有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 < targets.length; i++) {    gl.texImage2D(targets[i], 0, gl.RGBA, fboBuffer.width, fboBuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);  }  gl.bindFramebuffer(gl.FRAMEBUFFER, null);}function RenderFBO() {  gl.disable(gl.DEPTH_TEST);  gl.viewport(0, 0, fboBuffer.width, fboBuffer.height);  gl.clearColor(0.0, 0.0, 0.0, 1.0);  gl.bindFramebuffer(gl.FRAMEBUFFER, fboBuffer);  for (var i = 0; i < targets.length; i++) {    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, targets[i], cubeTexID, null);    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);  }  mat4.perspective(pMatrix, 45, fboBuffer.width / fboBuffer.height, 0.1, 100.0);  for (var i = 0; i < targets.length; i++) {    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, targets[i], cubeTexID, null);    var lookat = vec3.create();    var up = vec3.create();    up[1] = 1.0;    if (i == 0) {      lookat[0] = -1.0;    } else if (i == 1) {      lookat[0] = 1.0;          } else if (i == 2) {      lookat[1] = -1.0;      up[0] = 1.0;    } else if (i == 3) {      lookat[1] = 1.0;      up[0] = 1.0;    } else if (i == 4) {      lookat[2] == -1.0;          } else if (i == 5) {      lookat[2] = 1.0;          } else {         }    //vec3.fromValues(0.0, 0.0, 0.0)    vMatrix = mat4.create();    mat4.lookAt(vMatrix, vec3.fromValues(0.0, 0.0, 0.0), lookat, up);    //mat4.scale(vMatrix, vMatrix, vec3.fromValues(-1.0, -1.0, -1.0));    //mat4.translate(vMatrix, vMatrix, spherePos);        RenderCube();  }  gl.bindFramebuffer(gl.FRAMEBUFFER, null);  gl.enable(gl.DEPTH_TEST);}

在上面不知是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 < rings; y++) {    var phi = (y / (rings - 1)) * Math.PI;    for (var x = 0; x < segments; x++) {      var theta = (x / (segments - 1)) * 2 * Math.PI;      sphereData.push(radius * Math.sin(phi) * Math.cos(theta));      sphereData.push(radius * Math.cos(phi));      sphereData.push(radius * Math.sin(phi) * Math.sin(theta));      sphereData.push(Math.sin(phi) * Math.cos(theta));      sphereData.push(radius * Math.cos(phi))      sphereData.push(Math.sin(phi) * Math.sin(theta));    }  }  for (var y = 0; y < rings - 1; y++) {    for (var x = 0; x < segments - 1; x++) {      sphereIndex.push((y + 0) * segments + x);      sphereIndex.push((y + 1) * segments + x);      sphereIndex.push((y + 1) * segments + x + 1);      sphereIndex.push((y + 1) * segments + x + 1);      sphereIndex.push((y + 0) * segments + x + 1)      sphereIndex.push((y + 0) * segments + x);    }  }  sphereVBO = gl.createBuffer();  gl.bindBuffer(gl.ARRAY_BUFFER, sphereVBO);  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereData), gl.STATIC_DRAW);  sphereVBO.numItems = segments * rings;  sphereEBO = gl.createBuffer();  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereEBO);  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(sphereIndex), gl.STATIC_DRAW);  sphereEBO.numItems = sphereIndex.length;}function RenderSphere() {  gl.useProgram(glSphereProgram);  gl.bindBuffer(gl.ARRAY_BUFFER, sphereVBO);  gl.vertexAttribPointer(glSphereProgram.positionAttribute, 3, gl.FLOAT, false, 24, 0);  gl.enableVertexAttribArray(glSphereProgram.positionAttribute);  gl.vertexAttribPointer(glSphereProgram.normalAttribute, 3, gl.FLOAT, false, 24, 12);  gl.enableVertexAttribArray(glSphereProgram.normalAttribute);  var mMatrix = mat4.create();  mat4.translate(mMatrix, mMatrix, spherePos);  gl.uniform3f(glSphereProgram.eye, eyePos[0],eyePos[1],eyePos[2]);  gl.uniformMatrix4fv(glSphereProgram.model, false, mMatrix);  gl.uniformMatrix4fv(glSphereProgram.view, false, vMatrix);  gl.uniformMatrix4fv(glSphereProgram.perspective, false, pMatrix);  gl.activeTexture(gl.TEXTURE0);  gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexID);  //gl.uniformMatrix4fv(glSphereProgram.mapCube, 0);  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereEBO);  gl.drawElements(gl.TRIANGLES, sphereEBO.numItems, gl.UNSIGNED_SHORT, 0);  gl.bindTexture(gl.TEXTURE_2D, null);}

可以看到,也是和立方體一樣的三步,初始化著色器,初始化頂點與法線,繪畫。下面給出著色器代碼:

球體著色器:

<script id="sphereshader-fs" type="x-shader/x-fragment">    precision mediump float;    varying vec3 normal;    varying vec3 eyevec;    uniform samplerCube mapCube;    void main( void )    {    gl_FragColor = textureCube(mapCube, reflect(normalize(-eyevec), normalize(normal)));    }</script><script id="sphereshader-vs" type="x-shader/x-vertex">    attribute vec3 a_position;    attribute vec3 a_normal;    uniform mat4 model;    uniform mat4 view;    uniform mat4 perspective;    uniform vec3 eye;    varying vec3 normal;    varying vec3 eyevec;    void main( void )    {    gl_Position = perspective * view * model * vec4(a_position,1.0);    eyevec = -eye;// a_position.xyz;    normal = a_normal;    }</script>

和前面立方體有點不同的是,球體有自己的模型矩陣,這也是一般正常的用法,然后傳遞眼睛對應球體頂點矢量與法線傳遞在片斷著色器中,在片斷著色器中,就有用到前面所生成的立方體紋理,我們根據眼睛經過頂點通過對應法向量反射到立體體紋理上的點來獲取當前球體所對應的環境顏色,在這里,我們可以直接調用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程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 石门县| 汤阴县| 库车县| 桦甸市| 永安市| 理塘县| 扶风县| 洛川县| 福泉市| 名山县| 长沙县| 高青县| 建瓯市| 黄大仙区| 蓝田县| 沐川县| 怀柔区| 芜湖县| 天长市| 萨迦县| 元阳县| 云阳县| 什邡市| 尼木县| 精河县| 南昌县| 南平市| 海晏县| 蒲江县| 丰城市| 原阳县| 昌江| 应城市| 郴州市| 云南省| 湟源县| 郸城县| 竹山县| 阳泉市| 右玉县| 阿拉尔市|