[webGL学习]基于three.js构建WebGL实例第六讲

这里写图片描述

今天我们继续webGL的课程。 今天我们开始另一个主题,我们将使用sprites和纹理动画。 如果你不知道,sprites只是图像,可以附加到对象上。 这些sprites图像总是与我们的相机正交。 Three.js为sprites - THREE.SpriteMaterial提供了一个库,以及一个特殊的一些方法 - THREE.Sprite。 在本教程中,我们还将学习如何使用sprites播放动画。

#创建主要的webGL 场景

首先我们创建一个script.js文件,代码如下

var lesson8 = {
  scene: null,
  camera: null,
  renderer: null,
  container: null,
  controls: null,
  clock: null,
  stats: null,
  anim1: null, anim2: null, // animations
  animReady1: false, animReady2: false,

  init: function() { // initialization

    // create main scene
    this.scene = new THREE.Scene();
    this.scene.fog = new THREE.FogExp2(0xcce0ff, 0.0003);

    var SCREEN_WIDTH = window.innerWidth,
        SCREEN_HEIGHT = window.innerHeight;

    // prepare perspective camera
    var VIEW_ANGLE = 60, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 1000;
    this.camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    this.scene.add(this.camera);
    this.camera.position.set(100, 0, 0);
    this.camera.lookAt(new THREE.Vector3(0,0,0));

    // prepare webgl renderer
    this.renderer = new THREE.WebGLRenderer({ antialias:true });
    this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    this.renderer.setClearColor(this.scene.fog.color);
    this.renderer.shadowMapEnabled = true;
    this.renderer.shadowMapSoft = true;

    // prepare container
    this.container = document.createElement('div');
    document.body.appendChild(this.container);
    this.container.appendChild(this.renderer.domElement);

    // events
    THREEx.WindowResize(this.renderer, this.camera);

    // prepare controls (OrbitControls)
    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
    this.controls.target = new THREE.Vector3(0, 0, 0);
    this.controls.maxDistance = 3000;

    // prepare clock
    this.clock = new THREE.Clock();

    // prepare stats
    this.stats = new Stats();
    this.stats.domElement.style.position = 'absolute';
    this.stats.domElement.style.left = '50px';
    this.stats.domElement.style.bottom = '50px';
    this.stats.domElement.style.zIndex = 1;
    this.container.appendChild( this.stats.domElement );

    // add lights
    this.scene.add( new THREE.AmbientLight(0x606060) );

    var dirLight = new THREE.DirectionalLight(0xffffff);
    dirLight.position.set(200, 200, 1000).normalize();
    this.camera.add(dirLight);
    this.camera.add(dirLight.target);

    // display skybox
    this.addSkybox();

    // display animated objects
    this.addAnimatedObjects();
  },
  addSkybox: function() {
      // define path and box sides images
      var path = 'skybox/';
      var sides = [ path + 'sbox_px.jpg', path + 'sbox_nx.jpg', path + 'sbox_py.jpg', path + 'sbox_ny.jpg', path + 'sbox_pz.jpg', path + 'sbox_nz.jpg' ];

      // load images
      var scCube = THREE.ImageUtils.loadTextureCube(sides);
      scCube.format = THREE.RGBFormat;

      // prepare skybox material (shader)
      var skyShader = THREE.ShaderLib["cube"];
      skyShader.uniforms["tCube"].value = scCube;
      var skyMaterial = new THREE.ShaderMaterial( {
        fragmentShader: skyShader.fragmentShader, vertexShader: skyShader.vertexShader,
        uniforms: skyShader.uniforms, depthWrite: false, side: THREE.BackSide
      });

      // create Mesh with cube geometry and add to the scene
      var skyBox = new THREE.Mesh(new THREE.BoxGeometry(500, 500, 500), skyMaterial);
      skyMaterial.needsUpdate = true;

      this.scene.add(skyBox);
  }
};

// animate the scene
function animate() {
  requestAnimationFrame(animate);
  render();
  update();
}

// update controls and stats
function update() {
  var delta = lesson8.clock.getDelta();

  lesson8.controls.update(delta);
  lesson8.stats.update();
}

// Render the scene
function render() {
  if (lesson8.renderer) {
    lesson8.renderer.render(lesson8.scene, lesson8.camera);
  }
}

// Initialize lesson on page load
function initializeLesson() {
  lesson8.init();
  animate();
}

if (window.addEventListener)
  window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
  window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;

这段代码创建了渲染器,摄像头,控制装置,照明,统计数据和天空盒一个基本的场景。类似的代码你已经在前面前面的课程中看到的.

##Sprites

如前所述,子画面是与我们的相机正交(垂直)的(二维)图像。 现在让我们使用以下函数将sprites添加到场景中:

addAnimatedObjects: function() {

  var texture1 = new THREE.ImageUtils.loadTexture('images/sprite1.png', undefined, function() {

    var material1 = new THREE.SpriteMaterial( { map: texture1, useScreenCoordinates: false, side:THREE.DoubleSide, transparent: true } );
    var mesh1 = new THREE.Sprite(material1);
    mesh1.position.set(0, 0, -40);
    mesh1.scale.set(64, 64, 1.0);
    lesson8.scene.add(mesh1);

  });

  var texture2 = new THREE.ImageUtils.loadTexture('images/sprite2.png', undefined, function() {

    var material2 = new THREE.SpriteMaterial( { map: texture2, useScreenCoordinates: false, transparent: true } );
    var mesh2 = new THREE.Sprite(material2);
    mesh2.position.set(0, 0, 40);
    mesh2.scale.set(24, 46, 1.0);
    lesson8.scene.add(mesh2);

  });
}

此代码加载两个图片(sprite1.png和sprite2.png)。 在加载这两个图像之后,我们创建两个sprite材质和Sprite对象,并将它们添加到我们的场景中。 如果现在运行代码,您将在我们的场景中看到两个二维图像。 你可能已经注意到,图像是按照原样绘制 - 我们看到很多小图像(瓷砖) - 这些图像文件被拍摄,因为我们将使用这些瓷砖做动画的事实。

##Texture Animation

function TileTextureAnimator(texture, hTiles, vTiles, durationTile) {

  // current tile number
  this.currentTile = 0;

  // duration of every tile
  this.durationTile = durationTile;

  // internal time counter
  this.currentTime = 0;

  // amount of horizontal and vertical tiles, and total count of tiles
  this.hTiles = hTiles;
  this.vTiles = vTiles;
  this.cntTiles = this.hTiles * this.vTiles;

  texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  texture.repeat.set(1 / this.hTiles, 1 / this.vTiles);

  this.update = function(time) {
    this.currentTime += time;

    while (this.currentTime > this.durationTile) {
      this.currentTime -= this.durationTile;
      this.currentTile++;

      if (this.currentTile == this.cntTiles) {
        this.currentTile = 0;
      }

      var iColumn = this.currentTile % this.hTiles;
      texture.offset.x = iColumn / this.hTiles;
      var iRow = Math.floor(this.currentTile / this.hTiles);
      texture.offset.y = iRow / this.vTiles;
    }
  };
}

“TileTextureAnimator”函数调整原始图像以显示动画。 它在从第一个到最后一个图像的图像的图像之间切换。 这是在指定的时间间隔。 每个瓦片在一定的持续时间内可见,在它转向另一个瓦片之后。 现在让我们更新我们之前添加的’addAnimatedObjects’函数:

addAnimatedObjects: function() {

  var texture1 = new THREE.ImageUtils.loadTexture('images/sprite1.png', undefined, function() {
    lesson8.anim1 = new TileTextureAnimator(texture1, 8, 8, 100);
    var material1 = new THREE.SpriteMaterial( { map: texture1, useScreenCoordinates: false, side:THREE.DoubleSide, transparent: true } );
    var mesh1 = new THREE.Sprite(material1);
    mesh1.position.set(0, 0, -40);
    mesh1.scale.set(64, 64, 1.0);
    lesson8.scene.add(mesh1);
    lesson8.animReady1 = true;
  });

  var texture2 = new THREE.ImageUtils.loadTexture('images/sprite2.png', undefined, function() {
    lesson8.anim2 = new TileTextureAnimator(texture2, 9, 8, 100);
    var material2 = new THREE.SpriteMaterial( { map: texture2, useScreenCoordinates: false, transparent: true } );
    var mesh2 = new THREE.Sprite(material2);
    mesh2.position.set(0, 0, 40);
    mesh2.scale.set(24, 46, 1.0);
    lesson8.scene.add(mesh2);
    lesson8.animReady2 = true;
  });
}

第一个sprite图像在行中包含8个图块,总共8个行,第二个图像包含行中的9个图块。 每个图块将可见100ms。 最后,在main’update’函数中,我们需要放置以下代码:

if (lesson8.animReady1) {
  lesson8.anim1.update(1000 * delta);
}
if (lesson8.animReady2) {
  lesson8.anim2.update(1000 * delta);  
}

###结束
未完继续
源码下载请关注我的微信公众号
这里写图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图解AI

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值